/**********************************************************************
 
	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.

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


#define STREAM_LIB

#include	<stdlib.h>
#include	<fcntl.h>
#ifdef VA2
#include	<varargs.h>
#else
#include	<stdarg.h>
#endif

#include	"memory_debug.h"
#include	"stream.h"
#include	"task.h"

extern SEM stream_lock;

int s_close_string();
int s_read_string();
int s_write_string();
int s_vprintf_string();
int s_vprintf_string_cr();
int s_error();
STREAM * s_error_stream();

S_TABLE s_string_table = {
 	't',
 	0,
	{0,0},
	0,
	s_error_stream,
	s_close_string,
	s_write_string,
	s_read_string,
	s_error,
	s_error,
	s_error_stream,
	s_error
};

void
init_string_stream()
{
	insert_s_table(&s_string_table);
}


STREAM *
s_open_string_read(void * str,CODE_METHOD * cm,int size,int flag)
{
STREAM * ret;

	ret = d_alloc(sizeof(S_STRING));
	ret->h.tbl = &s_string_table;
	ret->str.size = size;
	ret->str.flag = flag;
	if ( flag ) {
		ret->str.ptr = ret->str.str = d_alloc(size);
		memcpy(ret->str.str,str,size);
	}
	else	ret->str.ptr = ret->str.str = str;
	lock_task(stream_lock);
	_s_open(ret,O_RDONLY);
	unlock_task(stream_lock,"s_open_string_read");
	(*ret->h.cm->close)(0,ret->h.cm_work); 
	ret->h.cm = cm;
	ret->h.cm_work = (*ret->h.cm->open)();
	return ret;
}

STREAM *
s_open_string_write(CODE_METHOD * cm)
{
STREAM * ret;

	ret = d_alloc(sizeof(S_STRING));
	ret->h.tbl = &s_string_table;
	ret->str.flag = 1;
	ret->str.size = 0;
	ret->str.str = 0;
	ret->str.ptr = 0;
	lock_task(stream_lock);
	_s_open(ret,O_RDWR);
	unlock_task(stream_lock,"s_open_string_read");
	(*ret->h.cm->close)(0,ret->h.cm_work); 
	ret->h.cm = cm;
	ret->h.cm_work = (*ret->h.cm->open)();
	return ret;
}

int
s_close_string(STREAM * s)
{
	if ( s->str.flag && s->str.str )
		d_f_ree(s->str.str);
	return 0;
}

int
s_read_string(STREAM * s,void * data,int len)
{
int size;
	if ( s->str.size == 0 ) {
		s->str.size = -1;
		return 0;
	}
	if ( s->str.size == -1 )
		return -1;
	if ( s->str.size < len ) {
		size = s->str.size;
		memcpy(data,s->str.ptr,size);
		s->str.ptr += size;
		s->str.size = 0;
		return size;
	}
	else {
		memcpy(data,s->str.ptr,len);
		s->str.ptr += len;
		s->str.size -= len;
		return len;
	}
}

int
s_write_string(STREAM * st,void * data,int len)
{
	if ( len == 0 )
		return 0;
	if ( st->str.str )
		st->str.str = d_re_alloc(st->str.str,st->str.size + len);
	else	st->str.str = d_alloc(len);
	memcpy(&st->str.str[st->str.size],data,len);
	st->str.size += len;
	return len;
}

int
s_vprintf_string(STREAM * s,char * fmt,va_list p)
{
int ret;
int len;
int i;
	i = 0;
	for ( len = 100 ; ; ) {
		if ( s->str.str )
			s->str.str = d_re_alloc(s->str.str,s->str.size+len+1);
		else	s->str.str = d_alloc(len+1);
		ret = vsnprintf(&s->str.str[s->str.size],len,fmt,p);
		for ( ; i < len ; i ++ ) {
			if ( s->str.str[s->str.size+i] == 0 )
				break;
		}
		if ( i >= len-1 ) {
			len += 100;
			continue;
		}
		break;
	}
	s->str.size += strlen(&s->str.str[s->str.size]);
	s->str.str = d_re_alloc(s->str.str,s->str.size);
	return ret;
}

int
s_vprintf_string_cr(STREAM * s,char * fmt,va_list p)
{
int ret;
int len;
int i;
	i = 0;
	lock_task(stream_lock);
	for ( len = 100 ; ; ) {
		if ( s->str.str )
			s->str.str = d_re_alloc(s->str.str,s->str.size+len+1);
		else	s->str.str = d_alloc(len+1);
		ret = vsnprintf(&s->str.str[s->str.size],len,fmt,p);
		for ( ; i < len ; i ++ ) {
			if ( s->str.str[s->str.size+1] == '\n' ||
				s->str.str[s->str.size+1] == '\r' )
				s->h.cr_cnt ++;
			if ( s->str.str[s->str.size+i] == 0 )
				break;
		}
		if ( i >= len-1 ) {
			len += 100;
			continue;
		}
		break;
	}
	s->str.size += strlen(&s->str.str[s->str.size]);
	s->str.str = d_re_alloc(s->str.str,s->str.size);
	unlock_task(stream_lock,"string");
	return ret;
}

char *
s_get_string(STREAM * st)
{
char * ret;
int size;
	if ( st->h.tbl != &s_string_table )
		return 0;
	if ( st->str.str == 0 )
		return 0;
	size = st->str.size;
	ret = st->str.str = d_re_alloc(st->str.str,size+1);
	ret[size] = 0;
	return ret;
}


L_CHAR *
s_get_l_string(STREAM * st)
{
char * ret;
int size;
int i;
	if ( st->h.tbl != &s_string_table )
		return 0;
	if ( st->str.str == 0 )
		return 0;
	size = st->str.size;
	ret = st->str.str = d_re_alloc(st->str.str,size+sizeof(L_CHAR));
	for ( i = 0 ; i < sizeof(L_CHAR) ; i ++ )
		ret[size+i] = 0;
	return (L_CHAR*)ret;
}


