
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <db.h>
#include <sys/types.h>
#include <dirent.h>

#define KEYNAME "keyname"
#define DBFILE "dbfile"
#ifdef WIN32
#define WORKDIR "c:\\temp"
#else
#define WORKDIR "."
#endif

static int dup_compare_fcn(const DBT *a, const DBT *b)
#ifdef WIN32
{
	/* Compare values as 32-bit integers */
	int av;
	int bv;
	if ( (a->size != sizeof(int)) || (b->size != sizeof(int)) ) {
		fprintf(stderr, "Error---DBT size wrong\n");
		return(1);
	}
	av = *((int *)a->data);
	bv = *((int *)b->data);

	if (av > bv) {
		return 1;
	}
	if (av < bv) {
		return -1;
	}
	return 0;
}
#else
{
    return memcmp((const void *)a->data, (const void *)b->data,
									(size_t)sizeof(int));
}
#endif

void
cleanup(int verbose)
{
	DIR *dir;
	struct dirent *dp;

	dir = opendir(WORKDIR);
	if (NULL == dir) {
		fprintf(stderr, "no such dir: %s\n", WORKDIR);
		return;
	}
	while (dp = readdir(dir)) {
		if (!strncmp(dp->d_name, "__db", 4) ||
			!strncmp(dp->d_name, "log.", 4) ||
			!strncmp(dp->d_name, DBFILE, strlen(DBFILE))) {
			if (verbose) {
				fprintf(stderr, "remove(%s)\n", dp->d_name);
			}
			remove(dp->d_name);
		}
	}
	closedir(dir);
}

main(int argc, char **argv)
{
	DB_ENV *env = NULL;
	DB *db = NULL;
	int ret = 0;
	int cnt = 10000;
	int doScan = 0;
	int doDelete = 0;
	int doLock = 0;
	int verbose = 0;
	char *me;

	me = *argv;
	while (--argc > 0) {
		if (**++argv == '-') {
			if (*(*argv+1) == 'n') {
				cnt = atoi(*++argv);
				--argc;
			} else if (*(*argv+1) == 's')	/* scan by cursor */
				doScan = 1;
			else if (*(*argv+1) == 'd')		/* delete by cursor */
				doDelete = 1;
			else if (*(*argv+1) == 'l')		/* lock/log/txn */
				doLock = 1;
			else if (*(*argv+1) == 'v')		/* verbose */
				verbose = 1;
			else if (*(*argv+1) == 'h') {		/* help */
				fprintf(stderr,
						"%s [-n #] [-d] [-s] [-l] [-v] [-h]\n", me);
				fprintf(stderr,
						"\t-n #: data count\n"
						"\t-d  : scan and delete\n"
						"\t-s  : scan\n"
						"\t-l  : penv w/ LOCK|LOG|TXN\n"
						"\t-v  : verbose\n"
						"\t-h  : help\n");
				exit(0);
			}
		}
	}
	ret = db_env_create(&env, 0);
	if (0 != ret) {
		fprintf(stderr, "db_env_create failed %d\n",ret);
		exit(1);
	}	

	cleanup(verbose);
#ifdef WIN32
	if (doLock)
		ret = env->open(env, WORKDIR, NULL,
					DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN |
					DB_CREATE | DB_INIT_MPOOL | DB_THREAD, 0);
	else
		ret = env->open(env, WORKDIR, NULL,
					DB_CREATE | DB_INIT_MPOOL | DB_THREAD, 0);
#else
	if (doLock)
		ret = env->open(env, WORKDIR, NULL,
					DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN |
					DB_CREATE | DB_INIT_MPOOL | DB_THREAD, 0);
	else
		ret = env->open(env, WORKDIR, NULL,
					DB_CREATE | DB_INIT_MPOOL | DB_THREAD, 0);
#endif
	if (0 != ret) {
		fprintf(stderr, "db_env->open failed %d\n",ret);
		exit(1);
	}	

	/* OK, environment now open, let's open a file */
	ret = db_create(&db,env,0);
	if (0 != ret) {
		fprintf(stderr, "db_create failed %d\n",ret);
		exit(1);
	}	
	ret = db->set_flags(db, DB_DUP | DB_DUPSORT);
	if (0 != ret) {
		fprintf(stderr, "db->set_flags failed %d\n",ret);
		exit(1);
	}	
	ret = db->set_dup_compare(db,dup_compare_fcn);
	if (0 != ret) {
		fprintf(stderr, "db->set_dup_compare failed %d\n",ret);
		exit(1);
	}	

	ret = db->open(db, DBFILE, NULL, DB_BTREE, DB_THREAD | DB_CREATE, 0);
	if (0 != ret) {
		fprintf(stderr, "db->open failed %d\n",ret);
		exit(1);
	}	
	
	/* Now insert stuff */
	{
		DBT key = {0};
		DBT value = {0};
		int value_int = 0;
		int x = 0;

		x = 0;
		while (x < cnt) {
			value_int = x;
			key.data = KEYNAME;
			key.size = strlen(key.data);
			key.flags = DB_DBT_DUPOK;
			value.size = sizeof(int);
			value.data = &value_int;
			value.flags = 0;
			ret = db->put(db,NULL,&key,&value,0);
			if (0 != ret) {
				fprintf(stderr, "db->put failed %d\n",ret);
				exit(1);
			}
			if (verbose && 0 == (x % 1000)) {
				fprintf(stderr, "%d\n",x);
			}
			x++;
		}
	}

	if (doDelete) {
		DBC *cursor = NULL;
		DBT key = {0};
		DBT data = {0};
		char *key_data;
		int i;

		fprintf(stderr, "cursor scan/delete ...\n");
		/* Make a cursor */
		ret = db->cursor(db,NULL,&cursor,0);
		if (0 != ret) {
			fprintf(stderr, "db->cursor failed %d\n", ret);
			exit(1);
		}
		key_data = strdup(KEYNAME);
		key.data = key_data;
		key.flags = DB_DBT_MALLOC;
		key.size = strlen(key.data);
		data.flags = DB_DBT_MALLOC;

		i = 0;
		ret = cursor->c_get(cursor,&key,&data,DB_SET);
		if (0 != ret) {
			if (DB_NOTFOUND == ret) {
				ret = 0; /* Not Found is OK, return immediately */
			} else {
				fprintf(stderr, "cursor->c_get(DB_SET) failed %d\n", ret);
				exit(1);
			}
			goto done_dodelete;
		}
		ret = cursor->c_del(cursor,0);
		if (0 != ret && DB_NOTFOUND != ret) {
			fprintf(stderr, "%d: cursor->c_del failed %d\n", i, ret);
			exit(1);
		}
		free(key_data);
		free(key.data); key.data = NULL;
		free(data.data); data.data = NULL;
		while (0 == ret) {
			i++;
			ret = cursor->c_get(cursor,&key,&data,DB_NEXT_DUP);
			if (0 != ret) {
				if (DB_NOTFOUND != ret) {
					fprintf(stderr, "%d: cursor->c_get(NEXT_DUP) failed %d\n", i, ret);
					exit(1);
				}
				break;
			}
			ret = cursor->c_del(cursor,0);
			if (0 != ret && DB_NOTFOUND != ret) {
				fprintf(stderr, "%d: cursor->c_del failed %d\n", i, ret);
				exit(1);
			}
			if (verbose && 0 == (i % 1000)) {
				fprintf(stderr, "%d\n",i);
			}
			free(key.data); key.data = NULL;
			free(data.data); data.data = NULL;
		}
done_dodelete:
		ret = cursor->c_close(cursor);
		if (0 != ret) {
			fprintf(stderr, "cursor->c_close failed %d\n", ret);
			exit(1);
		}
		fprintf(stderr, "cursor scan/delete done (%d entries).\n", i);
	}

	if (doScan) {
		DBC *cursor = NULL;
		DBT key = {0};
		DBT data = {0};
		char *key_data;
		int i;

		fprintf(stderr, "cursor scan ...\n");
		/* Make a cursor */
		ret = db->cursor(db,NULL,&cursor,0);
		if (0 != ret) {
			fprintf(stderr, "db->cursor failed %d\n", ret);
			exit(1);
		}
		key_data = strdup(KEYNAME);
		key.data = key_data;
		key.flags = DB_DBT_MALLOC;
		key.data = KEYNAME;
		key.size = key.ulen = strlen(key.data);
		data.flags = DB_DBT_MALLOC;

		i = 0;
		ret = cursor->c_get(cursor,&key,&data,DB_SET);
		if (0 != ret) {
			if (DB_NOTFOUND == ret) {
				ret = 0; /* Not Found is OK, return immediately */
			} else {
				fprintf(stderr, "%d: cursor->c_get(DB_SET) failed %d\n", i, ret);
				exit(1);
			}
			goto done_doscan;
		}
		free(key_data); 
		free(key.data); key.data = NULL;
		free(data.data); data.data = NULL;
		while (0 == ret) {
			i++;
			ret = cursor->c_get(cursor,&key,&data,DB_NEXT_DUP);
			if (0 != ret) {
				if (DB_NOTFOUND != ret) {
					fprintf(stderr, "%d: cursor->c_get(NEXT_DUP) failed %d\n", i, ret);
					exit(1);
				}
				break;
			}
			if (verbose && 0 == (i % 1000)) {
				fprintf(stderr, "%d\n",i);
			}
			free(key.data); key.data = NULL;
			free(data.data); data.data = NULL;
		}
done_doscan:
		ret = cursor->c_close(cursor);
		if (0 != ret) {
			fprintf(stderr, "cursor->c_close failed %d\n", ret);
			exit(1);
		}
		fprintf(stderr, "cursor scan done (%d entries).\n", i);
	}
	ret = db->close(db,0);
	if (0 != ret) {
		fprintf(stderr, "db->close failed %d\n",ret);
		exit(1);
	}	

	ret = env->close(env,0);
	if (0 != ret) {
		fprintf(stderr, "db_env->close failed %d\n",ret);
		exit(1);
	}	
}


