// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2000-2001 Silicon Graphics, Inc.
 * All Rights Reserved.
 */
 
#include "global.h"

#ifdef HAVE_GDBM_NDBM_H_
#include <gdbm/ndbm.h>
#elif HAVE_GDBM_NDBM_H
#include <gdbm-ndbm.h>
#elif HAVE_NDBM_H
#include <ndbm.h>
#else
bozo!
#endif


/* #define WorkDir	"/xfs" */
#define DBNAME	"DBtest"
#define DBRECS	100000
#define LOOPS	100

typedef struct _myDB {
	char		data[976];
	unsigned short	checksum;
	long		align;
} myDB;	

int InitDbmLookup(int);
int DoDbmLookup(void);
void CleanupDbmLookup(void);
void DoSysError(char *, int);
int creatDbRec(myDB *);
unsigned short calccksum(char *, int);
void CkSumError(char *, unsigned short, unsigned short);
int InsertKey(unsigned short *, int, unsigned short);
void DumpKeys(int, unsigned short);

DBM *dbm;
int numDbmEntries, keyIdx, Dups = 0, Errors = 0;
unsigned short *KeyArray, *DupsArray;
int debugflg = 0;	/* are we in debugging mode? */
int errflg = 0;		/* usage option errors */
int ignoreflg = 1;	/* ignore errors in lookup; default = on */
int loops = LOOPS;
int randseed = 0;

int
main(int argc, char *argv[])
{
	int	numrecs = DBRECS, c, l;

	while ((c = getopt(argc, argv, "idn:l:s:")) != EOF) {
		switch(c) {
			case 'i':
				ignoreflg++; break;
			case 'd':
				debugflg++; break;
			case 'n':
				numrecs = atoi(optarg);
				numrecs = (numrecs < 1) ? DBRECS : numrecs;
				break;
			case 'l':
				loops = atoi(optarg);
				loops = (loops < 1) ? LOOPS : loops;
				break;
			case 's':
				randseed = atoi(optarg);
				randseed = (randseed < 0) ? 0 : randseed;
				break;
			case '?':
				errflg++;
				break;
		}
	}
	if (errflg) {
		printf("Usage: dbtest [-di] [-n number] [-l loop] [-s randseed]\n");
		exit(0);
	}
	if (optind > argc) {
		printf("Extra arguments ignored\n");
		for ( ; optind<argc; optind++)
			printf("\t%s\n", argv[optind]);
	}

	printf("dbtest v1.0\n\n");
	printf("Creating database containing %d records...\n", numrecs);
	printf("\tperforming lookups for %d iterations...\n", loops);
	if (randseed)
		printf("\tusing %d as seed for srandom()...\n\n", randseed);
	fflush(stdout);
	InitDbmLookup(numrecs);
	printf("\t\nThere were %d duplicate checksums generated\n", Dups);
	for (l=0; l<loops; l++) {
		printf("\nPerforming lookups on database...\n");
		if (DoDbmLookup() != numrecs)
			printf("\nError performing lookups!\n");
		else
			printf("\nLookups succeeded...\n");
	}
	printf("\nCleaning up database...\n");
	printf("\t\nThere were %d duplicate checksums generated\n", Dups);
	fflush(stdout);
	CleanupDbmLookup();
	if (debugflg)
		for (l=0; l<Dups; l++) {
			if ((l % 8) == 0)
				putchar('\n');
			printf("0x%x\t", DupsArray[l]);
		}

	return 0;
}


int InitDbmLookup(int howmany)
{
	char	filename[100];
	datum	key, content;
	int	i, rc;
	myDB	dbRec;

	sprintf(filename, "%s-%d", DBNAME, (int)getpid());
	if (debugflg) {
		printf("dbm_open(%s, O_RDWR|O_CREAT, 0644)\n", filename);
		fflush(stdout);
	}
	dbm = dbm_open(filename, O_RDWR|O_CREAT, 0644);
	if(dbm == NULL)
		DoSysError("\ndbm_open", -1);

	if ((KeyArray = (unsigned short *)calloc( howmany,
				sizeof(unsigned short))) == NULL)
		DoSysError("\ncalloc:KeyArray", -1);
	if ((DupsArray = (unsigned short *)calloc( 100,
				sizeof(unsigned short))) == NULL)
		DoSysError("\ncalloc:DupsArray", -1);

	keyIdx = 0;
	for(i=0; i<howmany; i++) {

		if ( creatDbRec(&dbRec) )
			DoSysError("\ncreatDbRec", -1);
		if (debugflg) {
			printf("created rec #%-7d\t%x\r", i+1, dbRec.checksum);
			fflush(stdout);
		}

		if (InsertKey(KeyArray, keyIdx, dbRec.checksum))
			keyIdx++;

		key.dptr = (char *)&(dbRec.checksum);
		key.dsize = sizeof(dbRec.checksum);
		content.dptr = (char *)&dbRec;
		content.dsize = sizeof(dbRec);

		write(2, NULL, 0);

		if (debugflg) {
			printf("dbm_store(dbm, key, content, DBM_INSERT)\n");
		}
		rc = dbm_store(dbm, key, content, DBM_INSERT);
		if (rc < 0)
			DoSysError("\ndbm_store", rc);
		else if (rc == 1) {
			keyIdx--;	/* key is already in database */
			DupsArray[Dups++] = dbRec.checksum;
		}
	}
	numDbmEntries = i;
	if (debugflg) {
		printf("dbm_close(dbm)\n");
		fflush(stdout);
	}
	dbm_close(dbm); /* close to eliminate chance of in-memory caching */
	if (debugflg) {
		printf("dbm_open(%s, O_RDONLY, 0)\n", filename);
		fflush(stdout);
	}
	dbm = dbm_open(filename, O_RDONLY, 0);
	if(dbm == NULL)
		DoSysError("\ndbm_open", -1);
	return 0;
}

int DoDbmLookup(void)
{
	datum key, content;
	int i, j, n;
	myDB *dbp;
	unsigned tmpck;

	printf("\n\tSequential lookups...\n");
	fflush(stdout);
	for(i=0, j=0; i<numDbmEntries; i++) {
		key.dptr = (char *)(KeyArray+j);
		key.dsize = sizeof(KeyArray[0]);

write(2, NULL, 0);
		if (debugflg) {
			printf("dbm_fetch(dbm, key = %d)\n", j);
			fflush(stdout);
		}
		content = dbm_fetch(dbm, key);
		dbp = (myDB *)content.dptr;
		if ( !content.dptr ) {
			n = dbm_error(dbm);
			if (n) printf("\n\ndbm_error: %d\n", n);
			printf("\n%d: Sequential Lookup of key %4x failed!\n",
				i, KeyArray[j]);
			DumpKeys(i, KeyArray[j]);
			Errors++;
			if (ignoreflg) {
				j++;
				continue;
			}
			assert( dbp );
		}

		if (debugflg && dbp) {
			printf("Seq rec #%-6d: checksum %4x (%4x)\r", i,
				dbp->checksum, KeyArray[j]);
			fflush(stdout);
		}

		if (content.dsize == 0) {
			printf("\nrec #%-8d: key = %4x (%d)\n", i, KeyArray[j], j);
			fflush(stdout);
			DoSysError("\ndbm_fetch", content.dsize);
		}
		if (dbp->checksum != KeyArray[j])
			CkSumError("KeySequential", dbp->checksum, KeyArray[j]);
		if ((tmpck = calccksum(dbp->data, sizeof(dbp->data))) != KeyArray[j])
			CkSumError("DataSequential", tmpck, KeyArray[j]);
		if (++j >= keyIdx)
			j = 0;
	}
	printf("\n\n\tRandom lookups...\n");
	fflush(stdout);
	for(i=0; i<numDbmEntries; i++) {
		n = random() % keyIdx;
		key.dptr = (char *)(KeyArray+n);
		key.dsize = sizeof(KeyArray[0]);
		if (debugflg) {
			printf("dbm_fetch(dbm, key = %d)\n", n);
			fflush(stdout);
		}
		content = dbm_fetch(dbm, key);
		dbp = (myDB *)content.dptr;
		if ( !content.dptr ) {
			n = dbm_error(dbm);
			if (n) printf("\n\ndbm_error: %d\n", n);
			printf("\n%d: Random Lookup of key %4x failed!\n",
				i, KeyArray[n]);
			DumpKeys(n, KeyArray[n]);
			Errors++;
			if (ignoreflg) {
				continue;
			}
			assert( dbp );
		}

		if (debugflg && dbp) {
			printf("Rnd rec #%-6d: checksum %4x (%4x)\r", i,
				dbp->checksum, KeyArray[n]);
			fflush(stdout);
		}

		if (content.dsize == 0)
			DoSysError("\ndbm_fetch", content.dsize);
		if (dbp->checksum != KeyArray[n])
			CkSumError("KeyRand", dbp->checksum, KeyArray[n]);
		if ((tmpck = calccksum(dbp->data, sizeof(dbp->data))) != KeyArray[n])
			CkSumError("DataRand", tmpck, KeyArray[n]);
	}
	return i;
}

void CleanupDbmLookup(void)
{
	char filename[100];
	int rc;

	if (debugflg) {
		printf("dbm_close(dbm)\n");
		fflush(stdout);
	}
	dbm_close(dbm);
	sprintf(filename, "%s-%d.dir", DBNAME, (int)getpid());
	rc = unlink(filename);
	if (rc) DoSysError("\nunlink", rc);
	sprintf(filename, "%s-%d.pag", DBNAME, (int)getpid());
	rc = unlink(filename);
	if (rc) DoSysError("\nunlink", rc);
}

void DoSysError(char *msg, int rc)
{
	perror(msg);
	fprintf(stderr, "Bailing out! status = %d\n", rc);
	exit(-1);
}

int creatDbRec(myDB *dbp)
{
	static int once = 0;
	int	i, j;
	long	*ptr;
	long    timeseed;

	if (!once) {
		once++;
		if ( !randseed ) {
			timeseed = time(0);
			printf("\tusing %ld as seed for srandom()...\n\n",
				timeseed);
			srandom(timeseed);
		} else
			srandom(randseed);
	}
	ptr = (long *)&(dbp->data);
	j = sizeof(dbp->data) / sizeof(long);
	for (i=0; i < j; i++, ptr++) {
		*ptr = random();
	}
	for (i=(j*sizeof(long)); i<sizeof(dbp->data); i++)
		dbp->data[i] = (char)time(0);

	dbp->checksum = calccksum(dbp->data, sizeof(dbp->data));
	return 0;
}

unsigned short calccksum(char *ptr, int size)
{
	unsigned short sum;
	int i, n;

	sum = 0;
	n = ((size > 100) ? 100 : size);
	for (i=0; i<n && ptr; i++, ptr++) {
		if (sum & 01)
			sum = (sum >> 1) + 0x8000;
		else
			sum >>= 1;
		sum += (unsigned short)*ptr;
		sum &= 0xffff;
	}
	return sum;
}

/*ARGSUSED*/
void CkSumError(char *msg, unsigned short ck1, unsigned short ck2)
{
	Errors++;
	printf("%s\n\tChecksum error, %u != %u, Total errors = %d\n",
		msg, ck1, ck2, Errors);
	exit(1);
}

int InsertKey(unsigned short *k, int idx, unsigned short val)
{
/*
	int i, found=0;
*/

	return( (k[idx] = val) );

/*
	for (i=0; i<idx; i++) {
		if (k[i] == val) {
			found++;
			break;
		}
	}

	if (!found)
		k[idx] = val;

	return(!found);
*/
}

void DumpKeys(int n, unsigned short key)
{
/*
	int	i;
	unsigned short *p;
*/
	FILE	*f;

	if ((f = fopen("keys", "a")) == NULL) {
		perror("open(keys)");
		exit(1);
	}
/*
	for (i=0, p=arr; i<keyIdx && p; i++, p++)
*/
		fprintf(f, "%d: 0x%x\n", n, key);

	fclose(f);
}
