/**********************************************************************
 
	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
#define LIBRARY
#define S_FILE_LIBRARY

#include	<stdlib.h>
#include	"machine/include.h"
#include	"memory_debug.h"
#include	"stream.h"
#include	"task.h"
#include	"utils.h"
#include	<sys/types.h>
#include	<sys/socket.h>
#include	<netinet/in.h>


extern SEM stream_lock;
int max_fid = 3;
int now_open_files = 4;

int s_close_file();
int s_write_file();
int s_read_file();
int s_vprintf_file();
int s_vprintf_cr_file();
int s_flush_file();
int s_vscanf_file();
int s_proc_send_file();
int s_get_socketip_file();
STREAM * s_proc_recv_file();
STREAM * s_open_file_descripter();

S_TABLE s_file_table = {
	'f',
	0,
	{0,0},
	0,
	s_open_file_descripter,
	s_close_file,
	s_write_file,
	s_read_file,
	s_flush_file,
	s_proc_send_file,
	s_proc_recv_file,
	s_get_socketip_file,
};

S_FILE __s_std[3] = {
	{	{	&s_file_table,
			O_RDONLY
		},
/*
		stdin,
*/
		0
	},
	{	{	&s_file_table,
			O_WRONLY
		},
/*
		stdout,
*/
		1
	},
	{	{	&s_file_table,
			O_WRONLY
		},
/*
		stderr,
*/
		2
	}
};

void
init_file_stream()
{
	insert_s_table(&s_file_table);
}

STREAM *
s_open_file_descripter(int d,S_TABLE * tbl)
{
STREAM * ret;
int mode;
	if ( d < 0 )
		return 0; 
	mode = fcntl(d,F_GETFL);
	if ( mode < 0 )
		return 0;
	mode &= 3;
//	fcntl(d,F_SETFL,mode);
	ret = d_alloc(sizeof(S_FILE));
	ret->h.tbl = tbl;
	ret->h.thread = 0;
	ret->file.fid = d;
	ret->file.filename = 0;

	lock_task(stream_lock);
	_s_open(ret,mode);
	now_open_files ++;
	if ( ret->file.fid > max_fid )
		max_fid = ret->file.fid;
	unlock_task(stream_lock,"s_open_descripter");
	return ret;
}

STREAM *
_s_open_file_descripter(int d,S_TABLE * tbl)
{
STREAM * ret;
int mode;
	if ( d < 0 )
		return 0; 
	mode = fcntl(d,F_GETFL);
	if ( mode < 0 )
		return 0;
	mode &= 3;
//	fcntl(d,F_SETFL,mode);
	ret = d_alloc(sizeof(S_FILE));
	ret->h.tbl = tbl;
	ret->h.thread = 0;
	ret->file.fid = d;
	ret->file.filename = 0;

	_s_open(ret,mode);
	now_open_files ++;
	if ( ret->file.fid > max_fid )
		max_fid = ret->file.fid;
	return ret;
}


STREAM *
s_open_file(char * filename,int mode,int flags)
{
STREAM * ret;
int f;

	f = 0;
retry:
	errno = 0;
	ret = d_alloc(sizeof(S_FILE));
	ret->h.tbl = &s_file_table;
	ret->h.thread = 0;

	ret->file.fid = u_open(filename,mode&(~O_NDELAY),flags);
	ret->file.filename = copy_str(filename);

	if ( ret->file.fid < 0 ) {
		d_f_ree(ret->file.filename);
		d_f_ree(ret);
		if ( f == 0 && errno == EMFILE && stream_gc ) {
			(*stream_gc)();
			f = 1;			
			goto retry;
		}
		return 0;
	}
	lock_task(stream_lock);
	_s_open(ret,mode);
	if ( ret->file.fid >= 0x10000 )
		ret->h.wb_flags |= STF_BUF_OUT;
	now_open_files ++;
	if ( ret->file.fid > max_fid )
		max_fid = ret->file.fid;
	unlock_task(stream_lock,"s_open_file");
	return ret;
}

int
s_close_file(STREAM * s)
{
int er;

	if ( s->file.fid < 3 ) {
/*
		fd = dup(s->file.fid);
		u_close(s->file.fid);
		if ( s->file.fid != dup(fd) )
			er_panic("s_close_file");
		u_close(fd);
*/
		return -1;
	}
	_s_close_sync(s);
	er = -1;
	for ( ; ; ) {
		if ( u_close(s->file.fid) < 0 )
			switch ( errno ) {
			case EBADF:
				break;
			case EINTR:
				continue;
			case ENOSPC:
			case EIO:
				sleep_sec(2);
				continue;
			default:
				break;
			}
		break;
	}
	if ( s->file.filename )
		d_f_ree(s->file.filename);
	now_open_files --;
	return 0;
}

int
s_write_file(STREAM * s,void * data,int len)
{
int ret;
WD_ENTRY * e;
int er;

retry:

	e = wait_data_lock(s->file.fid,WDT_WRITE);
	if ( e == 0 ) {
		errno = EINTR;
		return -1;
	}	
	errno = 0; 
	ret = u_write(s->file.fid,data,len);

	er = errno;
	s->h.last_size = len;
	s->h.last_err = errno;

	wait_data_unlock(e);

	if ( ret < 0 && er == EINTR && s->h.tbl )
		goto retry;

	errno = er;

	return ret;
}


int
s_read_file(STREAM * s,void * data,int len)
{
WD_ENTRY * e;
int ret;
int er;

retry:


	e = wait_data_lock(s->file.fid,WDT_READ);
	if ( e == 0 ) {
		errno = EINTR;
		return -1;
	}

	errno = 0; 
	ret = u_read(s->file.fid,data,len);

	er = errno;

	wait_data_unlock(e);

	if ( ret < 0 && er == EINTR && s->h.tbl )
		goto retry;

	errno = er;

	return ret;
}

int
s_flush_file(STREAM * s)
{
int ret;

	errno = 0;
	ret = fsync(s->file.fid);
	return ret;
}


int
s_get_socketip_file(STREAM * s)
{
int ret;
int er;
int size;
struct sockaddr_in ss;
	if ( s->h.tbl != &s_file_table )
		return 0;
	size = sizeof(ss);
	er = getpeername(s->file.fid,(struct sockaddr*)&ss,(void*)&size);
	if ( er < 0 )
		return 0;
	ret = ntohl(ss.sin_addr.s_addr);
	return ret;
}

int
s_seek_file(STREAM * s,unsigned int offset,int where)
{
	if ( s == 0 )
		return 0;
	if ( s->h.tbl != &s_file_table )
		return -1;
	return lseek(s->file.fid,offset,where);
}


int
s_proc_send_file(STREAM * s,PROC_SEND * ps)
{
	ps->msg[0] = s->h.tbl->type;
	sprintf(ps->msg+1, "%d", s->file.fid);
	ps->fid[0] = s->file.fid;
	ps->fid[1] = -1;
	return 0;
}


STREAM *
s_proc_recv_file(PROC_RECV * ps)
{
int fid;
int i = 0;
char buf[16];
	ps->ptr++;
	while ( *ps->ptr >= '0' && *ps->ptr <= '9' && i < sizeof(buf)-1 ) {
		buf[i++] = *ps->ptr++;
	}
	buf[i] = 0;
	fid = atoi(buf);
	return _s_open_file_descripter(fid,ps->tbl);
}

