/*
** Copyright (C) 2001-2025 Zabbix SIA
**
** This program is free software: you can redistribute it and/or modify it under the terms of
** the GNU Affero General Public License as published by the Free Software Foundation, version 3.
**
** 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 Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License along with this program.
** If not, see <https://www.gnu.org/licenses/>.
**/

#include "zbxexpr.h"

/******************************************************************************
 *                                                                            *
 * Parameters: expression - [IN]                                              *
 *             macro      - [IN] beginning of the token                       *
 *             token      - [OUT] token data                                  *
 *                                                                            *
 * Return value: SUCCEED - 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;
}

/******************************************************************************
 *                                                                            *
 * Parameters: expression - [IN]                                              *
 *             macro      - [IN] beginning of token                           *
 *             token      - [OUT]                                             *
 *                                                                            *
 * Return value: SUCCEED - 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;
}

/******************************************************************************
 *                                                                            *
 * Parameters: expression    - [IN]                                           *
 *             macro         - [IN] beginning of the token                    *
 *             token_search  - [IN] specify if references will be searched    *
 *             token         - [OUT]                                          *
 *                                                                            *
 * Return value: SUCCEED - 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 *
 *           the 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;
}

/******************************************************************************
 *                                                                            *
 * Parameters: expression - [IN]                                              *
 *             macro      - [IN] beginning of token                           *
 *             token      - [OUT]                                             *
 *                                                                            *
 * Return value: SUCCEED - 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]                                              *
 *             segment    - [IN] segment start                                *
 *             strict     - [OUT] 1 - macro contains only standard characters *
 *                                    (upper case alphanumeric characters,    *
 *                                     dots and underscores)                  *
 *                                0 - last segment contains lowercase or      *
 *                                    quoted characters                       *
 *             next       - [OUT] offset of next character after the segment  *
 *                                                                            *
 * Return value: SUCCEED - 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;
}

/******************************************************************************
 *                                                                            *
 * Parameters: expression - [IN]                                              *
 *             ptr        - [IN] beginning of macro name                      *
 *             loc        - [OUT] macro name location                         *
 *                                                                            *
 * Return value: SUCCEED - 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]                                              *
 *             macro      - [IN] beginning of the token                       *
 *             token      - [OUT]                                             *
 *                                                                            *
 * Return value: SUCCEED - 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 variable macro token                                       *
 *                                                                            *
 * Parameters: expression - [IN]                                              *
 *             macro      - [IN] beginning of the token                       *
 *             token      - [OUT]                                             *
 *                                                                            *
 * Return value: SUCCEED - 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.var_macro   *
 *           structure is filled with simple macro specific data.             *
 *                                                                            *
 ******************************************************************************/
static int	token_parse_var_macro(const char *expression, const char *macro, zbx_token_t *token)
{
	zbx_token_macro_t	*data;
	const char		*ptr;

	for (ptr = macro + 1; '}' != *ptr; ptr++)
	{
		if ('\0' == *ptr)
			return FAIL;
	}
	/* initialize token */
	token->type = ZBX_TOKEN_VAR_MACRO;
	token->loc.l = macro - expression;
	token->loc.r = ptr - expression;

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

	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Purpose: parses function inside token                                      *
 *                                                                            *
 * Parameters: expression - [IN]                                              *
 *             func       - [IN] beginning of the function                    *
 *             func_loc   - [OUT] function location relative to the           *
 *                                expression (including parameters)           *
 *             func_param - [OUT]                                             *
 *                                                                            *
 * Return value: SUCCEED - 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;
}

/******************************************************************************
 *                                                                            *
 * Parameters: expression - [IN]                                              *
 *             macro      - [IN] beginning of the token                       *
 *             func       - [IN] beginning of the macro function in token     *
 *             token      - [OUT]                                             *
 *             token_type - [IN] type flag ZBX_TOKEN_FUNC_MACRO,              *
 *                               ZBX_TOKEN_USER_FUNC_MACRO or                 *
 *                               ZBX_TOKEN_LLD_FUNC_MACRO                     *
 *                                                                            *
 * Return value: SUCCEED - 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 */
	switch (token_type)
	{
		case ZBX_TOKEN_FUNC_MACRO:
		case ZBX_TOKEN_USER_FUNC_MACRO:
			data = &token->data.func_macro;
			break;
		case ZBX_TOKEN_LLD_FUNC_MACRO:
			data = &token->data.lld_func_macro;
			break;
		case ZBX_TOKEN_VAR_FUNC_MACRO:
			data = &token->data.var_func_macro;
			break;
		default:
			return FAIL;
	}

	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]                                              *
 *             macro      - [IN] beginning of token                           *
 *             key        - [IN] beginning of host key inside token           *
 *             token      - [OUT]                                             *
 *                                                                            *
 * Return value: SUCCEED - 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;
}

/******************************************************************************
 *                                                                            *
 * Parameters: expression - [IN]                                              *
 *             macro      - [IN] beginning of the token                       *
 *             token      - [OUT]                                             *
 *                                                                            *
 * Return value: SUCCEED - 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 the specified        *
 *          position and also searches for reference if requested.            *
 *                                                                            *
 * Parameters: expression   - [IN]                                            *
 *             pos          - [IN] starting position                          *
 *             token        - [OUT]                                           *
 *             token_search - [IN] specify if references will be searched     *
 *                                                                            *
 * Return value: SUCCEED - 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;

		/* ZBX_TOKEN_SEARCH_VAR_MACRO will never return other macro types (except var func macro) */
		if (0 != (token_search & ZBX_TOKEN_SEARCH_VAR_MACRO) && '{' != ptr[1])
		{
			if (SUCCEED == (ret = token_parse_var_macro(expression, ptr, token)))
				continue;
		}

		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]                                            *
 *             macro        - [IN] beginning of token                         *
 *             token_search - [IN] specify if references will be searched     *
 *             token        - [OUT]                                           *
 *                                                                            *
 * Return value: SUCCEED - 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     *
 *           four types of nested macros - low-level discovery function       *
 *           macros, built-in function macros, user 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;
	int		token_type = ZBX_TOKEN_UNKNOWN;
	zbx_token_t	inner_token;

	if (0 != (token_search & ZBX_TOKEN_SEARCH_VAR_MACRO))
	{
		if (SUCCEED != token_parse_var_macro(expression, macro + 1, &inner_token))
			return FAIL;

		token_type = ZBX_TOKEN_VAR_FUNC_MACRO;
		ptr = expression + inner_token.loc.r;
	}
	else 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;

		token_type = ZBX_TOKEN_LLD_FUNC_MACRO;
	}
	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;
		token_type = ZBX_TOKEN_FUNC_MACRO;
	}
	else if ('$' == macro[2])
	{
		zbx_token_t	expr_token;

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

		ptr = expression + expr_token.loc.r;
		token_type = ZBX_TOKEN_USER_FUNC_MACRO;
	}
	else
	{
		if (SUCCEED != token_parse_macro(expression, macro + 1, &inner_token))
			return FAIL;

		token_type = ZBX_TOKEN_FUNC_MACRO;

		ptr = expression + inner_token.loc.r;
	}

	/* 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, token_type);
	}
	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;
}

/******************************************************************************
 *                                                                            *
 * Purpose: compares substring at the specified location with the specified   *
 *          text                                                              *
 *                                                                            *
 * Parameters: src      - [IN] the source string                              *
 *             loc      - [IN] the substring location                         *
 *             text     - [IN] the text to compare with                       *
 *             text_len - [IN] the text length                                *
 *                                                                            *
 * Return value: -1 - the substring is less than the specified text           *
 *                0 - the substring is equal to the specified text            *
 *                1 - the substring is greater than the specified text        *
 *                                                                            *
 ******************************************************************************/
int	zbx_strloc_cmp(const char *src, const zbx_strloc_t *loc, const char *text, size_t text_len)
{
	size_t	src_len = loc->r - loc->l + 1;

	if (src_len < text_len)
		return -1;

	if (src_len > text_len)
		return 1;

	return memcmp(src + loc->l, text, text_len);
}