1 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 // vim: expandtab:ts=8:sw=4:softtabstop=4: 3 /////////////////////////////////////////////////////////////////////////////// 4 // 5 /// \file options.c 6 /// \brief Parser for filter-specific options 7 // 8 // Author: Lasse Collin 9 // 10 // This file has been put into the public domain. 11 // You can do whatever you want with this file. 12 // 13 /////////////////////////////////////////////////////////////////////////////// 14 15 #include "private.h" 16 17 18 /////////////////// 19 // Generic stuff // 20 /////////////////// 21 22 typedef struct { 23 const char *name; 24 uint64_t id; 25 } name_id_map; 26 27 28 typedef struct { 29 const char *name; 30 const name_id_map *map; 31 uint64_t min; 32 uint64_t max; 33 } option_map; 34 35 36 /// Parses option=value pairs that are separated with colons, semicolons, 37 /// or commas: opt=val:opt=val;opt=val,opt=val 38 /// 39 /// Each option is a string, that is converted to an integer using the 40 /// index where the option string is in the array. 41 /// 42 /// Value can be 43 /// - a string-id map mapping a list of possible string values to integers 44 /// (opts[i].map != NULL, opts[i].min and opts[i].max are ignored); 45 /// - a number with minimum and maximum value limit 46 /// (opts[i].map == NULL && opts[i].min != UINT64_MAX); 47 /// - a string that will be parsed by the filter-specific code 48 /// (opts[i].map == NULL && opts[i].min == UINT64_MAX, opts[i].max ignored) 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, const char *valuestr), 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 if (*name == ',') { 75 if (*++name == '\0') 76 break; 77 78 continue; 79 } 80 81 char *split = strchr(name, ','); 82 if (split != NULL) 83 *split = '\0'; 84 85 char *value = strchr(name, '='); 86 if (value != NULL) 87 *value++ = '\0'; 88 89 if (value == NULL || value[0] == '\0') 90 message_fatal(_("%s: Options must be `name=value' " 91 "pairs separated with commas"), str); 92 93 // Look for the option name from the option map. 94 bool found = false; 95 for (size_t i = 0; opts[i].name != NULL; ++i) { 96 if (strcmp(name, opts[i].name) != 0) 97 continue; 98 99 if (opts[i].map != NULL) { 100 // value is a string which we should map 101 // to an integer. 102 size_t j; 103 for (j = 0; opts[i].map[j].name != NULL; ++j) { 104 if (strcmp(opts[i].map[j].name, value) 105 == 0) 106 break; 107 } 108 109 if (opts[i].map[j].name == NULL) 110 message_fatal(_("%s: Invalid option " 111 "value"), value); 112 113 set(filter_options, i, opts[i].map[j].id, 114 value); 115 116 } else if (opts[i].min == UINT64_MAX) { 117 // value is a special string that will be 118 // parsed by set(). 119 set(filter_options, i, 0, value); 120 121 } else { 122 // value is an integer. 123 const uint64_t v = str_to_uint64(name, value, 124 opts[i].min, opts[i].max); 125 set(filter_options, i, v, value); 126 } 127 128 found = true; 129 break; 130 } 131 132 if (!found) 133 message_fatal(_("%s: Invalid option name"), name); 134 135 if (split == NULL) 136 break; 137 138 name = split + 1; 139 } 140 141 free(s); 142 return; 143 } 144 145 146 ////////////// 147 // Subblock // 148 ////////////// 149 150 enum { 151 OPT_SIZE, 152 OPT_RLE, 153 OPT_ALIGN, 154 }; 155 156 157 static void 158 set_subblock(void *options, uint32_t key, uint64_t value, 159 const char *valuestr lzma_attribute((unused))) 160 { 161 lzma_options_subblock *opt = options; 162 163 switch (key) { 164 case OPT_SIZE: 165 opt->subblock_data_size = value; 166 break; 167 168 case OPT_RLE: 169 opt->rle = value; 170 break; 171 172 case OPT_ALIGN: 173 opt->alignment = value; 174 break; 175 } 176 } 177 178 179 extern lzma_options_subblock * 180 options_subblock(const char *str) 181 { 182 static const option_map opts[] = { 183 { "size", NULL, LZMA_SUBBLOCK_DATA_SIZE_MIN, 184 LZMA_SUBBLOCK_DATA_SIZE_MAX }, 185 { "rle", NULL, LZMA_SUBBLOCK_RLE_OFF, 186 LZMA_SUBBLOCK_RLE_MAX }, 187 { "align",NULL, LZMA_SUBBLOCK_ALIGNMENT_MIN, 188 LZMA_SUBBLOCK_ALIGNMENT_MAX }, 189 { NULL, NULL, 0, 0 } 190 }; 191 192 lzma_options_subblock *options 193 = xmalloc(sizeof(lzma_options_subblock)); 194 *options = (lzma_options_subblock){ 195 .allow_subfilters = false, 196 .alignment = LZMA_SUBBLOCK_ALIGNMENT_DEFAULT, 197 .subblock_data_size = LZMA_SUBBLOCK_DATA_SIZE_DEFAULT, 198 .rle = LZMA_SUBBLOCK_RLE_OFF, 199 }; 200 201 parse_options(str, opts, &set_subblock, options); 202 203 return options; 204 } 205 206 207 /////////// 208 // Delta // 209 /////////// 210 211 enum { 212 OPT_DIST, 213 }; 214 215 216 static void 217 set_delta(void *options, uint32_t key, uint64_t value, 218 const char *valuestr lzma_attribute((unused))) 219 { 220 lzma_options_delta *opt = options; 221 switch (key) { 222 case OPT_DIST: 223 opt->dist = value; 224 break; 225 } 226 } 227 228 229 extern lzma_options_delta * 230 options_delta(const char *str) 231 { 232 static const option_map opts[] = { 233 { "dist", NULL, LZMA_DELTA_DIST_MIN, 234 LZMA_DELTA_DIST_MAX }, 235 { NULL, NULL, 0, 0 } 236 }; 237 238 lzma_options_delta *options = xmalloc(sizeof(lzma_options_delta)); 239 *options = (lzma_options_delta){ 240 // It's hard to give a useful default for this. 241 .type = LZMA_DELTA_TYPE_BYTE, 242 .dist = LZMA_DELTA_DIST_MIN, 243 }; 244 245 parse_options(str, opts, &set_delta, options); 246 247 return options; 248 } 249 250 251 ///////// 252 // BCJ // 253 ///////// 254 255 enum { 256 OPT_START_OFFSET, 257 }; 258 259 260 static void 261 set_bcj(void *options, uint32_t key, uint64_t value, 262 const char *valuestr lzma_attribute((unused))) 263 { 264 lzma_options_bcj *opt = options; 265 switch (key) { 266 case OPT_START_OFFSET: 267 opt->start_offset = value; 268 break; 269 } 270 } 271 272 273 extern lzma_options_bcj * 274 options_bcj(const char *str) 275 { 276 static const option_map opts[] = { 277 { "start", NULL, 0, UINT32_MAX }, 278 { NULL, NULL, 0, 0 } 279 }; 280 281 lzma_options_bcj *options = xmalloc(sizeof(lzma_options_bcj)); 282 *options = (lzma_options_bcj){ 283 .start_offset = 0, 284 }; 285 286 parse_options(str, opts, &set_bcj, options); 287 288 return options; 289 } 290 291 292 ////////// 293 // LZMA // 294 ////////// 295 296 enum { 297 OPT_PRESET, 298 OPT_DICT, 299 OPT_LC, 300 OPT_LP, 301 OPT_PB, 302 OPT_MODE, 303 OPT_NICE, 304 OPT_MF, 305 OPT_DEPTH, 306 }; 307 308 309 static void lzma_attribute((noreturn)) 310 error_lzma_preset(const char *valuestr) 311 { 312 message_fatal(_("Unsupported LZMA1/LZMA2 preset: %s"), valuestr); 313 } 314 315 316 static void 317 set_lzma(void *options, uint32_t key, uint64_t value, const char *valuestr) 318 { 319 lzma_options_lzma *opt = options; 320 321 switch (key) { 322 case OPT_PRESET: { 323 if (valuestr[0] < '0' || valuestr[0] > '9') 324 error_lzma_preset(valuestr); 325 326 uint32_t preset = valuestr[0] - '0'; 327 328 // Currently only "e" is supported as a modifier, 329 // so keep this simple for now. 330 if (valuestr[1] != '\0') { 331 if (valuestr[1] == 'e') 332 preset |= LZMA_PRESET_EXTREME; 333 else 334 error_lzma_preset(valuestr); 335 336 if (valuestr[2] != '\0') 337 error_lzma_preset(valuestr); 338 } 339 340 if (lzma_lzma_preset(options, preset)) 341 error_lzma_preset(valuestr); 342 343 break; 344 } 345 346 case OPT_DICT: 347 opt->dict_size = value; 348 break; 349 350 case OPT_LC: 351 opt->lc = value; 352 break; 353 354 case OPT_LP: 355 opt->lp = value; 356 break; 357 358 case OPT_PB: 359 opt->pb = value; 360 break; 361 362 case OPT_MODE: 363 opt->mode = value; 364 break; 365 366 case OPT_NICE: 367 opt->nice_len = value; 368 break; 369 370 case OPT_MF: 371 opt->mf = value; 372 break; 373 374 case OPT_DEPTH: 375 opt->depth = value; 376 break; 377 } 378 } 379 380 381 extern lzma_options_lzma * 382 options_lzma(const char *str) 383 { 384 static const name_id_map modes[] = { 385 { "fast", LZMA_MODE_FAST }, 386 { "normal", LZMA_MODE_NORMAL }, 387 { NULL, 0 } 388 }; 389 390 static const name_id_map mfs[] = { 391 { "hc3", LZMA_MF_HC3 }, 392 { "hc4", LZMA_MF_HC4 }, 393 { "bt2", LZMA_MF_BT2 }, 394 { "bt3", LZMA_MF_BT3 }, 395 { "bt4", LZMA_MF_BT4 }, 396 { NULL, 0 } 397 }; 398 399 static const option_map opts[] = { 400 { "preset", NULL, UINT64_MAX, 0 }, 401 { "dict", NULL, LZMA_DICT_SIZE_MIN, 402 (UINT32_C(1) << 30) + (UINT32_C(1) << 29) }, 403 { "lc", NULL, LZMA_LCLP_MIN, LZMA_LCLP_MAX }, 404 { "lp", NULL, LZMA_LCLP_MIN, LZMA_LCLP_MAX }, 405 { "pb", NULL, LZMA_PB_MIN, LZMA_PB_MAX }, 406 { "mode", modes, 0, 0 }, 407 { "nice", NULL, 2, 273 }, 408 { "mf", mfs, 0, 0 }, 409 { "depth", NULL, 0, UINT32_MAX }, 410 { NULL, NULL, 0, 0 } 411 }; 412 413 lzma_options_lzma *options = xmalloc(sizeof(lzma_options_lzma)); 414 *options = (lzma_options_lzma){ 415 .dict_size = LZMA_DICT_SIZE_DEFAULT, 416 .preset_dict = NULL, 417 .preset_dict_size = 0, 418 .lc = LZMA_LC_DEFAULT, 419 .lp = LZMA_LP_DEFAULT, 420 .pb = LZMA_PB_DEFAULT, 421 .persistent = false, 422 .mode = LZMA_MODE_NORMAL, 423 .nice_len = 64, 424 .mf = LZMA_MF_BT4, 425 .depth = 0, 426 }; 427 428 parse_options(str, opts, &set_lzma, options); 429 430 if (options->lc + options->lp > LZMA_LCLP_MAX) 431 message_fatal(_("The sum of lc and lp must be at " 432 "maximum of 4")); 433 434 const uint32_t nice_len_min = options->mf & 0x0F; 435 if (options->nice_len < nice_len_min) 436 message_fatal(_("The selected match finder requires at " 437 "least nice=%" PRIu32), nice_len_min); 438 439 return options; 440 } 441