xref: /freebsd/contrib/xz/src/xz/options.c (revision 3157ba21)
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 
20 typedef struct {
21 	const char *name;
22 	uint64_t id;
23 } name_id_map;
24 
25 
26 typedef 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 colons, semicolons,
35 /// or commas: opt=val: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 ///
59 static void
60 parse_options(const char *str, const option_map *opts,
61 		void (*set)(void *filter_options,
62 			uint32_t 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 		size_t 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 			size_t 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 // Subblock //
144 //////////////
145 
146 enum {
147 	OPT_SIZE,
148 	OPT_RLE,
149 	OPT_ALIGN,
150 };
151 
152 
153 static void
154 set_subblock(void *options, uint32_t key, uint64_t value,
155 		const char *valuestr lzma_attribute((unused)))
156 {
157 	lzma_options_subblock *opt = options;
158 
159 	switch (key) {
160 	case OPT_SIZE:
161 		opt->subblock_data_size = value;
162 		break;
163 
164 	case OPT_RLE:
165 		opt->rle = value;
166 		break;
167 
168 	case OPT_ALIGN:
169 		opt->alignment = value;
170 		break;
171 	}
172 }
173 
174 
175 extern lzma_options_subblock *
176 options_subblock(const char *str)
177 {
178 	static const option_map opts[] = {
179 		{ "size", NULL,   LZMA_SUBBLOCK_DATA_SIZE_MIN,
180 		                  LZMA_SUBBLOCK_DATA_SIZE_MAX },
181 		{ "rle",  NULL,   LZMA_SUBBLOCK_RLE_OFF,
182 		                  LZMA_SUBBLOCK_RLE_MAX },
183 		{ "align",NULL,   LZMA_SUBBLOCK_ALIGNMENT_MIN,
184 		                  LZMA_SUBBLOCK_ALIGNMENT_MAX },
185 		{ NULL,   NULL,   0, 0 }
186 	};
187 
188 	lzma_options_subblock *options
189 			= xmalloc(sizeof(lzma_options_subblock));
190 	*options = (lzma_options_subblock){
191 		.allow_subfilters = false,
192 		.alignment = LZMA_SUBBLOCK_ALIGNMENT_DEFAULT,
193 		.subblock_data_size = LZMA_SUBBLOCK_DATA_SIZE_DEFAULT,
194 		.rle = LZMA_SUBBLOCK_RLE_OFF,
195 	};
196 
197 	parse_options(str, opts, &set_subblock, options);
198 
199 	return options;
200 }
201 
202 
203 ///////////
204 // Delta //
205 ///////////
206 
207 enum {
208 	OPT_DIST,
209 };
210 
211 
212 static void
213 set_delta(void *options, uint32_t key, uint64_t value,
214 		const char *valuestr lzma_attribute((unused)))
215 {
216 	lzma_options_delta *opt = options;
217 	switch (key) {
218 	case OPT_DIST:
219 		opt->dist = value;
220 		break;
221 	}
222 }
223 
224 
225 extern lzma_options_delta *
226 options_delta(const char *str)
227 {
228 	static const option_map opts[] = {
229 		{ "dist",     NULL,  LZMA_DELTA_DIST_MIN,
230 		                     LZMA_DELTA_DIST_MAX },
231 		{ NULL,       NULL,  0, 0 }
232 	};
233 
234 	lzma_options_delta *options = xmalloc(sizeof(lzma_options_delta));
235 	*options = (lzma_options_delta){
236 		// It's hard to give a useful default for this.
237 		.type = LZMA_DELTA_TYPE_BYTE,
238 		.dist = LZMA_DELTA_DIST_MIN,
239 	};
240 
241 	parse_options(str, opts, &set_delta, options);
242 
243 	return options;
244 }
245 
246 
247 /////////
248 // BCJ //
249 /////////
250 
251 enum {
252 	OPT_START_OFFSET,
253 };
254 
255 
256 static void
257 set_bcj(void *options, uint32_t key, uint64_t value,
258 		const char *valuestr lzma_attribute((unused)))
259 {
260 	lzma_options_bcj *opt = options;
261 	switch (key) {
262 	case OPT_START_OFFSET:
263 		opt->start_offset = value;
264 		break;
265 	}
266 }
267 
268 
269 extern lzma_options_bcj *
270 options_bcj(const char *str)
271 {
272 	static const option_map opts[] = {
273 		{ "start",    NULL,  0, UINT32_MAX },
274 		{ NULL,       NULL,  0, 0 }
275 	};
276 
277 	lzma_options_bcj *options = xmalloc(sizeof(lzma_options_bcj));
278 	*options = (lzma_options_bcj){
279 		.start_offset = 0,
280 	};
281 
282 	parse_options(str, opts, &set_bcj, options);
283 
284 	return options;
285 }
286 
287 
288 //////////
289 // LZMA //
290 //////////
291 
292 enum {
293 	OPT_PRESET,
294 	OPT_DICT,
295 	OPT_LC,
296 	OPT_LP,
297 	OPT_PB,
298 	OPT_MODE,
299 	OPT_NICE,
300 	OPT_MF,
301 	OPT_DEPTH,
302 };
303 
304 
305 static void lzma_attribute((noreturn))
306 error_lzma_preset(const char *valuestr)
307 {
308 	message_fatal(_("Unsupported LZMA1/LZMA2 preset: %s"), valuestr);
309 }
310 
311 
312 static void
313 set_lzma(void *options, uint32_t key, uint64_t value, const char *valuestr)
314 {
315 	lzma_options_lzma *opt = options;
316 
317 	switch (key) {
318 	case OPT_PRESET: {
319 		if (valuestr[0] < '0' || valuestr[0] > '9')
320 			error_lzma_preset(valuestr);
321 
322 		uint32_t preset = valuestr[0] - '0';
323 
324 		// Currently only "e" is supported as a modifier,
325 		// so keep this simple for now.
326 		if (valuestr[1] != '\0') {
327 			if (valuestr[1] == 'e')
328 				preset |= LZMA_PRESET_EXTREME;
329 			else
330 				error_lzma_preset(valuestr);
331 
332 			if (valuestr[2] != '\0')
333 				error_lzma_preset(valuestr);
334 		}
335 
336 		if (lzma_lzma_preset(options, preset))
337 			error_lzma_preset(valuestr);
338 
339 		break;
340 	}
341 
342 	case OPT_DICT:
343 		opt->dict_size = value;
344 		break;
345 
346 	case OPT_LC:
347 		opt->lc = value;
348 		break;
349 
350 	case OPT_LP:
351 		opt->lp = value;
352 		break;
353 
354 	case OPT_PB:
355 		opt->pb = value;
356 		break;
357 
358 	case OPT_MODE:
359 		opt->mode = value;
360 		break;
361 
362 	case OPT_NICE:
363 		opt->nice_len = value;
364 		break;
365 
366 	case OPT_MF:
367 		opt->mf = value;
368 		break;
369 
370 	case OPT_DEPTH:
371 		opt->depth = value;
372 		break;
373 	}
374 }
375 
376 
377 extern lzma_options_lzma *
378 options_lzma(const char *str)
379 {
380 	static const name_id_map modes[] = {
381 		{ "fast",   LZMA_MODE_FAST },
382 		{ "normal", LZMA_MODE_NORMAL },
383 		{ NULL,     0 }
384 	};
385 
386 	static const name_id_map mfs[] = {
387 		{ "hc3", LZMA_MF_HC3 },
388 		{ "hc4", LZMA_MF_HC4 },
389 		{ "bt2", LZMA_MF_BT2 },
390 		{ "bt3", LZMA_MF_BT3 },
391 		{ "bt4", LZMA_MF_BT4 },
392 		{ NULL,  0 }
393 	};
394 
395 	static const option_map opts[] = {
396 		{ "preset", NULL,   UINT64_MAX, 0 },
397 		{ "dict",   NULL,   LZMA_DICT_SIZE_MIN,
398 				(UINT32_C(1) << 30) + (UINT32_C(1) << 29) },
399 		{ "lc",     NULL,   LZMA_LCLP_MIN, LZMA_LCLP_MAX },
400 		{ "lp",     NULL,   LZMA_LCLP_MIN, LZMA_LCLP_MAX },
401 		{ "pb",     NULL,   LZMA_PB_MIN, LZMA_PB_MAX },
402 		{ "mode",   modes,  0, 0 },
403 		{ "nice",   NULL,   2, 273 },
404 		{ "mf",     mfs,    0, 0 },
405 		{ "depth",  NULL,   0, UINT32_MAX },
406 		{ NULL,     NULL,   0, 0 }
407 	};
408 
409 	lzma_options_lzma *options = xmalloc(sizeof(lzma_options_lzma));
410 	*options = (lzma_options_lzma){
411 		.dict_size = LZMA_DICT_SIZE_DEFAULT,
412 		.preset_dict =  NULL,
413 		.preset_dict_size = 0,
414 		.lc = LZMA_LC_DEFAULT,
415 		.lp = LZMA_LP_DEFAULT,
416 		.pb = LZMA_PB_DEFAULT,
417 		.mode = LZMA_MODE_NORMAL,
418 		.nice_len = 64,
419 		.mf = LZMA_MF_BT4,
420 		.depth = 0,
421 	};
422 
423 	parse_options(str, opts, &set_lzma, options);
424 
425 	if (options->lc + options->lp > LZMA_LCLP_MAX)
426 		message_fatal(_("The sum of lc and lp must be at "
427 				"maximum of 4"));
428 
429 	const uint32_t nice_len_min = options->mf & 0x0F;
430 	if (options->nice_len < nice_len_min)
431 		message_fatal(_("The selected match finder requires at "
432 				"least nice=%" PRIu32), nice_len_min);
433 
434 	return options;
435 }
436