1*3b35e7eeSXin LI // SPDX-License-Identifier: 0BSD 2*3b35e7eeSXin LI 373ed8e77SXin LI /////////////////////////////////////////////////////////////////////////////// 473ed8e77SXin LI // 573ed8e77SXin LI /// \file string_conversion.c 673ed8e77SXin LI /// \brief Conversion of strings to filter chain and vice versa 773ed8e77SXin LI // 873ed8e77SXin LI // Author: Lasse Collin 973ed8e77SXin LI // 1073ed8e77SXin LI /////////////////////////////////////////////////////////////////////////////// 1173ed8e77SXin LI 1273ed8e77SXin LI #include "filter_common.h" 1373ed8e77SXin LI 1473ed8e77SXin LI 1573ed8e77SXin LI ///////////////////// 1673ed8e77SXin LI // String building // 1773ed8e77SXin LI ///////////////////// 1873ed8e77SXin LI 1973ed8e77SXin LI /// How much memory to allocate for strings. For now, no realloc is used 2073ed8e77SXin LI /// so this needs to be big enough even though there of course is 2173ed8e77SXin LI /// an overflow check still. 2273ed8e77SXin LI /// 2373ed8e77SXin LI /// FIXME? Using a fixed size is wasteful if the application doesn't free 2473ed8e77SXin LI /// the string fairly quickly but this can be improved later if needed. 2573ed8e77SXin LI #define STR_ALLOC_SIZE 800 2673ed8e77SXin LI 2773ed8e77SXin LI 2873ed8e77SXin LI typedef struct { 2973ed8e77SXin LI char *buf; 3073ed8e77SXin LI size_t pos; 3173ed8e77SXin LI } lzma_str; 3273ed8e77SXin LI 3373ed8e77SXin LI 3473ed8e77SXin LI static lzma_ret 3573ed8e77SXin LI str_init(lzma_str *str, const lzma_allocator *allocator) 3673ed8e77SXin LI { 3773ed8e77SXin LI str->buf = lzma_alloc(STR_ALLOC_SIZE, allocator); 3873ed8e77SXin LI if (str->buf == NULL) 3973ed8e77SXin LI return LZMA_MEM_ERROR; 4073ed8e77SXin LI 4173ed8e77SXin LI str->pos = 0; 4273ed8e77SXin LI return LZMA_OK; 4373ed8e77SXin LI } 4473ed8e77SXin LI 4573ed8e77SXin LI 4673ed8e77SXin LI static void 4773ed8e77SXin LI str_free(lzma_str *str, const lzma_allocator *allocator) 4873ed8e77SXin LI { 4973ed8e77SXin LI lzma_free(str->buf, allocator); 5073ed8e77SXin LI return; 5173ed8e77SXin LI } 5273ed8e77SXin LI 5373ed8e77SXin LI 5473ed8e77SXin LI static bool 5573ed8e77SXin LI str_is_full(const lzma_str *str) 5673ed8e77SXin LI { 5773ed8e77SXin LI return str->pos == STR_ALLOC_SIZE - 1; 5873ed8e77SXin LI } 5973ed8e77SXin LI 6073ed8e77SXin LI 6173ed8e77SXin LI static lzma_ret 6273ed8e77SXin LI str_finish(char **dest, lzma_str *str, const lzma_allocator *allocator) 6373ed8e77SXin LI { 6473ed8e77SXin LI if (str_is_full(str)) { 6573ed8e77SXin LI // The preallocated buffer was too small. 6673ed8e77SXin LI // This shouldn't happen as STR_ALLOC_SIZE should 6773ed8e77SXin LI // be adjusted if new filters are added. 6873ed8e77SXin LI lzma_free(str->buf, allocator); 6973ed8e77SXin LI *dest = NULL; 7073ed8e77SXin LI assert(0); 7173ed8e77SXin LI return LZMA_PROG_ERROR; 7273ed8e77SXin LI } 7373ed8e77SXin LI 7473ed8e77SXin LI str->buf[str->pos] = '\0'; 7573ed8e77SXin LI *dest = str->buf; 7673ed8e77SXin LI return LZMA_OK; 7773ed8e77SXin LI } 7873ed8e77SXin LI 7973ed8e77SXin LI 8073ed8e77SXin LI static void 8173ed8e77SXin LI str_append_str(lzma_str *str, const char *s) 8273ed8e77SXin LI { 8373ed8e77SXin LI const size_t len = strlen(s); 8473ed8e77SXin LI const size_t limit = STR_ALLOC_SIZE - 1 - str->pos; 8573ed8e77SXin LI const size_t copy_size = my_min(len, limit); 8673ed8e77SXin LI 8773ed8e77SXin LI memcpy(str->buf + str->pos, s, copy_size); 8873ed8e77SXin LI str->pos += copy_size; 8973ed8e77SXin LI return; 9073ed8e77SXin LI } 9173ed8e77SXin LI 9273ed8e77SXin LI 9373ed8e77SXin LI static void 9473ed8e77SXin LI str_append_u32(lzma_str *str, uint32_t v, bool use_byte_suffix) 9573ed8e77SXin LI { 9673ed8e77SXin LI if (v == 0) { 9773ed8e77SXin LI str_append_str(str, "0"); 9873ed8e77SXin LI } else { 9973ed8e77SXin LI // NOTE: Don't use plain "B" because xz and the parser in this 10073ed8e77SXin LI // file don't support it and at glance it may look like 8 10173ed8e77SXin LI // (there cannot be a space before the suffix). 10273ed8e77SXin LI static const char suffixes[4][4] = { "", "KiB", "MiB", "GiB" }; 10373ed8e77SXin LI 10473ed8e77SXin LI size_t suf = 0; 10573ed8e77SXin LI if (use_byte_suffix) { 10673ed8e77SXin LI while ((v & 1023) == 0 10773ed8e77SXin LI && suf < ARRAY_SIZE(suffixes) - 1) { 10873ed8e77SXin LI v >>= 10; 10973ed8e77SXin LI ++suf; 11073ed8e77SXin LI } 11173ed8e77SXin LI } 11273ed8e77SXin LI 11373ed8e77SXin LI // UINT32_MAX in base 10 would need 10 + 1 bytes. Remember 11473ed8e77SXin LI // that initializing to "" initializes all elements to 11573ed8e77SXin LI // zero so '\0'-termination gets handled by this. 11673ed8e77SXin LI char buf[16] = ""; 11773ed8e77SXin LI size_t pos = sizeof(buf) - 1; 11873ed8e77SXin LI 11973ed8e77SXin LI do { 12073ed8e77SXin LI buf[--pos] = '0' + (v % 10); 12173ed8e77SXin LI v /= 10; 12273ed8e77SXin LI } while (v != 0); 12373ed8e77SXin LI 12473ed8e77SXin LI str_append_str(str, buf + pos); 12573ed8e77SXin LI str_append_str(str, suffixes[suf]); 12673ed8e77SXin LI } 12773ed8e77SXin LI 12873ed8e77SXin LI return; 12973ed8e77SXin LI } 13073ed8e77SXin LI 13173ed8e77SXin LI 13273ed8e77SXin LI ////////////////////////////////////////////// 13373ed8e77SXin LI // Parsing and stringification declarations // 13473ed8e77SXin LI ////////////////////////////////////////////// 13573ed8e77SXin LI 13673ed8e77SXin LI /// Maximum length for filter and option names. 13773ed8e77SXin LI /// 11 chars + terminating '\0' + sizeof(uint32_t) = 16 bytes 13873ed8e77SXin LI #define NAME_LEN_MAX 11 13973ed8e77SXin LI 14073ed8e77SXin LI 14173ed8e77SXin LI /// For option_map.flags: Use .u.map to do convert the input value 14273ed8e77SXin LI /// to an integer. Without this flag, .u.range.{min,max} are used 14373ed8e77SXin LI /// as the allowed range for the integer. 14473ed8e77SXin LI #define OPTMAP_USE_NAME_VALUE_MAP 0x01 14573ed8e77SXin LI 14673ed8e77SXin LI /// For option_map.flags: Allow KiB/MiB/GiB in input string and use them in 14773ed8e77SXin LI /// the stringified output if the value is an exact multiple of these. 14873ed8e77SXin LI /// This is used e.g. for LZMA1/2 dictionary size. 14973ed8e77SXin LI #define OPTMAP_USE_BYTE_SUFFIX 0x02 15073ed8e77SXin LI 15173ed8e77SXin LI /// For option_map.flags: If the integer value is zero then this option 15273ed8e77SXin LI /// won't be included in the stringified output. It's used e.g. for 15373ed8e77SXin LI /// BCJ filter start offset which usually is zero. 15473ed8e77SXin LI #define OPTMAP_NO_STRFY_ZERO 0x04 15573ed8e77SXin LI 15673ed8e77SXin LI /// Possible values for option_map.type. Since OPTMAP_TYPE_UINT32 is 0, 15773ed8e77SXin LI /// it doesn't need to be specified in the initializers as it is 15873ed8e77SXin LI /// the implicit value. 15973ed8e77SXin LI enum { 16073ed8e77SXin LI OPTMAP_TYPE_UINT32, 16173ed8e77SXin LI OPTMAP_TYPE_LZMA_MODE, 16273ed8e77SXin LI OPTMAP_TYPE_LZMA_MATCH_FINDER, 16373ed8e77SXin LI OPTMAP_TYPE_LZMA_PRESET, 16473ed8e77SXin LI }; 16573ed8e77SXin LI 16673ed8e77SXin LI 16773ed8e77SXin LI /// This is for mapping string values in options to integers. 16873ed8e77SXin LI /// The last element of an array must have "" as the name. 16973ed8e77SXin LI /// It's used e.g. for match finder names in LZMA1/2. 17073ed8e77SXin LI typedef struct { 17173ed8e77SXin LI const char name[NAME_LEN_MAX + 1]; 17273ed8e77SXin LI const uint32_t value; 17373ed8e77SXin LI } name_value_map; 17473ed8e77SXin LI 17573ed8e77SXin LI 17673ed8e77SXin LI /// Each filter that has options needs an array of option_map structures. 17773ed8e77SXin LI /// The array doesn't need to be terminated as the functions take the 17873ed8e77SXin LI /// length of the array as an argument. 17973ed8e77SXin LI /// 18073ed8e77SXin LI /// When converting a string to filter options structure, option values 18173ed8e77SXin LI /// will be handled in a few different ways: 18273ed8e77SXin LI /// 18373ed8e77SXin LI /// (1) If .type equals OPTMAP_TYPE_LZMA_PRESET then LZMA1/2 preset string 18473ed8e77SXin LI /// is handled specially. 18573ed8e77SXin LI /// 18673ed8e77SXin LI /// (2) If .flags has OPTMAP_USE_NAME_VALUE_MAP set then the string is 18773ed8e77SXin LI /// converted to an integer using the name_value_map pointed by .u.map. 18873ed8e77SXin LI /// The last element in .u.map must have .name = "" as the terminator. 18973ed8e77SXin LI /// 19073ed8e77SXin LI /// (3) Otherwise the string is treated as a non-negative unsigned decimal 19173ed8e77SXin LI /// integer which must be in the range set in .u.range. If .flags has 19273ed8e77SXin LI /// OPTMAP_USE_BYTE_SUFFIX then KiB, MiB, and GiB suffixes are allowed. 19373ed8e77SXin LI /// 19473ed8e77SXin LI /// The integer value from (2) or (3) is then stored to filter_options 19573ed8e77SXin LI /// at the offset specified in .offset using the type specified in .type 19673ed8e77SXin LI /// (default is uint32_t). 19773ed8e77SXin LI /// 19873ed8e77SXin LI /// Stringifying a filter is done by processing a given number of options 1991f3ced26SXin LI /// in order from the beginning of an option_map array. The integer is 20073ed8e77SXin LI /// read from filter_options at .offset using the type from .type. 20173ed8e77SXin LI /// 20273ed8e77SXin LI /// If the integer is zero and .flags has OPTMAP_NO_STRFY_ZERO then the 20373ed8e77SXin LI /// option is skipped. 20473ed8e77SXin LI /// 20573ed8e77SXin LI /// If .flags has OPTMAP_USE_NAME_VALUE_MAP set then .u.map will be used 20673ed8e77SXin LI /// to convert the option to a string. If the map doesn't contain a string 20773ed8e77SXin LI /// for the integer value then "UNKNOWN" is used. 20873ed8e77SXin LI /// 20973ed8e77SXin LI /// If .flags doesn't have OPTMAP_USE_NAME_VALUE_MAP set then the integer is 21073ed8e77SXin LI /// converted to a decimal value. If OPTMAP_USE_BYTE_SUFFIX is used then KiB, 21173ed8e77SXin LI /// MiB, or GiB suffix is used if the value is an exact multiple of these. 21273ed8e77SXin LI /// Plain "B" suffix is never used. 21373ed8e77SXin LI typedef struct { 21473ed8e77SXin LI char name[NAME_LEN_MAX + 1]; 21573ed8e77SXin LI uint8_t type; 21673ed8e77SXin LI uint8_t flags; 21773ed8e77SXin LI uint16_t offset; 21873ed8e77SXin LI 21973ed8e77SXin LI union { 220*3b35e7eeSXin LI // NVHPC has problems with unions that contain pointers that 221*3b35e7eeSXin LI // are not the first members, so keep "map" at the top. 222*3b35e7eeSXin LI const name_value_map *map; 223*3b35e7eeSXin LI 22473ed8e77SXin LI struct { 22573ed8e77SXin LI uint32_t min; 22673ed8e77SXin LI uint32_t max; 22773ed8e77SXin LI } range; 22873ed8e77SXin LI } u; 22973ed8e77SXin LI } option_map; 23073ed8e77SXin LI 23173ed8e77SXin LI 23273ed8e77SXin LI static const char *parse_options(const char **const str, const char *str_end, 23373ed8e77SXin LI void *filter_options, 23473ed8e77SXin LI const option_map *const optmap, const size_t optmap_size); 23573ed8e77SXin LI 23673ed8e77SXin LI 23773ed8e77SXin LI ///////// 23873ed8e77SXin LI // BCJ // 23973ed8e77SXin LI ///////// 24073ed8e77SXin LI 24173ed8e77SXin LI #if defined(HAVE_ENCODER_X86) \ 24273ed8e77SXin LI || defined(HAVE_DECODER_X86) \ 24373ed8e77SXin LI || defined(HAVE_ENCODER_ARM) \ 24473ed8e77SXin LI || defined(HAVE_DECODER_ARM) \ 24573ed8e77SXin LI || defined(HAVE_ENCODER_ARMTHUMB) \ 24673ed8e77SXin LI || defined(HAVE_DECODER_ARMTHUMB) \ 24773ed8e77SXin LI || defined(HAVE_ENCODER_ARM64) \ 24873ed8e77SXin LI || defined(HAVE_DECODER_ARM64) \ 24973ed8e77SXin LI || defined(HAVE_ENCODER_POWERPC) \ 25073ed8e77SXin LI || defined(HAVE_DECODER_POWERPC) \ 25173ed8e77SXin LI || defined(HAVE_ENCODER_IA64) \ 25273ed8e77SXin LI || defined(HAVE_DECODER_IA64) \ 25373ed8e77SXin LI || defined(HAVE_ENCODER_SPARC) \ 254*3b35e7eeSXin LI || defined(HAVE_DECODER_SPARC) \ 255*3b35e7eeSXin LI || defined(HAVE_ENCODER_RISCV) \ 256*3b35e7eeSXin LI || defined(HAVE_DECODER_RISCV) 25773ed8e77SXin LI static const option_map bcj_optmap[] = { 25873ed8e77SXin LI { 25973ed8e77SXin LI .name = "start", 26073ed8e77SXin LI .flags = OPTMAP_NO_STRFY_ZERO | OPTMAP_USE_BYTE_SUFFIX, 26173ed8e77SXin LI .offset = offsetof(lzma_options_bcj, start_offset), 26273ed8e77SXin LI .u.range.min = 0, 26373ed8e77SXin LI .u.range.max = UINT32_MAX, 26473ed8e77SXin LI } 26573ed8e77SXin LI }; 26673ed8e77SXin LI 26773ed8e77SXin LI 26873ed8e77SXin LI static const char * 26973ed8e77SXin LI parse_bcj(const char **const str, const char *str_end, void *filter_options) 27073ed8e77SXin LI { 27173ed8e77SXin LI // filter_options was zeroed on allocation and that is enough 27273ed8e77SXin LI // for the default value. 27373ed8e77SXin LI return parse_options(str, str_end, filter_options, 27473ed8e77SXin LI bcj_optmap, ARRAY_SIZE(bcj_optmap)); 27573ed8e77SXin LI } 27673ed8e77SXin LI #endif 27773ed8e77SXin LI 27873ed8e77SXin LI 27973ed8e77SXin LI /////////// 28073ed8e77SXin LI // Delta // 28173ed8e77SXin LI /////////// 28273ed8e77SXin LI 28373ed8e77SXin LI #if defined(HAVE_ENCODER_DELTA) || defined(HAVE_DECODER_DELTA) 28473ed8e77SXin LI static const option_map delta_optmap[] = { 28573ed8e77SXin LI { 28673ed8e77SXin LI .name = "dist", 28773ed8e77SXin LI .offset = offsetof(lzma_options_delta, dist), 28873ed8e77SXin LI .u.range.min = LZMA_DELTA_DIST_MIN, 28973ed8e77SXin LI .u.range.max = LZMA_DELTA_DIST_MAX, 29073ed8e77SXin LI } 29173ed8e77SXin LI }; 29273ed8e77SXin LI 29373ed8e77SXin LI 29473ed8e77SXin LI static const char * 29573ed8e77SXin LI parse_delta(const char **const str, const char *str_end, void *filter_options) 29673ed8e77SXin LI { 29773ed8e77SXin LI lzma_options_delta *opts = filter_options; 29873ed8e77SXin LI opts->type = LZMA_DELTA_TYPE_BYTE; 29973ed8e77SXin LI opts->dist = LZMA_DELTA_DIST_MIN; 30073ed8e77SXin LI 30173ed8e77SXin LI return parse_options(str, str_end, filter_options, 30273ed8e77SXin LI delta_optmap, ARRAY_SIZE(delta_optmap)); 30373ed8e77SXin LI } 30473ed8e77SXin LI #endif 30573ed8e77SXin LI 30673ed8e77SXin LI 30773ed8e77SXin LI /////////////////// 30873ed8e77SXin LI // LZMA1 & LZMA2 // 30973ed8e77SXin LI /////////////////// 31073ed8e77SXin LI 31173ed8e77SXin LI /// Help string for presets 31273ed8e77SXin LI #define LZMA12_PRESET_STR "0-9[e]" 31373ed8e77SXin LI 31473ed8e77SXin LI 31573ed8e77SXin LI static const char * 31673ed8e77SXin LI parse_lzma12_preset(const char **const str, const char *str_end, 31773ed8e77SXin LI uint32_t *preset) 31873ed8e77SXin LI { 31973ed8e77SXin LI assert(*str < str_end); 32073ed8e77SXin LI *preset = (uint32_t)(**str - '0'); 32173ed8e77SXin LI 322c917796cSXin LI // NOTE: Remember to update LZMA12_PRESET_STR if this is modified! 32373ed8e77SXin LI while (++*str < str_end) { 32473ed8e77SXin LI switch (**str) { 32573ed8e77SXin LI case 'e': 32673ed8e77SXin LI *preset |= LZMA_PRESET_EXTREME; 32773ed8e77SXin LI break; 32873ed8e77SXin LI 32973ed8e77SXin LI default: 33073ed8e77SXin LI return "Unsupported preset flag"; 33173ed8e77SXin LI } 33273ed8e77SXin LI } 33373ed8e77SXin LI 33473ed8e77SXin LI return NULL; 33573ed8e77SXin LI } 33673ed8e77SXin LI 33773ed8e77SXin LI 33873ed8e77SXin LI static const char * 33973ed8e77SXin LI set_lzma12_preset(const char **const str, const char *str_end, 34073ed8e77SXin LI void *filter_options) 34173ed8e77SXin LI { 34273ed8e77SXin LI uint32_t preset; 34373ed8e77SXin LI const char *errmsg = parse_lzma12_preset(str, str_end, &preset); 34473ed8e77SXin LI if (errmsg != NULL) 34573ed8e77SXin LI return errmsg; 34673ed8e77SXin LI 34773ed8e77SXin LI lzma_options_lzma *opts = filter_options; 34873ed8e77SXin LI if (lzma_lzma_preset(opts, preset)) 34973ed8e77SXin LI return "Unsupported preset"; 35073ed8e77SXin LI 35173ed8e77SXin LI return NULL; 35273ed8e77SXin LI } 35373ed8e77SXin LI 35473ed8e77SXin LI 35573ed8e77SXin LI static const name_value_map lzma12_mode_map[] = { 35673ed8e77SXin LI { "fast", LZMA_MODE_FAST }, 35773ed8e77SXin LI { "normal", LZMA_MODE_NORMAL }, 35873ed8e77SXin LI { "", 0 } 35973ed8e77SXin LI }; 36073ed8e77SXin LI 36173ed8e77SXin LI 36273ed8e77SXin LI static const name_value_map lzma12_mf_map[] = { 36373ed8e77SXin LI { "hc3", LZMA_MF_HC3 }, 36473ed8e77SXin LI { "hc4", LZMA_MF_HC4 }, 36573ed8e77SXin LI { "bt2", LZMA_MF_BT2 }, 36673ed8e77SXin LI { "bt3", LZMA_MF_BT3 }, 36773ed8e77SXin LI { "bt4", LZMA_MF_BT4 }, 36873ed8e77SXin LI { "", 0 } 36973ed8e77SXin LI }; 37073ed8e77SXin LI 37173ed8e77SXin LI 37273ed8e77SXin LI static const option_map lzma12_optmap[] = { 37373ed8e77SXin LI { 37473ed8e77SXin LI .name = "preset", 37573ed8e77SXin LI .type = OPTMAP_TYPE_LZMA_PRESET, 37673ed8e77SXin LI }, { 37773ed8e77SXin LI .name = "dict", 37873ed8e77SXin LI .flags = OPTMAP_USE_BYTE_SUFFIX, 37973ed8e77SXin LI .offset = offsetof(lzma_options_lzma, dict_size), 38073ed8e77SXin LI .u.range.min = LZMA_DICT_SIZE_MIN, 38173ed8e77SXin LI // FIXME? The max is really max for encoding but decoding 38273ed8e77SXin LI // would allow 4 GiB - 1 B. 38373ed8e77SXin LI .u.range.max = (UINT32_C(1) << 30) + (UINT32_C(1) << 29), 38473ed8e77SXin LI }, { 38573ed8e77SXin LI .name = "lc", 38673ed8e77SXin LI .offset = offsetof(lzma_options_lzma, lc), 38773ed8e77SXin LI .u.range.min = LZMA_LCLP_MIN, 38873ed8e77SXin LI .u.range.max = LZMA_LCLP_MAX, 38973ed8e77SXin LI }, { 39073ed8e77SXin LI .name = "lp", 39173ed8e77SXin LI .offset = offsetof(lzma_options_lzma, lp), 39273ed8e77SXin LI .u.range.min = LZMA_LCLP_MIN, 39373ed8e77SXin LI .u.range.max = LZMA_LCLP_MAX, 39473ed8e77SXin LI }, { 39573ed8e77SXin LI .name = "pb", 39673ed8e77SXin LI .offset = offsetof(lzma_options_lzma, pb), 39773ed8e77SXin LI .u.range.min = LZMA_PB_MIN, 39873ed8e77SXin LI .u.range.max = LZMA_PB_MAX, 39973ed8e77SXin LI }, { 40073ed8e77SXin LI .name = "mode", 40173ed8e77SXin LI .type = OPTMAP_TYPE_LZMA_MODE, 40273ed8e77SXin LI .flags = OPTMAP_USE_NAME_VALUE_MAP, 40373ed8e77SXin LI .offset = offsetof(lzma_options_lzma, mode), 40473ed8e77SXin LI .u.map = lzma12_mode_map, 40573ed8e77SXin LI }, { 40673ed8e77SXin LI .name = "nice", 40773ed8e77SXin LI .offset = offsetof(lzma_options_lzma, nice_len), 40873ed8e77SXin LI .u.range.min = 2, 40973ed8e77SXin LI .u.range.max = 273, 41073ed8e77SXin LI }, { 41173ed8e77SXin LI .name = "mf", 41273ed8e77SXin LI .type = OPTMAP_TYPE_LZMA_MATCH_FINDER, 41373ed8e77SXin LI .flags = OPTMAP_USE_NAME_VALUE_MAP, 41473ed8e77SXin LI .offset = offsetof(lzma_options_lzma, mf), 41573ed8e77SXin LI .u.map = lzma12_mf_map, 41673ed8e77SXin LI }, { 41773ed8e77SXin LI .name = "depth", 41873ed8e77SXin LI .offset = offsetof(lzma_options_lzma, depth), 41973ed8e77SXin LI .u.range.min = 0, 42073ed8e77SXin LI .u.range.max = UINT32_MAX, 42173ed8e77SXin LI } 42273ed8e77SXin LI }; 42373ed8e77SXin LI 42473ed8e77SXin LI 42573ed8e77SXin LI static const char * 42673ed8e77SXin LI parse_lzma12(const char **const str, const char *str_end, void *filter_options) 42773ed8e77SXin LI { 42873ed8e77SXin LI lzma_options_lzma *opts = filter_options; 42973ed8e77SXin LI 43073ed8e77SXin LI // It cannot fail. 43173ed8e77SXin LI const bool preset_ret = lzma_lzma_preset(opts, LZMA_PRESET_DEFAULT); 43273ed8e77SXin LI assert(!preset_ret); 43373ed8e77SXin LI (void)preset_ret; 43473ed8e77SXin LI 43573ed8e77SXin LI const char *errmsg = parse_options(str, str_end, filter_options, 43673ed8e77SXin LI lzma12_optmap, ARRAY_SIZE(lzma12_optmap)); 43773ed8e77SXin LI if (errmsg != NULL) 43873ed8e77SXin LI return errmsg; 43973ed8e77SXin LI 44073ed8e77SXin LI if (opts->lc + opts->lp > LZMA_LCLP_MAX) 44173ed8e77SXin LI return "The sum of lc and lp must not exceed 4"; 44273ed8e77SXin LI 44373ed8e77SXin LI return NULL; 44473ed8e77SXin LI } 44573ed8e77SXin LI 44673ed8e77SXin LI 44773ed8e77SXin LI ///////////////////////////////////////// 44873ed8e77SXin LI // Generic parsing and stringification // 44973ed8e77SXin LI ///////////////////////////////////////// 45073ed8e77SXin LI 45173ed8e77SXin LI static const struct { 45273ed8e77SXin LI /// Name of the filter 45373ed8e77SXin LI char name[NAME_LEN_MAX + 1]; 45473ed8e77SXin LI 45573ed8e77SXin LI /// For lzma_str_to_filters: 45673ed8e77SXin LI /// Size of the filter-specific options structure. 45773ed8e77SXin LI uint32_t opts_size; 45873ed8e77SXin LI 45973ed8e77SXin LI /// Filter ID 46073ed8e77SXin LI lzma_vli id; 46173ed8e77SXin LI 46273ed8e77SXin LI /// For lzma_str_to_filters: 46373ed8e77SXin LI /// Function to parse the filter-specific options. The filter_options 46473ed8e77SXin LI /// will already have been allocated using lzma_alloc_zero(). 46573ed8e77SXin LI const char *(*parse)(const char **str, const char *str_end, 46673ed8e77SXin LI void *filter_options); 46773ed8e77SXin LI 46873ed8e77SXin LI /// For lzma_str_from_filters: 46973ed8e77SXin LI /// If the flag LZMA_STR_ENCODER is used then the first 47073ed8e77SXin LI /// strfy_encoder elements of optmap are stringified. 47173ed8e77SXin LI /// With LZMA_STR_DECODER strfy_decoder is used. 4721f3ced26SXin LI /// Currently encoders use all options that decoders do but if 47373ed8e77SXin LI /// that changes then this needs to be changed too, for example, 4741f3ced26SXin LI /// add a new OPTMAP flag to skip printing some decoder-only options. 47573ed8e77SXin LI const option_map *optmap; 47673ed8e77SXin LI uint8_t strfy_encoder; 47773ed8e77SXin LI uint8_t strfy_decoder; 47873ed8e77SXin LI 47973ed8e77SXin LI /// For lzma_str_from_filters: 48073ed8e77SXin LI /// If true, lzma_filter.options is allowed to be NULL. In that case, 48173ed8e77SXin LI /// only the filter name is printed without any options. 48273ed8e77SXin LI bool allow_null; 48373ed8e77SXin LI 48473ed8e77SXin LI } filter_name_map[] = { 48573ed8e77SXin LI #if defined (HAVE_ENCODER_LZMA1) || defined(HAVE_DECODER_LZMA1) 48673ed8e77SXin LI { "lzma1", sizeof(lzma_options_lzma), LZMA_FILTER_LZMA1, 48773ed8e77SXin LI &parse_lzma12, lzma12_optmap, 9, 5, false }, 48873ed8e77SXin LI #endif 48973ed8e77SXin LI 49073ed8e77SXin LI #if defined(HAVE_ENCODER_LZMA2) || defined(HAVE_DECODER_LZMA2) 49173ed8e77SXin LI { "lzma2", sizeof(lzma_options_lzma), LZMA_FILTER_LZMA2, 49273ed8e77SXin LI &parse_lzma12, lzma12_optmap, 9, 2, false }, 49373ed8e77SXin LI #endif 49473ed8e77SXin LI 49573ed8e77SXin LI #if defined(HAVE_ENCODER_X86) || defined(HAVE_DECODER_X86) 49673ed8e77SXin LI { "x86", sizeof(lzma_options_bcj), LZMA_FILTER_X86, 49773ed8e77SXin LI &parse_bcj, bcj_optmap, 1, 1, true }, 49873ed8e77SXin LI #endif 49973ed8e77SXin LI 50073ed8e77SXin LI #if defined(HAVE_ENCODER_ARM) || defined(HAVE_DECODER_ARM) 50173ed8e77SXin LI { "arm", sizeof(lzma_options_bcj), LZMA_FILTER_ARM, 50273ed8e77SXin LI &parse_bcj, bcj_optmap, 1, 1, true }, 50373ed8e77SXin LI #endif 50473ed8e77SXin LI 50573ed8e77SXin LI #if defined(HAVE_ENCODER_ARMTHUMB) || defined(HAVE_DECODER_ARMTHUMB) 50673ed8e77SXin LI { "armthumb", sizeof(lzma_options_bcj), LZMA_FILTER_ARMTHUMB, 50773ed8e77SXin LI &parse_bcj, bcj_optmap, 1, 1, true }, 50873ed8e77SXin LI #endif 50973ed8e77SXin LI 51073ed8e77SXin LI #if defined(HAVE_ENCODER_ARM64) || defined(HAVE_DECODER_ARM64) 51173ed8e77SXin LI { "arm64", sizeof(lzma_options_bcj), LZMA_FILTER_ARM64, 51273ed8e77SXin LI &parse_bcj, bcj_optmap, 1, 1, true }, 51373ed8e77SXin LI #endif 51473ed8e77SXin LI 515*3b35e7eeSXin LI #if defined(HAVE_ENCODER_RISCV) || defined(HAVE_DECODER_RISCV) 516*3b35e7eeSXin LI { "riscv", sizeof(lzma_options_bcj), LZMA_FILTER_RISCV, 517*3b35e7eeSXin LI &parse_bcj, bcj_optmap, 1, 1, true }, 518*3b35e7eeSXin LI #endif 519*3b35e7eeSXin LI 52073ed8e77SXin LI #if defined(HAVE_ENCODER_POWERPC) || defined(HAVE_DECODER_POWERPC) 52173ed8e77SXin LI { "powerpc", sizeof(lzma_options_bcj), LZMA_FILTER_POWERPC, 52273ed8e77SXin LI &parse_bcj, bcj_optmap, 1, 1, true }, 52373ed8e77SXin LI #endif 52473ed8e77SXin LI 52573ed8e77SXin LI #if defined(HAVE_ENCODER_IA64) || defined(HAVE_DECODER_IA64) 52673ed8e77SXin LI { "ia64", sizeof(lzma_options_bcj), LZMA_FILTER_IA64, 52773ed8e77SXin LI &parse_bcj, bcj_optmap, 1, 1, true }, 52873ed8e77SXin LI #endif 52973ed8e77SXin LI 53073ed8e77SXin LI #if defined(HAVE_ENCODER_SPARC) || defined(HAVE_DECODER_SPARC) 53173ed8e77SXin LI { "sparc", sizeof(lzma_options_bcj), LZMA_FILTER_SPARC, 53273ed8e77SXin LI &parse_bcj, bcj_optmap, 1, 1, true }, 53373ed8e77SXin LI #endif 53473ed8e77SXin LI 53573ed8e77SXin LI #if defined(HAVE_ENCODER_DELTA) || defined(HAVE_DECODER_DELTA) 53673ed8e77SXin LI { "delta", sizeof(lzma_options_delta), LZMA_FILTER_DELTA, 53773ed8e77SXin LI &parse_delta, delta_optmap, 1, 1, false }, 53873ed8e77SXin LI #endif 53973ed8e77SXin LI }; 54073ed8e77SXin LI 54173ed8e77SXin LI 54273ed8e77SXin LI /// Decodes options from a string for one filter (name1=value1,name2=value2). 54373ed8e77SXin LI /// Caller must have allocated memory for filter_options already and set 54473ed8e77SXin LI /// the initial default values. This is called from the filter-specific 54573ed8e77SXin LI /// parse_* functions. 54673ed8e77SXin LI /// 54773ed8e77SXin LI /// The input string starts at *str and the address in str_end is the first 54873ed8e77SXin LI /// char that is not part of the string anymore. So no '\0' terminator is 54973ed8e77SXin LI /// used. *str is advanced every time something has been decoded successfully. 55073ed8e77SXin LI static const char * 55173ed8e77SXin LI parse_options(const char **const str, const char *str_end, 55273ed8e77SXin LI void *filter_options, 55373ed8e77SXin LI const option_map *const optmap, const size_t optmap_size) 55473ed8e77SXin LI { 55573ed8e77SXin LI while (*str < str_end && **str != '\0') { 55673ed8e77SXin LI // Each option is of the form name=value. 55773ed8e77SXin LI // Commas (',') separate options. Extra commas are ignored. 55873ed8e77SXin LI // Ignoring extra commas makes it simpler if an optional 55973ed8e77SXin LI // option stored in a shell variable which can be empty. 56073ed8e77SXin LI if (**str == ',') { 56173ed8e77SXin LI ++*str; 56273ed8e77SXin LI continue; 56373ed8e77SXin LI } 56473ed8e77SXin LI 56573ed8e77SXin LI // Find where the next name=value ends. 56673ed8e77SXin LI const size_t str_len = (size_t)(str_end - *str); 56773ed8e77SXin LI const char *name_eq_value_end = memchr(*str, ',', str_len); 56873ed8e77SXin LI if (name_eq_value_end == NULL) 56973ed8e77SXin LI name_eq_value_end = str_end; 57073ed8e77SXin LI 57173ed8e77SXin LI const char *equals_sign = memchr(*str, '=', 57273ed8e77SXin LI (size_t)(name_eq_value_end - *str)); 57373ed8e77SXin LI 57473ed8e77SXin LI // Fail if the '=' wasn't found or the option name is missing 57573ed8e77SXin LI // (the first char is '='). 57673ed8e77SXin LI if (equals_sign == NULL || **str == '=') 57773ed8e77SXin LI return "Options must be 'name=value' pairs separated " 57873ed8e77SXin LI "with commas"; 57973ed8e77SXin LI 58073ed8e77SXin LI // Reject a too long option name so that the memcmp() 58173ed8e77SXin LI // in the loop below won't read past the end of the 58273ed8e77SXin LI // string in optmap[i].name. 58373ed8e77SXin LI const size_t name_len = (size_t)(equals_sign - *str); 58473ed8e77SXin LI if (name_len > NAME_LEN_MAX) 58573ed8e77SXin LI return "Unknown option name"; 58673ed8e77SXin LI 58773ed8e77SXin LI // Find the option name from optmap[]. 58873ed8e77SXin LI size_t i = 0; 58973ed8e77SXin LI while (true) { 59073ed8e77SXin LI if (i == optmap_size) 59173ed8e77SXin LI return "Unknown option name"; 59273ed8e77SXin LI 59373ed8e77SXin LI if (memcmp(*str, optmap[i].name, name_len) == 0 59473ed8e77SXin LI && optmap[i].name[name_len] == '\0') 59573ed8e77SXin LI break; 59673ed8e77SXin LI 59773ed8e77SXin LI ++i; 59873ed8e77SXin LI } 59973ed8e77SXin LI 60073ed8e77SXin LI // The input string is good at least until the start of 60173ed8e77SXin LI // the option value. 60273ed8e77SXin LI *str = equals_sign + 1; 60373ed8e77SXin LI 60473ed8e77SXin LI // The code assumes that the option value isn't an empty 60573ed8e77SXin LI // string so check it here. 60673ed8e77SXin LI const size_t value_len = (size_t)(name_eq_value_end - *str); 60773ed8e77SXin LI if (value_len == 0) 60873ed8e77SXin LI return "Option value cannot be empty"; 60973ed8e77SXin LI 61073ed8e77SXin LI // LZMA1/2 preset has its own parsing function. 61173ed8e77SXin LI if (optmap[i].type == OPTMAP_TYPE_LZMA_PRESET) { 61273ed8e77SXin LI const char *errmsg = set_lzma12_preset(str, 61373ed8e77SXin LI name_eq_value_end, filter_options); 61473ed8e77SXin LI if (errmsg != NULL) 61573ed8e77SXin LI return errmsg; 61673ed8e77SXin LI 61773ed8e77SXin LI continue; 61873ed8e77SXin LI } 61973ed8e77SXin LI 62073ed8e77SXin LI // It's an integer value. 62173ed8e77SXin LI uint32_t v; 62273ed8e77SXin LI if (optmap[i].flags & OPTMAP_USE_NAME_VALUE_MAP) { 62373ed8e77SXin LI // The integer is picked from a string-to-integer map. 62473ed8e77SXin LI // 62573ed8e77SXin LI // Reject a too long value string so that the memcmp() 62673ed8e77SXin LI // in the loop below won't read past the end of the 62773ed8e77SXin LI // string in optmap[i].u.map[j].name. 62873ed8e77SXin LI if (value_len > NAME_LEN_MAX) 62973ed8e77SXin LI return "Invalid option value"; 63073ed8e77SXin LI 63173ed8e77SXin LI const name_value_map *map = optmap[i].u.map; 63273ed8e77SXin LI size_t j = 0; 63373ed8e77SXin LI while (true) { 63473ed8e77SXin LI // The array is terminated with an empty name. 63573ed8e77SXin LI if (map[j].name[0] == '\0') 63673ed8e77SXin LI return "Invalid option value"; 63773ed8e77SXin LI 63873ed8e77SXin LI if (memcmp(*str, map[j].name, value_len) == 0 63973ed8e77SXin LI && map[j].name[value_len] 64073ed8e77SXin LI == '\0') { 64173ed8e77SXin LI v = map[j].value; 64273ed8e77SXin LI break; 64373ed8e77SXin LI } 64473ed8e77SXin LI 64573ed8e77SXin LI ++j; 64673ed8e77SXin LI } 64773ed8e77SXin LI } else if (**str < '0' || **str > '9') { 64873ed8e77SXin LI // Note that "max" isn't supported while it is 64973ed8e77SXin LI // supported in xz. It's not useful here. 65073ed8e77SXin LI return "Value is not a non-negative decimal integer"; 65173ed8e77SXin LI } else { 65273ed8e77SXin LI // strtoul() has locale-specific behavior so it cannot 65373ed8e77SXin LI // be relied on to get reproducible results since we 65473ed8e77SXin LI // cannot change the locate in a thread-safe library. 65573ed8e77SXin LI // It also needs '\0'-termination. 65673ed8e77SXin LI // 65773ed8e77SXin LI // Use a temporary pointer so that *str will point 65873ed8e77SXin LI // to the beginning of the value string in case 65973ed8e77SXin LI // an error occurs. 66073ed8e77SXin LI const char *p = *str; 66173ed8e77SXin LI v = 0; 66273ed8e77SXin LI do { 66373ed8e77SXin LI if (v > UINT32_MAX / 10) 66473ed8e77SXin LI return "Value out of range"; 66573ed8e77SXin LI 66673ed8e77SXin LI v *= 10; 66773ed8e77SXin LI 66873ed8e77SXin LI const uint32_t add = (uint32_t)(*p - '0'); 66973ed8e77SXin LI if (UINT32_MAX - add < v) 67073ed8e77SXin LI return "Value out of range"; 67173ed8e77SXin LI 67273ed8e77SXin LI v += add; 67373ed8e77SXin LI ++p; 67473ed8e77SXin LI } while (p < name_eq_value_end 67573ed8e77SXin LI && *p >= '0' && *p <= '9'); 67673ed8e77SXin LI 67773ed8e77SXin LI if (p < name_eq_value_end) { 678c917796cSXin LI // Remember this position so that it can be 67973ed8e77SXin LI // used for error messages that are 68073ed8e77SXin LI // specifically about the suffix. (Out of 68173ed8e77SXin LI // range values are about the whole value 68273ed8e77SXin LI // and those error messages point to the 68373ed8e77SXin LI // beginning of the number part, 68473ed8e77SXin LI // not to the suffix.) 68573ed8e77SXin LI const char *multiplier_start = p; 68673ed8e77SXin LI 68773ed8e77SXin LI // If multiplier suffix shouldn't be used 68873ed8e77SXin LI // then don't allow them even if the value 68973ed8e77SXin LI // would stay within limits. This is a somewhat 69073ed8e77SXin LI // unnecessary check but it rejects silly 69173ed8e77SXin LI // things like lzma2:pb=0MiB which xz allows. 69273ed8e77SXin LI if ((optmap[i].flags & OPTMAP_USE_BYTE_SUFFIX) 69373ed8e77SXin LI == 0) { 69473ed8e77SXin LI *str = multiplier_start; 69573ed8e77SXin LI return "This option does not support " 69673ed8e77SXin LI "any integer suffixes"; 69773ed8e77SXin LI } 69873ed8e77SXin LI 69973ed8e77SXin LI uint32_t shift; 70073ed8e77SXin LI 70173ed8e77SXin LI switch (*p) { 70273ed8e77SXin LI case 'k': 70373ed8e77SXin LI case 'K': 70473ed8e77SXin LI shift = 10; 70573ed8e77SXin LI break; 70673ed8e77SXin LI 70773ed8e77SXin LI case 'm': 70873ed8e77SXin LI case 'M': 70973ed8e77SXin LI shift = 20; 71073ed8e77SXin LI break; 71173ed8e77SXin LI 71273ed8e77SXin LI case 'g': 71373ed8e77SXin LI case 'G': 71473ed8e77SXin LI shift = 30; 71573ed8e77SXin LI break; 71673ed8e77SXin LI 71773ed8e77SXin LI default: 71873ed8e77SXin LI *str = multiplier_start; 71973ed8e77SXin LI return "Invalid multiplier suffix " 72073ed8e77SXin LI "(KiB, MiB, or GiB)"; 72173ed8e77SXin LI } 72273ed8e77SXin LI 72373ed8e77SXin LI ++p; 72473ed8e77SXin LI 72573ed8e77SXin LI // Allow "M", "Mi", "MB", "MiB" and the same 72673ed8e77SXin LI // for the other five characters from the 72773ed8e77SXin LI // switch-statement above. All are handled 72873ed8e77SXin LI // as base-2 (perhaps a mistake, perhaps not). 72973ed8e77SXin LI // Note that 'i' and 'B' are case sensitive. 73073ed8e77SXin LI if (p < name_eq_value_end && *p == 'i') 73173ed8e77SXin LI ++p; 73273ed8e77SXin LI 73373ed8e77SXin LI if (p < name_eq_value_end && *p == 'B') 73473ed8e77SXin LI ++p; 73573ed8e77SXin LI 73673ed8e77SXin LI // Now we must have no chars remaining. 73773ed8e77SXin LI if (p < name_eq_value_end) { 73873ed8e77SXin LI *str = multiplier_start; 73973ed8e77SXin LI return "Invalid multiplier suffix " 74073ed8e77SXin LI "(KiB, MiB, or GiB)"; 74173ed8e77SXin LI } 74273ed8e77SXin LI 74373ed8e77SXin LI if (v > (UINT32_MAX >> shift)) 74473ed8e77SXin LI return "Value out of range"; 74573ed8e77SXin LI 74673ed8e77SXin LI v <<= shift; 74773ed8e77SXin LI } 74873ed8e77SXin LI 74973ed8e77SXin LI if (v < optmap[i].u.range.min 75073ed8e77SXin LI || v > optmap[i].u.range.max) 75173ed8e77SXin LI return "Value out of range"; 75273ed8e77SXin LI } 75373ed8e77SXin LI 75473ed8e77SXin LI // Set the value in filter_options. Enums are handled 75573ed8e77SXin LI // specially since the underlying type isn't the same 75673ed8e77SXin LI // as uint32_t on all systems. 75773ed8e77SXin LI void *ptr = (char *)filter_options + optmap[i].offset; 75873ed8e77SXin LI switch (optmap[i].type) { 75973ed8e77SXin LI case OPTMAP_TYPE_LZMA_MODE: 76073ed8e77SXin LI *(lzma_mode *)ptr = (lzma_mode)v; 76173ed8e77SXin LI break; 76273ed8e77SXin LI 76373ed8e77SXin LI case OPTMAP_TYPE_LZMA_MATCH_FINDER: 76473ed8e77SXin LI *(lzma_match_finder *)ptr = (lzma_match_finder)v; 76573ed8e77SXin LI break; 76673ed8e77SXin LI 76773ed8e77SXin LI default: 76873ed8e77SXin LI *(uint32_t *)ptr = v; 76973ed8e77SXin LI break; 77073ed8e77SXin LI } 77173ed8e77SXin LI 77273ed8e77SXin LI // This option has been successfully handled. 77373ed8e77SXin LI *str = name_eq_value_end; 77473ed8e77SXin LI } 77573ed8e77SXin LI 77673ed8e77SXin LI // No errors. 77773ed8e77SXin LI return NULL; 77873ed8e77SXin LI } 77973ed8e77SXin LI 78073ed8e77SXin LI 78173ed8e77SXin LI /// Finds the name of the filter at the beginning of the string and 78273ed8e77SXin LI /// calls filter_name_map[i].parse() to decode the filter-specific options. 78373ed8e77SXin LI /// The caller must have set str_end so that exactly one filter and its 78473ed8e77SXin LI /// options are present without any trailing characters. 78573ed8e77SXin LI static const char * 78673ed8e77SXin LI parse_filter(const char **const str, const char *str_end, lzma_filter *filter, 78773ed8e77SXin LI const lzma_allocator *allocator, bool only_xz) 78873ed8e77SXin LI { 78973ed8e77SXin LI // Search for a colon or equals sign that would separate the filter 79073ed8e77SXin LI // name from filter options. If neither is found, then the input 79173ed8e77SXin LI // string only contains a filter name and there are no options. 79273ed8e77SXin LI // 79373ed8e77SXin LI // First assume that a colon or equals sign won't be found: 79473ed8e77SXin LI const char *name_end = str_end; 79573ed8e77SXin LI const char *opts_start = str_end; 79673ed8e77SXin LI 79773ed8e77SXin LI for (const char *p = *str; p < str_end; ++p) { 79873ed8e77SXin LI if (*p == ':' || *p == '=') { 79973ed8e77SXin LI name_end = p; 80073ed8e77SXin LI 80173ed8e77SXin LI // Filter options (name1=value1,name2=value2,...) 80273ed8e77SXin LI // begin after the colon or equals sign. 80373ed8e77SXin LI opts_start = p + 1; 80473ed8e77SXin LI break; 80573ed8e77SXin LI } 80673ed8e77SXin LI } 80773ed8e77SXin LI 80873ed8e77SXin LI // Reject a too long filter name so that the memcmp() 80973ed8e77SXin LI // in the loop below won't read past the end of the 81073ed8e77SXin LI // string in filter_name_map[i].name. 81173ed8e77SXin LI const size_t name_len = (size_t)(name_end - *str); 81273ed8e77SXin LI if (name_len > NAME_LEN_MAX) 81373ed8e77SXin LI return "Unknown filter name"; 81473ed8e77SXin LI 81573ed8e77SXin LI for (size_t i = 0; i < ARRAY_SIZE(filter_name_map); ++i) { 81673ed8e77SXin LI if (memcmp(*str, filter_name_map[i].name, name_len) == 0 81773ed8e77SXin LI && filter_name_map[i].name[name_len] == '\0') { 81873ed8e77SXin LI if (only_xz && filter_name_map[i].id 81973ed8e77SXin LI >= LZMA_FILTER_RESERVED_START) 82073ed8e77SXin LI return "This filter cannot be used in " 82173ed8e77SXin LI "the .xz format"; 82273ed8e77SXin LI 82373ed8e77SXin LI // Allocate the filter-specific options and 82473ed8e77SXin LI // initialize the memory with zeros. 82573ed8e77SXin LI void *options = lzma_alloc_zero( 82673ed8e77SXin LI filter_name_map[i].opts_size, 82773ed8e77SXin LI allocator); 82873ed8e77SXin LI if (options == NULL) 82973ed8e77SXin LI return "Memory allocation failed"; 83073ed8e77SXin LI 83173ed8e77SXin LI // Filter name was found so the input string is good 83273ed8e77SXin LI // at least this far. 83373ed8e77SXin LI *str = opts_start; 83473ed8e77SXin LI 83573ed8e77SXin LI const char *errmsg = filter_name_map[i].parse( 83673ed8e77SXin LI str, str_end, options); 83773ed8e77SXin LI if (errmsg != NULL) { 83873ed8e77SXin LI lzma_free(options, allocator); 83973ed8e77SXin LI return errmsg; 84073ed8e77SXin LI } 84173ed8e77SXin LI 84273ed8e77SXin LI // *filter is modified only when parsing is successful. 84373ed8e77SXin LI filter->id = filter_name_map[i].id; 84473ed8e77SXin LI filter->options = options; 84573ed8e77SXin LI return NULL; 84673ed8e77SXin LI } 84773ed8e77SXin LI } 84873ed8e77SXin LI 84973ed8e77SXin LI return "Unknown filter name"; 85073ed8e77SXin LI } 85173ed8e77SXin LI 85273ed8e77SXin LI 85373ed8e77SXin LI /// Converts the string to a filter chain (array of lzma_filter structures). 85473ed8e77SXin LI /// 85573ed8e77SXin LI /// *str is advanced every time something has been decoded successfully. 85673ed8e77SXin LI /// This way the caller knows where in the string a possible error occurred. 85773ed8e77SXin LI static const char * 85873ed8e77SXin LI str_to_filters(const char **const str, lzma_filter *filters, uint32_t flags, 85973ed8e77SXin LI const lzma_allocator *allocator) 86073ed8e77SXin LI { 86173ed8e77SXin LI const char *errmsg; 86273ed8e77SXin LI 86373ed8e77SXin LI // Skip leading spaces. 86473ed8e77SXin LI while (**str == ' ') 86573ed8e77SXin LI ++*str; 86673ed8e77SXin LI 86773ed8e77SXin LI if (**str == '\0') 86873ed8e77SXin LI return "Empty string is not allowed, " 86973ed8e77SXin LI "try \"6\" if a default value is needed"; 87073ed8e77SXin LI 87173ed8e77SXin LI // Detect the type of the string. 87273ed8e77SXin LI // 87373ed8e77SXin LI // A string beginning with a digit or a string beginning with 87473ed8e77SXin LI // one dash and a digit are treated as presets. Trailing spaces 87573ed8e77SXin LI // will be ignored too (leading spaces were already ignored above). 87673ed8e77SXin LI // 87773ed8e77SXin LI // For example, "6", "7 ", "-9e", or " -3 " are treated as presets. 87873ed8e77SXin LI // Strings like "-" or "- " aren't preset. 87973ed8e77SXin LI #define MY_IS_DIGIT(c) ((c) >= '0' && (c) <= '9') 88073ed8e77SXin LI if (MY_IS_DIGIT(**str) || (**str == '-' && MY_IS_DIGIT((*str)[1]))) { 88173ed8e77SXin LI if (**str == '-') 88273ed8e77SXin LI ++*str; 88373ed8e77SXin LI 88473ed8e77SXin LI // Ignore trailing spaces. 88573ed8e77SXin LI const size_t str_len = strlen(*str); 88673ed8e77SXin LI const char *str_end = memchr(*str, ' ', str_len); 88773ed8e77SXin LI if (str_end != NULL) { 88873ed8e77SXin LI // There is at least one trailing space. Check that 88973ed8e77SXin LI // there are no chars other than spaces. 89073ed8e77SXin LI for (size_t i = 1; str_end[i] != '\0'; ++i) 89173ed8e77SXin LI if (str_end[i] != ' ') 89273ed8e77SXin LI return "Unsupported preset"; 89373ed8e77SXin LI } else { 89473ed8e77SXin LI // There are no trailing spaces. Use the whole string. 89573ed8e77SXin LI str_end = *str + str_len; 89673ed8e77SXin LI } 89773ed8e77SXin LI 89873ed8e77SXin LI uint32_t preset; 89973ed8e77SXin LI errmsg = parse_lzma12_preset(str, str_end, &preset); 90073ed8e77SXin LI if (errmsg != NULL) 90173ed8e77SXin LI return errmsg; 90273ed8e77SXin LI 90373ed8e77SXin LI lzma_options_lzma *opts = lzma_alloc(sizeof(*opts), allocator); 90473ed8e77SXin LI if (opts == NULL) 90573ed8e77SXin LI return "Memory allocation failed"; 90673ed8e77SXin LI 90773ed8e77SXin LI if (lzma_lzma_preset(opts, preset)) { 90873ed8e77SXin LI lzma_free(opts, allocator); 90973ed8e77SXin LI return "Unsupported preset"; 91073ed8e77SXin LI } 91173ed8e77SXin LI 91273ed8e77SXin LI filters[0].id = LZMA_FILTER_LZMA2; 91373ed8e77SXin LI filters[0].options = opts; 91473ed8e77SXin LI filters[1].id = LZMA_VLI_UNKNOWN; 91573ed8e77SXin LI filters[1].options = NULL; 91673ed8e77SXin LI 91773ed8e77SXin LI return NULL; 91873ed8e77SXin LI } 91973ed8e77SXin LI 92073ed8e77SXin LI // Not a preset so it must be a filter chain. 92173ed8e77SXin LI // 92273ed8e77SXin LI // If LZMA_STR_ALL_FILTERS isn't used we allow only filters that 92373ed8e77SXin LI // can be used in .xz. 92473ed8e77SXin LI const bool only_xz = (flags & LZMA_STR_ALL_FILTERS) == 0; 92573ed8e77SXin LI 92673ed8e77SXin LI // Use a temporary array so that we don't modify the caller-supplied 92773ed8e77SXin LI // one until we know that no errors occurred. 92873ed8e77SXin LI lzma_filter temp_filters[LZMA_FILTERS_MAX + 1]; 92973ed8e77SXin LI 93073ed8e77SXin LI size_t i = 0; 93173ed8e77SXin LI do { 93273ed8e77SXin LI if (i == LZMA_FILTERS_MAX) { 93373ed8e77SXin LI errmsg = "The maximum number of filters is four"; 93473ed8e77SXin LI goto error; 93573ed8e77SXin LI } 93673ed8e77SXin LI 93773ed8e77SXin LI // Skip "--" if present. 93873ed8e77SXin LI if ((*str)[0] == '-' && (*str)[1] == '-') 93973ed8e77SXin LI *str += 2; 94073ed8e77SXin LI 94173ed8e77SXin LI // Locate the end of "filter:name1=value1,name2=value2", 94273ed8e77SXin LI // stopping at the first "--" or a single space. 94373ed8e77SXin LI const char *filter_end = *str; 94473ed8e77SXin LI while (filter_end[0] != '\0') { 94573ed8e77SXin LI if ((filter_end[0] == '-' && filter_end[1] == '-') 94673ed8e77SXin LI || filter_end[0] == ' ') 94773ed8e77SXin LI break; 94873ed8e77SXin LI 94973ed8e77SXin LI ++filter_end; 95073ed8e77SXin LI } 95173ed8e77SXin LI 95273ed8e77SXin LI // Inputs that have "--" at the end or "-- " in the middle 95373ed8e77SXin LI // will result in an empty filter name. 95473ed8e77SXin LI if (filter_end == *str) { 95573ed8e77SXin LI errmsg = "Filter name is missing"; 95673ed8e77SXin LI goto error; 95773ed8e77SXin LI } 95873ed8e77SXin LI 95973ed8e77SXin LI errmsg = parse_filter(str, filter_end, &temp_filters[i], 96073ed8e77SXin LI allocator, only_xz); 96173ed8e77SXin LI if (errmsg != NULL) 96273ed8e77SXin LI goto error; 96373ed8e77SXin LI 96473ed8e77SXin LI // Skip trailing spaces. 96573ed8e77SXin LI while (**str == ' ') 96673ed8e77SXin LI ++*str; 96773ed8e77SXin LI 96873ed8e77SXin LI ++i; 96973ed8e77SXin LI } while (**str != '\0'); 97073ed8e77SXin LI 97173ed8e77SXin LI // Seems to be good, terminate the array so that 97273ed8e77SXin LI // basic validation can be done. 97373ed8e77SXin LI temp_filters[i].id = LZMA_VLI_UNKNOWN; 97473ed8e77SXin LI temp_filters[i].options = NULL; 97573ed8e77SXin LI 97673ed8e77SXin LI // Do basic validation if the application didn't prohibit it. 97773ed8e77SXin LI if ((flags & LZMA_STR_NO_VALIDATION) == 0) { 97873ed8e77SXin LI size_t dummy; 97973ed8e77SXin LI const lzma_ret ret = lzma_validate_chain(temp_filters, &dummy); 98073ed8e77SXin LI assert(ret == LZMA_OK || ret == LZMA_OPTIONS_ERROR); 98173ed8e77SXin LI if (ret != LZMA_OK) { 98273ed8e77SXin LI errmsg = "Invalid filter chain " 98373ed8e77SXin LI "('lzma2' missing at the end?)"; 98473ed8e77SXin LI goto error; 98573ed8e77SXin LI } 98673ed8e77SXin LI } 98773ed8e77SXin LI 98873ed8e77SXin LI // All good. Copy the filters to the application supplied array. 98973ed8e77SXin LI memcpy(filters, temp_filters, (i + 1) * sizeof(lzma_filter)); 99073ed8e77SXin LI return NULL; 99173ed8e77SXin LI 99273ed8e77SXin LI error: 99373ed8e77SXin LI // Free the filter options that were successfully decoded. 99473ed8e77SXin LI while (i-- > 0) 99573ed8e77SXin LI lzma_free(temp_filters[i].options, allocator); 99673ed8e77SXin LI 99773ed8e77SXin LI return errmsg; 99873ed8e77SXin LI } 99973ed8e77SXin LI 100073ed8e77SXin LI 100173ed8e77SXin LI extern LZMA_API(const char *) 100273ed8e77SXin LI lzma_str_to_filters(const char *str, int *error_pos, lzma_filter *filters, 100373ed8e77SXin LI uint32_t flags, const lzma_allocator *allocator) 100473ed8e77SXin LI { 1005*3b35e7eeSXin LI // If error_pos isn't NULL, *error_pos must always be set. 1006*3b35e7eeSXin LI // liblzma <= 5.4.6 and <= 5.6.1 have a bug and don't do this 1007*3b35e7eeSXin LI // when str == NULL or filters == NULL or flags are unsupported. 1008*3b35e7eeSXin LI if (error_pos != NULL) 1009*3b35e7eeSXin LI *error_pos = 0; 1010*3b35e7eeSXin LI 101173ed8e77SXin LI if (str == NULL || filters == NULL) 101273ed8e77SXin LI return "Unexpected NULL pointer argument(s) " 101373ed8e77SXin LI "to lzma_str_to_filters()"; 101473ed8e77SXin LI 101573ed8e77SXin LI // Validate the flags. 101673ed8e77SXin LI const uint32_t supported_flags 101773ed8e77SXin LI = LZMA_STR_ALL_FILTERS 101873ed8e77SXin LI | LZMA_STR_NO_VALIDATION; 101973ed8e77SXin LI 102073ed8e77SXin LI if (flags & ~supported_flags) 102173ed8e77SXin LI return "Unsupported flags to lzma_str_to_filters()"; 102273ed8e77SXin LI 102373ed8e77SXin LI const char *used = str; 102473ed8e77SXin LI const char *errmsg = str_to_filters(&used, filters, flags, allocator); 102573ed8e77SXin LI 102673ed8e77SXin LI if (error_pos != NULL) { 102773ed8e77SXin LI const size_t n = (size_t)(used - str); 102873ed8e77SXin LI *error_pos = n > INT_MAX ? INT_MAX : (int)n; 102973ed8e77SXin LI } 103073ed8e77SXin LI 103173ed8e77SXin LI return errmsg; 103273ed8e77SXin LI } 103373ed8e77SXin LI 103473ed8e77SXin LI 103573ed8e77SXin LI /// Converts options of one filter to a string. 103673ed8e77SXin LI /// 103773ed8e77SXin LI /// The caller must have already put the filter name in the destination 103873ed8e77SXin LI /// string. Since it is possible that no options will be needed, the caller 103973ed8e77SXin LI /// won't have put a delimiter character (':' or '=') in the string yet. 104073ed8e77SXin LI /// We will add it if at least one option will be added to the string. 104173ed8e77SXin LI static void 104273ed8e77SXin LI strfy_filter(lzma_str *dest, const char *delimiter, 104373ed8e77SXin LI const option_map *optmap, size_t optmap_count, 104473ed8e77SXin LI const void *filter_options) 104573ed8e77SXin LI { 104673ed8e77SXin LI for (size_t i = 0; i < optmap_count; ++i) { 104773ed8e77SXin LI // No attempt is made to reverse LZMA1/2 preset. 104873ed8e77SXin LI if (optmap[i].type == OPTMAP_TYPE_LZMA_PRESET) 104973ed8e77SXin LI continue; 105073ed8e77SXin LI 105173ed8e77SXin LI // All options have integer values, some just are mapped 105273ed8e77SXin LI // to a string with a name_value_map. LZMA1/2 preset 105373ed8e77SXin LI // isn't reversed back to preset=PRESET form. 105473ed8e77SXin LI uint32_t v; 105573ed8e77SXin LI const void *ptr 105673ed8e77SXin LI = (const char *)filter_options + optmap[i].offset; 105773ed8e77SXin LI switch (optmap[i].type) { 105873ed8e77SXin LI case OPTMAP_TYPE_LZMA_MODE: 105973ed8e77SXin LI v = *(const lzma_mode *)ptr; 106073ed8e77SXin LI break; 106173ed8e77SXin LI 106273ed8e77SXin LI case OPTMAP_TYPE_LZMA_MATCH_FINDER: 106373ed8e77SXin LI v = *(const lzma_match_finder *)ptr; 106473ed8e77SXin LI break; 106573ed8e77SXin LI 106673ed8e77SXin LI default: 106773ed8e77SXin LI v = *(const uint32_t *)ptr; 106873ed8e77SXin LI break; 106973ed8e77SXin LI } 107073ed8e77SXin LI 107173ed8e77SXin LI // Skip this if this option should be omitted from 107273ed8e77SXin LI // the string when the value is zero. 107373ed8e77SXin LI if (v == 0 && (optmap[i].flags & OPTMAP_NO_STRFY_ZERO)) 107473ed8e77SXin LI continue; 107573ed8e77SXin LI 107673ed8e77SXin LI // Before the first option we add whatever delimiter 107773ed8e77SXin LI // the caller gave us. For later options a comma is used. 107873ed8e77SXin LI str_append_str(dest, delimiter); 107973ed8e77SXin LI delimiter = ","; 108073ed8e77SXin LI 108173ed8e77SXin LI // Add the option name and equals sign. 108273ed8e77SXin LI str_append_str(dest, optmap[i].name); 108373ed8e77SXin LI str_append_str(dest, "="); 108473ed8e77SXin LI 108573ed8e77SXin LI if (optmap[i].flags & OPTMAP_USE_NAME_VALUE_MAP) { 108673ed8e77SXin LI const name_value_map *map = optmap[i].u.map; 108773ed8e77SXin LI size_t j = 0; 108873ed8e77SXin LI while (true) { 108973ed8e77SXin LI if (map[j].name[0] == '\0') { 109073ed8e77SXin LI str_append_str(dest, "UNKNOWN"); 109173ed8e77SXin LI break; 109273ed8e77SXin LI } 109373ed8e77SXin LI 109473ed8e77SXin LI if (map[j].value == v) { 109573ed8e77SXin LI str_append_str(dest, map[j].name); 109673ed8e77SXin LI break; 109773ed8e77SXin LI } 109873ed8e77SXin LI 109973ed8e77SXin LI ++j; 110073ed8e77SXin LI } 110173ed8e77SXin LI } else { 110273ed8e77SXin LI str_append_u32(dest, v, 110373ed8e77SXin LI optmap[i].flags & OPTMAP_USE_BYTE_SUFFIX); 110473ed8e77SXin LI } 110573ed8e77SXin LI } 110673ed8e77SXin LI 110773ed8e77SXin LI return; 110873ed8e77SXin LI } 110973ed8e77SXin LI 111073ed8e77SXin LI 111173ed8e77SXin LI extern LZMA_API(lzma_ret) 111273ed8e77SXin LI lzma_str_from_filters(char **output_str, const lzma_filter *filters, 111373ed8e77SXin LI uint32_t flags, const lzma_allocator *allocator) 111473ed8e77SXin LI { 111573ed8e77SXin LI // On error *output_str is always set to NULL. 111673ed8e77SXin LI // Do it as the very first step. 111773ed8e77SXin LI if (output_str == NULL) 111873ed8e77SXin LI return LZMA_PROG_ERROR; 111973ed8e77SXin LI 112073ed8e77SXin LI *output_str = NULL; 112173ed8e77SXin LI 112273ed8e77SXin LI if (filters == NULL) 112373ed8e77SXin LI return LZMA_PROG_ERROR; 112473ed8e77SXin LI 112573ed8e77SXin LI // Validate the flags. 112673ed8e77SXin LI const uint32_t supported_flags 112773ed8e77SXin LI = LZMA_STR_ENCODER 112873ed8e77SXin LI | LZMA_STR_DECODER 112973ed8e77SXin LI | LZMA_STR_GETOPT_LONG 113073ed8e77SXin LI | LZMA_STR_NO_SPACES; 113173ed8e77SXin LI 113273ed8e77SXin LI if (flags & ~supported_flags) 113373ed8e77SXin LI return LZMA_OPTIONS_ERROR; 113473ed8e77SXin LI 113573ed8e77SXin LI // There must be at least one filter. 113673ed8e77SXin LI if (filters[0].id == LZMA_VLI_UNKNOWN) 113773ed8e77SXin LI return LZMA_OPTIONS_ERROR; 113873ed8e77SXin LI 113973ed8e77SXin LI // Allocate memory for the output string. 114073ed8e77SXin LI lzma_str dest; 114173ed8e77SXin LI return_if_error(str_init(&dest, allocator)); 114273ed8e77SXin LI 114373ed8e77SXin LI const bool show_opts = (flags & (LZMA_STR_ENCODER | LZMA_STR_DECODER)); 114473ed8e77SXin LI 114573ed8e77SXin LI const char *opt_delim = (flags & LZMA_STR_GETOPT_LONG) ? "=" : ":"; 114673ed8e77SXin LI 114773ed8e77SXin LI for (size_t i = 0; filters[i].id != LZMA_VLI_UNKNOWN; ++i) { 1148c917796cSXin LI // If we reach LZMA_FILTERS_MAX, then the filters array 1149c917796cSXin LI // is too large since the ID cannot be LZMA_VLI_UNKNOWN here. 1150c917796cSXin LI if (i == LZMA_FILTERS_MAX) { 1151c917796cSXin LI str_free(&dest, allocator); 1152c917796cSXin LI return LZMA_OPTIONS_ERROR; 1153c917796cSXin LI } 1154c917796cSXin LI 115573ed8e77SXin LI // Don't add a space between filters if the caller 115673ed8e77SXin LI // doesn't want them. 115773ed8e77SXin LI if (i > 0 && !(flags & LZMA_STR_NO_SPACES)) 115873ed8e77SXin LI str_append_str(&dest, " "); 115973ed8e77SXin LI 116073ed8e77SXin LI // Use dashes for xz getopt_long() compatible syntax but also 116173ed8e77SXin LI // use dashes to separate filters when spaces weren't wanted. 116273ed8e77SXin LI if ((flags & LZMA_STR_GETOPT_LONG) 116373ed8e77SXin LI || (i > 0 && (flags & LZMA_STR_NO_SPACES))) 116473ed8e77SXin LI str_append_str(&dest, "--"); 116573ed8e77SXin LI 116673ed8e77SXin LI size_t j = 0; 116773ed8e77SXin LI while (true) { 116873ed8e77SXin LI if (j == ARRAY_SIZE(filter_name_map)) { 116973ed8e77SXin LI // Filter ID in filters[i].id isn't supported. 117073ed8e77SXin LI str_free(&dest, allocator); 117173ed8e77SXin LI return LZMA_OPTIONS_ERROR; 117273ed8e77SXin LI } 117373ed8e77SXin LI 117473ed8e77SXin LI if (filter_name_map[j].id == filters[i].id) { 117573ed8e77SXin LI // Add the filter name. 117673ed8e77SXin LI str_append_str(&dest, filter_name_map[j].name); 117773ed8e77SXin LI 117873ed8e77SXin LI // If only the filter names were wanted then 117973ed8e77SXin LI // skip to the next filter. In this case 118073ed8e77SXin LI // .options is ignored and may be NULL even 118173ed8e77SXin LI // when the filter doesn't allow NULL options. 118273ed8e77SXin LI if (!show_opts) 118373ed8e77SXin LI break; 118473ed8e77SXin LI 118573ed8e77SXin LI if (filters[i].options == NULL) { 118673ed8e77SXin LI if (!filter_name_map[j].allow_null) { 118773ed8e77SXin LI // Filter-specific options 118873ed8e77SXin LI // are missing but with 118973ed8e77SXin LI // this filter the options 119073ed8e77SXin LI // structure is mandatory. 119173ed8e77SXin LI str_free(&dest, allocator); 119273ed8e77SXin LI return LZMA_OPTIONS_ERROR; 119373ed8e77SXin LI } 119473ed8e77SXin LI 119573ed8e77SXin LI // .options is allowed to be NULL. 119673ed8e77SXin LI // There is no need to add any 119773ed8e77SXin LI // options to the string. 119873ed8e77SXin LI break; 119973ed8e77SXin LI } 120073ed8e77SXin LI 120173ed8e77SXin LI // Options structure is available. Add 120273ed8e77SXin LI // the filter options to the string. 120373ed8e77SXin LI const size_t optmap_count 120473ed8e77SXin LI = (flags & LZMA_STR_ENCODER) 120573ed8e77SXin LI ? filter_name_map[j].strfy_encoder 120673ed8e77SXin LI : filter_name_map[j].strfy_decoder; 120773ed8e77SXin LI strfy_filter(&dest, opt_delim, 120873ed8e77SXin LI filter_name_map[j].optmap, 120973ed8e77SXin LI optmap_count, 121073ed8e77SXin LI filters[i].options); 121173ed8e77SXin LI break; 121273ed8e77SXin LI } 121373ed8e77SXin LI 121473ed8e77SXin LI ++j; 121573ed8e77SXin LI } 121673ed8e77SXin LI } 121773ed8e77SXin LI 121873ed8e77SXin LI return str_finish(output_str, &dest, allocator); 121973ed8e77SXin LI } 122073ed8e77SXin LI 122173ed8e77SXin LI 122273ed8e77SXin LI extern LZMA_API(lzma_ret) 122373ed8e77SXin LI lzma_str_list_filters(char **output_str, lzma_vli filter_id, uint32_t flags, 122473ed8e77SXin LI const lzma_allocator *allocator) 122573ed8e77SXin LI { 122673ed8e77SXin LI // On error *output_str is always set to NULL. 122773ed8e77SXin LI // Do it as the very first step. 122873ed8e77SXin LI if (output_str == NULL) 122973ed8e77SXin LI return LZMA_PROG_ERROR; 123073ed8e77SXin LI 123173ed8e77SXin LI *output_str = NULL; 123273ed8e77SXin LI 123373ed8e77SXin LI // Validate the flags. 123473ed8e77SXin LI const uint32_t supported_flags 123573ed8e77SXin LI = LZMA_STR_ALL_FILTERS 123673ed8e77SXin LI | LZMA_STR_ENCODER 123773ed8e77SXin LI | LZMA_STR_DECODER 123873ed8e77SXin LI | LZMA_STR_GETOPT_LONG; 123973ed8e77SXin LI 124073ed8e77SXin LI if (flags & ~supported_flags) 124173ed8e77SXin LI return LZMA_OPTIONS_ERROR; 124273ed8e77SXin LI 124373ed8e77SXin LI // Allocate memory for the output string. 124473ed8e77SXin LI lzma_str dest; 124573ed8e77SXin LI return_if_error(str_init(&dest, allocator)); 124673ed8e77SXin LI 124773ed8e77SXin LI // If only listing the filter names then separate them with spaces. 124873ed8e77SXin LI // Otherwise use newlines. 124973ed8e77SXin LI const bool show_opts = (flags & (LZMA_STR_ENCODER | LZMA_STR_DECODER)); 125073ed8e77SXin LI const char *filter_delim = show_opts ? "\n" : " "; 125173ed8e77SXin LI 125273ed8e77SXin LI const char *opt_delim = (flags & LZMA_STR_GETOPT_LONG) ? "=" : ":"; 125373ed8e77SXin LI bool first_filter_printed = false; 125473ed8e77SXin LI 125573ed8e77SXin LI for (size_t i = 0; i < ARRAY_SIZE(filter_name_map); ++i) { 125673ed8e77SXin LI // If we are printing only one filter then skip others. 125773ed8e77SXin LI if (filter_id != LZMA_VLI_UNKNOWN 125873ed8e77SXin LI && filter_id != filter_name_map[i].id) 125973ed8e77SXin LI continue; 126073ed8e77SXin LI 126173ed8e77SXin LI // If we are printing only .xz filters then skip the others. 126273ed8e77SXin LI if (filter_name_map[i].id >= LZMA_FILTER_RESERVED_START 126373ed8e77SXin LI && (flags & LZMA_STR_ALL_FILTERS) == 0 126473ed8e77SXin LI && filter_id == LZMA_VLI_UNKNOWN) 126573ed8e77SXin LI continue; 126673ed8e77SXin LI 126773ed8e77SXin LI // Add a new line if this isn't the first filter being 126873ed8e77SXin LI // written to the string. 126973ed8e77SXin LI if (first_filter_printed) 127073ed8e77SXin LI str_append_str(&dest, filter_delim); 127173ed8e77SXin LI 127273ed8e77SXin LI first_filter_printed = true; 127373ed8e77SXin LI 127473ed8e77SXin LI if (flags & LZMA_STR_GETOPT_LONG) 127573ed8e77SXin LI str_append_str(&dest, "--"); 127673ed8e77SXin LI 127773ed8e77SXin LI str_append_str(&dest, filter_name_map[i].name); 127873ed8e77SXin LI 127973ed8e77SXin LI // If only the filter names were wanted then continue 128073ed8e77SXin LI // to the next filter. 128173ed8e77SXin LI if (!show_opts) 128273ed8e77SXin LI continue; 128373ed8e77SXin LI 128473ed8e77SXin LI const option_map *optmap = filter_name_map[i].optmap; 128573ed8e77SXin LI const char *d = opt_delim; 128673ed8e77SXin LI 128773ed8e77SXin LI const size_t end = (flags & LZMA_STR_ENCODER) 128873ed8e77SXin LI ? filter_name_map[i].strfy_encoder 128973ed8e77SXin LI : filter_name_map[i].strfy_decoder; 129073ed8e77SXin LI 129173ed8e77SXin LI for (size_t j = 0; j < end; ++j) { 129273ed8e77SXin LI // The first option is delimited from the filter 129373ed8e77SXin LI // name using "=" or ":" and the rest of the options 129473ed8e77SXin LI // are separated with ",". 129573ed8e77SXin LI str_append_str(&dest, d); 129673ed8e77SXin LI d = ","; 129773ed8e77SXin LI 129873ed8e77SXin LI // optname=<possible_values> 129973ed8e77SXin LI str_append_str(&dest, optmap[j].name); 130073ed8e77SXin LI str_append_str(&dest, "=<"); 130173ed8e77SXin LI 130273ed8e77SXin LI if (optmap[j].type == OPTMAP_TYPE_LZMA_PRESET) { 130373ed8e77SXin LI // LZMA1/2 preset has its custom help string. 130473ed8e77SXin LI str_append_str(&dest, LZMA12_PRESET_STR); 130573ed8e77SXin LI } else if (optmap[j].flags 130673ed8e77SXin LI & OPTMAP_USE_NAME_VALUE_MAP) { 130773ed8e77SXin LI // Separate the possible option values by "|". 130873ed8e77SXin LI const name_value_map *m = optmap[j].u.map; 130973ed8e77SXin LI for (size_t k = 0; m[k].name[0] != '\0'; ++k) { 131073ed8e77SXin LI if (k > 0) 131173ed8e77SXin LI str_append_str(&dest, "|"); 131273ed8e77SXin LI 131373ed8e77SXin LI str_append_str(&dest, m[k].name); 131473ed8e77SXin LI } 131573ed8e77SXin LI } else { 131673ed8e77SXin LI // Integer range is shown as min-max. 131773ed8e77SXin LI const bool use_byte_suffix = optmap[j].flags 131873ed8e77SXin LI & OPTMAP_USE_BYTE_SUFFIX; 131973ed8e77SXin LI str_append_u32(&dest, optmap[j].u.range.min, 132073ed8e77SXin LI use_byte_suffix); 132173ed8e77SXin LI str_append_str(&dest, "-"); 132273ed8e77SXin LI str_append_u32(&dest, optmap[j].u.range.max, 132373ed8e77SXin LI use_byte_suffix); 132473ed8e77SXin LI } 132573ed8e77SXin LI 132673ed8e77SXin LI str_append_str(&dest, ">"); 132773ed8e77SXin LI } 132873ed8e77SXin LI } 132973ed8e77SXin LI 133073ed8e77SXin LI // If no filters were added to the string then it must be because 133173ed8e77SXin LI // the caller provided an unsupported Filter ID. 133273ed8e77SXin LI if (!first_filter_printed) { 133373ed8e77SXin LI str_free(&dest, allocator); 133473ed8e77SXin LI return LZMA_OPTIONS_ERROR; 133573ed8e77SXin LI } 133673ed8e77SXin LI 133773ed8e77SXin LI return str_finish(output_str, &dest, allocator); 133873ed8e77SXin LI } 1339