1207753Smm///////////////////////////////////////////////////////////////////////////////
2207753Smm//
3207753Smm/// \file       options.c
4207753Smm/// \brief      Parser for filter-specific options
5207753Smm//
6207753Smm//  Author:     Lasse Collin
7207753Smm//
8207753Smm//  This file has been put into the public domain.
9207753Smm//  You can do whatever you want with this file.
10207753Smm//
11207753Smm///////////////////////////////////////////////////////////////////////////////
12207753Smm
13207753Smm#include "private.h"
14207753Smm
15207753Smm
16207753Smm///////////////////
17207753Smm// Generic stuff //
18207753Smm///////////////////
19207753Smm
20207753Smmtypedef struct {
21207753Smm	const char *name;
22207753Smm	uint64_t id;
23207753Smm} name_id_map;
24207753Smm
25207753Smm
26207753Smmtypedef struct {
27207753Smm	const char *name;
28207753Smm	const name_id_map *map;
29207753Smm	uint64_t min;
30207753Smm	uint64_t max;
31207753Smm} option_map;
32207753Smm
33207753Smm
34292588Sdelphij/// Parses option=value pairs that are separated with commas:
35292588Sdelphij/// opt=val,opt=val,opt=val
36207753Smm///
37207753Smm/// Each option is a string, that is converted to an integer using the
38207753Smm/// index where the option string is in the array.
39207753Smm///
40207753Smm/// Value can be
41207753Smm///  - a string-id map mapping a list of possible string values to integers
42207753Smm///    (opts[i].map != NULL, opts[i].min and opts[i].max are ignored);
43207753Smm///  - a number with minimum and maximum value limit
44207753Smm///    (opts[i].map == NULL && opts[i].min != UINT64_MAX);
45207753Smm///  - a string that will be parsed by the filter-specific code
46207753Smm///    (opts[i].map == NULL && opts[i].min == UINT64_MAX, opts[i].max ignored)
47207753Smm///
48207753Smm/// When parsing both option and value succeed, a filter-specific function
49207753Smm/// is called, which should update the given value to filter-specific
50207753Smm/// options structure.
51207753Smm///
52207753Smm/// \param      str     String containing the options from the command line
53207753Smm/// \param      opts    Filter-specific option map
54207753Smm/// \param      set     Filter-specific function to update filter_options
55207753Smm/// \param      filter_options  Pointer to filter-specific options structure
56207753Smm///
57207753Smm/// \return     Returns only if no errors occur.
58207753Smm///
59207753Smmstatic void
60207753Smmparse_options(const char *str, const option_map *opts,
61207753Smm		void (*set)(void *filter_options,
62292588Sdelphij			unsigned key, uint64_t value, const char *valuestr),
63207753Smm		void *filter_options)
64207753Smm{
65207753Smm	if (str == NULL || str[0] == '\0')
66207753Smm		return;
67207753Smm
68207753Smm	char *s = xstrdup(str);
69207753Smm	char *name = s;
70207753Smm
71207753Smm	while (*name != '\0') {
72207753Smm		if (*name == ',') {
73207753Smm			++name;
74207753Smm			continue;
75207753Smm		}
76207753Smm
77207753Smm		char *split = strchr(name, ',');
78207753Smm		if (split != NULL)
79207753Smm			*split = '\0';
80207753Smm
81207753Smm		char *value = strchr(name, '=');
82207753Smm		if (value != NULL)
83207753Smm			*value++ = '\0';
84207753Smm
85207753Smm		if (value == NULL || value[0] == '\0')
86207753Smm			message_fatal(_("%s: Options must be `name=value' "
87207753Smm					"pairs separated with commas"), str);
88207753Smm
89207753Smm		// Look for the option name from the option map.
90292588Sdelphij		unsigned i = 0;
91207753Smm		while (true) {
92207753Smm			if (opts[i].name == NULL)
93207753Smm				message_fatal(_("%s: Invalid option name"),
94207753Smm						name);
95207753Smm
96207753Smm			if (strcmp(name, opts[i].name) == 0)
97207753Smm				break;
98207753Smm
99207753Smm			++i;
100207753Smm		}
101207753Smm
102207753Smm		// Option was found from the map. See how we should handle it.
103207753Smm		if (opts[i].map != NULL) {
104207753Smm			// value is a string which we should map
105207753Smm			// to an integer.
106292588Sdelphij			unsigned j;
107207753Smm			for (j = 0; opts[i].map[j].name != NULL; ++j) {
108207753Smm				if (strcmp(opts[i].map[j].name, value) == 0)
109207753Smm					break;
110207753Smm			}
111207753Smm
112207753Smm			if (opts[i].map[j].name == NULL)
113207753Smm				message_fatal(_("%s: Invalid option value"),
114207753Smm						value);
115207753Smm
116207753Smm			set(filter_options, i, opts[i].map[j].id, value);
117207753Smm
118207753Smm		} else if (opts[i].min == UINT64_MAX) {
119207753Smm			// value is a special string that will be
120207753Smm			// parsed by set().
121207753Smm			set(filter_options, i, 0, value);
122207753Smm
123207753Smm		} else {
124207753Smm			// value is an integer.
125207753Smm			const uint64_t v = str_to_uint64(name, value,
126207753Smm					opts[i].min, opts[i].max);
127207753Smm			set(filter_options, i, v, value);
128207753Smm		}
129207753Smm
130207753Smm		// Check if it was the last option.
131207753Smm		if (split == NULL)
132207753Smm			break;
133207753Smm
134207753Smm		name = split + 1;
135207753Smm	}
136207753Smm
137207753Smm	free(s);
138207753Smm	return;
139207753Smm}
140207753Smm
141207753Smm
142207753Smm///////////
143207753Smm// Delta //
144207753Smm///////////
145207753Smm
146207753Smmenum {
147207753Smm	OPT_DIST,
148207753Smm};
149207753Smm
150207753Smm
151207753Smmstatic void
152292588Sdelphijset_delta(void *options, unsigned key, uint64_t value,
153223935Smm		const char *valuestr lzma_attribute((__unused__)))
154207753Smm{
155207753Smm	lzma_options_delta *opt = options;
156207753Smm	switch (key) {
157207753Smm	case OPT_DIST:
158207753Smm		opt->dist = value;
159207753Smm		break;
160207753Smm	}
161207753Smm}
162207753Smm
163207753Smm
164207753Smmextern lzma_options_delta *
165207753Smmoptions_delta(const char *str)
166207753Smm{
167207753Smm	static const option_map opts[] = {
168207753Smm		{ "dist",     NULL,  LZMA_DELTA_DIST_MIN,
169207753Smm		                     LZMA_DELTA_DIST_MAX },
170207753Smm		{ NULL,       NULL,  0, 0 }
171207753Smm	};
172207753Smm
173207753Smm	lzma_options_delta *options = xmalloc(sizeof(lzma_options_delta));
174207753Smm	*options = (lzma_options_delta){
175207753Smm		// It's hard to give a useful default for this.
176207753Smm		.type = LZMA_DELTA_TYPE_BYTE,
177207753Smm		.dist = LZMA_DELTA_DIST_MIN,
178207753Smm	};
179207753Smm
180207753Smm	parse_options(str, opts, &set_delta, options);
181207753Smm
182207753Smm	return options;
183207753Smm}
184207753Smm
185207753Smm
186207753Smm/////////
187207753Smm// BCJ //
188207753Smm/////////
189207753Smm
190207753Smmenum {
191207753Smm	OPT_START_OFFSET,
192207753Smm};
193207753Smm
194207753Smm
195207753Smmstatic void
196292588Sdelphijset_bcj(void *options, unsigned key, uint64_t value,
197223935Smm		const char *valuestr lzma_attribute((__unused__)))
198207753Smm{
199207753Smm	lzma_options_bcj *opt = options;
200207753Smm	switch (key) {
201207753Smm	case OPT_START_OFFSET:
202207753Smm		opt->start_offset = value;
203207753Smm		break;
204207753Smm	}
205207753Smm}
206207753Smm
207207753Smm
208207753Smmextern lzma_options_bcj *
209207753Smmoptions_bcj(const char *str)
210207753Smm{
211207753Smm	static const option_map opts[] = {
212207753Smm		{ "start",    NULL,  0, UINT32_MAX },
213207753Smm		{ NULL,       NULL,  0, 0 }
214207753Smm	};
215207753Smm
216207753Smm	lzma_options_bcj *options = xmalloc(sizeof(lzma_options_bcj));
217207753Smm	*options = (lzma_options_bcj){
218207753Smm		.start_offset = 0,
219207753Smm	};
220207753Smm
221207753Smm	parse_options(str, opts, &set_bcj, options);
222207753Smm
223207753Smm	return options;
224207753Smm}
225207753Smm
226207753Smm
227207753Smm//////////
228207753Smm// LZMA //
229207753Smm//////////
230207753Smm
231207753Smmenum {
232207753Smm	OPT_PRESET,
233207753Smm	OPT_DICT,
234207753Smm	OPT_LC,
235207753Smm	OPT_LP,
236207753Smm	OPT_PB,
237207753Smm	OPT_MODE,
238207753Smm	OPT_NICE,
239207753Smm	OPT_MF,
240207753Smm	OPT_DEPTH,
241207753Smm};
242207753Smm
243207753Smm
244223935Smmstatic void lzma_attribute((__noreturn__))
245207753Smmerror_lzma_preset(const char *valuestr)
246207753Smm{
247207753Smm	message_fatal(_("Unsupported LZMA1/LZMA2 preset: %s"), valuestr);
248207753Smm}
249207753Smm
250207753Smm
251207753Smmstatic void
252292588Sdelphijset_lzma(void *options, unsigned key, uint64_t value, const char *valuestr)
253207753Smm{
254207753Smm	lzma_options_lzma *opt = options;
255207753Smm
256207753Smm	switch (key) {
257207753Smm	case OPT_PRESET: {
258207753Smm		if (valuestr[0] < '0' || valuestr[0] > '9')
259207753Smm			error_lzma_preset(valuestr);
260207753Smm
261207753Smm		uint32_t preset = valuestr[0] - '0';
262207753Smm
263207753Smm		// Currently only "e" is supported as a modifier,
264207753Smm		// so keep this simple for now.
265207753Smm		if (valuestr[1] != '\0') {
266207753Smm			if (valuestr[1] == 'e')
267207753Smm				preset |= LZMA_PRESET_EXTREME;
268207753Smm			else
269207753Smm				error_lzma_preset(valuestr);
270207753Smm
271207753Smm			if (valuestr[2] != '\0')
272207753Smm				error_lzma_preset(valuestr);
273207753Smm		}
274207753Smm
275207753Smm		if (lzma_lzma_preset(options, preset))
276207753Smm			error_lzma_preset(valuestr);
277207753Smm
278207753Smm		break;
279207753Smm	}
280207753Smm
281207753Smm	case OPT_DICT:
282207753Smm		opt->dict_size = value;
283207753Smm		break;
284207753Smm
285207753Smm	case OPT_LC:
286207753Smm		opt->lc = value;
287207753Smm		break;
288207753Smm
289207753Smm	case OPT_LP:
290207753Smm		opt->lp = value;
291207753Smm		break;
292207753Smm
293207753Smm	case OPT_PB:
294207753Smm		opt->pb = value;
295207753Smm		break;
296207753Smm
297207753Smm	case OPT_MODE:
298207753Smm		opt->mode = value;
299207753Smm		break;
300207753Smm
301207753Smm	case OPT_NICE:
302207753Smm		opt->nice_len = value;
303207753Smm		break;
304207753Smm
305207753Smm	case OPT_MF:
306207753Smm		opt->mf = value;
307207753Smm		break;
308207753Smm
309207753Smm	case OPT_DEPTH:
310207753Smm		opt->depth = value;
311207753Smm		break;
312207753Smm	}
313207753Smm}
314207753Smm
315207753Smm
316207753Smmextern lzma_options_lzma *
317207753Smmoptions_lzma(const char *str)
318207753Smm{
319207753Smm	static const name_id_map modes[] = {
320207753Smm		{ "fast",   LZMA_MODE_FAST },
321207753Smm		{ "normal", LZMA_MODE_NORMAL },
322207753Smm		{ NULL,     0 }
323207753Smm	};
324207753Smm
325207753Smm	static const name_id_map mfs[] = {
326207753Smm		{ "hc3", LZMA_MF_HC3 },
327207753Smm		{ "hc4", LZMA_MF_HC4 },
328207753Smm		{ "bt2", LZMA_MF_BT2 },
329207753Smm		{ "bt3", LZMA_MF_BT3 },
330207753Smm		{ "bt4", LZMA_MF_BT4 },
331207753Smm		{ NULL,  0 }
332207753Smm	};
333207753Smm
334207753Smm	static const option_map opts[] = {
335207753Smm		{ "preset", NULL,   UINT64_MAX, 0 },
336207753Smm		{ "dict",   NULL,   LZMA_DICT_SIZE_MIN,
337207753Smm				(UINT32_C(1) << 30) + (UINT32_C(1) << 29) },
338207753Smm		{ "lc",     NULL,   LZMA_LCLP_MIN, LZMA_LCLP_MAX },
339207753Smm		{ "lp",     NULL,   LZMA_LCLP_MIN, LZMA_LCLP_MAX },
340207753Smm		{ "pb",     NULL,   LZMA_PB_MIN, LZMA_PB_MAX },
341207753Smm		{ "mode",   modes,  0, 0 },
342207753Smm		{ "nice",   NULL,   2, 273 },
343207753Smm		{ "mf",     mfs,    0, 0 },
344207753Smm		{ "depth",  NULL,   0, UINT32_MAX },
345207753Smm		{ NULL,     NULL,   0, 0 }
346207753Smm	};
347207753Smm
348207753Smm	lzma_options_lzma *options = xmalloc(sizeof(lzma_options_lzma));
349213700Smm	if (lzma_lzma_preset(options, LZMA_PRESET_DEFAULT))
350213700Smm		message_bug();
351207753Smm
352207753Smm	parse_options(str, opts, &set_lzma, options);
353207753Smm
354207753Smm	if (options->lc + options->lp > LZMA_LCLP_MAX)
355213700Smm		message_fatal(_("The sum of lc and lp must not exceed 4"));
356207753Smm
357207753Smm	const uint32_t nice_len_min = options->mf & 0x0F;
358207753Smm	if (options->nice_len < nice_len_min)
359207753Smm		message_fatal(_("The selected match finder requires at "
360207753Smm				"least nice=%" PRIu32), nice_len_min);
361207753Smm
362207753Smm	return options;
363207753Smm}
364