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