/*
 * Copyright (c) 2005, 2006 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: t-strexpmac.c,v 1.4 2006/01/05 22:41:55 ca Exp $")

#include "sm/assert.h"
#include "sm/magic.h"
#include "sm/error.h"
#include "sm/rpool.h"
#include "sm/test.h"
#include "sm/str.h"
#include "sm/strexp.h"
#include "sm/io.h"

extern char *optarg;
extern int optind;
extern int optopt;
extern int opterr;

#define SMAXLEN	1024

static int Verbose = 0;

#define NMACROS	10

#if TEST_STREXPMAC_CB
#define sm_str_expmac(src, dst, f, n, macros, repl)	\
	sm_str_expmac_cb(src, dst, f, expmac, &t_ctx)

struct t_ctx_S
{
	uint		 t_nmacros;
	sm_str_P	*t_macros;
	sm_str_P	*t_repl;
};
typedef struct t_ctx_S t_ctx_T, *t_ctx_P;

static sm_ret_T
expmac(const sm_str_P src, uint len, sm_str_P dst, uint mac_begin, uint mac_end, void *ctx)
{
	sm_ret_T ret;
	uint idx;
	t_ctx_P t_ctx;

	SM_REQUIRE(ctx != NULL);
	t_ctx = (t_ctx_P)ctx;
	ret = sm_find_macro(src, len, mac_begin, mac_end, &idx,
			t_ctx->t_nmacros, t_ctx->t_macros);
	if (sm_is_err(ret))
		return ret;
	if (idx < NMACROS && t_ctx->t_repl[idx] != NULL)
	{
		ret = sm_str_cat(dst, t_ctx->t_repl[idx]);
		if (sm_is_err(ret))
			return ret;
		return 1;
	}
	return ret;
}

#endif /* TEST_STREXPMAC_CB */

static void
test_harness(sm_rpool_P a)
{
	uint argc;
	sm_str_P src, dst, macros[NMACROS], repl[NMACROS];
	sm_ret_T ret;
#if TEST_STREXPMAC_CB
	t_ctx_T t_ctx;
#endif /* TEST_STREXPMAC_CB */

#if TEST_STREXPMAC_CB
	t_ctx.t_nmacros = 1;
	t_ctx.t_macros = macros;
	t_ctx.t_repl = repl;
#endif /* TEST_STREXPMAC_CB */
	src = sm_str_new(a, SMAXLEN, SMAXLEN);
	SM_TEST(src != NULL);
	if (src == NULL)
		return;
	dst = sm_str_new(a, SMAXLEN, SMAXLEN);
	SM_TEST(dst != NULL);
	if (dst == NULL)
		return;

	for (argc = 0; argc < NMACROS; argc++)
	{
		macros[argc] = sm_str_new(a, SMAXLEN, SMAXLEN);
		SM_TEST(macros[argc] != NULL);
		if (macros[argc] == NULL)
			return;
		sm_strprintf(macros[argc], "macro%u", argc);

		repl[argc] = sm_str_new(a, SMAXLEN, SMAXLEN);
		SM_TEST(repl[argc] != NULL);
		if (repl[argc] == NULL)
			return;
		sm_strprintf(repl[argc], "repl%uace", argc);
	}

	/* simple expansion */
	sm_str_clr(src);
	sm_str_clr(dst);
	ret = sm_str_scat0(src, "str${macro0}");
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return;
	ret = sm_str_expmac(src, dst, 0, 1, macros, repl);
	SM_TEST(sm_is_success(ret));
	if (!sm_is_success(ret))
		return;
	SM_TEST(strcmp((char *)sm_str_getdata(dst), "strrepl0ace") == 0);
	if (Verbose > 2)
		sm_io_fprintf(smioerr, "dst='%S'\n", dst);

	/* simple expansion */
	sm_str_clr(src);
	sm_str_clr(dst);
	ret = sm_str_scat0(src, "${macro0}");
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return;
	ret = sm_str_expmac(src, dst, 0, 1, macros, repl);
	SM_TEST(sm_is_success(ret));
	if (!sm_is_success(ret))
		return;
	SM_TEST(strcmp((char *)sm_str_getdata(dst), "repl0ace") == 0);
	if (Verbose > 2)
		sm_io_fprintf(smioerr, "dst='%S'\n", dst);

	/* expansion suppressed */
	sm_str_clr(src);
	sm_str_clr(dst);
	ret = sm_str_scat0(src, "\\${macro0}");
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return;
	ret = sm_str_expmac(src, dst, 0, 1, macros, repl);
	SM_TEST(sm_is_success(ret));
	if (!sm_is_success(ret))
		return;
	SM_TEST(strcmp((char *)sm_str_getdata(dst), "${macro0}") == 0);
	if (Verbose > 2)
		sm_io_fprintf(smioerr, "dst='%S'\n", dst);

	/* expansion suppressed */
	sm_str_clr(src);
	sm_str_clr(dst);
	ret = sm_str_scat0(src, "\\\\\\${macro0}");
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return;
	ret = sm_str_expmac(src, dst, 0, 1, macros, repl);
	SM_TEST(sm_is_success(ret));
	if (!sm_is_success(ret))
		return;
	SM_TEST(strcmp((char *)sm_str_getdata(dst), "\\\\${macro0}") == 0);
	if (Verbose > 2)
		sm_io_fprintf(smioerr, "dst='%S'\n", dst);

	/* expansion */
	sm_str_clr(src);
	sm_str_clr(dst);
	ret = sm_str_scat0(src, "\\\\${macro0}");
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return;
	ret = sm_str_expmac(src, dst, 0, 1, macros, repl);
	SM_TEST(sm_is_success(ret));
	if (!sm_is_success(ret))
		return;
	SM_TEST(strcmp((char *)sm_str_getdata(dst), "\\\\repl0ace") == 0);
	if (Verbose > 2)
		sm_io_fprintf(smioerr, "dst='%S'\n", dst);

	/* macro is not "defined", replaced with empty string */
	sm_str_clr(src);
	sm_str_clr(dst);
	ret = sm_str_scat0(src, "str${macro}");
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return;
	ret = sm_str_expmac(src, dst, 0, 1, macros, repl);
	SM_TEST(ret == 1);
	if (!sm_is_success(ret))
		return;
	SM_TEST(strcmp((char *)sm_str_getdata(dst), "str") == 0);
	if (Verbose > 2)
		sm_io_fprintf(smioerr, "dst='%S'\n", dst);

	/* expansion fails */
	sm_str_clr(src);
	sm_str_clr(dst);
	ret = sm_str_scat0(src, "str${macro");
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return;
	ret = sm_str_expmac(src, dst, 0, 1, macros, repl);
	SM_TEST(ret == 0);
	if (!sm_is_success(ret))
		return;
	SM_TEST(strcmp((char *)sm_str_getdata(dst), "str${macro") == 0);
	if (Verbose > 2)
		sm_io_fprintf(smioerr, "ret=%r, dst='%S'\n", ret, dst);

	sm_str_clr(src);
	sm_str_clr(dst);
	ret = sm_str_scat0(src, "str$${macro0}");
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return;
	ret = sm_str_expmac(src, dst, 0, 1, macros, repl);
	SM_TEST(ret == 1);
	if (!sm_is_success(ret))
		return;
	SM_TEST(strcmp((char *)sm_str_getdata(dst), "str$repl0ace") == 0);
	if (Verbose > 2)
		sm_io_fprintf(smioerr, "ret=%r, dst='%S'\n", ret, dst);

	sm_str_clr(src);
	sm_str_clr(dst);
	ret = sm_str_scat0(src, "str$${macro0}${}");
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return;
	ret = sm_str_expmac(src, dst, 0, 1, macros, repl);
	SM_TEST(ret == 1);
	if (!sm_is_success(ret))
		return;
	SM_TEST(strcmp((char *)sm_str_getdata(dst), "str$repl0ace${}") == 0);
	if (Verbose > 2)
		sm_io_fprintf(smioerr, "ret=%r, dst='%S'\n", ret, dst);

	sm_str_clr(src);
	sm_str_clr(dst);
	ret = sm_str_scat0(src, "str$${macro0}${A}");
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return;
	ret = sm_str_expmac(src, dst, 0, 1, macros, repl);
	SM_TEST(ret == 2);
	if (!sm_is_success(ret))
		return;
	SM_TEST(strcmp((char *)sm_str_getdata(dst), "str$repl0ace") == 0);
	if (Verbose > 2)
		sm_io_fprintf(smioerr, "ret=%r, dst='%S'\n", ret, dst);

	sm_str_clr(src);
	sm_str_clr(dst);
	ret = sm_str_scat0(src, "str${macro0\\}");
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return;
	ret = sm_str_expmac(src, dst, 0, 1, macros, repl);
	SM_TEST(ret == 1);
	if (!sm_is_success(ret))
		return;
	SM_TEST(strcmp((char *)sm_str_getdata(dst), "str") == 0);
	if (Verbose > 2)
		sm_io_fprintf(smioerr, "dst='%S'\n", dst);

	sm_str_clr(src);
	sm_str_clr(dst);
	ret = sm_str_scat0(src, "str${macro0}${macro0}");
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return;
	ret = sm_str_expmac(src, dst, 0, 1, macros, repl);
	SM_TEST(ret == 2);
	if (!sm_is_success(ret))
		return;
	SM_TEST(strcmp((char *)sm_str_getdata(dst), "strrepl0acerepl0ace")
		== 0);
	if (Verbose > 2)
		sm_io_fprintf(smioerr, "ret=%r, dst='%S'\n", ret, dst);

	sm_str_clr(src);
	sm_str_clr(dst);
	ret = sm_str_scat0(src, "${macro1}X${macro0}");
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return;
#if TEST_STREXPMAC_CB
	t_ctx.t_nmacros = 2;
#endif
	ret = sm_str_expmac(src, dst, 0, 2, macros, repl);
	SM_TEST(ret == 2);
	if (!sm_is_success(ret))
		return;
	SM_TEST(strcmp((char *)sm_str_getdata(dst), "repl1aceXrepl0ace")
		== 0);
	if (Verbose > 2)
		sm_io_fprintf(smioerr, "ret=%r, dst='%S'\n", ret, dst);

	sm_str_clr(src);
	sm_str_clr(dst);
	ret = sm_str_scat0(src, "${macro1}X${macro0}");
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return;
#if TEST_STREXPMAC_CB
	t_ctx.t_nmacros = 1;
#endif
	ret = sm_str_expmac(src, dst, 0, 1, macros, repl);
	SM_TEST(ret == 2);
	if (!sm_is_success(ret))
		return;
	SM_TEST(strcmp((char *)sm_str_getdata(dst), "Xrepl0ace")
		== 0);
	if (Verbose > 2)
		sm_io_fprintf(smioerr, "ret=%r, dst='%S'\n", ret, dst);

	sm_str_clr(src);
	sm_str_clr(dst);
	ret = sm_str_scat0(src, "\\");
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return;
	ret = sm_str_expmac(src, dst, 0, 1, macros, repl);
	SM_TEST(ret == 0);
	if (!sm_is_success(ret))
		return;

	sm_str_clr(src);
	sm_str_clr(dst);
	ret = sm_str_scat0(src, "\\$\\");
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return;
	ret = sm_str_expmac(src, dst, 0, 1, macros, repl);
	SM_TEST(ret > 0);
	if (!sm_is_success(ret))
		return;

	sm_str_clr(src);
	sm_str_clr(dst);
	ret = sm_str_scat0(src, "\\a");
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return;
	ret = sm_str_expmac(src, dst, 0, 1, macros, repl);
	SM_TEST(ret == 0);
	if (!sm_is_success(ret))
		return;

	sm_str_clr(src);
	sm_str_clr(dst);
	ret = sm_str_scat0(src, "\\\\");
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return;
	ret = sm_str_expmac(src, dst, 0, 1, macros, repl);
	SM_TEST(ret == 0);
	if (!sm_is_success(ret))
		return;

#if 0
	/* two expansions and keep '%%' */
	sm_str_clr(src);
	sm_str_clr(dst);
	ret = sm_str_scat0(src, "%0-%%-%1");
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return;
	ret = sm_str_expdig(src, dst, '\0', 2, argv);
	SM_TEST(sm_is_success(ret));
	if (!sm_is_success(ret))
		return;
	SM_TEST(strcmp((char *)sm_str_getdata(dst), "arg-0!-%-arg-1!") == 0);
	if (Verbose > 2)
		sm_io_fprintf(smioerr, "dst='%S'\n", dst);

	/* keep '%%', one expansion does not have an argument */
	sm_str_clr(src);
	sm_str_clr(dst);
	ret = sm_str_scat0(src, "%0-%%-%1");
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return;
	ret = sm_str_expdig(src, dst, '\0', 1, argv);
	SM_TEST(sm_is_success(ret));
	if (!sm_is_success(ret))
		return;
	SM_TEST(strcmp((char *)sm_str_getdata(dst), "arg-0!-%-") == 0);
	if (Verbose > 2)
		sm_io_fprintf(smioerr, "dst='%S'\n", dst);

	/* two expansions and keep trailing '%' */
	sm_str_clr(src);
	sm_str_clr(dst);
	ret = sm_str_scat0(src, "%0-%1-%");
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return;
	ret = sm_str_expdig(src, dst, '\0', 2, argv);
	SM_TEST(sm_is_success(ret));
	if (!sm_is_success(ret))
		return;
	SM_TEST(strcmp((char *)sm_str_getdata(dst), "arg-0!-arg-1!-%") == 0);
	if (Verbose > 2)
		sm_io_fprintf(smioerr, "dst='%S'\n", dst);

	/* many expansions */
	sm_str_clr(src);
	sm_str_clr(dst);
	ret = sm_str_scat0(src, "%0.%1.%2.%3.%4.%5.%6.%7.%8.%9.%A");
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return;
	ret = sm_str_expdig(src, dst, '\0', 10, argv);
	SM_TEST(sm_is_success(ret));
	if (!sm_is_success(ret))
		return;
	SM_TEST(strcmp((char *)sm_str_getdata(dst),
		"arg-0!.arg-1!.arg-2!.arg-3!.arg-4!.arg-5!.arg-6!.arg-7!.arg-8!.arg-9!.A") == 0);
	if (Verbose > 2)
		sm_io_fprintf(smioerr, "dst='%S'\n", dst);
#endif

	for (argc = 0; argc < NMACROS; argc++)
	{
		SM_STR_FREE(macros[argc]);
		SM_STR_FREE(repl[argc]);
	}
	sm_str_free(src);
	sm_str_free(dst);
}

int
main(int argc, char *argv[])
{
	int c;
	sm_rpool_P a;

	while ((c = getopt(argc, argv, "V")) != -1)
	{
		switch (c)
		{
		  case 'V':
			++Verbose;
			break;
		  default:
			/* usage(prg); */
			break;
		}
	}
	sm_test_begin(argc, argv, "test strexpmac()");
	argc -= optind;
	argv += optind;

	/* create an rpool for entire test */
	a = sm_rpool_new(NULL);
	SM_TEST(a != NULL);
	test_harness(a);
	sm_rpool_delete(a);

	/* test without rpool */
	test_harness(NULL);
	return sm_test_end();
}


syntax highlighted by Code2HTML, v. 0.9.1