/**********************************************************************
 
	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	<fcntl.h>
#ifdef VA2
#include	<varargs.h>
#else
#include	<stdarg.h>
#endif
#include	<errno.h>

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

#ifndef _SOCKLEN_T
typedef size_t socklen_t;
#endif

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();
STREAM * s_proc_recv_file();
STREAM * s_open_file_descripter();
int s_write_terminal(STREAM * s,void * data,int len);
int s_error();

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_error,
};

S_TABLE s_file_table_terminal = {
	'f',
	0,
	{0,0},
	0,
	s_open_file_descripter,
	s_close_file,
	s_write_terminal,
	s_read_file,
	s_flush_file,
	s_proc_send_file,
	s_proc_recv_file,
	s_error,
};

S_FILE __s_std[3] = {
	{	{	&s_file_table_terminal,
			O_RDONLY
		},
/*
		stdin,
*/
		0
	},
	{	{	&s_file_table_terminal,
			O_WRONLY
		},
/*
		stdout,
*/
		1
	},
	{	{	&s_file_table_terminal,
			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 = 0;
	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;

	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 = 0;
	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;

	_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;
char * _filename;

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

	ret->file.fid = open(_filename,mode,flags);

	if ( ret->file.fid < 0 ) {
		d_f_ree(ret);
		if ( f == 0 && errno == EMFILE && stream_gc ) {
			(*stream_gc)();
			f = 1;			
			goto retry;
		}
		if ( _filename != filename )
			d_f_ree(_filename);
		return 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_file");
	if ( _filename != filename )
		d_f_ree(_filename);
	return ret;
}

int
s_close_file(STREAM * s)
{
int fd;
	if ( s->file.fid < 3 ) {
		return -1;
	}
	_s_close_sync(s);
	close(s->file.fid);
	now_open_files --;
	return 0;
}

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

retry:
	errno = 0; 
	ret = write(s->file.fid,data,len);
	if ( ret < 0 && errno == EINTR && s->h.tbl )
		goto retry;
	s->h.last_size = len;
	s->h.last_err = errno;
	return ret;
}

int
s_read_file(STREAM * s,void * data,int len)
{
int ret;
int cnt;

unsigned int ip;

retry:
	errno = 0;
	ret = read(s->file.fid,data,len);
	if ( ret < 0 && errno == EINTR && s->h.tbl )
		goto retry;
	return ret;
}

int
s_flush_file(STREAM * s)
{
	errno = 0;
	return 0;
}


int
s_seek_file(STREAM * s,unsigned int offset,int where)
{
	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;
	ps->msg[1] = 0;
	ps->fid[0] = s->file.fid;
	ps->fid[1] = -1;
	return 0;
}


STREAM *
s_proc_recv_file(PROC_RECV * ps)
{
int fid;
	ps->ptr++;
	fid = ps->fidp++;
	return _s_open_file_descripter(fid,ps->tbl);
}

