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