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