Build: Comment that elf_aux_info(3) will be available on OpenBSD >= 7.6
[xz.git] / src / xz / options.c
blobbc8bc1a6c36c53304d12c3db13781480943fd778
1 // SPDX-License-Identifier: 0BSD
3 ///////////////////////////////////////////////////////////////////////////////
4 //
5 /// \file options.c
6 /// \brief Parser for filter-specific options
7 //
8 // Author: Lasse Collin
9 //
10 ///////////////////////////////////////////////////////////////////////////////
12 #include "private.h"
15 ///////////////////
16 // Generic stuff //
17 ///////////////////
19 typedef struct {
20 const char *name;
21 uint64_t id;
22 } name_id_map;
25 typedef struct {
26 const char *name;
27 const name_id_map *map;
28 uint64_t min;
29 uint64_t max;
30 } option_map;
33 /// Parses option=value pairs that are separated with commas:
34 /// opt=val,opt=val,opt=val
35 ///
36 /// Each option is a string, that is converted to an integer using the
37 /// index where the option string is in the array.
38 ///
39 /// Value can be
40 /// - a string-id map mapping a list of possible string values to integers
41 /// (opts[i].map != NULL, opts[i].min and opts[i].max are ignored);
42 /// - a number with minimum and maximum value limit
43 /// (opts[i].map == NULL && opts[i].min != UINT64_MAX);
44 /// - a string that will be parsed by the filter-specific code
45 /// (opts[i].map == NULL && opts[i].min == UINT64_MAX, opts[i].max ignored)
46 ///
47 /// When parsing both option and value succeed, a filter-specific function
48 /// is called, which should update the given value to filter-specific
49 /// options structure.
50 ///
51 /// This returns only if no errors occur.
52 ///
53 /// \param str String containing the options from the command line
54 /// \param opts Filter-specific option map
55 /// \param set Filter-specific function to update filter_options
56 /// \param filter_options Pointer to filter-specific options structure
57 ///
58 static void
59 parse_options(const char *str, const option_map *opts,
60 void (*set)(void *filter_options,
61 unsigned key, uint64_t value, const char *valuestr),
62 void *filter_options)
64 if (str == NULL || str[0] == '\0')
65 return;
67 char *s = xstrdup(str);
68 char *name = s;
70 while (*name != '\0') {
71 if (*name == ',') {
72 ++name;
73 continue;
76 char *split = strchr(name, ',');
77 if (split != NULL)
78 *split = '\0';
80 char *value = strchr(name, '=');
81 if (value != NULL)
82 *value++ = '\0';
84 if (value == NULL || value[0] == '\0')
85 message_fatal(_("%s: Options must be 'name=value' "
86 "pairs separated with commas"), str);
88 // Look for the option name from the option map.
89 unsigned i = 0;
90 while (true) {
91 if (opts[i].name == NULL)
92 message_fatal(_("%s: Invalid option name"),
93 name);
95 if (strcmp(name, opts[i].name) == 0)
96 break;
98 ++i;
101 // Option was found from the map. See how we should handle it.
102 if (opts[i].map != NULL) {
103 // value is a string which we should map
104 // to an integer.
105 unsigned j;
106 for (j = 0; opts[i].map[j].name != NULL; ++j) {
107 if (strcmp(opts[i].map[j].name, value) == 0)
108 break;
111 if (opts[i].map[j].name == NULL)
112 message_fatal(_("%s: Invalid option value"),
113 value);
115 set(filter_options, i, opts[i].map[j].id, value);
117 } else if (opts[i].min == UINT64_MAX) {
118 // value is a special string that will be
119 // parsed by set().
120 set(filter_options, i, 0, value);
122 } else {
123 // value is an integer.
124 const uint64_t v = str_to_uint64(name, value,
125 opts[i].min, opts[i].max);
126 set(filter_options, i, v, value);
129 // Check if it was the last option.
130 if (split == NULL)
131 break;
133 name = split + 1;
136 free(s);
137 return;
141 ///////////
142 // Delta //
143 ///////////
145 enum {
146 OPT_DIST,
150 static void
151 set_delta(void *options, unsigned key, uint64_t value,
152 const char *valuestr lzma_attribute((__unused__)))
154 lzma_options_delta *opt = options;
155 switch (key) {
156 case OPT_DIST:
157 opt->dist = value;
158 break;
163 extern lzma_options_delta *
164 options_delta(const char *str)
166 static const option_map opts[] = {
167 { "dist", NULL, LZMA_DELTA_DIST_MIN,
168 LZMA_DELTA_DIST_MAX },
169 { NULL, NULL, 0, 0 }
172 lzma_options_delta *options = xmalloc(sizeof(lzma_options_delta));
173 *options = (lzma_options_delta){
174 // It's hard to give a useful default for this.
175 .type = LZMA_DELTA_TYPE_BYTE,
176 .dist = LZMA_DELTA_DIST_MIN,
179 parse_options(str, opts, &set_delta, options);
181 return options;
185 /////////
186 // BCJ //
187 /////////
189 enum {
190 OPT_START_OFFSET,
194 static void
195 set_bcj(void *options, unsigned key, uint64_t value,
196 const char *valuestr lzma_attribute((__unused__)))
198 lzma_options_bcj *opt = options;
199 switch (key) {
200 case OPT_START_OFFSET:
201 opt->start_offset = value;
202 break;
207 extern lzma_options_bcj *
208 options_bcj(const char *str)
210 static const option_map opts[] = {
211 { "start", NULL, 0, UINT32_MAX },
212 { NULL, NULL, 0, 0 }
215 lzma_options_bcj *options = xmalloc(sizeof(lzma_options_bcj));
216 *options = (lzma_options_bcj){
217 .start_offset = 0,
220 parse_options(str, opts, &set_bcj, options);
222 return options;
226 //////////
227 // LZMA //
228 //////////
230 enum {
231 OPT_PRESET,
232 OPT_DICT,
233 OPT_LC,
234 OPT_LP,
235 OPT_PB,
236 OPT_MODE,
237 OPT_NICE,
238 OPT_MF,
239 OPT_DEPTH,
243 tuklib_attr_noreturn
244 static void
245 error_lzma_preset(const char *valuestr)
247 message_fatal(_("Unsupported LZMA1/LZMA2 preset: %s"), valuestr);
251 static void
252 set_lzma(void *options, unsigned key, uint64_t value, const char *valuestr)
254 lzma_options_lzma *opt = options;
256 switch (key) {
257 case OPT_PRESET: {
258 if (valuestr[0] < '0' || valuestr[0] > '9')
259 error_lzma_preset(valuestr);
261 uint32_t preset = (uint32_t)(valuestr[0] - '0');
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);
271 if (valuestr[2] != '\0')
272 error_lzma_preset(valuestr);
275 if (lzma_lzma_preset(options, preset))
276 error_lzma_preset(valuestr);
278 break;
281 case OPT_DICT:
282 opt->dict_size = value;
283 break;
285 case OPT_LC:
286 opt->lc = value;
287 break;
289 case OPT_LP:
290 opt->lp = value;
291 break;
293 case OPT_PB:
294 opt->pb = value;
295 break;
297 case OPT_MODE:
298 opt->mode = value;
299 break;
301 case OPT_NICE:
302 opt->nice_len = value;
303 break;
305 case OPT_MF:
306 opt->mf = value;
307 break;
309 case OPT_DEPTH:
310 opt->depth = value;
311 break;
316 extern lzma_options_lzma *
317 options_lzma(const char *str)
319 static const name_id_map modes[] = {
320 { "fast", LZMA_MODE_FAST },
321 { "normal", LZMA_MODE_NORMAL },
322 { NULL, 0 }
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 }
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 }
348 lzma_options_lzma *options = xmalloc(sizeof(lzma_options_lzma));
349 if (lzma_lzma_preset(options, LZMA_PRESET_DEFAULT))
350 message_bug();
352 parse_options(str, opts, &set_lzma, options);
354 if (options->lc + options->lp > LZMA_LCLP_MAX)
355 message_fatal(_("The sum of lc and lp must not exceed 4"));
357 return options;