1 // SPDX-License-Identifier: 0BSD 2 3 /////////////////////////////////////////////////////////////////////////////// 4 // 5 /// \file options.c 6 /// \brief Parser for filter-specific options 7 // 8 // Author: Lasse Collin 9 // 10 /////////////////////////////////////////////////////////////////////////////// 11 12 #include "private.h" 13 14 15 /////////////////// 16 // Generic stuff // 17 /////////////////// 18 19 typedef struct { 20 const char *name; 21 uint64_t id; 22 } name_id_map; 23 24 25 typedef struct { 26 const char *name; 27 const name_id_map *map; 28 uint64_t min; 29 uint64_t max; 30 } option_map; 31 32 33 /// Parses option=value pairs that are separated with commas: 34 /// opt=val,opt=val,opt=val 35 /// 36 /// Each option is a string, that is converted to an integer using the 37 /// index where the option string is in the array. 38 /// 39 /// Value can be 40 /// - a string-id map mapping a list of possible string values to integers 41 /// (opts[i].map != NULL, opts[i].min and opts[i].max are ignored); 42 /// - a number with minimum and maximum value limit 43 /// (opts[i].map == NULL && opts[i].min != UINT64_MAX); 44 /// - a string that will be parsed by the filter-specific code 45 /// (opts[i].map == NULL && opts[i].min == UINT64_MAX, opts[i].max ignored) 46 /// 47 /// When parsing both option and value succeed, a filter-specific function 48 /// is called, which should update the given value to filter-specific 49 /// options structure. 50 /// 51 /// This returns only if no errors occur. 52 /// 53 /// \param str String containing the options from the command line 54 /// \param opts Filter-specific option map 55 /// \param set Filter-specific function to update filter_options 56 /// \param filter_options Pointer to filter-specific options structure 57 /// 58 static void 59 parse_options(const char *str, const option_map *opts, 60 void (*set)(void *filter_options, 61 unsigned key, uint64_t value, const char *valuestr), 62 void *filter_options) 63 { 64 if (str == NULL || str[0] == '\0') 65 return; 66 67 char *s = xstrdup(str); 68 char *name = s; 69 70 while (*name != '\0') { 71 if (*name == ',') { 72 ++name; 73 continue; 74 } 75 76 char *split = strchr(name, ','); 77 if (split != NULL) 78 *split = '\0'; 79 80 char *value = strchr(name, '='); 81 if (value != NULL) 82 *value++ = '\0'; 83 84 if (value == NULL || value[0] == '\0') 85 message_fatal(_("%s: %s"), tuklib_mask_nonprint(str), 86 _("Options must be 'name=value' " 87 "pairs separated with commas")); 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 tuklib_mask_nonprint(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: %s"), 114 tuklib_mask_nonprint(value), 115 _("Invalid option value")); 116 117 set(filter_options, i, opts[i].map[j].id, value); 118 119 } else if (opts[i].min == UINT64_MAX) { 120 // value is a special string that will be 121 // parsed by set(). 122 set(filter_options, i, 0, value); 123 124 } else { 125 // value is an integer. 126 const uint64_t v = str_to_uint64(name, value, 127 opts[i].min, opts[i].max); 128 set(filter_options, i, v, value); 129 } 130 131 // Check if it was the last option. 132 if (split == NULL) 133 break; 134 135 name = split + 1; 136 } 137 138 free(s); 139 return; 140 } 141 142 143 /////////// 144 // Delta // 145 /////////// 146 147 enum { 148 OPT_DIST, 149 }; 150 151 152 static void 153 set_delta(void *options, unsigned key, uint64_t value, 154 const char *valuestr lzma_attribute((__unused__))) 155 { 156 lzma_options_delta *opt = options; 157 switch (key) { 158 case OPT_DIST: 159 opt->dist = value; 160 break; 161 } 162 } 163 164 165 extern lzma_options_delta * 166 options_delta(const char *str) 167 { 168 static const option_map opts[] = { 169 { "dist", NULL, LZMA_DELTA_DIST_MIN, 170 LZMA_DELTA_DIST_MAX }, 171 { NULL, NULL, 0, 0 } 172 }; 173 174 lzma_options_delta *options = xmalloc(sizeof(lzma_options_delta)); 175 *options = (lzma_options_delta){ 176 // It's hard to give a useful default for this. 177 .type = LZMA_DELTA_TYPE_BYTE, 178 .dist = LZMA_DELTA_DIST_MIN, 179 }; 180 181 parse_options(str, opts, &set_delta, options); 182 183 return options; 184 } 185 186 187 ///////// 188 // BCJ // 189 ///////// 190 191 enum { 192 OPT_START_OFFSET, 193 }; 194 195 196 static void 197 set_bcj(void *options, unsigned key, uint64_t value, 198 const char *valuestr lzma_attribute((__unused__))) 199 { 200 lzma_options_bcj *opt = options; 201 switch (key) { 202 case OPT_START_OFFSET: 203 opt->start_offset = value; 204 break; 205 } 206 } 207 208 209 extern lzma_options_bcj * 210 options_bcj(const char *str) 211 { 212 static const option_map opts[] = { 213 { "start", NULL, 0, UINT32_MAX }, 214 { NULL, NULL, 0, 0 } 215 }; 216 217 lzma_options_bcj *options = xmalloc(sizeof(lzma_options_bcj)); 218 *options = (lzma_options_bcj){ 219 .start_offset = 0, 220 }; 221 222 parse_options(str, opts, &set_bcj, options); 223 224 return options; 225 } 226 227 228 ////////// 229 // LZMA // 230 ////////// 231 232 enum { 233 OPT_PRESET, 234 OPT_DICT, 235 OPT_LC, 236 OPT_LP, 237 OPT_PB, 238 OPT_MODE, 239 OPT_NICE, 240 OPT_MF, 241 OPT_DEPTH, 242 }; 243 244 245 tuklib_attr_noreturn 246 static void 247 error_lzma_preset(const char *valuestr) 248 { 249 message_fatal(_("Unsupported LZMA1/LZMA2 preset: %s"), 250 tuklib_mask_nonprint(valuestr)); 251 } 252 253 254 static void 255 set_lzma(void *options, unsigned key, uint64_t value, const char *valuestr) 256 { 257 lzma_options_lzma *opt = options; 258 259 switch (key) { 260 case OPT_PRESET: { 261 if (valuestr[0] < '0' || valuestr[0] > '9') 262 error_lzma_preset(valuestr); 263 264 uint32_t preset = (uint32_t)(valuestr[0] - '0'); 265 266 // Currently only "e" is supported as a modifier, 267 // so keep this simple for now. 268 if (valuestr[1] != '\0') { 269 if (valuestr[1] == 'e') 270 preset |= LZMA_PRESET_EXTREME; 271 else 272 error_lzma_preset(valuestr); 273 274 if (valuestr[2] != '\0') 275 error_lzma_preset(valuestr); 276 } 277 278 if (lzma_lzma_preset(options, preset)) 279 error_lzma_preset(valuestr); 280 281 break; 282 } 283 284 case OPT_DICT: 285 opt->dict_size = value; 286 break; 287 288 case OPT_LC: 289 opt->lc = value; 290 break; 291 292 case OPT_LP: 293 opt->lp = value; 294 break; 295 296 case OPT_PB: 297 opt->pb = value; 298 break; 299 300 case OPT_MODE: 301 opt->mode = value; 302 break; 303 304 case OPT_NICE: 305 opt->nice_len = value; 306 break; 307 308 case OPT_MF: 309 opt->mf = value; 310 break; 311 312 case OPT_DEPTH: 313 opt->depth = value; 314 break; 315 } 316 } 317 318 319 extern lzma_options_lzma * 320 options_lzma(const char *str) 321 { 322 static const name_id_map modes[] = { 323 { "fast", LZMA_MODE_FAST }, 324 { "normal", LZMA_MODE_NORMAL }, 325 { NULL, 0 } 326 }; 327 328 static const name_id_map mfs[] = { 329 { "hc3", LZMA_MF_HC3 }, 330 { "hc4", LZMA_MF_HC4 }, 331 { "bt2", LZMA_MF_BT2 }, 332 { "bt3", LZMA_MF_BT3 }, 333 { "bt4", LZMA_MF_BT4 }, 334 { NULL, 0 } 335 }; 336 337 static const option_map opts[] = { 338 { "preset", NULL, UINT64_MAX, 0 }, 339 { "dict", NULL, LZMA_DICT_SIZE_MIN, 340 (UINT32_C(1) << 30) + (UINT32_C(1) << 29) }, 341 { "lc", NULL, LZMA_LCLP_MIN, LZMA_LCLP_MAX }, 342 { "lp", NULL, LZMA_LCLP_MIN, LZMA_LCLP_MAX }, 343 { "pb", NULL, LZMA_PB_MIN, LZMA_PB_MAX }, 344 { "mode", modes, 0, 0 }, 345 { "nice", NULL, 2, 273 }, 346 { "mf", mfs, 0, 0 }, 347 { "depth", NULL, 0, UINT32_MAX }, 348 { NULL, NULL, 0, 0 } 349 }; 350 351 lzma_options_lzma *options = xmalloc(sizeof(lzma_options_lzma)); 352 if (lzma_lzma_preset(options, LZMA_PRESET_DEFAULT)) 353 message_bug(); 354 355 parse_options(str, opts, &set_lzma, options); 356 357 if (options->lc + options->lp > LZMA_LCLP_MAX) 358 message_fatal(_("The sum of lc and lp must not exceed 4")); 359 360 return options; 361 } 362