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