/*
 * 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.
 */

/*
**  Use a tree for sorting.
**  Note: this requires unique keys, hence it doesn't work for something
**  like sorting domains.
*/

#include "sm/generic.h"
SM_RCSID("@(#)$Id: t-treeinsert.c,v 1.5 2005/04/14 17:08:56 ca Exp $")
#include "sm/types.h"
#include "sm/str.h"
#include "sm/bsd-tree.h"
#include "sm/mta.h"
#include "sm/test.h"
#include "sm/io.h"
#include "sm/sysexits.h"

static int Verbose = 0;

#define SMT_NOCHECKS	0x01
#define SMT_APPEND	0x02
#define SMT_LIST	0x04

#define SM_AQ_RCPTS	256
#define RCPT_MAX_LEN	256

static char *list[] = {
	"Sendmail.ORG",
	"sendmailx.org",
	"sendmail.com",
	"Sendmail.ORG",
	"sendmail.com",
	"Sendmail.COM",
	"Sendmail.COM",
	"sendmail.com",
	"sendmail.com",
	"sendmail.com",
	"sendmail.com",
	"sendmail.com",
	"sendmail.com",
	"sendmail.org",
	"csuchico.edu",
	"niu.edu",
	"uni-kiel.de",
	"vt.edu",
	"andrew.cmu.edu",
	"onet.pl",
	"Sendmail.ORG",
	"debian.org",
	"nmt.edu",
	"uwaterloo.ca",
	"yale.edu",
	"uni-erlangen.de",
	"yahoo.com",
	"earthlink.net",
	"hotmail.com",
	"hp.com",
	"polytechnique.fr",
	"gmail.com",
	NULL
	};

typedef struct aqt_root_S	aqt_root_T, *aqt_root_P;
typedef struct aqr_tree_S	aqr_tree_T, *aqr_tree_P;
typedef struct aq_rcpt_S	aq_rcpt_T, *aq_rcpt_P;

struct aqt_root_S
{
	RB_HEAD(aqr_tree_S, aq_rcpt_S)	aqt_root;
};


struct aq_rcpt_S
{
	sm_str_P		 aqr_domain;
	RB_ENTRY(aq_rcpt_S)	 aqr_entry;
};


/*
**  AQR_CMP -- compare two tnodes
**
**	Parameters:
**		na -- aq_rcpt a
**		nb -- aq_rcpt b
**
**	Returns:
**		usual comparison code (<0, 0, >0)
*/

static int
aqr_cmp(aq_rcpt_P na, aq_rcpt_P nb)
{
	SM_REQUIRE(na != NULL);
	SM_REQUIRE(nb != NULL);
	return sm_str_casecmp(na->aqr_domain, nb->aqr_domain);
}

RB_PROTOTYPE(aqr_tree_S, aq_rcpt_S, aqr_entry, aqr_cmp)
RB_GENERATE(aqr_tree_S, aq_rcpt_S, aqr_entry, aqr_cmp)

/*
**  TREE_INSERT -- insert new elem into tree
**
**	Parameters:
**		elem -- previous element (may be NULL)
**		new -- element to insert
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
tree_insert(aqt_root_P aqt_root, aq_rcpt_P aq_rcpt)
{
	aq_rcpt_P aq_rcpt_h;

	aq_rcpt_h = RB_INSERT(aqr_tree_S, &(aqt_root->aqt_root), aq_rcpt);
#if 0
	if (aq_rcpt_h != NULL)
		return sm_error_perm(SM_EM_Q_EDBC, EEXIST);
#endif /* 0 */
	return SM_SUCCESS;
}

static sm_ret_T
aqr_test1(aqt_root_P aqt_root, uint n)
{
	uint i;
	aq_rcpt_P aq_rcpt_nxt, aq_rcpt_prev;

	i = 0;
	aq_rcpt_prev = NULL;
	RB_FOREACH(aq_rcpt_nxt, aqr_tree_S, &(aqt_root->aqt_root))
	{
		if (Verbose > 0)
			sm_io_fprintf(smioerr,
				"LIST: %2u: aq_rcpt=%p, domain=%S\n",
				i, aq_rcpt_nxt, aq_rcpt_nxt->aqr_domain);
		++i;
		if (aq_rcpt_prev != NULL)
			SM_TEST(aqr_cmp(aq_rcpt_prev, aq_rcpt_nxt) <= 0);
		aq_rcpt_prev = aq_rcpt_nxt;
	}
	if (Verbose > 0)
	{
		if (i != n)
			sm_io_fprintf(smioerr, "i=%u, n=%u\n", i, n);
		else
			sm_io_fprintf(smioerr, "\n");
	}
	SM_TEST(i == n);
	sm_io_flush(smioerr);
	return SM_SUCCESS;
}

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

	aq_rcpt_prev = NULL;
	aq_rcpt = NULL;
	ret = SM_SUCCESS;
	srand(time(0));
	RB_INIT(&(aqt_root.aqt_root));
	for (i = 0; i < lim; i++)
	{
		aq_rcpt = sm_zalloc(sizeof(*aq_rcpt));
		SM_TEST_ERR(aq_rcpt != NULL);

		if (SM_IS_FLAG(flags, SMT_LIST) && i < SM_ARRAY_SIZE(list))
		{
			if (list[i] == NULL)
				break;
			strlcpy(buf, list[i], sizeof(buf));
		}
		else
		{
			r = rand();
			if (Verbose > 1)
				sm_io_fprintf(smioout, "i=%u, r=%8d\n", i, r);
			sm_snprintf(buf, sizeof(buf), "%d-domain.tld-%u", r, i);
		}

		str = sm_str_scpy0(NULL, buf, sizeof(buf));
		aq_rcpt->aqr_domain = str;
		str = NULL;
		ret = tree_insert(&aqt_root, aq_rcpt);
		if (sm_is_err(ret))
			goto error;
		aq_rcpt_prev = aq_rcpt;
		if (!SM_IS_FLAG(flags, SMT_NOCHECKS))
			ret = aqr_test1(&aqt_root, i + 1);
	}

	if (!SM_IS_FLAG(flags, SMT_NOCHECKS))
		ret = aqr_test1(&aqt_root, i);
	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(0);
}


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;
	while ((c = getopt(argc, argv, "aln:tV")) != -1)
	{
		switch (c)
		{
		  case 'a':
			flags |= SMT_NOCHECKS|SMT_APPEND;
			break;
		  case 'l':
			flags |= SMT_LIST;
			break;
		  case 'n':
			n = (uint) strtoul(optarg, NULL, 0);
			break;
		  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);

	return sm_test_end();
}


syntax highlighted by Code2HTML, v. 0.9.1