/* vi: set tabstop=4 nocindent paste : */


/**
簡易データベースライブラリ for Berkeley BD4:  bdb_tool.c  v2.0

  ヘッダ
	  #include "bdb_tool.h"

  ライブラリ
	  -ldb-4.2  for Berkeley DB4



  Berkeley DBのマニュアル
		http://pybsddb.sourceforge.net/reftoc.html

*/



#include "bdb_tool.h"



#ifdef  ENABLE_BERKELEYDB




/**
DB*  open_fdb(char* fname, unsigned int mode)
  
	機能: データベースのオープン．
		  データベースファイル fnameをオープンする．
		  存在しない場合はファイルを作成する．

	引数: fname -- データベースのファイル名(通常拡張子は除く)．

	戻り値: データベースファイルのファイルディスクリプタ．
			データベースファイルが存在せず,作成にも失敗した場合は NULL が返る．

*/
DB*  open_fdb(char* fname, unsigned int mode)
{
	DB* dp;

	if (fname==NULL) return NULL;

	int ret = db_create(&dp, NULL, 0);
	if (ret==0) {
		ret = dp->open(dp, NULL, fname, NULL, DB_HASH, (u_int32_t)mode, 0600);
	}

	if (ret!=0) {
		DEBUG_MODE {
			dp->err(dp, ret, "DB->open");
			print_message("dbname is %s\n", fname);
		}
		return NULL;
	}
	return dp;
}




/**
void  close_fdb(DB** dp)

	機能：データベースのクローズ．データを全てフラッシュする．

*/
void  close_fdb(DB** dp)
{
	if (dp==NULL || *dp==NULL) return;

	(*dp)->close((*dp), 0);
	*dp = NULL;
	return;
}




/**
int  check_fdb_file(char* fname)
  
	機能: データベースのチェック．
		  データベースファイル fnameが存在するかどうかをチェックする．
		  存在しない場合はファイルを作成する．

	引数: fname -- データベースのファイル名(通常拡張子は除く)．

	戻り値: データベースファイルが存在する．または作成に成功した場合には TRUEが返る．
			データベースファイルが存在せず,作成にも失敗した場合は FALSEが返る．

*/
int  check_fdb_file(char* fname)
{
	DB* dp = open_fdb(fname, DB_CREATE);

	if (dp==NULL)  return FALSE;
	close_fdb(&dp);
	return TRUE;
}




/**
int   set_fdb_data(DB* dp, Buffer id, Buffer dbdata)

	機能: ディスクリプタを使ったデータベースへの登録．
		  データベースディスクリプタ dp にキー id で dbdata を登録する．
		  同じキーで既にデータが登録されている場合は dbdataで置き代わる．
		  ファイルのオープンとクローズは行わない．

	引数: dp	 -- データベースディスクリプタ
		  id	 -- データベースのキー．
		  dbdata -- データベースに登録するデータ．

	戻り値: データの登録に成功した場合はデータの長さが返る．
			入力データのサイズが分らない場合は -1が返る．
			データの登録に失敗した場合は 0が返る．

*/
int   set_fdb_data(DB* dp, Buffer id, Buffer dbdata)
{
	DBT  key, data;
	int  ret;
 
	memset(&key,  0, sizeof(DBT));
	memset(&data, 0, sizeof(DBT));

	key.data = id.buf;
	key.size = id.vldsz;
	if (key.size<=0) {
		key.size = strlen((char*)id.buf);
		if (key.size<=0) return -1;
	}

	data.data = dbdata.buf;
	data.size = dbdata.vldsz;
	if (data.size<=0) return -1;

	ret = dp->put(dp, NULL, &key, &data, 0);
	if (ret!=0) {
		DEBUG_MODE dp->err(dp, ret, "DB->put");
		return 0;
	}
	return  data.size;
}




/**
int   set_fdb_data_file(char* fname, Buffer id, Buffer dbdata)

	機能: データベースへの登録．
		  データベースファイル fname にキー id で dbdata を登録する．
		  同じキーで既にデータが登録されている場合は dbdataで置き代わる．
		  ファイルのオープンとクローズを行う．

	引数: fname  -- データベースのファイル名(通常拡張子は除く)．
		  id	 -- データベースのキー．
		  dbdata -- データベースに登録するデータ．

	戻り値: データの登録に成功した場合はデータの長さが返る．
			データベースファイルのオープンに失敗した場合，入力データのサイズが分らない場合は -1が返る．
			データの登録に失敗した場合は 0が返る．

*/
int   set_fdb_data_file(char* fname, Buffer id, Buffer dbdata)
{
	DBT  key, data;
	DB*  dp;
	int  ret;
 
	dp = open_fdb(fname, DB_CREATE);
	if (dp==NULL) return -1;

	memset(&key,  0, sizeof(DBT));
	memset(&data, 0, sizeof(DBT));

	key.data  = (char*)(id.buf);
	key.size = id.vldsz;
	if (key.size<=0) {
		key.size = strlen((char*)id.buf);
		if (key.size<=0) return -1;
	}

	data.data = (char*)dbdata.buf;
	data.size = dbdata.vldsz;
	if (data.size<=0) return -1;

	ret = dp->put(dp, NULL, &key, &data, 0);
	close_fdb(&dp);

	if (ret!=0) {
		DEBUG_MODE dp->err(dp, ret, "DB->put");
		return 0;
	}
	return  data.size;
}




/**
Buffer  get_fdb_data(DB* dp, Buffer id)

	機能: データベースからデータを取得．
		  データベースディスクリプタ dp からキー id でデータを獲得する．
		  キー idは文字列でなければならない．
		  ファイルのオープンとクローズは行わない．

		  成功した場合には, 戻り値として Buffer型データへのポインタを返す．
		  読み取ったデータの型はプログラマが把握していなければならない．
		  読み取ったデータは使用が終ったら free_Buffer()関数等により開放されなければならない．

		  データの取得に失敗した場合は -2が,メモリの確保に失敗した場合は -3 が stateに格納されて返される．
		  失敗した場合はいずれの場合も, メンバ変数 bufの値がNULLになって返される．

	引数: dp	 -- データベースのファイルディスクリプタ．
		  id	 -- 読み取るデータのキー．

	戻り値:  読み取ったデータ(Buffer型データ)へのポインタを返す．

	メモ：data.dataは freeしてはいけない．

*/
Buffer  get_fdb_data(DB* dp, Buffer id)
{
	int  ret;
	DBT  key, data;
	Buffer dbdata;

	memset(&key,  0, sizeof(DBT));
	memset(&data, 0, sizeof(DBT));

	dbdata   = init_Buffer();
	key.data = (void*)id.buf;
	key.size = (u_int32_t)id.vldsz;
	if (key.size<=0) {
		key.size = strlen((char*)id.buf);
		if (key.size<=0) {
			dbdata.state = -2;
			return dbdata;
		}
	}

	ret = dp->get(dp, NULL, &key, &data, 0);
	if (ret!=0) {
		dbdata.state = -2;
		return  dbdata;
	}

	dbdata = make_Buffer(data.size+1);		// +1 is for string
	if (dbdata.buf==NULL) {
		dbdata.state = -3;
		return  dbdata;
	}
	memcpy(dbdata.buf, data.data, data.size);
	dbdata.vldsz = data.size;
	dbdata.buf[data.size] = '\0';			// for string

	return  dbdata;
}




/**
Buffer  get_fdb_data_file(char* fname, Buffer id)

	機能: データベースからデータを取得．
		  データベースファイル fname からキー id でデータを獲得する．
		  キー idは文字列でなければならない．
		  ファイルのオープンとクローズを行う．

		  成功した場合には, 戻り値として Buffer型データへのポインタを返す．
		  読み取ったデータの型はプログラマが把握していなければならない．
		  読み取ったデータは使用が終ったら free_Buffer()関数等により開放されなければならない．

		  データベースファイル fnameのオープンに失敗した場合は Buffer型変数の
		  メンバ変数 stateに -1が格納されて返される．データの取得に失敗した
		  場合は -2が,メモリの確保に失敗した場合は -3が格納されて返される．
		  失敗した場合はいずれの場合も,メンバ変数bufの値がNULLになって返される．

	引数: fname  -- データベースのファイル名(通常拡張子は除く)．
		  id	 -- 読み取るデータのキー．

	戻り値:  読み取ったデータ(Buffer型データ)へのポインタを返す．

	メモ：data.dataは freeしてはいけない．

*/
Buffer  get_fdb_data_file(char* fname, Buffer id)
{
	int  ret;
	DBT  key, data;
	DB*  dp;
	Buffer dbdata;

	dbdata = init_Buffer();
	dp = open_fdb(fname, DB_RDONLY);
	if (dp==NULL) {
		dbdata.state = -1;
		return  dbdata;
	}

	memset(&key,  0, sizeof(DBT));
	memset(&data, 0, sizeof(DBT));

	key.data = (char*)(id.buf);
	key.size = id.vldsz;
	if (key.size<=0) {
		key.size = strlen((char*)id.buf);
		if (key.size<=0) {
			dbdata.state = -2;
			return dbdata;
		}
	}

	ret = dp->get(dp, NULL, &key, &data, 0);
	close_fdb(&dp);
	if (ret!=0) {
		dbdata.state = -2;
		return  dbdata;
	}

	dbdata = make_Buffer(data.size+1);		// +1 is for string
	if (dbdata.buf==NULL) {
		dbdata.state = -3;
		return  dbdata;
	}

	dbdata.vldsz = data.size;
	memcpy(dbdata.buf, data.data, data.size);
	dbdata.buf[data.size] = '\0';			// for string

	return  dbdata;
}




/**
int   del_fdb_data(DB* dp, Buffer id)

	機能: データベースからの削除．
		  データベースディスクリプタ dp からキー id のデータを削除する．
		  キー idは文字列でなければならない．
		  ファイルのオープンとクローズは行わない．

	引数: dp	-- データベースディスクリプタ
		  id	-- 削除するデータのキー．

	戻り値: データの削除に成功した場合は TRUEが返る．
			データの削除に失敗した場合は FALSEが返る．

*/
int   del_fdb_data(DB* dp, Buffer id)
{
	DBT  key;
	int  ret;
 
	memset(&key, 0, sizeof(DBT));
	key.data = (char*)(id.buf);
	key.size = id.vldsz;
	if (key.size<=0) {
		key.size = strlen((char*)id.buf);
		if (key.size<=0) return FALSE;
	}
	ret = dp->del(dp, NULL, &key, 0);

	if (ret!=0) {
		DEBUG_MODE dp->err(dp, ret, "DB->del");
		return FALSE;
	}
	return  TRUE;
}




/**
int   del_fdb_data_file(char* fname, Buffer id)

	機能: データベースからの削除．
		  データベースファイル fname からキー id のデータを削除する．
		  キー idは文字列でなければならない．
		  ファイルのオープンとクローズを行う．

	引数: fname -- データベースのファイル名(通常拡張子は除く)．
		  id	-- 削除するデータのキー．

	戻り値: データの削除に成功した場合は TRUEが返る．
			データの削除に失敗した場合は FALSEが返る．

*/
int   del_fdb_data_file(char* fname, Buffer id)
{
	DBT  key;
	DB*  dp;
	int  ret;
 
	dp = open_fdb(fname, 0);
	if (dp==NULL) return FALSE;

	memset(&key, 0, sizeof(DBT));
	key.data = (char*)(id.buf);
	key.size = id.vldsz;
	if (key.size<=0) {
		key.size = strlen((char*)id.buf);
		if (key.size<=0) return FALSE;
	}

	ret = dp->del(dp, NULL, &key, 0);
	close_fdb(&dp);

	if (ret!=0) {
		DEBUG_MODE dp->err(dp, ret, "DB->del");
		return FALSE;
	}
	return  TRUE;
}





/**
tList*  get_all_fdb_data(DB* dp)

	機能: データベース全体の取得 

	引数: db -- データベース識別子．

*/
tList*  get_all_fdb_data(DB* dp)
{
	int  ret;
	DBT  key, data;
	DBC* crsp;

	tList* lt = NULL;
	tList* lp = NULL;
	Buffer id, dt;

	if (dp==NULL) return NULL;

	dp->cursor(dp, NULL, &crsp, 0);
	if (crsp==NULL) return NULL;
	
	memset(&key,  0, sizeof(DBT));
	memset(&data, 0, sizeof(DBT));

	ret = crsp->c_get(crsp, &key, &data, DB_NEXT);
	while (ret==0) {
		id = make_Buffer_bin(key.data,  key.size);
		dt = make_Buffer_bin(data.data, data.size);
		lt = add_tList_node_Buffer(lt, id, dt);
		if (lp==NULL) lp = lt;
		free_Buffer(&id);
		free_Buffer(&dt);

		ret = crsp->c_get(crsp, &key, &data, DB_NEXT);
	}

	crsp->c_close(crsp);

	return lp;
}





/**
tList*  get_all_fdb_data_file(char* fname)

	機能: データベース全体の取得 

	引数: *fname -- データベースのファイル名

*/
tList*  get_all_fdb_data_file(char* fname)
{
	DB*    dp;
	tList* lp = NULL;

	if (fname==NULL) return NULL;

	dp = open_fdb(fname, DB_RDONLY);
	if (dp==NULL) return NULL;

	lp = get_all_fdb_data(dp);
	close_fdb(&dp);

	return lp;
}





#endif
