/*
 * Copyright (c) 2004, 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_IDSTR(id, "@(#)$Id: t-adbr-0.c,v 1.10 2006/10/21 05:02:41 ca Exp $")

#include "sm/debug.h"
#include "sm/heap.h"
#include "sm/sysexits.h"
#include "sm/test.h"
#include "sm/io.h"
#include "sm/rfc2821.h"
#include "sm/actdb-int.h"
#define QMGR_DEBUG_DEFINE 1
#include "sm/qmgrdbg.h"

static int Verbose = 0;

#if SM_HEAP_CHECK
# include "sm/io.h"
extern SM_DEBUG_T SmHeapCheck;
# define HEAP_CHECK (SmHeapCheck > 0)
#else
# define HEAP_CHECK 0
#endif

#define SM_AQ_RCPTS	256
#define RCPT_MAX_LEN	256

#define SMT_NOCHECKS	0x01
#define SMT_APPEND	0x02

static sm_ret_T
aqr_test1(aq_rcpt_P aq_rcpt, int n)
{
	int direction, i;
	aq_rcpt_P aq_rcpt_nxt, aq_rcpt_prev, aq_rcpt_f;

	if (Verbose > 0)
		sm_io_fprintf(smioerr, "\naq_rcpt=%p, domain=%S\n",
			aq_rcpt, aq_rcpt->aqr_domain);

#define DIR(d)	((d > 0) ? 1 : ((d < 0) ? (-1) : 0))
	if (Verbose > 0)
		sm_io_fprintf(smioerr, "LIST:  0: aq_rcpt=%p, domain=%S\n",
			aq_rcpt, aq_rcpt->aqr_domain);
	for (aq_rcpt_prev = aq_rcpt, aq_rcpt_nxt = AQR_SS_SUCC(aq_rcpt), i = 1;
	     aq_rcpt_nxt != aq_rcpt;
	     aq_rcpt_prev = aq_rcpt_nxt, aq_rcpt_nxt = AQR_SS_SUCC(aq_rcpt_nxt),
		i++)
	{
		direction = sm_str_casecmp(aq_rcpt_prev->aqr_domain,
					aq_rcpt_nxt->aqr_domain);
		direction = DIR(direction);
		if (Verbose > 0)
			sm_io_fprintf(smioerr,
				"LIST: %2d: aq_rcpt=%p, domain=%S, dir=%d\n",
				i, aq_rcpt_nxt, aq_rcpt_nxt->aqr_domain,
				direction);
	}

	aq_rcpt_f = NULL;
	for (aq_rcpt_prev = aq_rcpt, aq_rcpt_nxt = AQR_SS_SUCC(aq_rcpt), i = 0;
	     aq_rcpt_nxt != aq_rcpt;
	     aq_rcpt_prev = aq_rcpt_nxt, aq_rcpt_nxt = AQR_SS_SUCC(aq_rcpt_nxt),
		i++)
	{
		direction = sm_str_casecmp(aq_rcpt_prev->aqr_domain,
					aq_rcpt_nxt->aqr_domain);
		direction = DIR(direction);
		if (Verbose > 0)
			sm_io_fprintf(smioerr,
				"%2d/0: aq_rcpt=%p, domain=%S, dir=%d\n",
				i, aq_rcpt_prev, aq_rcpt_prev->aqr_domain,
				direction);
		if (direction > 0)
		{
			if (Verbose > 0)
				sm_io_fprintf(smioerr,
					"BREAK: %p, %S, nxt=%p, %S, dir=%d\n",
					aq_rcpt_prev, aq_rcpt_prev->aqr_domain,
					aq_rcpt_nxt, aq_rcpt_nxt->aqr_domain,
					direction);
			aq_rcpt_f = aq_rcpt_nxt;
			break;
		}
	}

	if (aq_rcpt_f != NULL)
	{
		for (aq_rcpt_prev = aq_rcpt_f,
			aq_rcpt_nxt = AQR_SS_SUCC(aq_rcpt_f), i = 0;
		     aq_rcpt_nxt != aq_rcpt_f;
		     aq_rcpt_prev = aq_rcpt_nxt,
			aq_rcpt_nxt = AQR_SS_SUCC(aq_rcpt_nxt), i++)
		{
			direction = sm_str_casecmp(aq_rcpt_prev->aqr_domain,
					aq_rcpt_nxt->aqr_domain);
			direction = DIR(direction);
			if (Verbose > 0)
				sm_io_fprintf(smioerr,
					"%2d: aq_rcpt=%p, domain=%S, dir=%d\n",
					i, aq_rcpt_prev, aq_rcpt_prev->aqr_domain,
					direction);
			SM_TEST(direction <= 0);
			if (direction > 0 && Verbose > 0)
				sm_io_fprintf(smioerr,
					"ERROR: %p, %S, nxt=%p, %S, dir=%d\n",
					aq_rcpt_prev, aq_rcpt_prev->aqr_domain,
					aq_rcpt_nxt, aq_rcpt_nxt->aqr_domain,
					direction);
		}
	}


	SM_TEST(i == n);
	if (i != n && Verbose > 0)
		sm_io_fprintf(smioerr, "i=%2d, n=%2d\n", i, n);
	if (Verbose > 0)
		sm_io_fprintf(smioerr, "%2d: aq_rcpt=%p, domain=%S [LAST]\n",
			i, aq_rcpt_nxt, aq_rcpt_nxt->aqr_domain);
	sm_io_flush(smioerr);
	return SM_SUCCESS;
}

static sm_ret_T
aqr_tests(aq_rcpt_P aq_rcpt)
{
	int direction;
	aq_rcpt_P aq_rcpt_nxt, aq_rcpt_prev, aq_rcpt_f, aq_rcpt_l;

	aq_rcpt_f = aq_rcpt_l = aq_rcpt;
	if (Verbose > 0)
		sm_io_fprintf(smioout, "\naq_rcpt=%p, domain=%S\n",
			aq_rcpt, aq_rcpt->aqr_domain);
	for (aq_rcpt_nxt = AQR_SS_SUCC(aq_rcpt);
	     aq_rcpt_nxt != aq_rcpt;
	     aq_rcpt_nxt = AQR_SS_SUCC(aq_rcpt_nxt))
	{
		direction = sm_str_casecmp(aq_rcpt_nxt->aqr_domain,
				aq_rcpt_f->aqr_domain);
		if (direction < 0)
			aq_rcpt_f = aq_rcpt_nxt;
		direction = sm_str_casecmp(aq_rcpt_nxt->aqr_domain,
				aq_rcpt_l->aqr_domain);
		if (direction > 0)
			aq_rcpt_l = aq_rcpt_nxt;
	}

	for (aq_rcpt_nxt = aq_rcpt_f, aq_rcpt_prev = aq_rcpt_f;
	     aq_rcpt_nxt != aq_rcpt_l;
	     aq_rcpt_nxt = AQR_SS_SUCC(aq_rcpt_nxt))
	{
		if (Verbose > 0)
			sm_io_fprintf(smioout, "aq_rcpt=%p, domain=%S\n",
				aq_rcpt_nxt, aq_rcpt_nxt->aqr_domain);

		direction = sm_str_casecmp(aq_rcpt_prev->aqr_domain,
			aq_rcpt_nxt->aqr_domain);

		SM_TEST(direction <= 0);
		aq_rcpt_prev = aq_rcpt_nxt;
	}
	if (Verbose > 0)
		sm_io_fprintf(smioout, "aq_rcpt=%p, domain=%S\n",
			aq_rcpt_nxt, aq_rcpt_nxt->aqr_domain);
	sm_io_flush(smioout);
	return SM_SUCCESS;
}

static sm_ret_T
aqr_test0(uint lim, uint flags)
{
	sm_ret_T ret;
	int i, r;
	sm_str_P str;
	char buf[RCPT_MAX_LEN];
	aq_rcpt_P aq_rcpt, aq_rcpt_prev;

	aq_rcpt_prev = NULL;
	ret = SM_SUCCESS;
	srand(time(0));
	for (i = 0; i < lim; i++)
	{
		ret = aq_rcpt_new(&aq_rcpt);
		SM_TEST_ERR(ret);

		sm_snprintf(aq_rcpt->aqr_ss_ta_id,
			sizeof(aq_rcpt->aqr_ss_ta_id), SMTPS_STID_FORMAT,
			(ulonglong_T) i, i);

#if 1
		r = rand();
		if (Verbose > 1)
			sm_io_fprintf(smioout, "i=%d, r=%8d\n", i, r);
#else /* 0 */
		switch (i)
		{
		   case 0: r = 1074254950;	break;
		   case 1: r = 1391506343;	break;
		   case 2: r = 23302228;	break;
		   case 3: r = 1649818621;	break;
		   default:			break;
		}

#endif /* 0 */
		sm_snprintf(buf, sizeof(buf), "<a-%d@%d-domain.tld>", i, r);

		str = sm_str_scpy0(NULL, buf, sizeof(buf));
		if (aq_rcpt->aqr_pa != NULL)
			sm_str_free(aq_rcpt->aqr_pa);
		aq_rcpt->aqr_pa = str;
		str = NULL;

		/* get domain part */
		ret = aq_rcpt_set_domain(aq_rcpt, NULL);
		if (sm_is_err(ret))
			goto error;

		AQR_DA_INIT(aq_rcpt);
		if (SM_IS_FLAG(flags, SMT_APPEND))
		{
			if (aq_rcpt_prev == NULL)
				AQR_SS_INIT(aq_rcpt);
			else
				AQR_SS_APP(aq_rcpt_prev, aq_rcpt);
		}
		else
		{
			ret = aq_rcpt_ss_insert(aq_rcpt_prev, aq_rcpt);
			if (sm_is_err(ret))
				goto error;
		}
		aq_rcpt_prev = aq_rcpt;
		if (!SM_IS_FLAG(flags, SMT_NOCHECKS))
		{
			ret = aqr_tests(aq_rcpt);
			ret = aqr_test1(aq_rcpt, i);
		}
	}

	if (!SM_IS_FLAG(flags, SMT_NOCHECKS))
	{
		ret = aqr_tests(aq_rcpt);
		ret = aqr_test1(aq_rcpt, i - 1);
	}
	return ret;

  error:
	return ret;
}

static void
usage(const char *prg)
{
	sm_io_fprintf(smioerr, "usage: %s [options]\n", prg);
	sm_io_fprintf(smioerr, "Test AQ recipient sorting (domains are random numbers)\n");
	sm_io_fprintf(smioerr, "options:\n");
	sm_io_fprintf(smioerr, "-a    append only, don't sort\n");
	sm_io_fprintf(smioerr, "-n n  specify number of recipients to sort\n");
	sm_io_fprintf(smioerr, "-t    don't perform tests, just sort\n");
	sm_io_fprintf(smioerr, "-V    increase verbosity\n");
	exit(EX_USAGE);
}


int
main(int argc, char **argv)
{
	int c;
	uint n, flags;
	sm_ret_T r;
	char *prg;

	prg = argv[0];
	n = SM_AQ_RCPTS;
	flags = 0;
#if SM_HEAP_CHECK
	SmHeapCheck = 0;
#endif
	while ((c = getopt(argc, argv, "aH:n:tV")) != -1)
	{
		switch (c)
		{
		  case 'a':
			flags |= SMT_NOCHECKS|SMT_APPEND;
			break;
		  case 'n':
			n = (uint) strtoul(optarg, NULL, 0);
			break;
#if SM_HEAP_CHECK
		  case 'H':
			SmHeapCheck = atoi(optarg);
			break;
#endif
		  case 't':
			flags |= SMT_NOCHECKS;
			break;
		  case 'V':
			++Verbose;
			break;
		  default:
			usage(prg);
			return EX_USAGE;
		}
	}

	sm_test_begin(argc, argv, "test adbr 0");

	r = aqr_test0(n, flags);

#if SM_HEAP_CHECK
	if (HEAP_CHECK)
	{
		sm_io_fprintf(smioout, "heap should be empty except for makebuf:\n");
		sm_heap_report(smioout, 3);
	}
#endif
	return sm_test_end();
}


syntax highlighted by Code2HTML, v. 0.9.1