#include <stdio.h>
#include <sys/file.h>
#ifdef SDBM
#include "EXTERN.h"
#include "sdbm.h"
#else
#include <ndbm.h>
#endif
#include <string.h>

#ifdef BSD42
#define strchr	index
#endif

extern int	getopt();
extern char	*strchr();
extern void	oops();

char *progname;

static int rflag;
static char *usage = "%s [-R] cat | look |... dbmname";

#define DERROR	0
#define DLOOK	1
#define DINSERT	2
#define DDELETE 3
#define	DCAT	4
#define DBUILD	5
#define DPRESS	6
#define DCREAT	7

#define LINEMAX	8192

typedef struct {
	char *sname;
	int scode;
	int flags;
} cmd;

static cmd cmds[] = {

	"fetch", DLOOK, 	O_RDONLY,
	"get", DLOOK,		O_RDONLY,
	"look", DLOOK,		O_RDONLY,
	"add", DINSERT,		O_RDWR,
	"insert", DINSERT,	O_RDWR,
	"store", DINSERT,	O_RDWR,
	"delete", DDELETE,	O_RDWR,
	"remove", DDELETE,	O_RDWR,
	"dump", DCAT,		O_RDONLY,
	"list", DCAT, 		O_RDONLY,
	"cat", DCAT,		O_RDONLY,
	"creat", DCREAT,	O_RDWR | O_CREAT | O_TRUNC,
	"new", DCREAT,		O_RDWR | O_CREAT | O_TRUNC,
	"build", DBUILD,	O_RDWR | O_CREAT,
	"squash", DPRESS,	O_RDWR,
	"compact", DPRESS,	O_RDWR,
	"compress", DPRESS,	O_RDWR
};

#define CTABSIZ (sizeof (cmds)/sizeof (cmd))

static cmd *parse();
static void badk(), doit(), prdatum();

int
main(int argc, char **argv)
{
	int c;
	cmd *act;
	extern int optind;
	extern char *optarg;

	progname = argv[0];

	while ((c = getopt(argc, argv, "R")) != EOF)
		switch (c) {
		case 'R':	       /* raw processing  */
			rflag++;
			break;

		default:
			oops("usage: %s", usage);
			break;
		}

	if ((argc -= optind) < 2)
		oops("usage: %s", usage);

	if ((act = parse(argv[optind])) == NULL)
		badk(argv[optind]);
	optind++;
	doit(act, argv[optind]);
	return 0;
}

static void
doit(cmd *act, char *file)
{
	datum key;
	datum val;
	DBM *db;
	char *op;
	int n;
	char *line;
#ifdef TIME
	long start;
	extern long time();
#endif

	if ((db = dbm_open(file, act->flags, 0644)) == NULL)
		oops("cannot open: %s", file);

	if ((line = (char *) malloc(LINEMAX)) == NULL)
		oops("%s: cannot get memory", "line alloc");

	switch (act->scode) {

	case DLOOK:
		while (fgets(line, LINEMAX, stdin) != NULL) {
			n = strlen(line) - 1;
			line[n] = 0;
			key.dptr = line;
			key.dsize = n;
			val = dbm_fetch(db, key);
			if (val.dptr != NULL) {
				prdatum(stdout, val);
				putchar('\n');
				continue;
			}
			prdatum(stderr, key);
			fprintf(stderr, ": not found.\n");
		}
		break;
	case DINSERT:
		break;
	case DDELETE:
		while (fgets(line, LINEMAX, stdin) != NULL) {
			n = strlen(line) - 1;
			line[n] = 0;
			key.dptr = line;
			key.dsize = n;
			if (dbm_delete(db, key) == -1) {
				prdatum(stderr, key);
				fprintf(stderr, ": not found.\n");
			}
		}
		break;
	case DCAT:
		for (key = dbm_firstkey(db); key.dptr != 0; 
		     key = dbm_nextkey(db)) {
			prdatum(stdout, key);
			putchar('\t');
			prdatum(stdout, dbm_fetch(db, key));
			putchar('\n');
		}
		break;
	case DBUILD:
#ifdef TIME
		start = time(0);
#endif
		while (fgets(line, LINEMAX, stdin) != NULL) {
			n = strlen(line) - 1;
			line[n] = 0;
			key.dptr = line;
			if ((op = strchr(line, '\t')) != 0) {
				key.dsize = op - line;
				*op++ = 0;
				val.dptr = op;
				val.dsize = line + n - op;
			}
			else
				oops("bad input; %s", line);
	
			if (dbm_store(db, key, val, DBM_REPLACE) < 0) {
				prdatum(stderr, key);
				fprintf(stderr, ": ");
				oops("store: %s", "failed");
			}
		}
#ifdef TIME
		printf("done: %d seconds.\n", time(0) - start);
#endif
		break;
	case DPRESS:
		break;
	case DCREAT:
		break;
	}

	dbm_close(db);
}

static void
badk(char *word)
{
	int i;

	if (progname)
		fprintf(stderr, "%s: ", progname);
	fprintf(stderr, "bad keywd %s. use one of\n", word);
	for (i = 0; i < (int)CTABSIZ; i++)
		fprintf(stderr, "%-8s%c", cmds[i].sname,
			((i + 1) % 6 == 0) ? '\n' : ' ');
	fprintf(stderr, "\n");
	exit(1);
	/*NOTREACHED*/
}

static cmd *
parse(char *str)
{
	int i = CTABSIZ;
	cmd *p;
	
	for (p = cmds; i--; p++)
		if (strcmp(p->sname, str) == 0)
			return p;
	return NULL;
}

static void
prdatum(FILE *stream, datum d)
{
	int c;
	U8 *p = (U8 *) d.dptr;
	int n = d.dsize;

	while (n--) {
		c = *p++;
#ifndef EBCDIC /* Meta notation doesn't make sense on EBCDIC systems*/
		if (c & 0200) {
                    fprintf(stream, "M-");
                    c &= 0177;
		}
#endif
                /* \c notation applies for \0 . \x1f, plus \c? */
                if (c <= 0x1F || c == QUESTION_MARK_CTRL) {
                    fprintf(stream, "^%c", toCTRL(c));
                }
#ifdef EBCDIC   /* Instead of meta, use \x{} for non-printables */
                else if (! isPRINT_A(c)) {
                    fprintf(stream, "\\x{%02x}", c);
		}
#endif
		else { /* must be an ASCII printable */
                    putc(c, stream);
                }
	}
}


