]> icculus.org git repositories - icculus/xz.git/blob - src/xz/options.c
Accept --lzma2=preset=6e where "e" is equivalent to --extreme
[icculus/xz.git] / src / xz / options.c
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 (true) {
72                 char *split = strchr(name, ',');
73                 if (split != NULL)
74                         *split = '\0';
75
76                 char *value = strchr(name, '=');
77                 if (value != NULL)
78                         *value++ = '\0';
79
80                 if (value == NULL || value[0] == '\0')
81                         message_fatal(_("%s: Options must be `name=value' "
82                                         "pairs separated with commas"), str);
83
84                 // Look for the option name from the option map.
85                 bool found = false;
86                 for (size_t i = 0; opts[i].name != NULL; ++i) {
87                         if (strcmp(name, opts[i].name) != 0)
88                                 continue;
89
90                         if (opts[i].map != NULL) {
91                                 // value is a string which we should map
92                                 // to an integer.
93                                 size_t j;
94                                 for (j = 0; opts[i].map[j].name != NULL; ++j) {
95                                         if (strcmp(opts[i].map[j].name, value)
96                                                         == 0)
97                                                 break;
98                                 }
99
100                                 if (opts[i].map[j].name == NULL)
101                                         message_fatal(_("%s: Invalid option "
102                                                         "value"), value);
103
104                                 set(filter_options, i, opts[i].map[j].id,
105                                                 value);
106
107                         } else if (opts[i].min == UINT64_MAX) {
108                                 // value is a special string that will be
109                                 // parsed by set().
110                                 set(filter_options, i, 0, value);
111
112                         } else {
113                                 // value is an integer.
114                                 const uint64_t v = str_to_uint64(name, value,
115                                                 opts[i].min, opts[i].max);
116                                 set(filter_options, i, v, value);
117                         }
118
119                         found = true;
120                         break;
121                 }
122
123                 if (!found)
124                         message_fatal(_("%s: Invalid option name"), name);
125
126                 if (split == NULL)
127                         break;
128
129                 name = split + 1;
130         }
131
132         free(s);
133         return;
134 }
135
136
137 //////////////
138 // Subblock //
139 //////////////
140
141 enum {
142         OPT_SIZE,
143         OPT_RLE,
144         OPT_ALIGN,
145 };
146
147
148 static void
149 set_subblock(void *options, uint32_t key, uint64_t value,
150                 const char *valuestr lzma_attribute((unused)))
151 {
152         lzma_options_subblock *opt = options;
153
154         switch (key) {
155         case OPT_SIZE:
156                 opt->subblock_data_size = value;
157                 break;
158
159         case OPT_RLE:
160                 opt->rle = value;
161                 break;
162
163         case OPT_ALIGN:
164                 opt->alignment = value;
165                 break;
166         }
167 }
168
169
170 extern lzma_options_subblock *
171 options_subblock(const char *str)
172 {
173         static const option_map opts[] = {
174                 { "size", NULL,   LZMA_SUBBLOCK_DATA_SIZE_MIN,
175                                   LZMA_SUBBLOCK_DATA_SIZE_MAX },
176                 { "rle",  NULL,   LZMA_SUBBLOCK_RLE_OFF,
177                                   LZMA_SUBBLOCK_RLE_MAX },
178                 { "align",NULL,   LZMA_SUBBLOCK_ALIGNMENT_MIN,
179                                   LZMA_SUBBLOCK_ALIGNMENT_MAX },
180                 { NULL,   NULL,   0, 0 }
181         };
182
183         lzma_options_subblock *options
184                         = xmalloc(sizeof(lzma_options_subblock));
185         *options = (lzma_options_subblock){
186                 .allow_subfilters = false,
187                 .alignment = LZMA_SUBBLOCK_ALIGNMENT_DEFAULT,
188                 .subblock_data_size = LZMA_SUBBLOCK_DATA_SIZE_DEFAULT,
189                 .rle = LZMA_SUBBLOCK_RLE_OFF,
190         };
191
192         parse_options(str, opts, &set_subblock, options);
193
194         return options;
195 }
196
197
198 ///////////
199 // Delta //
200 ///////////
201
202 enum {
203         OPT_DIST,
204 };
205
206
207 static void
208 set_delta(void *options, uint32_t key, uint64_t value,
209                 const char *valuestr lzma_attribute((unused)))
210 {
211         lzma_options_delta *opt = options;
212         switch (key) {
213         case OPT_DIST:
214                 opt->dist = value;
215                 break;
216         }
217 }
218
219
220 extern lzma_options_delta *
221 options_delta(const char *str)
222 {
223         static const option_map opts[] = {
224                 { "dist",     NULL,  LZMA_DELTA_DIST_MIN,
225                                      LZMA_DELTA_DIST_MAX },
226                 { NULL,       NULL,  0, 0 }
227         };
228
229         lzma_options_delta *options = xmalloc(sizeof(lzma_options_delta));
230         *options = (lzma_options_delta){
231                 // It's hard to give a useful default for this.
232                 .type = LZMA_DELTA_TYPE_BYTE,
233                 .dist = LZMA_DELTA_DIST_MIN,
234         };
235
236         parse_options(str, opts, &set_delta, options);
237
238         return options;
239 }
240
241
242 /////////
243 // BCJ //
244 /////////
245
246 enum {
247         OPT_START_OFFSET,
248 };
249
250
251 static void
252 set_bcj(void *options, uint32_t key, uint64_t value,
253                 const char *valuestr lzma_attribute((unused)))
254 {
255         lzma_options_bcj *opt = options;
256         switch (key) {
257         case OPT_START_OFFSET:
258                 opt->start_offset = value;
259                 break;
260         }
261 }
262
263
264 extern lzma_options_bcj *
265 options_bcj(const char *str)
266 {
267         static const option_map opts[] = {
268                 { "start",    NULL,  0, UINT32_MAX },
269                 { NULL,       NULL,  0, 0 }
270         };
271
272         lzma_options_bcj *options = xmalloc(sizeof(lzma_options_bcj));
273         *options = (lzma_options_bcj){
274                 .start_offset = 0,
275         };
276
277         parse_options(str, opts, &set_bcj, options);
278
279         return options;
280 }
281
282
283 //////////
284 // LZMA //
285 //////////
286
287 enum {
288         OPT_PRESET,
289         OPT_DICT,
290         OPT_LC,
291         OPT_LP,
292         OPT_PB,
293         OPT_MODE,
294         OPT_NICE,
295         OPT_MF,
296         OPT_DEPTH,
297 };
298
299
300 static void lzma_attribute((noreturn))
301 error_lzma_preset(const char *valuestr)
302 {
303         message_fatal(_("Unsupported LZMA1/LZMA2 preset: %s"), valuestr);
304 }
305
306
307 static void
308 set_lzma(void *options, uint32_t key, uint64_t value, const char *valuestr)
309 {
310         lzma_options_lzma *opt = options;
311
312         switch (key) {
313         case OPT_PRESET: {
314                 if (valuestr[0] < '0' || valuestr[0] > '9')
315                         error_lzma_preset(valuestr);
316
317                 uint32_t preset = valuestr[0] - '0';
318
319                 // Currently only "e" is supported as a modifier,
320                 // so keep this simple for now.
321                 if (valuestr[1] != '\0') {
322                         if (valuestr[1] == 'e')
323                                 preset |= LZMA_PRESET_EXTREME;
324                         else
325                                 error_lzma_preset(valuestr);
326
327                         if (valuestr[2] != '\0')
328                                 error_lzma_preset(valuestr);
329                 }
330
331                 if (lzma_lzma_preset(options, preset))
332                         error_lzma_preset(valuestr);
333
334                 break;
335         }
336
337         case OPT_DICT:
338                 opt->dict_size = value;
339                 break;
340
341         case OPT_LC:
342                 opt->lc = value;
343                 break;
344
345         case OPT_LP:
346                 opt->lp = value;
347                 break;
348
349         case OPT_PB:
350                 opt->pb = value;
351                 break;
352
353         case OPT_MODE:
354                 opt->mode = value;
355                 break;
356
357         case OPT_NICE:
358                 opt->nice_len = value;
359                 break;
360
361         case OPT_MF:
362                 opt->mf = value;
363                 break;
364
365         case OPT_DEPTH:
366                 opt->depth = value;
367                 break;
368         }
369 }
370
371
372 extern lzma_options_lzma *
373 options_lzma(const char *str)
374 {
375         static const name_id_map modes[] = {
376                 { "fast",   LZMA_MODE_FAST },
377                 { "normal", LZMA_MODE_NORMAL },
378                 { NULL,     0 }
379         };
380
381         static const name_id_map mfs[] = {
382                 { "hc3", LZMA_MF_HC3 },
383                 { "hc4", LZMA_MF_HC4 },
384                 { "bt2", LZMA_MF_BT2 },
385                 { "bt3", LZMA_MF_BT3 },
386                 { "bt4", LZMA_MF_BT4 },
387                 { NULL,  0 }
388         };
389
390         static const option_map opts[] = {
391                 { "preset", NULL,   UINT64_MAX, 0 },
392                 { "dict",   NULL,   LZMA_DICT_SIZE_MIN,
393                                 (UINT32_C(1) << 30) + (UINT32_C(1) << 29) },
394                 { "lc",     NULL,   LZMA_LCLP_MIN, LZMA_LCLP_MAX },
395                 { "lp",     NULL,   LZMA_LCLP_MIN, LZMA_LCLP_MAX },
396                 { "pb",     NULL,   LZMA_PB_MIN, LZMA_PB_MAX },
397                 { "mode",   modes,  0, 0 },
398                 { "nice",   NULL,   2, 273 },
399                 { "mf",     mfs,    0, 0 },
400                 { "depth",  NULL,   0, UINT32_MAX },
401                 { NULL,     NULL,   0, 0 }
402         };
403
404         lzma_options_lzma *options = xmalloc(sizeof(lzma_options_lzma));
405         *options = (lzma_options_lzma){
406                 .dict_size = LZMA_DICT_SIZE_DEFAULT,
407                 .preset_dict =  NULL,
408                 .preset_dict_size = 0,
409                 .lc = LZMA_LC_DEFAULT,
410                 .lp = LZMA_LP_DEFAULT,
411                 .pb = LZMA_PB_DEFAULT,
412                 .persistent = false,
413                 .mode = LZMA_MODE_NORMAL,
414                 .nice_len = 64,
415                 .mf = LZMA_MF_BT4,
416                 .depth = 0,
417         };
418
419         parse_options(str, opts, &set_lzma, options);
420
421         if (options->lc + options->lp > LZMA_LCLP_MAX)
422                 message_fatal(_("The sum of lc and lp must be at "
423                                 "maximum of 4"));
424
425         const uint32_t nice_len_min = options->mf & 0x0F;
426         if (options->nice_len < nice_len_min)
427                 message_fatal(_("The selected match finder requires at "
428                                 "least nice=%" PRIu32), nice_len_min);
429
430         return options;
431 }