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
str_init(lzma_str * str,const lzma_allocator * allocator)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
str_free(lzma_str * str,const lzma_allocator * allocator)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
str_is_full(const lzma_str * str)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
str_finish(char ** dest,lzma_str * str,const lzma_allocator * allocator)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
str_append_str(lzma_str * str,const char * s)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
str_append_u32(lzma_str * str,uint32_t v,bool use_byte_suffix)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 *
parse_bcj(const char ** const str,const char * str_end,void * filter_options)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 *
parse_delta(const char ** const str,const char * str_end,void * filter_options)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 *
parse_lzma12_preset(const char ** const str,const char * str_end,uint32_t * preset)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 *
set_lzma12_preset(const char ** const str,const char * str_end,void * filter_options)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 *
parse_lzma12(const char ** const str,const char * str_end,void * filter_options)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 *
parse_options(const char ** const str,const char * str_end,void * filter_options,const option_map * const optmap,const size_t optmap_size)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 *
parse_filter(const char ** const str,const char * str_end,lzma_filter * filter,const lzma_allocator * allocator,bool only_xz)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 *
str_to_filters(const char ** const str,lzma_filter * filters,uint32_t flags,const lzma_allocator * allocator)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 *)
lzma_str_to_filters(const char * str,int * error_pos,lzma_filter * filters,uint32_t flags,const lzma_allocator * allocator)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
strfy_filter(lzma_str * dest,const char * delimiter,const option_map * optmap,size_t optmap_count,const void * filter_options)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)
lzma_str_from_filters(char ** output_str,const lzma_filter * filters,uint32_t flags,const lzma_allocator * allocator)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)
lzma_str_list_filters(char ** output_str,lzma_vli filter_id,uint32_t flags,const lzma_allocator * allocator)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