/*-
 * See the file LICENSE for redistribution information.
 * 
 * Copyright (c) 2000
 *      Sleepycat Software.  All rights reserved.   
 */    
      
#include "db_config.h"

#ifndef lint
static const char revid[] = "$Id: gen_client_ret.c,v 1.1.1.1.2.2 2000/02/08 00:47:56 noriko Exp $";
#endif /* not lint */

#ifndef NO_SYSTEM_INCLUDES
#include <sys/types.h>

#include <rpc/rpc.h>

#include <errno.h>
#include <string.h>
#endif

#include "db_int.h"
#include "db_page.h"
#include "db_server.h"
#include "txn.h"
#include "db_ext.h"
#include "rpc_client_ext.h"

static void __db_dbstat_statsfree __P((u_int32_t *));
static int __db_dbstat_statslist __P((__dbstat_statsreplist *, u_int32_t **));

int
__dbcl_envclose_ret(dbenv, flags, replyp)
	DB_ENV *dbenv;
	u_int32_t flags;
	__envclose_reply *replyp;
{
	int ret;

	COMPQUIET(flags, 0);

	ret = __dbcl_refresh(dbenv);
	__os_free(dbenv, sizeof(*dbenv));
	if (replyp->status == 0 && ret != 0)
		return (ret);
	else
		return (replyp->status);
}

int
__dbcl_envopen_ret(dbenv, home, config, flags, mode, replyp)
	DB_ENV *dbenv;
	const char *home;
	char * const *config;
	u_int32_t flags;
	int mode;
	__envopen_reply *replyp;
{
	DB_TXNMGR *tmgrp;
	int ret;

	COMPQUIET(home, NULL);
	COMPQUIET(config, NULL);
	COMPQUIET(mode, 0);

	/*
	 * If error, return it.
	 */
	if (replyp->status != 0)
		return (replyp->status);

	/*
	 * If the user requested transactions, then we have some
	 * local client-side setup to do also.
	 */
	if (LF_ISSET(DB_INIT_TXN)) {
		if ((ret = __os_calloc(1, sizeof(DB_TXNMGR), &tmgrp)) != 0)
			return (ret);
		TAILQ_INIT(&tmgrp->txn_chain);
		tmgrp->dbenv = dbenv;
		dbenv->tx_handle = tmgrp;
	}
	
	return (replyp->status);
}

int
__dbcl_envremove_ret(dbenv, home, config, flags, replyp)
	DB_ENV *dbenv;
	const char *home;
	char * const *config;
	u_int32_t flags;
	__envremove_reply *replyp;
{
	int ret;

	COMPQUIET(home, NULL);
	COMPQUIET(config, NULL);
	COMPQUIET(flags, 0);

	ret = __dbcl_refresh(dbenv);
	__os_free(dbenv, sizeof(*dbenv));
	if (replyp->status == 0 && ret != 0)
		return (ret);
	else
		return (replyp->status);
}

int
__dbcl_txnabort_ret(txnp, replyp)
	DB_TXN *txnp;
	__txnabort_reply *replyp;
{
	__dbcl_txn_end(txnp);
	return (replyp->status);
}

int
__dbcl_txnbegin_ret(envp, parent, txnpp, flags, replyp)
	DB_ENV *envp;
	DB_TXN *parent, **txnpp;
	u_int32_t flags;
	__txnbegin_reply *replyp;
{
	DB_TXN *txn;
	int ret;

	COMPQUIET(flags, 0);

	if (replyp->status != 0)
		return (replyp->status);

        if ((ret = __os_calloc(1, sizeof(DB_TXN), &txn)) != 0)
		return (ret);
	txn->txnid = replyp->txnidcl_id;
	txn->mgrp = envp->tx_handle;
	txn->parent = parent;
	TAILQ_INIT(&txn->kids);
	txn->flags = TXN_MALLOC;
	if (parent != NULL)
		TAILQ_INSERT_HEAD(&parent->kids, txn, klinks);

	/*
	 * XXX
	 * In DB library the txn_chain is protected by the mgrp->mutexp.
	 * However, that mutex is implemented in the environments shared
	 * memory region.  The client library does not support all of the
	 * region - that just get forwarded to the server.  Therefore,
	 * the chain is unprotected here, but properly protected on the
	 * server.
	 */
	TAILQ_INSERT_TAIL(&txn->mgrp->txn_chain, txn, links);

	*txnpp = txn;
	return (replyp->status);
}

int
__dbcl_txncommit_ret(txnp, flags, replyp)
	DB_TXN *txnp;
	u_int32_t flags;
	__txncommit_reply *replyp;
{
	COMPQUIET(flags, 0);

	__dbcl_txn_end(txnp);
	return (replyp->status);
}

int
__dbcl_dbclose_ret(dbp, flags, replyp)
	DB *dbp;
	u_int32_t flags;
	__dbclose_reply *replyp;
{
	int ret;

	COMPQUIET(flags, 0);

	ret = __dbcl_dbclose_common(dbp);

	if (replyp->status != 0)
		return (replyp->status);
	else
		return (ret);
}

int
__dbcl_dbget_ret(dbp, txnp, key, data, flags, replyp)
	DB *dbp;
	DB_TXN *txnp;
	DBT *key, *data;
	u_int32_t flags;
	__dbget_reply *replyp;
{
	int ret;
	void *oldkey;

	COMPQUIET(dbp, NULL);
	COMPQUIET(txnp, NULL);
	COMPQUIET(flags, 0);

	ret = 0;
	if (replyp->status != 0)
		return (replyp->status);
	oldkey = key->data;
	ret = __dbcl_retcopy(key, replyp->keydata.keydata_val,
	    replyp->keydata.keydata_len);
	if (ret)
		return (ret);
	ret = __dbcl_retcopy(data, replyp->datadata.datadata_val,
	    replyp->datadata.datadata_len);
	/*
	 * If an error on copying 'data' and we allocated for 'key'
	 * free it before returning the error.
	 */
	if (ret && oldkey != NULL)
		__os_free(key->data, key->size);
	return (ret);
}

int
__dbcl_dbopen_ret(dbp, name, subdb, type, flags, mode, replyp)
	DB *dbp;
	const char *name, *subdb;
	DBTYPE type;
	u_int32_t flags;
	int mode;
	__dbopen_reply *replyp;
{
	COMPQUIET(name, NULL);
	COMPQUIET(subdb, NULL);
	COMPQUIET(type, 0);
	COMPQUIET(flags, 0);
	COMPQUIET(mode, 0);

	dbp->type = replyp->type;

	/*
	 * XXX
	 * This is only for Tcl which peeks at the dbp flags.
	 * When dbp->get_flags exists, this should go away.
	 */
	dbp->flags = replyp->dbflags;
	return (replyp->status);
}

int
__dbcl_dbput_ret(dbp, txnp, key, data, flags, replyp)
	DB *dbp;
	DB_TXN *txnp;
	DBT *key, *data;
	u_int32_t flags;
	__dbput_reply *replyp;
{
	int ret;

	COMPQUIET(dbp, NULL);
	COMPQUIET(txnp, NULL);
	COMPQUIET(data, NULL);

	ret = replyp->status;
	if (replyp->status == 0 && (flags & DB_APPEND))
		*(db_recno_t *)key->data = 
		    *(db_recno_t *)replyp->keydata.keydata_val;
	return (ret);
}

int
__dbcl_dbremove_ret(dbp, name, subdb, flags, replyp)
	DB * dbp;
	const char * name;
	const char * subdb;
	u_int32_t flags;
	__dbremove_reply *replyp;
{
	int ret;

	COMPQUIET(name, 0);
	COMPQUIET(subdb, 0);
	COMPQUIET(flags, 0);

	ret = __dbcl_dbclose_common(dbp);

	if (replyp->status != 0)
		return (replyp->status);
	else
		return (ret);
}

int
__dbcl_dbstat_ret(dbp, sp, func, flags, replyp)
	DB *dbp;
	void *sp;
	void *(*func) __P((size_t));
	u_int32_t flags;
	__dbstat_reply *replyp;
{
	int ret;
	u_int32_t *__db_statslist;

	COMPQUIET(dbp, NULL);
	COMPQUIET(func, NULL);
	COMPQUIET(flags, 0);

	if (replyp->status != 0)
		return (replyp->status);

	if ((ret =
	    __db_dbstat_statslist(replyp->statslist, &__db_statslist)) != 0)
		return (ret);

	if (sp == NULL)
		__db_dbstat_statsfree(__db_statslist);
	else 
		*(u_int32_t **)sp = __db_statslist;
	return (replyp->status);
}

static int
__db_dbstat_statslist(locp, ppp)
	__dbstat_statsreplist *locp;
	u_int32_t **ppp;
{
	u_int32_t *pp;
	int cnt, ret, size;
	__dbstat_statsreplist *nl;

	for (cnt = 0, nl = locp; nl != NULL; cnt++, nl = nl->next)
		;

	if (cnt == 0) {
		*ppp = NULL;
		return (0);
	}
	size = sizeof(*pp) * cnt;
	if ((ret = __os_malloc(size, NULL, ppp)) != 0)
		return (ret);
	memset(*ppp, 0, size);
	for (pp = *ppp, nl = locp; nl != NULL; nl = nl->next, pp++) {
		*pp = *(u_int32_t *)nl->ent.ent_val;
	}
	return (0);
}

static void
__db_dbstat_statsfree(pp)
	u_int32_t *pp;
{
	size_t size;
	u_int32_t *p;

	if (pp == NULL)
		return;
	size = sizeof(*p);
	for (p = pp; *p != 0; p++)
		size += sizeof(*p);

	__os_free(pp, size);
}

int
__dbcl_dbcursor_ret(dbp, txnp, dbcpp, flags, replyp)
	DB *dbp;
	DB_TXN *txnp;
	DBC **dbcpp;
	u_int32_t flags;
	__dbcursor_reply *replyp;
{
	COMPQUIET(txnp, NULL);
	COMPQUIET(flags, 0);

	if (replyp->status != 0)
		return (replyp->status);

	return (__dbcl_c_setup(replyp->dbcidcl_id, dbp, dbcpp));
}

int
__dbcl_dbjoin_ret(dbp, curs, dbcpp, flags, replyp)
	DB *dbp;
	DBC **curs, **dbcpp;
	u_int32_t flags;
	__dbjoin_reply *replyp;
{
	COMPQUIET(curs, NULL);
	COMPQUIET(flags, 0);

	if (replyp->status != 0)
		return (replyp->status);

	/*
	 * We set this up as a normal cursor.  We do not need
	 * to treat a join cursor any differently than a normal
	 * cursor, even though DB itself must.  We only need the
	 * client-side cursor/db relationship to know what cursors
	 * are open in the db, and to store their ID.  Nothing else.
	 */
	return (__dbcl_c_setup(replyp->dbcidcl_id, dbp, dbcpp));
}

int
__dbcl_dbc_close_ret(dbcp, replyp)
	DBC *dbcp;
	__dbc_close_reply *replyp;
{
	DB *dbp;

	dbp = dbcp->dbp;
	__dbcl_c_refresh(dbcp);
	return (replyp->status);
}

int
__dbcl_dbc_count_ret(dbc, countp, flags, replyp)
	DBC * dbc;
	db_recno_t * countp;
	u_int32_t flags;
	__dbc_count_reply *replyp;
{
	COMPQUIET(dbc, NULL);
	COMPQUIET(flags, 0);

	if (replyp->status != 0)
		return (replyp->status);
	*countp = replyp->dupcount;

	return (replyp->status);
}  

int
__dbcl_dbc_dup_ret(dbcp, dbcpp, flags, replyp)
	DBC *dbcp, **dbcpp;
	u_int32_t flags;
	__dbc_dup_reply *replyp;
{
	COMPQUIET(flags, 0);

	if (replyp->status != 0)
		return (replyp->status);

	return (__dbcl_c_setup(replyp->dbcidcl_id, dbcp->dbp, dbcpp));
}

int
__dbcl_dbc_get_ret(dbcp, key, data, flags, replyp)
	DBC *dbcp;
	DBT *key, *data;
	u_int32_t flags;
	__dbc_get_reply *replyp;
{
	int ret;
	void *oldkey;

	COMPQUIET(dbcp, NULL);
	COMPQUIET(flags, 0);

	ret = 0;
	if (replyp->status != 0)
		return (replyp->status);
	oldkey = key->data;
	ret = __dbcl_retcopy(key, replyp->keydata.keydata_val,
	    replyp->keydata.keydata_len);
	if (ret) 
		return (ret);
	ret = __dbcl_retcopy(data, replyp->datadata.datadata_val,
	    replyp->datadata.datadata_len);

	/*
	 * If an error on copying 'data' and we allocated for 'key'
	 * free it before returning the error.
	 */
	if (ret && oldkey != NULL)
		__os_free(key->data, key->size);
	return (ret);
}
