]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - contrib/xz/src/xz/options.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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 // Subblock //
144 //////////////
145
146 enum {
147         OPT_SIZE,
148         OPT_RLE,
149         OPT_ALIGN,
150 };
151
152
153 static void
154 set_subblock(void *options, uint32_t key, uint64_t value,
155                 const char *valuestr lzma_attribute((unused)))
156 {
157         lzma_options_subblock *opt = options;
158
159         switch (key) {
160         case OPT_SIZE:
161                 opt->subblock_data_size = value;
162                 break;
163
164         case OPT_RLE:
165                 opt->rle = value;
166                 break;
167
168         case OPT_ALIGN:
169                 opt->alignment = value;
170                 break;
171         }
172 }
173
174
175 extern lzma_options_subblock *
176 options_subblock(const char *str)
177 {
178         static const option_map opts[] = {
179                 { "size", NULL,   LZMA_SUBBLOCK_DATA_SIZE_MIN,
180                                   LZMA_SUBBLOCK_DATA_SIZE_MAX },
181                 { "rle",  NULL,   LZMA_SUBBLOCK_RLE_OFF,
182                                   LZMA_SUBBLOCK_RLE_MAX },
183                 { "align",NULL,   LZMA_SUBBLOCK_ALIGNMENT_MIN,
184                                   LZMA_SUBBLOCK_ALIGNMENT_MAX },
185                 { NULL,   NULL,   0, 0 }
186         };
187
188         lzma_options_subblock *options
189                         = xmalloc(sizeof(lzma_options_subblock));
190         *options = (lzma_options_subblock){
191                 .allow_subfilters = false,
192                 .alignment = LZMA_SUBBLOCK_ALIGNMENT_DEFAULT,
193                 .subblock_data_size = LZMA_SUBBLOCK_DATA_SIZE_DEFAULT,
194                 .rle = LZMA_SUBBLOCK_RLE_OFF,
195         };
196
197         parse_options(str, opts, &set_subblock, options);
198
199         return options;
200 }
201
202
203 ///////////
204 // Delta //
205 ///////////
206
207 enum {
208         OPT_DIST,
209 };
210
211
212 static void
213 set_delta(void *options, uint32_t key, uint64_t value,
214                 const char *valuestr lzma_attribute((unused)))
215 {
216         lzma_options_delta *opt = options;
217         switch (key) {
218         case OPT_DIST:
219                 opt->dist = value;
220                 break;
221         }
222 }
223
224
225 extern lzma_options_delta *
226 options_delta(const char *str)
227 {
228         static const option_map opts[] = {
229                 { "dist",     NULL,  LZMA_DELTA_DIST_MIN,
230                                      LZMA_DELTA_DIST_MAX },
231                 { NULL,       NULL,  0, 0 }
232         };
233
234         lzma_options_delta *options = xmalloc(sizeof(lzma_options_delta));
235         *options = (lzma_options_delta){
236                 // It's hard to give a useful default for this.
237                 .type = LZMA_DELTA_TYPE_BYTE,
238                 .dist = LZMA_DELTA_DIST_MIN,
239         };
240
241         parse_options(str, opts, &set_delta, options);
242
243         return options;
244 }
245
246
247 /////////
248 // BCJ //
249 /////////
250
251 enum {
252         OPT_START_OFFSET,
253 };
254
255
256 static void
257 set_bcj(void *options, uint32_t key, uint64_t value,
258                 const char *valuestr lzma_attribute((unused)))
259 {
260         lzma_options_bcj *opt = options;
261         switch (key) {
262         case OPT_START_OFFSET:
263                 opt->start_offset = value;
264                 break;
265         }
266 }
267
268
269 extern lzma_options_bcj *
270 options_bcj(const char *str)
271 {
272         static const option_map opts[] = {
273                 { "start",    NULL,  0, UINT32_MAX },
274                 { NULL,       NULL,  0, 0 }
275         };
276
277         lzma_options_bcj *options = xmalloc(sizeof(lzma_options_bcj));
278         *options = (lzma_options_bcj){
279                 .start_offset = 0,
280         };
281
282         parse_options(str, opts, &set_bcj, options);
283
284         return options;
285 }
286
287
288 //////////
289 // LZMA //
290 //////////
291
292 enum {
293         OPT_PRESET,
294         OPT_DICT,
295         OPT_LC,
296         OPT_LP,
297         OPT_PB,
298         OPT_MODE,
299         OPT_NICE,
300         OPT_MF,
301         OPT_DEPTH,
302 };
303
304
305 static void lzma_attribute((noreturn))
306 error_lzma_preset(const char *valuestr)
307 {
308         message_fatal(_("Unsupported LZMA1/LZMA2 preset: %s"), valuestr);
309 }
310
311
312 static void
313 set_lzma(void *options, uint32_t key, uint64_t value, const char *valuestr)
314 {
315         lzma_options_lzma *opt = options;
316
317         switch (key) {
318         case OPT_PRESET: {
319                 if (valuestr[0] < '0' || valuestr[0] > '9')
320                         error_lzma_preset(valuestr);
321
322                 uint32_t preset = valuestr[0] - '0';
323
324                 // Currently only "e" is supported as a modifier,
325                 // so keep this simple for now.
326                 if (valuestr[1] != '\0') {
327                         if (valuestr[1] == 'e')
328                                 preset |= LZMA_PRESET_EXTREME;
329                         else
330                                 error_lzma_preset(valuestr);
331
332                         if (valuestr[2] != '\0')
333                                 error_lzma_preset(valuestr);
334                 }
335
336                 if (lzma_lzma_preset(options, preset))
337                         error_lzma_preset(valuestr);
338
339                 break;
340         }
341
342         case OPT_DICT:
343                 opt->dict_size = value;
344                 break;
345
346         case OPT_LC:
347                 opt->lc = value;
348                 break;
349
350         case OPT_LP:
351                 opt->lp = value;
352                 break;
353
354         case OPT_PB:
355                 opt->pb = value;
356                 break;
357
358         case OPT_MODE:
359                 opt->mode = value;
360                 break;
361
362         case OPT_NICE:
363                 opt->nice_len = value;
364                 break;
365
366         case OPT_MF:
367                 opt->mf = value;
368                 break;
369
370         case OPT_DEPTH:
371                 opt->depth = value;
372                 break;
373         }
374 }
375
376
377 extern lzma_options_lzma *
378 options_lzma(const char *str)
379 {
380         static const name_id_map modes[] = {
381                 { "fast",   LZMA_MODE_FAST },
382                 { "normal", LZMA_MODE_NORMAL },
383                 { NULL,     0 }
384         };
385
386         static const name_id_map mfs[] = {
387                 { "hc3", LZMA_MF_HC3 },
388                 { "hc4", LZMA_MF_HC4 },
389                 { "bt2", LZMA_MF_BT2 },
390                 { "bt3", LZMA_MF_BT3 },
391                 { "bt4", LZMA_MF_BT4 },
392                 { NULL,  0 }
393         };
394
395         static const option_map opts[] = {
396                 { "preset", NULL,   UINT64_MAX, 0 },
397                 { "dict",   NULL,   LZMA_DICT_SIZE_MIN,
398                                 (UINT32_C(1) << 30) + (UINT32_C(1) << 29) },
399                 { "lc",     NULL,   LZMA_LCLP_MIN, LZMA_LCLP_MAX },
400                 { "lp",     NULL,   LZMA_LCLP_MIN, LZMA_LCLP_MAX },
401                 { "pb",     NULL,   LZMA_PB_MIN, LZMA_PB_MAX },
402                 { "mode",   modes,  0, 0 },
403                 { "nice",   NULL,   2, 273 },
404                 { "mf",     mfs,    0, 0 },
405                 { "depth",  NULL,   0, UINT32_MAX },
406                 { NULL,     NULL,   0, 0 }
407         };
408
409         lzma_options_lzma *options = xmalloc(sizeof(lzma_options_lzma));
410         *options = (lzma_options_lzma){
411                 .dict_size = LZMA_DICT_SIZE_DEFAULT,
412                 .preset_dict =  NULL,
413                 .preset_dict_size = 0,
414                 .lc = LZMA_LC_DEFAULT,
415                 .lp = LZMA_LP_DEFAULT,
416                 .pb = LZMA_PB_DEFAULT,
417                 .mode = LZMA_MODE_NORMAL,
418                 .nice_len = 64,
419                 .mf = LZMA_MF_BT4,
420                 .depth = 0,
421         };
422
423         parse_options(str, opts, &set_lzma, options);
424
425         if (options->lc + options->lp > LZMA_LCLP_MAX)
426                 message_fatal(_("The sum of lc and lp must be at "
427                                 "maximum of 4"));
428
429         const uint32_t nice_len_min = options->mf & 0x0F;
430         if (options->nice_len < nice_len_min)
431                 message_fatal(_("The selected match finder requires at "
432                                 "least nice=%" PRIu32), nice_len_min);
433
434         return options;
435 }