xref: /dragonfly/contrib/xz/src/xz/options.c (revision e0eb7cf0)
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 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 ///
59 static void
60 parse_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 
146 enum {
147 	OPT_DIST,
148 };
149 
150 
151 static void
152 set_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 
164 extern lzma_options_delta *
165 options_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 
190 enum {
191 	OPT_START_OFFSET,
192 };
193 
194 
195 static void
196 set_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 
208 extern lzma_options_bcj *
209 options_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 
231 enum {
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 
244 static void lzma_attribute((__noreturn__))
245 error_lzma_preset(const char *valuestr)
246 {
247 	message_fatal(_("Unsupported LZMA1/LZMA2 preset: %s"), valuestr);
248 }
249 
250 
251 static void
252 set_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 = (uint32_t)(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 
316 extern lzma_options_lzma *
317 options_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