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 } 436