/* dbmtry.c -- Copyright 1989, 1994 Liam R. Quin.
 * All Rights Reserved.
 * This code is NOT in the public domain.
 * See the file COPYRIGHT for full details.
 */

/* If you have problems with the dbm interface, try this program.
 * If it fails with ALERT messages when given an argument of 300 or so,
 * you almost certainly have a faulty dbm.
 *
 * On SysV, by the way, check for delitem() calling bcopy() with
 * overlapping arguments...
 *
 * This version of the test program prints messages even when things are OK.
 *
 * $Id: dbmtry.c,v 1.9 2001/05/31 03:50:55 liam Exp $
 *
 */

#include "globals.h" /* defines and declarations for database filenames */
#include "error.h"

#include <stdio.h>
#include <sys/types.h>
#ifdef HAVE_FCNTL_H
# ifdef HAVE_SYSV_FCNTL_H
#  include <sys/stat.h>
# endif
# include <fcntl.h>
#endif

#include "smalldb.h"
#include "lqutil.h"
#include "liblqtext.h"

#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif

char *progname = "dbmtry";

PRIVATE void printvalues(
#ifdef HAVE_PROTO
    t_LQTEXT_Database *db,
    int max,
    char *note
#endif
);

PRIVATE int FindMax(
#ifdef HAVE_PROTO
    DBM *kvpdb
#endif
);

PRIVATE void SetMax(
#ifdef HAVE_PROTO
    DBM *kvpdb,
    int max
#endif
);

/** **/

static char *TestFile = "/tmp/trydbm";
static int ErrorCount = 0;

int
main(argc, argv)
    int argc;
    char *argv[];
{
    DBM *kvpdb;
    int max;
    int i;
    datum key, data;
    char dbuf[30];
    int min;

    t_lqdbOptions *Options;
    t_LQTEXT_Database *db = 0;

    if (argc <= 1) {
	fprintf(stderr, "Usage: %s maxkey\n", argv[0]);
	exit(1);
    }

    Options = LQT_InitFromArgv(argc, argv);
    max = atoi(argv[1]);

    db = LQT_OpenDatabase(Options, O_RDWR, 0);
    if (!db || LQT_ObtainReadOnlyAccess(db) < 0) {
	Error(E_FATAL, "couldn't open lq-text database for writing");
    }
    fprintf(stderr, "%s: ** Using test database \"%s\"\n", progname, TestFile);

    LQT_ObtainWriteAccess(db);

    if ((kvpdb = LQT_OpenKeyValueDatabase(db, TestFile)) == (DBM *) 0) {
	Error(E_FATAL|E_SYS, "Couldn't open test database %s", TestFile);
    }

    if ((min = FindMax(kvpdb)) < 0) {
	min = 0;
    } else {
	if (min + 1 >= max) {
	    Error(E_WARN, "%s previously had a stored max of %d, using %d",
		TestFile,
		min,
		max + min
	    );
	    max += min;
	}
	printvalues(db, min, "previously stored");
	++min; /* start one above the last max */
    }

    fprintf(stderr, "%s: ** writing from %d up to %d.\n", progname, min, max);

    for (i = min; i <= max; i++) {
	char buf[20];
	register int s_val;

	sprintf(buf, "%d", i);
	sprintf(dbuf, "%d data item here", i);
	    /* Note: the number is at the start to help speed the
	     * strcmp, as it is most likely to differ
	     */
	key.dsize = strlen(buf) + 1; /* include the \0 so we can strcmp() */
	key.dptr = buf;
	data.dptr = dbuf;
	data.dsize = strlen(dbuf) + 1;
	s_val = dbm_store(kvpdb, key, data, DBM_REPLACE);
	if (s_val != 0) {
	    Error(E_WARN|E_SYS, "ALERT! dbm_store %d returned %d, not 0", i, s_val);
	    ++ErrorCount;
	}
    }

    fprintf(stderr, "%s: ** write test complete: %d error%s\n",
	progname,
	ErrorCount,
	(ErrorCount == 1) ? "" : "s"
    );

    SetMax(kvpdb, max);

    LQT_CloseKeyValueDatabase(kvpdb);
    LQT_SyncAndCloseAllKeyValueDatabases(db);

    printvalues(db, max, "all");

    if (ErrorCount) {
	Error(E_FATAL, "**** ALERT **** Total of %d errors, should be 0",
	    ErrorCount
	);
    } else {
	fprintf(stderr, "%s: test passed.\n", progname);
    }
    exit(0);
    return -1; /* for lint, gcc */
}

PRIVATE void
printvalues(db, max, note)
    t_LQTEXT_Database *db;
    int max;
    char *note;
{
    DBM *kvpdb;
    int i;
    char buf[20];
    datum key, data;

    LQT_ObtainReadOnlyAccess(db);

    kvpdb = LQT_OpenKeyValueDatabase(db, TestFile);

    if (!kvpdb) {
	Error(E_FATAL|E_SYS, "Unable to open database %s", TestFile);
    }

    i = FindMax(kvpdb);
    if (i != max) {
	Error(E_WARN, "FindMax() returned %d, but %d was expected", i, max);
    }

    fprintf(stderr, "%s: ** Checking %s stored data from 0 up to %d.\n",
	progname, note, max
    );

    /* Note: always start at zero */
    for (i = 0; i <= max; i++) {

	sprintf(buf, "%d", i);
	key.dsize = strlen(buf) + 1;
	key.dptr = buf;
	data = dbm_fetch(kvpdb, key);

	if (data.dsize == 0) {
	    Error(E_WARN, "ALERT! Item %d has been lost! ALERT!", i);
	    ++ErrorCount;
	} else {
	    char *Buf[100];
	    (void) sprintf(Buf, "%d data item here", i);
	    if (strncmp(Buf, data.dptr, data.dsize) != 0) {
		Error(E_WARN, "ALERT! %d: Corrupt: \"%s\" != \"%s\" ALERT!",
				i, data.dptr, Buf);
		++ErrorCount;
	    }
	}
    }
    LQT_CloseKeyValueDatabase(kvpdb);
}

static char *MaxValueString = "MAX VALUE";

PRIVATE int
FindMax(kvpdb)
    DBM *kvpdb;
{
    int i;
    datum key, data;

    key.dptr = MaxValueString;
    key.dsize = strlen(MaxValueString);

    data = dbm_fetch(kvpdb, key);

    if (data.dsize == 0 || !data.dptr) return -1;
    i = atoi(data.dptr);
    if (i <= 0) {
	Error(E_WARN, "Max Value stored in %s was as %d, strange", TestFile, i);
	return -1;
    }

    return i;
    
}

PRIVATE void
SetMax(kvpdb, max)
    DBM *kvpdb;
    int max;
{
    int i;
    char buf[20];
    datum key, data;

    key.dptr = MaxValueString;
    key.dsize = strlen(MaxValueString);

    (void) sprintf(buf, "%d", max);
    data.dptr = buf;
    data.dsize = strlen(buf) + 1; /* include the \0 so atoi() will work */

    i = dbm_store(kvpdb, key, data, DBM_REPLACE);
    if (i < 0) {
	Error(E_WARN|E_SYS,
	    "Failed to insert Max marker (%d) into %s",
	    max,
	    TestFile
	);
    }
}