OLD | NEW |
(Empty) | |
| 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 // Delta // |
| 144 /////////// |
| 145 |
| 146 enum { |
| 147 OPT_DIST, |
| 148 }; |
| 149 |
| 150 |
| 151 static void |
| 152 set_delta(void *options, uint32_t 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 |
| 164 extern lzma_options_delta * |
| 165 options_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 |
| 190 enum { |
| 191 OPT_START_OFFSET, |
| 192 }; |
| 193 |
| 194 |
| 195 static void |
| 196 set_bcj(void *options, uint32_t 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 |
| 208 extern lzma_options_bcj * |
| 209 options_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 |
| 231 enum { |
| 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 |
| 244 static void lzma_attribute((noreturn)) |
| 245 error_lzma_preset(const char *valuestr) |
| 246 { |
| 247 message_fatal(_("Unsupported LZMA1/LZMA2 preset: %s"), valuestr); |
| 248 } |
| 249 |
| 250 |
| 251 static void |
| 252 set_lzma(void *options, uint32_t 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 |
| 316 extern lzma_options_lzma * |
| 317 options_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 *options = (lzma_options_lzma){ |
| 350 .dict_size = LZMA_DICT_SIZE_DEFAULT, |
| 351 .preset_dict = NULL, |
| 352 .preset_dict_size = 0, |
| 353 .lc = LZMA_LC_DEFAULT, |
| 354 .lp = LZMA_LP_DEFAULT, |
| 355 .pb = LZMA_PB_DEFAULT, |
| 356 .mode = LZMA_MODE_NORMAL, |
| 357 .nice_len = 64, |
| 358 .mf = LZMA_MF_BT4, |
| 359 .depth = 0, |
| 360 }; |
| 361 |
| 362 parse_options(str, opts, &set_lzma, options); |
| 363 |
| 364 if (options->lc + options->lp > LZMA_LCLP_MAX) |
| 365 message_fatal(_("The sum of lc and lp must be at " |
| 366 "maximum of 4")); |
| 367 |
| 368 const uint32_t nice_len_min = options->mf & 0x0F; |
| 369 if (options->nice_len < nice_len_min) |
| 370 message_fatal(_("The selected match finder requires at " |
| 371 "least nice=%" PRIu32), nice_len_min); |
| 372 |
| 373 return options; |
| 374 } |
OLD | NEW |