1207753Smm/////////////////////////////////////////////////////////////////////////////// 2207753Smm// 3207753Smm/// \file options.c 4207753Smm/// \brief Parser for filter-specific options 5207753Smm// 6207753Smm// Author: Lasse Collin 7207753Smm// 8207753Smm// This file has been put into the public domain. 9207753Smm// You can do whatever you want with this file. 10207753Smm// 11207753Smm/////////////////////////////////////////////////////////////////////////////// 12207753Smm 13207753Smm#include "private.h" 14207753Smm 15207753Smm 16207753Smm/////////////////// 17207753Smm// Generic stuff // 18207753Smm/////////////////// 19207753Smm 20207753Smmtypedef struct { 21207753Smm const char *name; 22207753Smm uint64_t id; 23207753Smm} name_id_map; 24207753Smm 25207753Smm 26207753Smmtypedef struct { 27207753Smm const char *name; 28207753Smm const name_id_map *map; 29207753Smm uint64_t min; 30207753Smm uint64_t max; 31207753Smm} option_map; 32207753Smm 33207753Smm 34292588Sdelphij/// Parses option=value pairs that are separated with commas: 35292588Sdelphij/// opt=val,opt=val,opt=val 36207753Smm/// 37207753Smm/// Each option is a string, that is converted to an integer using the 38207753Smm/// index where the option string is in the array. 39207753Smm/// 40207753Smm/// Value can be 41207753Smm/// - a string-id map mapping a list of possible string values to integers 42207753Smm/// (opts[i].map != NULL, opts[i].min and opts[i].max are ignored); 43207753Smm/// - a number with minimum and maximum value limit 44207753Smm/// (opts[i].map == NULL && opts[i].min != UINT64_MAX); 45207753Smm/// - a string that will be parsed by the filter-specific code 46207753Smm/// (opts[i].map == NULL && opts[i].min == UINT64_MAX, opts[i].max ignored) 47207753Smm/// 48207753Smm/// When parsing both option and value succeed, a filter-specific function 49207753Smm/// is called, which should update the given value to filter-specific 50207753Smm/// options structure. 51207753Smm/// 52207753Smm/// \param str String containing the options from the command line 53207753Smm/// \param opts Filter-specific option map 54207753Smm/// \param set Filter-specific function to update filter_options 55207753Smm/// \param filter_options Pointer to filter-specific options structure 56207753Smm/// 57207753Smm/// \return Returns only if no errors occur. 58207753Smm/// 59207753Smmstatic void 60207753Smmparse_options(const char *str, const option_map *opts, 61207753Smm void (*set)(void *filter_options, 62292588Sdelphij unsigned key, uint64_t value, const char *valuestr), 63207753Smm void *filter_options) 64207753Smm{ 65207753Smm if (str == NULL || str[0] == '\0') 66207753Smm return; 67207753Smm 68207753Smm char *s = xstrdup(str); 69207753Smm char *name = s; 70207753Smm 71207753Smm while (*name != '\0') { 72207753Smm if (*name == ',') { 73207753Smm ++name; 74207753Smm continue; 75207753Smm } 76207753Smm 77207753Smm char *split = strchr(name, ','); 78207753Smm if (split != NULL) 79207753Smm *split = '\0'; 80207753Smm 81207753Smm char *value = strchr(name, '='); 82207753Smm if (value != NULL) 83207753Smm *value++ = '\0'; 84207753Smm 85207753Smm if (value == NULL || value[0] == '\0') 86207753Smm message_fatal(_("%s: Options must be `name=value' " 87207753Smm "pairs separated with commas"), str); 88207753Smm 89207753Smm // Look for the option name from the option map. 90292588Sdelphij unsigned i = 0; 91207753Smm while (true) { 92207753Smm if (opts[i].name == NULL) 93207753Smm message_fatal(_("%s: Invalid option name"), 94207753Smm name); 95207753Smm 96207753Smm if (strcmp(name, opts[i].name) == 0) 97207753Smm break; 98207753Smm 99207753Smm ++i; 100207753Smm } 101207753Smm 102207753Smm // Option was found from the map. See how we should handle it. 103207753Smm if (opts[i].map != NULL) { 104207753Smm // value is a string which we should map 105207753Smm // to an integer. 106292588Sdelphij unsigned j; 107207753Smm for (j = 0; opts[i].map[j].name != NULL; ++j) { 108207753Smm if (strcmp(opts[i].map[j].name, value) == 0) 109207753Smm break; 110207753Smm } 111207753Smm 112207753Smm if (opts[i].map[j].name == NULL) 113207753Smm message_fatal(_("%s: Invalid option value"), 114207753Smm value); 115207753Smm 116207753Smm set(filter_options, i, opts[i].map[j].id, value); 117207753Smm 118207753Smm } else if (opts[i].min == UINT64_MAX) { 119207753Smm // value is a special string that will be 120207753Smm // parsed by set(). 121207753Smm set(filter_options, i, 0, value); 122207753Smm 123207753Smm } else { 124207753Smm // value is an integer. 125207753Smm const uint64_t v = str_to_uint64(name, value, 126207753Smm opts[i].min, opts[i].max); 127207753Smm set(filter_options, i, v, value); 128207753Smm } 129207753Smm 130207753Smm // Check if it was the last option. 131207753Smm if (split == NULL) 132207753Smm break; 133207753Smm 134207753Smm name = split + 1; 135207753Smm } 136207753Smm 137207753Smm free(s); 138207753Smm return; 139207753Smm} 140207753Smm 141207753Smm 142207753Smm/////////// 143207753Smm// Delta // 144207753Smm/////////// 145207753Smm 146207753Smmenum { 147207753Smm OPT_DIST, 148207753Smm}; 149207753Smm 150207753Smm 151207753Smmstatic void 152292588Sdelphijset_delta(void *options, unsigned key, uint64_t value, 153223935Smm const char *valuestr lzma_attribute((__unused__))) 154207753Smm{ 155207753Smm lzma_options_delta *opt = options; 156207753Smm switch (key) { 157207753Smm case OPT_DIST: 158207753Smm opt->dist = value; 159207753Smm break; 160207753Smm } 161207753Smm} 162207753Smm 163207753Smm 164207753Smmextern lzma_options_delta * 165207753Smmoptions_delta(const char *str) 166207753Smm{ 167207753Smm static const option_map opts[] = { 168207753Smm { "dist", NULL, LZMA_DELTA_DIST_MIN, 169207753Smm LZMA_DELTA_DIST_MAX }, 170207753Smm { NULL, NULL, 0, 0 } 171207753Smm }; 172207753Smm 173207753Smm lzma_options_delta *options = xmalloc(sizeof(lzma_options_delta)); 174207753Smm *options = (lzma_options_delta){ 175207753Smm // It's hard to give a useful default for this. 176207753Smm .type = LZMA_DELTA_TYPE_BYTE, 177207753Smm .dist = LZMA_DELTA_DIST_MIN, 178207753Smm }; 179207753Smm 180207753Smm parse_options(str, opts, &set_delta, options); 181207753Smm 182207753Smm return options; 183207753Smm} 184207753Smm 185207753Smm 186207753Smm///////// 187207753Smm// BCJ // 188207753Smm///////// 189207753Smm 190207753Smmenum { 191207753Smm OPT_START_OFFSET, 192207753Smm}; 193207753Smm 194207753Smm 195207753Smmstatic void 196292588Sdelphijset_bcj(void *options, unsigned key, uint64_t value, 197223935Smm const char *valuestr lzma_attribute((__unused__))) 198207753Smm{ 199207753Smm lzma_options_bcj *opt = options; 200207753Smm switch (key) { 201207753Smm case OPT_START_OFFSET: 202207753Smm opt->start_offset = value; 203207753Smm break; 204207753Smm } 205207753Smm} 206207753Smm 207207753Smm 208207753Smmextern lzma_options_bcj * 209207753Smmoptions_bcj(const char *str) 210207753Smm{ 211207753Smm static const option_map opts[] = { 212207753Smm { "start", NULL, 0, UINT32_MAX }, 213207753Smm { NULL, NULL, 0, 0 } 214207753Smm }; 215207753Smm 216207753Smm lzma_options_bcj *options = xmalloc(sizeof(lzma_options_bcj)); 217207753Smm *options = (lzma_options_bcj){ 218207753Smm .start_offset = 0, 219207753Smm }; 220207753Smm 221207753Smm parse_options(str, opts, &set_bcj, options); 222207753Smm 223207753Smm return options; 224207753Smm} 225207753Smm 226207753Smm 227207753Smm////////// 228207753Smm// LZMA // 229207753Smm////////// 230207753Smm 231207753Smmenum { 232207753Smm OPT_PRESET, 233207753Smm OPT_DICT, 234207753Smm OPT_LC, 235207753Smm OPT_LP, 236207753Smm OPT_PB, 237207753Smm OPT_MODE, 238207753Smm OPT_NICE, 239207753Smm OPT_MF, 240207753Smm OPT_DEPTH, 241207753Smm}; 242207753Smm 243207753Smm 244223935Smmstatic void lzma_attribute((__noreturn__)) 245207753Smmerror_lzma_preset(const char *valuestr) 246207753Smm{ 247207753Smm message_fatal(_("Unsupported LZMA1/LZMA2 preset: %s"), valuestr); 248207753Smm} 249207753Smm 250207753Smm 251207753Smmstatic void 252292588Sdelphijset_lzma(void *options, unsigned key, uint64_t value, const char *valuestr) 253207753Smm{ 254207753Smm lzma_options_lzma *opt = options; 255207753Smm 256207753Smm switch (key) { 257207753Smm case OPT_PRESET: { 258207753Smm if (valuestr[0] < '0' || valuestr[0] > '9') 259207753Smm error_lzma_preset(valuestr); 260207753Smm 261207753Smm uint32_t preset = valuestr[0] - '0'; 262207753Smm 263207753Smm // Currently only "e" is supported as a modifier, 264207753Smm // so keep this simple for now. 265207753Smm if (valuestr[1] != '\0') { 266207753Smm if (valuestr[1] == 'e') 267207753Smm preset |= LZMA_PRESET_EXTREME; 268207753Smm else 269207753Smm error_lzma_preset(valuestr); 270207753Smm 271207753Smm if (valuestr[2] != '\0') 272207753Smm error_lzma_preset(valuestr); 273207753Smm } 274207753Smm 275207753Smm if (lzma_lzma_preset(options, preset)) 276207753Smm error_lzma_preset(valuestr); 277207753Smm 278207753Smm break; 279207753Smm } 280207753Smm 281207753Smm case OPT_DICT: 282207753Smm opt->dict_size = value; 283207753Smm break; 284207753Smm 285207753Smm case OPT_LC: 286207753Smm opt->lc = value; 287207753Smm break; 288207753Smm 289207753Smm case OPT_LP: 290207753Smm opt->lp = value; 291207753Smm break; 292207753Smm 293207753Smm case OPT_PB: 294207753Smm opt->pb = value; 295207753Smm break; 296207753Smm 297207753Smm case OPT_MODE: 298207753Smm opt->mode = value; 299207753Smm break; 300207753Smm 301207753Smm case OPT_NICE: 302207753Smm opt->nice_len = value; 303207753Smm break; 304207753Smm 305207753Smm case OPT_MF: 306207753Smm opt->mf = value; 307207753Smm break; 308207753Smm 309207753Smm case OPT_DEPTH: 310207753Smm opt->depth = value; 311207753Smm break; 312207753Smm } 313207753Smm} 314207753Smm 315207753Smm 316207753Smmextern lzma_options_lzma * 317207753Smmoptions_lzma(const char *str) 318207753Smm{ 319207753Smm static const name_id_map modes[] = { 320207753Smm { "fast", LZMA_MODE_FAST }, 321207753Smm { "normal", LZMA_MODE_NORMAL }, 322207753Smm { NULL, 0 } 323207753Smm }; 324207753Smm 325207753Smm static const name_id_map mfs[] = { 326207753Smm { "hc3", LZMA_MF_HC3 }, 327207753Smm { "hc4", LZMA_MF_HC4 }, 328207753Smm { "bt2", LZMA_MF_BT2 }, 329207753Smm { "bt3", LZMA_MF_BT3 }, 330207753Smm { "bt4", LZMA_MF_BT4 }, 331207753Smm { NULL, 0 } 332207753Smm }; 333207753Smm 334207753Smm static const option_map opts[] = { 335207753Smm { "preset", NULL, UINT64_MAX, 0 }, 336207753Smm { "dict", NULL, LZMA_DICT_SIZE_MIN, 337207753Smm (UINT32_C(1) << 30) + (UINT32_C(1) << 29) }, 338207753Smm { "lc", NULL, LZMA_LCLP_MIN, LZMA_LCLP_MAX }, 339207753Smm { "lp", NULL, LZMA_LCLP_MIN, LZMA_LCLP_MAX }, 340207753Smm { "pb", NULL, LZMA_PB_MIN, LZMA_PB_MAX }, 341207753Smm { "mode", modes, 0, 0 }, 342207753Smm { "nice", NULL, 2, 273 }, 343207753Smm { "mf", mfs, 0, 0 }, 344207753Smm { "depth", NULL, 0, UINT32_MAX }, 345207753Smm { NULL, NULL, 0, 0 } 346207753Smm }; 347207753Smm 348207753Smm lzma_options_lzma *options = xmalloc(sizeof(lzma_options_lzma)); 349213700Smm if (lzma_lzma_preset(options, LZMA_PRESET_DEFAULT)) 350213700Smm message_bug(); 351207753Smm 352207753Smm parse_options(str, opts, &set_lzma, options); 353207753Smm 354207753Smm if (options->lc + options->lp > LZMA_LCLP_MAX) 355213700Smm message_fatal(_("The sum of lc and lp must not exceed 4")); 356207753Smm 357207753Smm const uint32_t nice_len_min = options->mf & 0x0F; 358207753Smm if (options->nice_len < nice_len_min) 359207753Smm message_fatal(_("The selected match finder requires at " 360207753Smm "least nice=%" PRIu32), nice_len_min); 361207753Smm 362207753Smm return options; 363207753Smm} 364