/*
** Zabbix
** Copyright (C) 2001-2023 Zabbix SIA
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
**/

#include "zbxexpr.h"

/******************************************************************************
 *                                                                            *
 * Purpose: parses user macro token                                           *
 *                                                                            *
 * Parameters: expression - [IN] the expression                               *
 *             macro      - [IN] the beginning of the token                   *
 *             token      - [OUT] the token data                              *
 *                                                                            *
 * Return value: SUCCEED - the user macro was parsed successfully             *
 *               FAIL    - macro does not point at valid user macro           *
 *                                                                            *
 * Comments: If the macro points at valid user macro in the expression then   *
 *           the generic token fields are set and the token->data.user_macro  *
 *           structure is filled with user macro specific data.               *
 *                                                                            *
 ******************************************************************************/
static int	token_parse_user_macro(const char *expression, const char *macro, zbx_token_t *token)
{
	size_t			offset;
	int			macro_r, context_l, context_r;
	zbx_token_user_macro_t	*data;

	if (SUCCEED != zbx_user_macro_parse(macro, &macro_r, &context_l, &context_r, NULL))
		return FAIL;

	offset = macro - expression;

	/* initialize token */
	token->type = ZBX_TOKEN_USER_MACRO;
	token->loc.l = offset;
	token->loc.r = offset + macro_r;

	/* initialize token data */
	data = &token->data.user_macro;
	data->name.l = offset + 2;

	if (0 != context_l)
	{
		const char *ptr = macro + context_l;

		/* find the context separator ':' by stripping spaces before context */
		while (' ' == *(--ptr))
			;

		data->name.r = offset + (ptr - macro) - 1;

		data->context.l = offset + context_l;
		data->context.r = offset + context_r;
	}
	else
	{
		data->name.r = token->loc.r - 1;
		data->context.l = 0;
		data->context.r = 0;
	}

	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Purpose: parses lld macro token                                            *
 *                                                                            *
 * Parameters: expression - [IN] the expression                               *
 *             macro      - [IN] the beginning of the token                   *
 *             token      - [OUT] the token data                              *
 *                                                                            *
 * Return value: SUCCEED - the lld macro was parsed successfully              *
 *               FAIL    - macro does not point at valid lld macro            *
 *                                                                            *
 * Comments: If the macro points at valid lld macro in the expression then    *
 *           the generic token fields are set and the token->data.lld_macro   *
 *           structure is filled with lld macro specific data.                *
 *                                                                            *
 ******************************************************************************/
static int	token_parse_lld_macro(const char *expression, const char *macro, zbx_token_t *token)
{
	const char		*ptr;
	size_t			offset;
	zbx_token_macro_t	*data;

	/* find the end of lld macro by validating its name until the closing bracket } */
	for (ptr = macro + 2; '}' != *ptr; ptr++)
	{
		if ('\0' == *ptr)
			return FAIL;

		if (SUCCEED != zbx_is_macro_char(*ptr))
			return FAIL;
	}

	/* empty macro name */
	if (2 == ptr - macro)
		return FAIL;

	offset = macro - expression;

	/* initialize token */
	token->type = ZBX_TOKEN_LLD_MACRO;
	token->loc.l = offset;
	token->loc.r = offset + (ptr - macro);

	/* initialize token data */
	data = &token->data.lld_macro;
	data->name.l = offset + 2;
	data->name.r = token->loc.r - 1;

	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Purpose: parses expression macro token                                     *
 *                                                                            *
 * Parameters: expression    - [IN] the expression                            *
 *             macro         - [IN] the beginning of the token                *
 *             token_search - [IN] specify if references will be searched     *
 *             token         - [OUT] the token data                           *
 *                                                                            *
 * Return value: SUCCEED - the expression macro was parsed successfully       *
 *               FAIL    - macro does not point at valid expression macro     *
 *                                                                            *
 * Comments: If the macro points at valid expression macro in the expression  *
 *           then the generic token fields are set and the                    *
 *           token->data.expression_macro structure is filled with expression *
 *           macro specific data.                                             *
 *           Contents of macro are not validated because expression macro may *
 *           contain user macro contexts and item keys with string arguments. *
 *                                                                            *
 ******************************************************************************/
static int	token_parse_expression_macro(const char *expression, const char *macro, zbx_token_search_t token_search,
		zbx_token_t *token)
{
	const char			*ptr;
	size_t				offset;
	zbx_token_expression_macro_t	*data;
	int				quoted = 0;

	for (ptr = macro + 2; '\0' != *ptr ; ptr++)
	{
		if (1 == quoted)
		{
			if ('\\' == *ptr)
			{
				if ('\0' == *(++ptr))
					break;
				continue;
			}

			if ('"' == *ptr)
				quoted = 0;

			continue;
		}

		if ('{' == *ptr)
		{
			zbx_token_t	tmp;

			/* nested expression macros are not supported */
			if ('?' == ptr[1])
				continue;

			token_search &= ~ZBX_TOKEN_SEARCH_EXPRESSION_MACRO;
			if (SUCCEED == zbx_token_find(ptr, 0, &tmp, token_search))
			{
				switch (tmp.type)
				{
					case ZBX_TOKEN_MACRO:
					case ZBX_TOKEN_LLD_MACRO:
					case ZBX_TOKEN_LLD_FUNC_MACRO:
					case ZBX_TOKEN_USER_MACRO:
					case ZBX_TOKEN_SIMPLE_MACRO:
						ptr += tmp.loc.r;
						break;
				}
			}
		}
		else if ('}' == *ptr)
		{
			/* empty macro */
			if (ptr == macro + 2)
				return FAIL;

			offset = macro - expression;

			/* initialize token */
			token->type = ZBX_TOKEN_EXPRESSION_MACRO;
			token->loc.l = offset;
			token->loc.r = offset + (ptr - macro);

			/* initialize token data */
			data = &token->data.expression_macro;
			data->expression.l = offset + 2;
			data->expression.r = token->loc.r - 1;

			return SUCCEED;
		}
		else if ('"' == *ptr)
			quoted = 1;
	}

	return FAIL;
}

/******************************************************************************
 *                                                                            *
 * Purpose: parses object id token                                            *
 *                                                                            *
 * Parameters: expression - [IN] the expression                               *
 *             macro      - [IN] the beginning of the token                   *
 *             token      - [OUT] the token data                              *
 *                                                                            *
 * Return value: SUCCEED - the object id was parsed successfully              *
 *               FAIL    - macro does not point at valid object id            *
 *                                                                            *
 * Comments: If the macro points at valid object id in the expression then    *
 *           the generic token fields are set and the token->data.objectid    *
 *           structure is filled with object id specific data.                *
 *                                                                            *
 ******************************************************************************/
static int	token_parse_objectid(const char *expression, const char *macro, zbx_token_t *token)
{
	const char		*ptr;
	size_t			offset;
	zbx_token_macro_t	*data;

	/* find the end of object id by checking if it contains digits until the closing bracket } */
	for (ptr = macro + 1; '}' != *ptr; ptr++)
	{
		if ('\0' == *ptr)
			return FAIL;

		if (0 == isdigit(*ptr))
			return FAIL;
	}

	/* empty object id */
	if (1 == ptr - macro)
		return FAIL;

	offset = macro - expression;

	/* initialize token */
	token->type = ZBX_TOKEN_OBJECTID;
	token->loc.l = offset;
	token->loc.r = offset + (ptr - macro);

	/* initialize token data */
	data = &token->data.objectid;
	data->name.l = offset + 1;
	data->name.r = token->loc.r - 1;

	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Purpose: parses macro name segment                                         *
 *                                                                            *
 * Parameters: expression - [IN] the expression                               *
 *             segment    - [IN] the segment start                            *
 *             strict     - [OUT] 1 - macro contains only standard characters *
 *                                    (upper case alphanumeric characters,    *
 *                                     dots and underscores)                  *
 *                                0 - the last segment contains lowercase or  *
 *                                    quoted characters                       *
 *             next       - [OUT] offset of the next character after the      *
 *                                segment                                     *
 *                                                                            *
 * Return value: SUCCEED - the segment was parsed successfully                *
 *               FAIL    - otherwise                                          *
 *                                                                            *
 ******************************************************************************/
static int	token_parse_macro_segment(const char *expression, const char *segment, int *strict, int *next)
{
	const char	*ptr = segment;

	if ('"' != *ptr)
	{
		for (*strict = 1; '\0' != *ptr; ptr++)
		{
			if (0 != isalpha((unsigned char)*ptr))
			{
				if (0 == isupper((unsigned char)*ptr))
					*strict = 0;
				continue;
			}

			if (0 != isdigit((unsigned char)*ptr))
				continue;

			if ('_' == *ptr)
				continue;

			break;
		}

		/* check for empty segment */
		if (ptr == segment)
			return FAIL;

		*next = ptr - expression;
	}
	else
	{
		for (*strict = 0, ptr++; '"' != *ptr; ptr++)
		{
			if ('\0' == *ptr)
				return FAIL;

			if ('\\' == *ptr)
			{
				ptr++;
				if ('\\' != *ptr && '"' != *ptr)
					return FAIL;
			}
		}

		/* check for empty segment */
		if (1 == ptr - segment)
			return FAIL;

		*next = ptr - expression + 1;
	}

	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Purpose: parses macro name                                                 *
 *                                                                            *
 * Parameters: expression - [IN] the expression                               *
 *             ptr        - [IN] the beginning of macro name                  *
 *             loc        - [OUT] the macro name location                     *
 *                                                                            *
 * Return value: SUCCEED - the simple macro was parsed successfully           *
 *               FAIL    - macro does not point at valid macro                *
 *                                                                            *
 * Comments: Note that the character following macro name must be inspected   *
 *           to draw any conclusions. For example for normal macros it must   *
 *           be '}' or it's not a valid macro.                                *
 *                                                                            *
 ******************************************************************************/
static int	token_parse_macro_name(const char *expression, const char *ptr, zbx_strloc_t *loc)
{
	int	strict, offset, ret;

	loc->l = ptr - expression;

	while (SUCCEED == (ret = token_parse_macro_segment(expression, ptr, &strict, &offset)))
	{
		if (0 == strict && expression + loc->l == ptr)
			return FAIL;

		ptr = expression + offset;

		if ('.' != *ptr || 0 == strict)
		{
			loc->r = ptr - expression - 1;
			break;
		}
		ptr++;
	}
	return ret;
}

/******************************************************************************
 *                                                                            *
 * Purpose: parses normal macro token                                         *
 *                                                                            *
 * Parameters: expression - [IN] the expression                               *
 *             macro      - [IN] the beginning of the token                   *
 *             token      - [OUT] the token data                              *
 *                                                                            *
 * Return value: SUCCEED - the simple macro was parsed successfully           *
 *               FAIL    - macro does not point at valid macro                *
 *                                                                            *
 * Comments: If the macro points at valid macro in the expression then        *
 *           the generic token fields are set and the token->data.macro       *
 *           structure is filled with simple macro specific data.             *
 *                                                                            *
 ******************************************************************************/
static int	token_parse_macro(const char *expression, const char *macro, zbx_token_t *token)
{
	zbx_strloc_t		loc;
	zbx_token_macro_t	*data;

	if (SUCCEED != token_parse_macro_name(expression, macro + 1, &loc))
		return FAIL;

	if ('}' != expression[loc.r + 1])
		return FAIL;

	/* initialize token */
	token->type = ZBX_TOKEN_MACRO;
	token->loc.l = loc.l - 1;
	token->loc.r = loc.r + 1;

	/* initialize token data */
	data = &token->data.macro;
	data->name = loc;

	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Purpose: parses function inside token                                      *
 *                                                                            *
 * Parameters: expression - [IN] the expression                               *
 *             func       - [IN] the beginning of the function                *
 *             func_loc   - [OUT] the function location relative to the       *
 *                                expression (including parameters)           *
 *                                                                            *
 * Return value: SUCCEED - the function was parsed successfully               *
 *               FAIL    - func does not point at valid function              *
 *                                                                            *
 ******************************************************************************/
static int	token_parse_function(const char *expression, const char *func,
		zbx_strloc_t *func_loc, zbx_strloc_t *func_param)
{
	size_t	par_l, par_r;

	if (SUCCEED != zbx_function_validate(func, &par_l, &par_r, NULL, 0))
		return FAIL;

	func_loc->l = func - expression;
	func_loc->r = func_loc->l + par_r;

	func_param->l = func_loc->l + par_l;
	func_param->r = func_loc->l + par_r;

	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Purpose: parses function macro token                                       *
 *                                                                            *
 * Parameters: expression - [IN] the expression                               *
 *             macro      - [IN] the beginning of the token                   *
 *             func       - [IN] the beginning of the macro function in the   *
 *                               token                                        *
 *             token      - [OUT] the token data                              *
 *             token_type - [IN] type flag ZBX_TOKEN_FUNC_MACRO or            *
 *                               ZBX_TOKEN_LLD_FUNC_MACRO                     *
 *                                                                            *
 * Return value: SUCCEED - the function macro was parsed successfully         *
 *               FAIL    - macro does not point at valid function macro       *
 *                                                                            *
 * Comments: If the macro points at valid function macro in the expression    *
 *           then the generic token fields are set and the                    *
 *           token->data.func_macro or token->data.lld_func_macro structures  *
 *           depending on token type flag are filled with function macro      *
 *           specific data.                                                   *
 *                                                                            *
 ******************************************************************************/
static int	token_parse_func_macro(const char *expression, const char *macro, const char *func,
		zbx_token_t *token, int token_type)
{
	zbx_strloc_t		func_loc, func_param;
	zbx_token_func_macro_t	*data;
	const char		*ptr;
	size_t			offset;

	if ('\0' == *func)
		return FAIL;

	if (SUCCEED != token_parse_function(expression, func, &func_loc, &func_param))
		return FAIL;

	ptr = expression + func_loc.r + 1;

	/* skip trailing whitespace and verify that token ends with } */

	while (' ' == *ptr)
		ptr++;

	if ('}' != *ptr)
		return FAIL;

	offset = macro - expression;

	/* initialize token */
	token->type = token_type;
	token->loc.l = offset;
	token->loc.r = ptr - expression;

	/* initialize token data */
	data = ZBX_TOKEN_FUNC_MACRO == token_type ? &token->data.func_macro : &token->data.lld_func_macro;
	data->macro.l = offset + 1;
	data->macro.r = func_loc.l - 2;

	data->func = func_loc;
	data->func_param = func_param;

	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Purpose: parses simple macro token with given key                          *
 *                                                                            *
 * Parameters: expression - [IN] the expression                               *
 *             macro      - [IN] the beginning of the token                   *
 *             key        - [IN] the beginning of host key inside the token   *
 *             token      - [OUT] the token data                              *
 *                                                                            *
 * Return value: SUCCEED - the function macro was parsed successfully         *
 *               FAIL    - macro does not point at valid simple macro         *
 *                                                                            *
 * Comments: Simple macros have format {<host>:<key>.<func>(<params>)}        *
 *           {HOST.HOSTn} macro can be used for host name and {ITEM.KEYn}     *
 *           macro can be used for item key.                                  *
 *                                                                            *
 *           If the macro points at valid simple macro in the expression      *
 *           then the generic token fields are set and the                    *
 *           token->data.simple_macro structure is filled with simple macro   *
 *           specific data.                                                   *
 *                                                                            *
 ******************************************************************************/
static int	token_parse_simple_macro_key(const char *expression, const char *macro, const char *key,
		zbx_token_t *token)
{
	size_t				offset;
	zbx_token_simple_macro_t	*data;
	const char			*ptr = key;
	zbx_strloc_t			key_loc, func_loc, func_param;

	if (SUCCEED != zbx_parse_key(&ptr))
	{
		zbx_token_t	key_token;

		if (SUCCEED != token_parse_macro(expression, key, &key_token))
			return FAIL;

		ptr = expression + key_token.loc.r + 1;
	}

	/* If the key is without parameters, then zbx_parse_key() will move cursor past function name - */
	/* at the start of its parameters. In this case move cursor back before function.           */
	if ('(' == *ptr)
	{
		while ('.' != *(--ptr))
			;
	}

	/* check for empty key */
	if (0 == ptr - key)
		return FAIL;

	if (SUCCEED != token_parse_function(expression, ptr + 1, &func_loc, &func_param))
		return FAIL;

	key_loc.l = key - expression;
	key_loc.r = ptr - expression - 1;

	ptr = expression + func_loc.r + 1;

	/* skip trailing whitespace and verify that token ends with } */

	while (' ' == *ptr)
		ptr++;

	if ('}' != *ptr)
		return FAIL;

	offset = macro - expression;

	/* initialize token */
	token->type = ZBX_TOKEN_SIMPLE_MACRO;
	token->loc.l = offset;
	token->loc.r = ptr - expression;

	/* initialize token data */
	data = &token->data.simple_macro;
	data->host.l = offset + 1;
	data->host.r = offset + (key - macro) - 2;

	data->key = key_loc;
	data->func = func_loc;
	data->func_param = func_param;

	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Purpose: parses simple macro token                                         *
 *                                                                            *
 * Parameters: expression - [IN] the expression                               *
 *             macro      - [IN] the beginning of the token                   *
 *             token      - [OUT] the token data                              *
 *                                                                            *
 * Return value: SUCCEED - the simple macro was parsed successfully           *
 *               FAIL    - macro does not point at valid simple macro         *
 *                                                                            *
 * Comments: Simple macros have format {<host>:<key>.<func>(<params>)}        *
 *           {HOST.HOSTn} macro can be used for host name and {ITEM.KEYn}     *
 *           macro can be used for item key.                                  *
 *                                                                            *
 *           If the macro points at valid simple macro in the expression      *
 *           then the generic token fields are set and the                    *
 *           token->data.simple_macro structure is filled with simple macro   *
 *           specific data.                                                   *
 *                                                                            *
 ******************************************************************************/
static int	token_parse_simple_macro(const char *expression, const char *macro, zbx_token_t *token)
{
	const char	*ptr;

	/* Find the end of host name by validating its name until the closing bracket }.          */
	/* {HOST.HOSTn} macro usage in the place of host name is handled by nested macro parsing. */
	for (ptr = macro + 1; ':' != *ptr; ptr++)
	{
		if ('\0' == *ptr)
			return FAIL;

		if (SUCCEED != zbx_is_hostname_char(*ptr))
			return FAIL;
	}

	/* check for empty host name */
	if (1 == ptr - macro)
		return FAIL;

	return token_parse_simple_macro_key(expression, macro, ptr + 1, token);
}

/******************************************************************************
 *                                                                            *
 * Purpose: finds token {} inside expression starting at specified position   *
 *          also searches for reference if requested                          *
 *                                                                            *
 * Parameters: expression   - [IN] the expression                             *
 *             pos          - [IN] the starting position                      *
 *             token        - [OUT] the token data                            *
 *             token_search - [IN] specify if references will be searched     *
 *                                                                            *
 * Return value: SUCCEED - the token was parsed successfully                  *
 *               FAIL    - expression does not contain valid token.           *
 *                                                                            *
 * Comments: The token field locations are specified as offsets from the      *
 *           beginning of the expression.                                     *
 *                                                                            *
 *           Simply iterating through tokens can be done with:                *
 *                                                                            *
 *           zbx_token_t token = {0};                                         *
 *                                                                            *
 *           while (SUCCEED == zbx_token_find(expression, token.loc.r + 1,    *
 *                       &token))                                             *
 *           {                                                                *
 *                   process_token(expression, &token);                       *
 *           }                                                                *
 *                                                                            *
 ******************************************************************************/
int	zbx_token_find(const char *expression, int pos, zbx_token_t *token, zbx_token_search_t token_search)
{
	int		ret = FAIL;
	const char	*ptr = expression + pos, *dollar = ptr;

	while (SUCCEED != ret)
	{
		int	 quoted = 0;

		/* skip macros in string constants when looking for functionid */
		for (; '{' != *ptr || 0 != quoted; ptr++)
		{
			if ('\0' == *ptr)
				break;

			if (0 != (token_search & ZBX_TOKEN_SEARCH_FUNCTIONID))
			{
				switch (*ptr)
				{
					case '\\':
						if (0 != quoted)
						{
							if ('\0' == *(++ptr))
								return FAIL;
						}
						break;
					case '"':
						quoted = !quoted;
						break;
				}
			}
		}

		if (0 != (token_search & ZBX_TOKEN_SEARCH_REFERENCES))
		{
			while (NULL != (dollar = strchr(dollar, '$')) && ptr > dollar)
			{
				if (0 == isdigit(dollar[1]))
				{
					dollar++;
					continue;
				}

				token->data.reference.index = dollar[1] - '0';
				token->type = ZBX_TOKEN_REFERENCE;
				token->loc.l = dollar - expression;
				token->loc.r = token->loc.l + 1;
				return SUCCEED;
			}

			if (NULL == dollar)
				token_search &= ~ZBX_TOKEN_SEARCH_REFERENCES;
		}

		if ('\0' == *ptr)
			return FAIL;

		if ('\0' == ptr[1])
			return FAIL;

		switch (ptr[1])
		{
			case '$':
				ret = token_parse_user_macro(expression, ptr, token);
				break;
			case '#':
				ret = token_parse_lld_macro(expression, ptr, token);
				break;
			case '?':
				if (0 != (token_search & ZBX_TOKEN_SEARCH_EXPRESSION_MACRO))
					ret = token_parse_expression_macro(expression, ptr, token_search, token);
				break;
			case '{':
				ret = zbx_token_parse_nested_macro(expression, ptr, token_search, token);
				break;
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
				if (SUCCEED == (ret = token_parse_objectid(expression, ptr, token)))
					break;
				ZBX_FALLTHROUGH;
			default:
				if (SUCCEED != (ret = token_parse_macro(expression, ptr, token)) &&
						0 != (token_search & ZBX_TOKEN_SEARCH_SIMPLE_MACRO))
				{
					ret = token_parse_simple_macro(expression, ptr, token);
				}
		}

		ptr++;
	}

	return ret;
}

/******************************************************************************
 *                                                                            *
 * Purpose: public wrapper for token_parse_user_macro() function              *
 *                                                                            *
 ******************************************************************************/
int	zbx_token_parse_user_macro(const char *expression, const char *macro, zbx_token_t *token)
{
	return token_parse_user_macro(expression, macro, token);
}

/******************************************************************************
 *                                                                            *
 * Purpose: public wrapper for token_parse_macro() function                   *
 *                                                                            *
 ******************************************************************************/
int	zbx_token_parse_macro(const char *expression, const char *macro, zbx_token_t *token)
{
	return token_parse_macro(expression, macro, token);
}

/******************************************************************************
 *                                                                            *
 * Purpose: public wrapper for token_parse_objectid() function                *
 *                                                                            *
 ******************************************************************************/
int	zbx_token_parse_objectid(const char *expression, const char *macro, zbx_token_t *token)
{
	return token_parse_objectid(expression, macro, token);
}

/******************************************************************************
 *                                                                            *
 * Purpose: public wrapper for token_parse_lld_macro() function               *
 *                                                                            *
 ******************************************************************************/
int	zbx_token_parse_lld_macro(const char *expression, const char *macro, zbx_token_t *token)
{
	return token_parse_lld_macro(expression, macro, token);
}

/******************************************************************************
 *                                                                            *
 * Purpose: parses token with nested macros                                   *
 *                                                                            *
 * Parameters: expression   - [IN] the expression                             *
 *             macro        - [IN] the beginning of the token                 *
 *             token_search - [IN] specify if references will be searched     *
 *             token        - [OUT] the token data                            *
 *                                                                            *
 * Return value: SUCCEED - the token was parsed successfully                  *
 *               FAIL    - macro does not point at valid function or simple   *
 *                         macro                                              *
 *                                                                            *
 * Comments: This function parses token with a macro inside it. There are     *
 *           three types of nested macros - low-level discovery function      *
 *           macros, function macros and a specific case of simple macros     *
 *           where {HOST.HOSTn} macro is used as host name.                   *
 *                                                                            *
 *           If the macro points at valid macro in the expression then        *
 *           the generic token fields are set and either the                  *
 *           token->data.lld_func_macro, token->data.func_macro or            *
 *           token->data.simple_macro (depending on token type) structure is  *
 *           filled with macro specific data.                                 *
 *                                                                            *
 ******************************************************************************/
int	zbx_token_parse_nested_macro(const char *expression, const char *macro, zbx_token_search_t token_search,
		zbx_token_t *token)
{
	const char	*ptr;

	if ('#' == macro[2])
	{
		/* find the end of the nested macro by validating its name until the closing bracket '}' */
		for (ptr = macro + 3; '}' != *ptr; ptr++)
		{
			if ('\0' == *ptr)
				return FAIL;

			if (SUCCEED != zbx_is_macro_char(*ptr))
				return FAIL;
		}

		/* empty macro name */
		if (3 == ptr - macro)
			return FAIL;
	}
	else if ('?' == macro[2])
	{
		zbx_token_t	expr_token;

		if (0 == (token_search & ZBX_TOKEN_SEARCH_EXPRESSION_MACRO))
			return FAIL;

		if (SUCCEED != token_parse_expression_macro(expression, macro + 1, token_search, &expr_token))
			return FAIL;

		ptr = expression + expr_token.loc.r;
	}
	else
	{
		zbx_strloc_t	loc;

		if (SUCCEED != token_parse_macro_name(expression, macro + 2, &loc))
			return FAIL;

		if ('}' != expression[loc.r + 1])
			return FAIL;

		ptr = expression + loc.r + 1;
	}

	/* Determine the token type.                                                   */
	/* Nested macros formats:                                                      */
	/*               low-level discovery function macros  {{#MACRO}.function()}    */
	/*               function macros                      {{MACRO}.function()}     */
	/*               simple macros                        {{MACRO}:key.function()} */
	if ('.' == ptr[1])
	{
		return token_parse_func_macro(expression, macro, ptr + 2, token, '#' == macro[2] ?
				ZBX_TOKEN_LLD_FUNC_MACRO : ZBX_TOKEN_FUNC_MACRO);
	}
	else if (0 != (token_search & ZBX_TOKEN_SEARCH_SIMPLE_MACRO) && '#' != macro[2] && ':' == ptr[1])
		return token_parse_simple_macro_key(expression, macro, ptr + 2, token);

	return FAIL;
}