options.c revision 292588
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 20typedef struct { 21 const char *name; 22 uint64_t id; 23} name_id_map; 24 25 26typedef 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 commas: 35/// 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/// 59static void 60parse_options(const char *str, const option_map *opts, 61 void (*set)(void *filter_options, 62 unsigned 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 unsigned 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 unsigned 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 146enum { 147 OPT_DIST, 148}; 149 150 151static void 152set_delta(void *options, unsigned 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 164extern lzma_options_delta * 165options_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 190enum { 191 OPT_START_OFFSET, 192}; 193 194 195static void 196set_bcj(void *options, unsigned 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 208extern lzma_options_bcj * 209options_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 231enum { 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 244static void lzma_attribute((__noreturn__)) 245error_lzma_preset(const char *valuestr) 246{ 247 message_fatal(_("Unsupported LZMA1/LZMA2 preset: %s"), valuestr); 248} 249 250 251static void 252set_lzma(void *options, unsigned 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 316extern lzma_options_lzma * 317options_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} 364