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 |
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 |
// Delta // |
144 |
/////////// |
145 |
|
146 |
enum { |
147 |
OPT_DIST, |
148 |
}; |
149 |
|
150 |
|
151 |
static void |
152 |
set_delta(void *options, uint32_t 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, uint32_t 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, uint32_t 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 |
|
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 |
} |