/*
 * Copyright (c) 2006 Claus Assmann
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the license/LICENSE.3C file which can be found at the
 * top level of this source code distribution.
 */

#include "sm/generic.h"
SM_RCSID("@(#)$Id: cdb.c,v 1.6 2006/11/18 15:25:53 ca Exp $")

#include "sm/assert.h"
#include "sm/error.h"
#include "sm/memops.h"
#include "sm/heap.h"
#include "sm/fcntl.h"
#include "map.h"
#include "sm/map.h"
#include "sm/maps.h"
#include "sm/mapc.h"
#include "sm/mapclasses.h"
#include "sm/cdb_map.h"

#if MTA_USE_TINYCDB

/* XXX FIX XXX */
#define CDB_ERR2RET(ret)	ret

/*
**  ToDo: implementation of map for tinycdb.
*/

/* static sm_map_open_F	 sm_cdb_open; */
/* static sm_map_close_F	 sm_cdb_close; */
static sm_map_alloc_F	 sm_cdb_alloc;
static sm_map_free_F	 sm_cdb_free;
#if 0
static sm_map_locate_F	 sm_cdb_locate;
static sm_map_first_F	 sm_cdb_first;
static sm_map_next_F	 sm_cdb_next;
#endif /* 0 */

typedef struct cdb	cdb_map_T, *cdb_map_P;
typedef struct cdb_make	cdb_make_T, *cdb_make_P;
typedef union sm_cdbs_U sm_cdbs_T, *sm_cdbs_P;
union sm_cdbs_U
{
	cdb_map_T	 cdbs_cdb_rd;
	cdb_make_T	 cdbs_cdb_wr;
};

typedef struct sm_cdbmap_S sm_cdbmap_T, *sm_cdbmap_P;
struct sm_cdbmap_S
{
	int		 cdbmap_fd;
	bool	 	 cdbmap_create;
	sm_cdbs_T	 cdbmap_map;
};

/*
**  These are types of databases.
*/

#define SMDB_TYPE_DEFAULT	NULL
#define SMDB_TYPE_CDB		"cdb"

#if 0
/*
**  CDB_TYPE2CDB -- Translates database type to CDB type.
**
**	Parameters:
**		type -- The type to translate.
**
**	Returns:
**		The CDB type that corresponds to type.
*/

static DBTYPE
cdb_type2cdb2(const sm_cstr_P type)
{
	if (SMDB_TYPE_DEFAULT == type)
		return DB_CDB;
	if (strncmp((const char*)sm_cstr_data(type),
		    SMDB_TYPE_CDB, sm_cstr_getlen(type)) == 0)
		return DB_CDB;
	return DB_UNKNOWN;
}
#endif /* 0 */

/*
**  SM_CDB_SETOPT -- set options for map
**
**	Parameters:
**		map -- map
**		ap -- options
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_cdb_setopt(sm_map_P map, va_list ap)
{
	sm_ret_T ret;
	int k;

	SM_IS_MAP(map);
	ret = SM_SUCCESS;

	for (;;)
	{
		k = va_arg(ap, int);
		if (SMPO_END == k)
			break;

		switch (k)
		{
		  default:
			/* silently ignore bogus options? */
			break;
		}
	}
	return ret;
}

/*
**  SM_CDB_GETOPT -- get options for map
**
**	Parameters:
**		map -- map
**		which -- which option?
**		valp -- pointer to place where result should be stored
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_cdb_getopt(sm_map_P map, int which, void *valp)
{
	sm_ret_T ret;
	sm_cdbmap_P cdb;

	SM_IS_MAP(map);
	cdb = (sm_cdbmap_P) map->sm_map_db;
	if (NULL == cdb)
		return sm_error_perm(SM_EM_MAP, ENOENT);

	/* ... */

	ret = SM_SUCCESS;
	return ret;
}

/*
**  SM_CDB_CLOSE -- close map
**	more parameters???
**
**	Parameters:
**		map -- map
**		flags -- currently ignored
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_cdb_close(sm_map_P map, uint32_t flags)
{
	sm_ret_T ret;
	int fd;
	sm_mapc_P mapc;
	sm_cdbmap_P sm_cdbmap;

	SM_IS_MAP(map);
	mapc = map->sm_map_class;
	SM_IS_MAPC(mapc);
	ret = SM_SUCCESS;

	sm_cdbmap = (sm_cdbmap_P) map->sm_map_db;
	if (NULL == sm_cdbmap)
		return sm_error_perm(SM_EM_MAP, ENOENT);
	if (sm_cdbmap->cdbmap_create)
		ret = cdb_make_finish(&sm_cdbmap->cdbmap_map.cdbs_cdb_wr);

	fd = sm_cdbmap->cdbmap_fd;
	if (fd >= 0)
	{
		close(fd);
		sm_cdbmap->cdbmap_fd = INVALID_FD;
	}

	sm_free_size(sm_cdbmap, sizeof(sm_cdbmap));

	/* close CDB map */
	map->sm_map_db = NULL;

	return ret;
}

/*
**  SM_CDB_DESTROY -- destroy map
**	XXX more parameters...
**
**	Parameters:
**		map -- map
**		flags -- flags for map
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_cdb_destroy(sm_map_P map, uint32_t flags)
{
	sm_ret_T ret;
	sm_mapc_P mapc;
	sm_cdbmap_P sm_cdbmap;

	SM_IS_MAP(map);
	mapc = map->sm_map_class;
	SM_IS_MAPC(mapc);
	ret = SM_SUCCESS;

	sm_cdbmap = (sm_cdbmap_P) map->sm_map_db;
	if (NULL == sm_cdbmap)
		return sm_error_perm(SM_EM_MAP, ENOENT);

	/* close fd? */
	sm_free_size(sm_cdbmap, sizeof(sm_cdbmap));

	map->sm_map_db = NULL;
	return ret;
}

/*
**  SM_CDB_CREATE -- create map
**
**	Parameters:
**		mapc -- map context
**		type -- type of map
**		flags -- flags for map
**		map -- map
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_cdb_create(sm_mapc_P mapc, const sm_cstr_P type, uint32_t flags, sm_map_P map)
{
	sm_cdbmap_P sm_cdbmap;

	SM_IS_MAPC(mapc);
	SM_REQUIRE(map != NULL);
	sm_cdbmap = (sm_cdbmap_P) sm_zalloc(sizeof(*sm_cdbmap));
	if (NULL == sm_cdbmap)
		return sm_error_temp(SM_EM_MAP, ENOMEM);
	sm_cdbmap->cdbmap_fd = INVALID_FD;
	map->sm_map_db = sm_cdbmap;
	map->sm_map_caps = SMMAP_CAPS_LTALL;
	return SM_SUCCESS;
}

#if 0
/*
**  CDB_OPEN_FLAGS -- translate external (map) flags into internal flags
**
**	Paramters:
**		flags -- map flags
**
**	Returns:
**		Internal flag value matching user selected flags
*/

static uint32_t
cdb_open_flags(uint32_t flags)
{
	uint32_t ret;

	ret = 0;
	if (SM_IS_FLAG(flags, SMAP_MODE_RDONLY))
		ret |= DB_RDONLY;
	if (SM_IS_FLAG(flags, SMAP_MODE_CREATE))
		ret |= DB_CREATE;
	return ret;
}
#endif /* 0 */

/*
**  SM_CDB_OPEN -- open map
**
**	Parameters:
**		mapc -- map context
**		type -- type of map
**		flags -- flags for opening map
**		path -- path of map
**		mode -- open mode (currently ignored, DBMMODE is used)
**		map -- map
**		ap -- additional argument
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_cdb_open(sm_mapc_P mapc, const sm_cstr_P type, uint32_t flags, const char *path, int mode, sm_map_P map, va_list ap)
{
	sm_ret_T ret;
	int fd;
	sm_cdbmap_P sm_cdbmap;

	SM_IS_MAPC(mapc);
	SM_REQUIRE(map != NULL);
	SM_REQUIRE(path != NULL);
	ret = SM_SUCCESS;
	fd = -1;

	sm_cdbmap = (sm_cdbmap_P) map->sm_map_db;
	SM_REQUIRE(sm_cdbmap != NULL);

	if (SM_IS_FLAG(flags, SMAP_MODE_CREATE))
	{
		sm_cdbmap->cdbmap_create = true;
		fd = open(path, O_RDWR|O_CREAT|O_TRUNC, 0640);
		if (fd < 0)
			return sm_error_perm(SM_EM_MAP, errno);
		ret = cdb_make_start(&sm_cdbmap->cdbmap_map.cdbs_cdb_wr, fd);
	}
	else
	{
		fd = open(path, O_RDONLY);
		if (fd < 0)
			return sm_error_perm(SM_EM_MAP, errno);
		ret = cdb_init(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd, fd);
	}
	if (ret != 0)
	{
		ret = CDB_ERR2RET(ret);
		goto error;
	}
	sm_cdbmap->cdbmap_fd = fd;
	return ret;

  error:
	if (fd >= 0)
		close(fd);
	return ret;
}

/*
**  SM_CDB_LOOKUP -- lookup a key in CDB, return data if found
**
**	Parameters:
**		map -- map context
**		flags -- flags
**		key -- key
**		data -- data (output)
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_cdb_lookup(sm_map_P map, uint32_t flags, sm_map_key_P key, sm_map_data_P data)
{
	sm_ret_T ret;
	size_t l;
	sm_mapc_P mapc;
	sm_cdbmap_P sm_cdbmap;

	SM_IS_MAP(map);
	SM_IS_KEY(key);
	SM_IS_DATA(data);
	mapc = map->sm_map_class;
	SM_IS_MAPC(mapc);
	ret = SM_SUCCESS;

	sm_cdbmap = (sm_cdbmap_P) map->sm_map_db;
	if (NULL == sm_cdbmap )
		return sm_error_perm(SM_EM_MAP, ENOENT);	/* XXX */
	SM_ASSERT(!sm_cdbmap->cdbmap_create);
	if (!SMMAP_IS_FL(map, SMMAP_FL_OPEN))
	{
		/* map closed but can be reopened? */
		if (mapc->sm_mapc_reopenf != NULL)
			ret = mapc->sm_mapc_reopenf(map, 0);
		else
			ret = sm_error_perm(SM_EM_MAP, SM_E_CLOSEDMAP);
		if (sm_is_err(ret))
			return ret;
	}

	/* XXX WARNING: changes key inplace! */
	if (SM_IS_FLAG(flags, SMMAP_FL_LWR_KEY))
		sm_str2lower(key);

	/* need to lock access? single threaded access! */
	ret = cdb_find(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd,
			sm_str_data(key), sm_str_getlen(key));
	if (ret > 0)
	{
		l = cdb_datalen(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd);
		if (l > sm_str_getsize(data) &&
		    l < sm_str_getmax(data))
		{
			if (!sm_is_err(sm_str_resize_data(data, l)))
			{
				ret = cdb_find(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd,
						sm_str_data(key),
						sm_str_getlen(key));
				l = cdb_datalen(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd);
			}
		}
		if (ret > 0)
		{
			SM_ASSERT(l <= sm_str_getsize(data));
			ret = cdb_read(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd,
					sm_str_data(data), l,
					cdb_datapos(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd));
			if (ret < 0)
				ret = sm_error_temp(SM_EM_MAP, EINVAL);
			else
			{
				SM_STR_SETLEN(data, l);
				ret = SM_SUCCESS;
			}
		}
	}
	else
		ret = SM_MAP_NOTFOUND;

	return ret;
}

/*
**  SM_CDB_ADD -- add key/data to CDB
**
**	Parameters:
**		map -- map context
**		key -- key
**		data -- data
**		flags -- flags
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_cdb_add(sm_map_P map, sm_map_key_P key, sm_map_data_P data, uint flags)
{
	sm_ret_T ret;
	sm_mapc_P mapc;
	sm_cdbmap_P sm_cdbmap;

	SM_IS_MAP(map);
	SM_REQUIRE(key != NULL);
	SM_REQUIRE(data != NULL);
	mapc = map->sm_map_class;
	SM_IS_MAPC(mapc);
	ret = SM_SUCCESS;

	sm_cdbmap = (sm_cdbmap_P) map->sm_map_db;
	if (NULL == sm_cdbmap)
		return sm_error_perm(SM_EM_MAP, ENOENT);	/* XXX */
	SM_ASSERT(sm_cdbmap->cdbmap_create);

	ret = cdb_make_put(&sm_cdbmap->cdbmap_map.cdbs_cdb_wr,
			sm_str_data(key), sm_str_getlen(key),
			sm_str_data(data), sm_str_getlen(data),
			CDB_PUT_INSERT);
	return ret < 0 ? sm_error_perm(SM_EM_MAP, errno) : SM_SUCCESS;
}

/*
**  SM_CDB_CLASS_CREATE -- create CDB map class
**
**	Parameters:
**		maps -- map system context
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sm_cdb_class_create(sm_maps_P maps)
{
	sm_ret_T ret;
	sm_mapc_P mapc;
	sm_cstr_P htype;

#define CDB_TYPE	"cdb"

	ret = SM_SUCCESS;
	mapc = NULL;
	htype = sm_cstr_scpyn0((const uchar *)CDB_TYPE, strlen(CDB_TYPE));
	if (NULL == htype)
		goto error;

	ret = sm_mapc_create(maps, htype,
			SMMAPC_FL_GEN_REOPEN|SMMAPC_FL_LCK_FULL|
			SMMAPC_FL_FILE,
			sm_cdb_create,
			sm_cdb_open,
			sm_cdb_close,
			NULL,
			sm_cdb_destroy,
			sm_cdb_add,
			NULL,
			sm_cdb_alloc,
			sm_cdb_free,
			sm_cdb_lookup,
			NULL /* sm_cdb_locate */,
			NULL /* sm_cdb_first */,
			NULL /* sm_cdb_next */,
			sm_cdb_setopt,
			sm_cdb_getopt,
			&mapc);

	SM_CSTR_FREE(htype);
	return ret;

  error:
	if (SM_SUCCESS == ret)
		ret = sm_error_temp(SM_EM_MAP, ENOMEM);
	/* cleanup mapc? */
	return ret;
}
#endif /* MTA_USE_TINYCDB */


syntax highlighted by Code2HTML, v. 0.9.1