/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program 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.

**********************************************************************/

#include	<fcntl.h>
#include	"machine/include.h"
#include	"memory_debug.h"
#include	"pdb.h"
#include	"change_endian.h"
#include	"recordlist.h"
#include	"utils.h"

RECORD_LIST *
read_recordlist(int * sts,char * filename)
{
RECORD_LIST * ret, * tar, ** pt;
int fd;
char * _filename;
PN_HEADER * pn;
unsigned int fofs;
int _sts,er,size;
PN_HEADER h;
	ret = 0;
	pt = &ret;

	_filename = change_delim_str(filename);
	fd = u_open(_filename,O_RDWR);
	if ( fd < 0 ) {
/*
printf("RR1 %s\n",_filename);
*/
		ret = 0;
		_sts = RLS_CANNOT_OPEN;
		goto end;
	}
	fofs = 0;
/*
printf("RR2\n");
*/
	for ( ; ; ) {
		pn = d_alloc(sizeof(*pn));
		er = u_read(fd,pn,sizeof(*pn));
		if ( er == 0 ) {
			d_f_ree(pn);
			break;
		}
		if ( er < 0 ) {
			d_f_ree(pn);
			_sts = RLS_READ_ERROR;
			goto end;
		}
		if ( er != sizeof(*pn) ) {
			d_f_ree(pn);
			_sts = RLS_CORRUPT;
			goto end;
		}
		h = *pn;
		change_endian_header(&h);
		size = h.size - sizeof(PN_HEADER);
/*
printf("TYPE %x\n",(int)h.type);
*/
		pn = d_re_alloc(pn,h.size);
		er = u_read(fd,pn+1,size);
		if ( er < 0 ) {
			d_f_ree(pn);
			_sts = RLS_READ_ERROR;
			goto end;
		}
		if ( er != size ) {
			d_f_ree(pn);
			_sts = RLS_CORRUPT;
			goto end;
		}
		tar = d_alloc(sizeof(*tar));
		tar->data = pn;
		tar->fofs = fofs;
		tar->ptr = 0;
		tar->body = 0;
		tar->header = h;
		tar->next = 0;
		tar->chain_head = 0;
		tar->chain_tail = 0;

		*pt = tar;
		pt = &tar->next;

		fofs += h.size;
	}
	_sts = 0;
end:
	if ( filename != _filename )
		d_f_ree(_filename);
	if ( sts )
		*sts = _sts;
	u_close(fd);
	return ret;
}


int
write_recordlist(char * filename,RECORD_LIST * lst,int mode)
{
RECORD_LIST * p;
int fd;
int ret;
char * _filename;
	_filename = change_delim_str(filename);

	fd = u_open(_filename,O_RDWR|O_CREAT|O_TRUNC,mode);
	if ( fd < 0 ) {
		ret = RLS_CANNOT_OPEN;
		goto end;
	}
	for ( p = lst ; p ; p = p->next )
		u_write(fd,p->data,p->header.size);
	ret = RLS_OK;
end:
	if ( _filename != filename )
		d_f_ree(_filename);
	u_close(fd);
	return ret;
}

void
set_recordlist_ptr(RECORD_LIST * lst,int body)
{
	if ( lst->header.size > body ) {
		lst->body = 0;
		lst->ptr = 0;
	}
	else {
		lst->body = body;
		lst->ptr = (void*)(((char*)lst->data) + body);
	}
}

int
check_recordlist_ptr(RECORD_LIST * lst)
{
	if ( lst->ptr == 0 )
		return -1;
	if ( ((char*)lst->data) + lst->body > ((char*)lst->ptr) )
		return -1;
	if ( ((char*)lst->data) + lst->header.size <= ((char*)lst->ptr) )
		return 1;
	return 0;
}

void
set_recordlist_chain(RECORD_LIST * lst,void * ptr,int len,int free_flag)
{
CHAIN_LIST * c;
	c = d_alloc(sizeof(*c));
	c->ptr = ptr;
	c->len = len;
	c->free_flag = free_flag;
	c->next = 0;
	if ( lst->chain_head ) {
		lst->chain_tail->next = c;
		lst->chain_tail = c;
	}
	else lst->chain_head = lst->chain_tail = c;
}


void
_setup_recordlist(RECORD_LIST * lst)
{
int size;
CHAIN_LIST * c;
char * ptr;
PN_HEADER * hp;
	if ( lst->chain_head == 0 )
		return;
	size = lst->body = lst->header.size;
	for ( c = lst->chain_head ; c ; c = c->next )
		size += c->len;
	lst->data = d_re_alloc(lst->data,size);
	c = lst->chain_head;
	for ( ptr = ((char*)lst->data) + lst->body ; c ; c = c->next ) {
		memcpy(ptr,c->ptr,c->len);
		ptr += c->len;
	}
	lst->header.size = size;
	hp = (PN_HEADER*)lst->data;
	hp->size = size;
}

void
setup_recordlist(RECORD_LIST * lst)
{
	for ( ; lst ; lst = lst->next )
		_setup_recordlist(lst);
}

void
free_chain_list(CHAIN_LIST * c)
{
CHAIN_LIST * cc;
	for ( ; c ; ) {
		cc = c;
		c = c->next;
		if ( cc->free_flag )
			d_f_ree(cc->ptr);
		d_f_ree(cc);
	}
}

void
free_recordlist(RECORD_LIST * lst)
{
RECORD_LIST * r;
	for ( ; lst ; ) {
		r = lst;
		lst = lst->next;
		free_chain_list(r->chain_head);
		d_f_ree(r->data);
		d_f_ree(r);
	}
}

RECORD_LIST *
new_recordlist(unsigned short type,unsigned short size)
{
RECORD_LIST * ret;
	ret = d_alloc(sizeof(*ret));
	ret->header.type = type;
	ret->header.size = size;
	ret->data = d_alloc(size);
	*((PN_HEADER*)ret->data) = ret->header;
	ret->ptr = 0;
	ret->chain_head = ret->chain_tail = 0;
	ret->body = size;
	ret->next = 0;
	return ret;
}

