/*
* This file is part of SkunkDB
* Copyright (c) 2001 Gianni Tedesco
*
*    SkunkDB is free software; you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation; either version 2 of the License, or
*    (at your option) any later version.
*
*    SkunkDB is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with SkunkDB; if not, write to the Free Software
*    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/


#include <skunk_app.h>
#include <skunk.h>

#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>

#define min_buf 4096
char *mbuf=NULL;
size_t mlen=0;

/* this might be a tad confusing. This function esentially prints
* "key:value\n" in to a string. Just need to make sure the buffer
* is big enough each time if not we realloc a larger one.
*/
char *get_value(char *k, char *v, skunk_ofs_t klen, skunk_ofs_t vlen)
{
	size_t sz=klen+vlen+3;

	if ( ! (mbuf && mlen>=sz) ) {
		if ( sz<min_buf ) sz=min_buf;
		if ( mbuf ) {
			char *n;
			if ( !(n=realloc(mbuf, sz)) ) return NULL;
			mbuf=n;
		}else if ( !(mbuf=malloc(sz)) ) {
			return NULL;
		}
		mlen=sz;
	}

	memcpy(mbuf, k, klen);
	mbuf[klen]=':';
	memcpy(mbuf+klen+1, v, vlen);
	mbuf[klen+vlen+1]='\n';
	mbuf[klen+vlen+2]='\0';

	return mbuf;
}

int main ( int argc, char **argv ) 
{
	int i;
	int fd;
	char *map, *buf;
	struct stat st;
	p_skunk_table p;
	skunk_ofs_t data=0,count=0;
	skunk_ofs_t min_fill=0, max_fill=0;
	char *str;
	
	if ( argc < 2 ) {
		printf("Usage: %s <sdb_file>\n", argv[0]);
		return SKUNK_ERR;
	}

	if ( (buf=malloc(8192)) ) {
		setvbuf(stdout, buf, _IOFBF, 8192);
	}
	
	if ( (fd=open(argv[1], O_RDONLY)) < 0 ) {
		perror("open");
		return SKUNK_ERR;
	}

	if ( (fstat(fd, &st)) < 0 ) {
		perror("fstat");
		close(fd);
		return SKUNK_ERR;
	}

	if ( st.st_size < SKUNK_DIRECTORY_SIZE ) {
		close(fd);
		fprintf(stderr, "File is too small to be "
			"a skunk DB (%lu bytes).\n", st.st_size);
		return SKUNK_ERR;
	}
	
	if ( !(map=mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) )
	{
		perror("mmap");
		close(fd);
		return SKUNK_ERR;
	}

	/* count bucket slots */
	for (i=0; i<SKUNK_DIRECTORY_MOD; i++) {
		p=&((p_skunk_table)map)[i];

		if ( i==0 ) min_fill=p->len;

		count+=p->len;
		if ( p->len > max_fill ) max_fill=p->len;
		if ( p->len < min_fill ) min_fill=p->len;
	}

#if 0
	printf("STATISTICS: optimal=%u min=%u max=%u\n\n",
		count/SKUNK_DIRECTORY_MOD, min_fill, max_fill);
#endif

	/* loop through all hash table entries */
	data=SKUNK_DIRECTORY_MOD*sizeof(skunk_table_t);
	for(i=0; i<count; i++) {
		skunkrec_t r;
		p_skunk_bucket b;

		b=(p_skunk_bucket)(((long)map) + data);
		if ( !b->ofs ) goto game_over;
		
		r.key_len=b->klen;
		r.val_len=b->vlen;


		r.key=(char *)(((skunk_ofs_t )map)+b->ofs);
		r.val=(char *)(((skunk_ofs_t )map)+b->ofs+r.key_len);

		if ( b->ofs+r.key_len > st.st_size ) {
			fprintf(stderr, "bogus data for slot 0x%x\n", i);
			exit(SKUNK_ERR);
		}

		if ( (str=get_value(r.key, r.val, r.key_len, r.val_len)) ) {
			printf("%s", str);
		}else{
			fprintf(stderr, "unable to retrieve data at slot 0x%x\n", i);
		}
game_over:
		data+=sizeof(skunk_bucket_t);

		/* quick and dirty check */
		if ( data > st.st_size ) break;
	}
	
	munmap(map, st.st_size);
	close(fd);
	exit(SKUNK_OK);
}
