]> icculus.org git repositories - icculus/xz.git/blob - src/lzma/options.c
Fix a crash with --format=alone if other filters than LZMA
[icculus/xz.git] / src / lzma / 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                         errmsg(V_ERROR, _("%s: Options must be `name=value' "
84                                         "pairs separated with commas"),
85                                         str);
86                         my_exit(ERROR);
87                 }
88
89                 // Look for the option name from the option map.
90                 bool found = false;
91                 for (size_t i = 0; opts[i].name != NULL; ++i) {
92                         if (strcmp(name, opts[i].name) != 0)
93                                 continue;
94
95                         if (opts[i].map == NULL) {
96                                 // value is an integer.
97                                 const uint64_t v = str_to_uint64(name, value,
98                                                 opts[i].min, opts[i].max);
99                                 set(filter_options, i, v);
100                         } else {
101                                 // value is a string which we should map
102                                 // to an integer.
103                                 size_t j;
104                                 for (j = 0; opts[i].map[j].name != NULL; ++j) {
105                                         if (strcmp(opts[i].map[j].name, value)
106                                                         == 0)
107                                                 break;
108                                 }
109
110                                 if (opts[i].map[j].name == NULL) {
111                                         errmsg(V_ERROR, _("%s: Invalid option "
112                                                         "value"), value);
113                                         my_exit(ERROR);
114                                 }
115
116                                 set(filter_options, i, opts[i].map[j].id);
117                         }
118
119                         found = true;
120                         break;
121                 }
122
123                 if (!found) {
124                         errmsg(V_ERROR, _("%s: Invalid option name"), name);
125                         my_exit(ERROR);
126                 }
127
128                 if (split == NULL)
129                         break;
130
131                 name = split + 1;
132         }
133
134         free(s);
135         return;
136 }
137
138
139 //////////////
140 // Subblock //
141 //////////////
142
143 enum {
144         OPT_SIZE,
145         OPT_RLE,
146         OPT_ALIGN,
147 };
148
149
150 static void
151 set_subblock(void *options, uint32_t key, uint64_t value)
152 {
153         lzma_options_subblock *opt = options;
154
155         switch (key) {
156         case OPT_SIZE:
157                 opt->subblock_data_size = value;
158                 break;
159
160         case OPT_RLE:
161                 opt->rle = value;
162                 break;
163
164         case OPT_ALIGN:
165                 opt->alignment = value;
166                 break;
167         }
168 }
169
170
171 extern lzma_options_subblock *
172 parse_options_subblock(const char *str)
173 {
174         static const option_map opts[] = {
175                 { "size", NULL,   LZMA_SUBBLOCK_DATA_SIZE_MIN,
176                                   LZMA_SUBBLOCK_DATA_SIZE_MAX },
177                 { "rle",  NULL,   LZMA_SUBBLOCK_RLE_OFF,
178                                   LZMA_SUBBLOCK_RLE_MAX },
179                 { "align",NULL,   LZMA_SUBBLOCK_ALIGNMENT_MIN,
180                                   LZMA_SUBBLOCK_ALIGNMENT_MAX },
181                 { NULL,   NULL,   0, 0 }
182         };
183
184         lzma_options_subblock *options
185                         = xmalloc(sizeof(lzma_options_subblock));
186         *options = (lzma_options_subblock){
187                 .allow_subfilters = false,
188                 .alignment = LZMA_SUBBLOCK_ALIGNMENT_DEFAULT,
189                 .subblock_data_size = LZMA_SUBBLOCK_DATA_SIZE_DEFAULT,
190                 .rle = LZMA_SUBBLOCK_RLE_OFF,
191         };
192
193         parse_options(str, opts, &set_subblock, options);
194
195         return options;
196 }
197
198
199 ///////////
200 // Delta //
201 ///////////
202
203 enum {
204         OPT_DISTANCE,
205 };
206
207
208 static void
209 set_delta(void *options, uint32_t key, uint64_t value)
210 {
211         lzma_options_delta *opt = options;
212         switch (key) {
213         case OPT_DISTANCE:
214                 opt->distance = value;
215                 break;
216         }
217 }
218
219
220 extern lzma_options_delta *
221 parse_options_delta(const char *str)
222 {
223         static const option_map opts[] = {
224                 { "distance", NULL,  LZMA_DELTA_DISTANCE_MIN,
225                                      LZMA_DELTA_DISTANCE_MAX },
226                 { NULL,       NULL,  0, 0 }
227         };
228
229         lzma_options_delta *options = xmalloc(sizeof(lzma_options_subblock));
230         *options = (lzma_options_delta){
231                 // It's hard to give a useful default for this.
232                 .distance = LZMA_DELTA_DISTANCE_MIN,
233         };
234
235         parse_options(str, opts, &set_delta, options);
236
237         return options;
238 }
239
240
241 //////////
242 // LZMA //
243 //////////
244
245 enum {
246         OPT_DICT,
247         OPT_LC,
248         OPT_LP,
249         OPT_PB,
250         OPT_MODE,
251         OPT_FB,
252         OPT_MF,
253         OPT_MC
254 };
255
256
257 static void
258 set_lzma(void *options, uint32_t key, uint64_t value)
259 {
260         lzma_options_lzma *opt = options;
261
262         switch (key) {
263         case OPT_DICT:
264                 opt->dictionary_size = value;
265                 break;
266
267         case OPT_LC:
268                 opt->literal_context_bits = value;
269                 break;
270
271         case OPT_LP:
272                 opt->literal_pos_bits = value;
273                 break;
274
275         case OPT_PB:
276                 opt->pos_bits = value;
277                 break;
278
279         case OPT_MODE:
280                 opt->mode = value;
281                 break;
282
283         case OPT_FB:
284                 opt->fast_bytes = value;
285                 break;
286
287         case OPT_MF:
288                 opt->match_finder = value;
289                 break;
290
291         case OPT_MC:
292                 opt->match_finder_cycles = value;
293                 break;
294         }
295 }
296
297
298 extern lzma_options_lzma *
299 parse_options_lzma(const char *str)
300 {
301         static const name_id_map modes[] = {
302                 { "fast", LZMA_MODE_FAST },
303                 { "best", LZMA_MODE_BEST },
304                 { NULL,   0 }
305         };
306
307         static const name_id_map mfs[] = {
308                 { "hc3", LZMA_MF_HC3 },
309                 { "hc4", LZMA_MF_HC4 },
310                 { "bt2", LZMA_MF_BT2 },
311                 { "bt3", LZMA_MF_BT3 },
312                 { "bt4", LZMA_MF_BT4 },
313                 { NULL,  0 }
314         };
315
316         static const option_map opts[] = {
317                 { "dict", NULL,   LZMA_DICTIONARY_SIZE_MIN,
318                                 LZMA_DICTIONARY_SIZE_MAX },
319                 { "lc",   NULL,   LZMA_LITERAL_CONTEXT_BITS_MIN,
320                                   LZMA_LITERAL_CONTEXT_BITS_MAX },
321                 { "lp",   NULL,   LZMA_LITERAL_POS_BITS_MIN,
322                                   LZMA_LITERAL_POS_BITS_MAX },
323                 { "pb",   NULL,   LZMA_POS_BITS_MIN, LZMA_POS_BITS_MAX },
324                 { "mode", modes,  0, 0 },
325                 { "fb",   NULL,   LZMA_FAST_BYTES_MIN, LZMA_FAST_BYTES_MAX },
326                 { "mf",   mfs,    0, 0 },
327                 { "mc",   NULL,   0, UINT32_MAX },
328                 { NULL,   NULL,   0, 0 }
329         };
330
331         lzma_options_lzma *options = xmalloc(sizeof(lzma_options_lzma));
332         *options = (lzma_options_lzma){
333                 .dictionary_size = LZMA_DICTIONARY_SIZE_DEFAULT,
334                 .literal_context_bits = LZMA_LITERAL_CONTEXT_BITS_DEFAULT,
335                 .literal_pos_bits = LZMA_LITERAL_POS_BITS_DEFAULT,
336                 .pos_bits = LZMA_POS_BITS_DEFAULT,
337                 .mode = LZMA_MODE_BEST,
338                 .fast_bytes = LZMA_FAST_BYTES_DEFAULT,
339                 .match_finder = LZMA_MF_BT4,
340                 .match_finder_cycles = 0,
341         };
342
343         parse_options(str, opts, &set_lzma, options);
344
345         return options;
346 }