/*
 * Copyright (c) 2005 Sendmail, Inc. and its suppliers.
 *	All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 */

#include "sm/generic.h"
SM_RCSID("@(#)$Id: strexpmaccb.c,v 1.2 2005/07/05 17:20:49 ca Exp $")

#include "sm/assert.h"
#include "sm/magic.h"
#include "sm/ctype.h"
#include "sm/str-int.h"
#include "sm/strexp.h"

/*
**  SM_STR_EXPMAC_CB -- Expand ${macro} in string
**  \$ will be replaced by $ to suppress expansion of ${macro}
**
**	Parameters:
**		src -- str to expand (read)
**		dst -- str into which the expanded sequence is written (output)
**		flags -- flags (currently unused)
**		expmac -- callback to expand macro
**		ctx -- context for callback to expand macro
**
**	Returns:
**		==0: no macro in src, NOTHING written to dst!
**		>=0: number of replacements
**		<0: usual sm_error code
*/

sm_ret_T
sm_str_expmac_cb(const sm_str_P src, sm_str_P dst, uint flags, sm_str_expmac_cb_F expmac, void *ctx)
{
	uint i, len, idx, replacements, mac_begin;
	uchar ch;
	sm_ret_T ret;
	bool backslash, found;

	SM_IS_BUF(dst);
	SM_IS_BUF(src);
	SM_REQUIRE(expmac != NULL);

#define MACPUTCH(ch)				\
	do {					\
		ret = sm_str_put(dst, (ch));	\
		if (sm_is_err(ret))		\
			goto error;		\
	} while (0)

	len = sm_str_getlen(src);

	backslash = false;
	found = false;
	for (i = 0; i < len; i++)
	{
		ch = sm_str_rd_elem(src, i);

		/* need at least ${A} */
		if (!backslash &&
		    ch == '$' && i + 3 < len &&
		    (ch = sm_str_rd_elem(src, i + 1)) == '{')
		{
			found = true;
			break;
		}
		if (!backslash)
			backslash = ch == '\\';
		else
		{
			/* \$ will be replaced */
			if (ch == '$')
			{
				found = true;
				break;
			}
			backslash = false;
		}
	}
	if (!found)
		return 0;

	replacements = 0;
	backslash = false;
	for (i = 0; i < len; i++)
	{
		ch = sm_str_rd_elem(src, i);

		/* need at least ${A} */
		if (!backslash &&
		    ch == '$' && i + 3 < len &&
		    (ch = sm_str_rd_elem(src, i + 1)) == '{')
		{
			i += 2;
			mac_begin = i;
			for (;
			     i < len && (ch = sm_str_rd_elem(src, i)) != '}';
			     i++)
				;
			if (i < len && ch == '}')
			{
				ret = expmac(src, len, dst, mac_begin, i - 1,
					ctx);
				++replacements;
				if (sm_is_err(ret))
					continue;
			}
			else
			{
				/* copy "skipped" text over */
				MACPUTCH('$');
				MACPUTCH('{');
				for (idx = mac_begin; idx < i; idx++)
				{
					ch = sm_str_rd_elem(src, idx);
					MACPUTCH(ch);
				}
			}
		}
		else
		{
			if (!backslash)
			{
				backslash = ch == '\\';
				if (backslash)
				{
					++replacements;
					continue;
				}
			}
			else
			{
				backslash = false;
				if (ch != '$')
					MACPUTCH('\\');
				else
					++replacements;
			}
			ret = sm_str_put(dst, ch);
			if (sm_is_err(ret))
				goto error;
		}
	}
	if (backslash)
		MACPUTCH('\\');

	if (replacements > (uint)SM_RET_MAX)
		replacements = SM_RET_MAX;
	return (sm_ret_T) replacements;

  error:
	/* cleanup? */
	return ret;
}


syntax highlighted by Code2HTML, v. 0.9.1