Import xz-5.0.3.
[dragonfly.git] / contrib / xz / 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 (*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 }