]> icculus.org git repositories - icculus/xz.git/blob - src/xz/options.c
Some xz command line tool improvements.
[icculus/xz.git] / src / xz / options.c
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 /// \file       options.c
4 /// \brief      Parser for filter-specific options
5 //
6 //  Copyright (C) 2007 Lasse Collin
7 //
8 //  This program is free software; you can redistribute it and/or
9 //  modify it under the terms of the GNU Lesser General Public
10 //  License as published by the Free Software Foundation; either
11 //  version 2.1 of the License, or (at your option) any later version.
12 //
13 //  This program is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 //  Lesser General Public License for more details.
17 //
18 ///////////////////////////////////////////////////////////////////////////////
19
20 #include "private.h"
21
22
23 ///////////////////
24 // Generic stuff //
25 ///////////////////
26
27 typedef struct {
28         const char *name;
29         uint64_t id;
30 } name_id_map;
31
32
33 typedef struct {
34         const char *name;
35         const name_id_map *map;
36         uint64_t min;
37         uint64_t max;
38 } option_map;
39
40
41 /// Parses option=value pairs that are separated with colons, semicolons,
42 /// or commas: opt=val:opt=val;opt=val,opt=val
43 ///
44 /// Each option is a string, that is converted to an integer using the
45 /// index where the option string is in the array.
46 ///
47 /// Value can be either a number with minimum and maximum value limit, or
48 /// a string-id map mapping a list of possible string values to integers.
49 ///
50 /// When parsing both option and value succeed, a filter-specific function
51 /// is called, which should update the given value to filter-specific
52 /// options structure.
53 ///
54 /// \param      str     String containing the options from the command line
55 /// \param      opts    Filter-specific option map
56 /// \param      set     Filter-specific function to update filter_options
57 /// \param      filter_options  Pointer to filter-specific options structure
58 ///
59 /// \return     Returns only if no errors occur.
60 ///
61 static void
62 parse_options(const char *str, const option_map *opts,
63                 void (*set)(void *filter_options,
64                         uint32_t key, uint64_t value),
65                 void *filter_options)
66 {
67         if (str == NULL || str[0] == '\0')
68                 return;
69
70         char *s = xstrdup(str);
71         char *name = s;
72
73         while (true) {
74                 char *split = strchr(name, ',');
75                 if (split != NULL)
76                         *split = '\0';
77
78                 char *value = strchr(name, '=');
79                 if (value != NULL)
80                         *value++ = '\0';
81
82                 if (value == NULL || value[0] == '\0')
83                         message_fatal(_("%s: Options must be `name=value' "
84                                         "pairs separated with commas"), str);
85
86                 // Look for the option name from the option map.
87                 bool found = false;
88                 for (size_t i = 0; opts[i].name != NULL; ++i) {
89                         if (strcmp(name, opts[i].name) != 0)
90                                 continue;
91
92                         if (opts[i].map == NULL) {
93                                 // value is an integer.
94                                 const uint64_t v = str_to_uint64(name, value,
95                                                 opts[i].min, opts[i].max);
96                                 set(filter_options, i, v);
97                         } else {
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                         }
113
114                         found = true;
115                         break;
116                 }
117
118                 if (!found)
119                         message_fatal(_("%s: Invalid option name"), name);
120
121                 if (split == NULL)
122                         break;
123
124                 name = split + 1;
125         }
126
127         free(s);
128         return;
129 }
130
131
132 //////////////
133 // Subblock //
134 //////////////
135
136 enum {
137         OPT_SIZE,
138         OPT_RLE,
139         OPT_ALIGN,
140 };
141
142
143 static void
144 set_subblock(void *options, uint32_t key, uint64_t value)
145 {
146         lzma_options_subblock *opt = options;
147
148         switch (key) {
149         case OPT_SIZE:
150                 opt->subblock_data_size = value;
151                 break;
152
153         case OPT_RLE:
154                 opt->rle = value;
155                 break;
156
157         case OPT_ALIGN:
158                 opt->alignment = value;
159                 break;
160         }
161 }
162
163
164 extern lzma_options_subblock *
165 options_subblock(const char *str)
166 {
167         static const option_map opts[] = {
168                 { "size", NULL,   LZMA_SUBBLOCK_DATA_SIZE_MIN,
169                                   LZMA_SUBBLOCK_DATA_SIZE_MAX },
170                 { "rle",  NULL,   LZMA_SUBBLOCK_RLE_OFF,
171                                   LZMA_SUBBLOCK_RLE_MAX },
172                 { "align",NULL,   LZMA_SUBBLOCK_ALIGNMENT_MIN,
173                                   LZMA_SUBBLOCK_ALIGNMENT_MAX },
174                 { NULL,   NULL,   0, 0 }
175         };
176
177         lzma_options_subblock *options
178                         = xmalloc(sizeof(lzma_options_subblock));
179         *options = (lzma_options_subblock){
180                 .allow_subfilters = false,
181                 .alignment = LZMA_SUBBLOCK_ALIGNMENT_DEFAULT,
182                 .subblock_data_size = LZMA_SUBBLOCK_DATA_SIZE_DEFAULT,
183                 .rle = LZMA_SUBBLOCK_RLE_OFF,
184         };
185
186         parse_options(str, opts, &set_subblock, options);
187
188         return options;
189 }
190
191
192 ///////////
193 // Delta //
194 ///////////
195
196 enum {
197         OPT_DIST,
198 };
199
200
201 static void
202 set_delta(void *options, uint32_t key, uint64_t value)
203 {
204         lzma_options_delta *opt = options;
205         switch (key) {
206         case OPT_DIST:
207                 opt->dist = value;
208                 break;
209         }
210 }
211
212
213 extern lzma_options_delta *
214 options_delta(const char *str)
215 {
216         static const option_map opts[] = {
217                 { "dist",     NULL,  LZMA_DELTA_DIST_MIN,
218                                      LZMA_DELTA_DIST_MAX },
219                 { NULL,       NULL,  0, 0 }
220         };
221
222         lzma_options_delta *options = xmalloc(sizeof(lzma_options_delta));
223         *options = (lzma_options_delta){
224                 // It's hard to give a useful default for this.
225                 .type = LZMA_DELTA_TYPE_BYTE,
226                 .dist = LZMA_DELTA_DIST_MIN,
227         };
228
229         parse_options(str, opts, &set_delta, options);
230
231         return options;
232 }
233
234
235 //////////
236 // LZMA //
237 //////////
238
239 enum {
240         OPT_PRESET,
241         OPT_DICT,
242         OPT_LC,
243         OPT_LP,
244         OPT_PB,
245         OPT_MODE,
246         OPT_NICE,
247         OPT_MF,
248         OPT_DEPTH,
249 };
250
251
252 static void
253 set_lzma(void *options, uint32_t key, uint64_t value)
254 {
255         lzma_options_lzma *opt = options;
256
257         switch (key) {
258         case OPT_PRESET:
259                 if (lzma_lzma_preset(options, (uint32_t)(value)))
260                         message_fatal("LZMA1/LZMA2 preset %u is not supported",
261                                         (unsigned int)(value));
262                 break;
263
264         case OPT_DICT:
265                 opt->dict_size = value;
266                 break;
267
268         case OPT_LC:
269                 opt->lc = value;
270                 break;
271
272         case OPT_LP:
273                 opt->lp = value;
274                 break;
275
276         case OPT_PB:
277                 opt->pb = value;
278                 break;
279
280         case OPT_MODE:
281                 opt->mode = value;
282                 break;
283
284         case OPT_NICE:
285                 opt->nice_len = value;
286                 break;
287
288         case OPT_MF:
289                 opt->mf = value;
290                 break;
291
292         case OPT_DEPTH:
293                 opt->depth = value;
294                 break;
295         }
296 }
297
298
299 extern lzma_options_lzma *
300 options_lzma(const char *str)
301 {
302         static const name_id_map modes[] = {
303                 { "fast",   LZMA_MODE_FAST },
304                 { "normal", LZMA_MODE_NORMAL },
305                 { NULL,     0 }
306         };
307
308         static const name_id_map mfs[] = {
309                 { "hc3", LZMA_MF_HC3 },
310                 { "hc4", LZMA_MF_HC4 },
311                 { "bt2", LZMA_MF_BT2 },
312                 { "bt3", LZMA_MF_BT3 },
313                 { "bt4", LZMA_MF_BT4 },
314                 { NULL,  0 }
315         };
316
317         static const option_map opts[] = {
318                 { "preset", NULL,   0, 9 },
319                 { "dict",   NULL,   LZMA_DICT_SIZE_MIN,
320                                 (UINT32_C(1) << 30) + (UINT32_C(1) << 29) },
321                 { "lc",     NULL,   LZMA_LCLP_MIN, LZMA_LCLP_MAX },
322                 { "lp",     NULL,   LZMA_LCLP_MIN, LZMA_LCLP_MAX },
323                 { "pb",     NULL,   LZMA_PB_MIN, LZMA_PB_MAX },
324                 { "mode",   modes,  0, 0 },
325                 { "nice",   NULL,   2, 273 },
326                 { "mf",     mfs,    0, 0 },
327                 { "depth",  NULL,   0, UINT32_MAX },
328                 { NULL,     NULL,   0, 0 }
329         };
330
331         // TODO There should be a way to take some preset as the base for
332         // custom settings.
333         lzma_options_lzma *options = xmalloc(sizeof(lzma_options_lzma));
334         *options = (lzma_options_lzma){
335                 .dict_size = LZMA_DICT_SIZE_DEFAULT,
336                 .preset_dict =  NULL,
337                 .preset_dict_size = 0,
338                 .lc = LZMA_LC_DEFAULT,
339                 .lp = LZMA_LP_DEFAULT,
340                 .pb = LZMA_PB_DEFAULT,
341                 .persistent = false,
342                 .mode = LZMA_MODE_NORMAL,
343                 .nice_len = 64,
344                 .mf = LZMA_MF_BT4,
345                 .depth = 0,
346         };
347
348         parse_options(str, opts, &set_lzma, options);
349
350         if (options->lc + options->lp > LZMA_LCLP_MAX)
351                 message_fatal(_("The sum of lc and lp must be at "
352                                 "maximum of 4"));
353
354         const uint32_t nice_len_min = options->mf & 0x0F;
355         if (options->nice_len < nice_len_min)
356                 message_fatal(_("The selected match finder requires at "
357                                 "least nice=%" PRIu32), nice_len_min);
358
359         return options;
360 }