/*
 Plan 'b'
	Actually replace the open/close code with solfopen,solfclose...
	if not USE_SOL then uses 'defines' to restore original names.





    Define 'fopen,fclose,fread,fwrite,fseek...' and see if it still compiles...
*/
// IN SOLOPEN.H
#ifdef USE_SOL

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <fcntl.h>
#ifdef WIN32
#include <io.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif
#define SOL_MAIN 1
#include "solfile.h"

#include "lib.h"
#include "thread.h"
#ifdef SAVE_LOW
#include "tcplib.h"
#endif

#ifdef anybuf
#undef anybuf
#endif

#define mybuf(s)         ((s)->_flag & _IOMYBUF)
#define bigbuf(s)       ((s)->_flag & (_IOMYBUF|_IOYOURBUF))
#define anybuf(s)       ((s)->_flag & (_IOMYBUF|_IONBF|_IOYOURBUF))
#define inuse(s)        ((s)->_flag & (_IOREAD|_IOWRT|_IORW))
#define solgetc_lk(_stream)       (--(_stream)->_cnt >= 0 ? 0xff & *(_stream)->_ptr++ : sol_filbuf(_stream))                                             /* _MTHREAD_ONLY */
#define sol_fileno(x) (x->_file)
long solftell_lk(SOLFILE *stream);


#define _IOYOURBUF      0x0100
#define _IOSETVBUF      0x0400
#define _IOFEOF         0x0800

#ifdef WIN32

#define O_RDONLY _O_RDONLY
#define O_WRONLY _O_WRONLY
#define O_CREAT _O_CREAT
#define O_TRUNC _O_TRUNC

#define close _close
#define lseek _lseek
#define vsnprintf _vsnprintf
#define eof _eof
#endif

#ifdef is_freebsd
//#define _IOREAD   0x0001
//#define _IOWRT    0x0002
// these must be carefully arrnaged not to conflict with the other ones using the same flags variable.... 
#define _IOREAD   0x1000
#define _IOWRT    0x2000
#define _IOMYBUF  0x0008
#define _IOEOF    0x0010
#define _IOERR    0x0020
#define _IOSTRG   0x0040
#define _IORW     0x0080
#define _IOAPPEND 0x0200


#endif 

static int (*sol_emsg)( const char *format ,...);
int sol_filbuf(SOLFILE *stream);
size_t sol_fwrite_lk(const void *buffer, size_t size, size_t count, SOLFILE *stream);
long solftell_lk(SOLFILE *stream);
int sol_freebuf(SOLFILE *stream);
int solsetvbuf( SOLFILE *stream, char *buffer, int type, size_t size );

static Mutex *m;
int sol_init(int (*vemsg)( const char *format ,...))
{
	sol_emsg = vemsg;
	m = th_mutex_new("d_MUTEX");
	return TRUE;
}
#define MUTEX_TIME (1000*600) // 10 MINUTES 
void sol_mutex_wait(void)
{
	if (m==NULL) return;
	if (!th_mutex_wait(m,MUTEX_TIME)) {
		sol_emsg("Signal Cannot get mutex, fatal problem, dieing solfile\n");
		lib_abort();
	}
}
int solferror (SOLFILE *stream)
{
        return( ((stream)->_flag & _IOERR) );
}
void sol_mutex_release(void)
{
	if (m==NULL) return;
	th_mutex_release(m);
}
int sol_lock(SOLFILE *stream)
{
	sol_mutex_wait();
	for (;stream->locked;) {
		if (stream->lock_thid == th_id()) {
			char *s = 0;
			sol_mutex_wait();
			sol_emsg("Mutex is already locked by us, oops\n");
			*s = 1;
			lib_abort();
		}
		sol_mutex_release();
		lib_msleep(40);
		sol_mutex_wait();
	}
	stream->locked = TRUE;
	stream->lock_thid = th_id();
	sol_mutex_release();
	return TRUE;
}
int sol_unlock(SOLFILE *stream)
{
	sol_mutex_wait();
	stream->locked = FALSE;
	sol_mutex_release();
	return TRUE;
}
void solfcrash(SOLFILE *f, char *reason)
{
	sol_emsg("crashing %s\n",reason);
	lib_abort();
}
int solfclose(SOLFILE *f)
{
	int r;
	if (f==NULL) solfcrash(f,"Handle was null on solfclose");
	if (!inuse(f)) solfcrash(f,"Handle was not open");
	r = close(f->_file);  
#ifdef SAVE_LOW
	// On solaris free the table entry of handles used so we will no not to try and dup to it. 
	tcp_socket_low_free(f->_file,__LINE__);
#endif

	f->_file = -1;
	sol_freebuf(f);
	remove(f->_tmpfname);
	f->_flag = 0;
	free(f);
	return r;
}
int solfeof(SOLFILE *f)
{
	return ((f)->_flag & _IOEOF);
}
SOLFILE * solfopen (const char *filename, const char *mode)
{
        int modeflag;
        int commodeset = 0;
		int streamflag = 0;
        int scanset    = 0;
		int pmode;
        int whileflag;
        int filedes;
        SOLFILE *stream;

        /* Parse the user's specification string as set flags in
               (1) modeflag - system call flags word
               (2) streamflag - stream handle flags word. */

        /* First mode character must be 'r', 'w', or 'a'. */

        switch (*mode) {
        case ('r'):
                modeflag = O_RDONLY;
                streamflag |= _IOREAD;
                break;
        case ('w'):
                modeflag = O_WRONLY | O_CREAT | O_TRUNC;
                streamflag |= _IOWRT;
                break;
        case ('a'):
                modeflag = O_WRONLY | O_CREAT | O_APPEND;
                streamflag |= _IOWRT;
                break;
        default:
                return(NULL);
                break;
        }

        /* There can be up to three more optional mode characters:
           (1) A single '+' character,
           (2) One of 't' and 'b' and
           (3) One of 'c' and 'n'.
        */

        whileflag=1;

        while(*++mode && whileflag)
                switch(*mode) {

                case ('+'):
                        if (modeflag & O_RDWR)
                                whileflag=0;
                        else {
                                modeflag |= O_RDWR;
                                modeflag &= ~(O_RDONLY | O_WRONLY);
                                streamflag |= _IORW;
                                streamflag &= ~(_IOREAD | _IOWRT);
                        }
                        break;

                case ('b'):
                        break;

                case ('t'):
                        break;

                case ('c'):
                        break;

                case ('n'):
                        break;

                case ('S'):
                        break;

                case ('R'):
                        break;

                case ('T'):
                        break;

                case ('D'):
                        break;

                default:
                        whileflag=0;
                        break;
                }

        /* Try to open the file.  Note that if neither 't' nor 'b' is
           specified, _sopen will use the default. */


	if ( (modeflag & O_CREAT) && (modeflag & O_RDONLY) ) pmode = S_IREAD;
	else pmode = S_IREAD | S_IWRITE;
#ifdef WIN32
	modeflag |= O_NOINHERIT;
#endif
	filedes = open(filename, modeflag, pmode);
	if (filedes<0) return NULL;
#ifdef SAVE_LOW
	filedes = tcp_socket_low(filedes,__LINE__);
#endif
	

#ifndef WIN32
    lib_close_on_exec(filedes);
#endif
    stream = calloc(1,sizeof(SOLFILE));
    stream->_flag = streamflag;
    stream->_file = filedes;
	solsetvbuf(stream,NULL,_IOFBF,8000);
    return(stream);
}
int sol_flush(SOLFILE *str)
{
        SOLFILE *stream;
        int rc = 0; /* assume good return */
        int nchar;

        /* Init pointer to stream */
        stream = str;


        if ((stream->_flag & (_IOREAD | _IOWRT)) == _IOWRT && bigbuf(stream)
                && (nchar = stream->_ptr - stream->_base) > 0)
        {
                if ( write(solfileno(stream), stream->_base, nchar) == nchar ) {
                        /* if this is a read/write file, clear _IOWRT so that
                         * next operation can be a read
                         */
                        if ( _IORW & stream->_flag )
                                stream->_flag &= ~_IOWRT;
                }
                else {
                        stream->_flag |= _IOERR;
                        rc = EOF;
                }
        }

        stream->_ptr = stream->_base;
        stream->_cnt = 0;

        return(rc);
}

int solfflush(SOLFILE *f)
{
	int r;
	sol_lock(f);
	r = sol_flush(f);
	sol_unlock(f);
	return 0; // SINCE WE HAVE NO CACHE :-)
}
char *solfgets( char *string, int count, SOLFILE *stream )
{
	int ch;
	char *retval=string;
	char *pointer=string;
	if (count<=0) return NULL;
	sol_lock(stream);
    while (--count) {
            if ((ch = solgetc_lk(stream)) == EOF) {
                    if (pointer == string) {
                            retval=NULL;
                            goto done;
                    }
                    break;
            }
			*pointer++ = ch;
            if (ch == '\n')  break;
    }

done:
    *pointer = 0;
	sol_unlock(stream);	
	return retval;
}
int solfileno(SOLFILE *f)
{
	return f->_file;
}
#define MAXFPRINTF 66000
int solfprintf(SOLFILE *f,const char * arg_list, ...)
{
    va_list arg_ptr;
    char *format;
    int len;
	int t;
	char output[MAXFPRINTF];
    va_start(arg_ptr, arg_list);
    format = (char *) arg_list;
    len = vsnprintf(output,MAXFPRINTF-1, format, arg_ptr);
	if (len<0) return len;
	t = solfwrite(output,1,len,f);
	if (t!=len) return -1;
	return t;
}
size_t solfread_lk(void *buffer, size_t size, size_t count, SOLFILE *stream);
size_t solfread(void *buffer, size_t size, size_t count, SOLFILE *stream)
{
        size_t retval;
        sol_lock(stream); 
        retval = solfread_lk(buffer, size, count, stream);
        sol_unlock(stream);
        return retval;
}
size_t solfread_lk(void *buffer, size_t size, size_t num, SOLFILE *stream)
{
        char *data;                     /* point to where should be read next */
        unsigned total;                 /* total bytes to read */
        unsigned count;                 /* num bytes left to read */
        unsigned bufsize;               /* size of stream buffer */
        unsigned nbytes;                /* how much to read now */
        unsigned nread;                 /* how much we did read */
        int c;                          /* a temp char */

        /* initialize local vars */
        data = buffer;
        if ( (count = total = size * num) == 0 )  return 0;

        bufsize = stream->_bufsiz;
        while (count != 0) {
                /* if the buffer exists and has characters, copy them to user buffer */
                if (anybuf(stream) && stream->_cnt != 0) {
                        /* how much do we want? */
                        nbytes = (count < (unsigned)stream->_cnt) ? count : stream->_cnt;
                        memcpy(data, stream->_ptr, nbytes);

                        /* update stream and amt of data read */
                        count -= nbytes;
                        stream->_cnt -= nbytes;
                        stream->_ptr += nbytes;
                        data += nbytes;
                }
                else if (count >= bufsize) {
                        /* If we have more than bufsize chars to read, get data
                           by calling read with an integral number of bufsiz
                           blocks.  Note that if the stream is text mode, read
                           will return less chars than we ordered. */

                        /* calc chars to read -- (count/bufsize) * bufsize */
                        nbytes = ( bufsize ? (count - count % bufsize) :
                                   count );

                        nread = read(solfileno(stream), data, nbytes);
                        if (nread == 0) {
                                /* end of file -- out of here */
                                stream->_flag |= _IOEOF;
                                return (total - count) / size;
                        }
                        else if (nread == (unsigned)-1) {
                                /* error -- out of here */
                                stream->_flag |= _IOERR;
                                return (total - count) / size;
                        }

                        /* update count and data to reflect read */
                        count -= nread;
                        data += nread;
                }
                else {
                        /* less than bufsize chars to read, so call _filbuf to
                           fill buffer */
                        if ((c = sol_filbuf(stream)) == EOF) {
                                /* error or eof, stream flags set by _filbuf */
                                return (total - count) / size;
                        }

                        /* _filbuf returned a char -- store it */
                        *data++ = (char) c;
                        --count;

                        /* update buffer size */
                        bufsize = stream->_bufsiz;
                }
        }

        /* we finished successfully, so just return num */
        return num;
}
size_t solfwrite (const void *buffer, size_t size, size_t count, SOLFILE *stream)
{
    size_t retval;

    sol_lock(stream);                      /* lock stream */
    retval = sol_fwrite_lk(buffer, size, count, stream);  /* do the read */
    sol_unlock(stream);                    /* unlock stream */
    return retval;
}
size_t sol_fwrite_lk(const void *buffer, size_t size, size_t count, SOLFILE *stream)
{
	int tot = size*count;
	int nsent=0;
	int r=0;
	int chunk;
	for (;tot>0;) {
		chunk = 32000;
		if (tot<chunk) chunk = tot;
		r = write(solfileno(stream),buffer,chunk);
		if (r == -1) {
			stream->_flag |= _IOERR;
			return tot / size;
		}
		nsent += r;
		tot -= chunk;
	}
	return nsent/size;
}
int solfseek(SOLFILE *stream, long offset, int whence)
{
	int f;
	int retval=0;
    sol_lock(stream);

//printf("solfseek whence=%d, inused=%d\n",whence,inuse(stream));
	if ( !inuse(stream) || ((whence != SEEK_SET) && (whence != SEEK_CUR) &&(whence != SEEK_END)) ) {
		errno=EINVAL;
	    sol_unlock(stream);
		return(-1);
	}

	/* Clear EOF flag */
	stream->_flag &= ~_IOEOF;

	/* If seeking relative to current location, then convert to
	   a seek relative to beginning of file.  This accounts for
	   buffering, etc. by letting fseek() tell us where we are. */

	if (whence == SEEK_CUR) {
			offset += solftell_lk(stream);
			whence = SEEK_SET;
	}

	/* Flush buffer as necessary */
	sol_flush(stream);

	/* If file opened for read/write, clear flags since we don't know
	   what the user is going to do next. If the file was opened for
	   read access only, decrease _bufsiz so that the next _filbuf
	   won't cost quite so much */

	if (stream->_flag & _IORW)	stream->_flag &= ~(_IOWRT|_IOREAD);
	/* Seek to the desired locale and return. */
	stream->_cnt = 0;
	f = lseek(solfileno(stream), offset, whence);
    sol_unlock(stream);
	return f== -1L ? -1 : 0;
}
#define SOL_BUFSIZ 8000
void sol_getbuf(SOLFILE *stream)
{
	if (stream->_base!=NULL) return;
    stream->_base = malloc(SOL_BUFSIZ);
	if (stream->_base==NULL) {
		sol_emsg("Unable to allocate data buffer for stream\n");
		lib_abort();
	}

    stream->_flag |= _IOMYBUF;
    stream->_bufsiz = SOL_BUFSIZ;
    stream->_ptr = stream->_base;
    stream->_cnt = 0;

    return;
}

int sol_filbuf(SOLFILE *stream)
{
        if (!inuse(stream)) return(EOF);

        if (stream->_flag & _IOWRT) {
                stream->_flag |= _IOERR;
                return(EOF);
        }
        stream->_flag |= _IOREAD;

        if (!anybuf(stream)) {
                sol_getbuf(stream);
        } else {
                stream->_ptr = stream->_base;
		}

        stream->_cnt = read(sol_fileno(stream), stream->_base, stream->_bufsiz);

        if ((stream->_cnt == 0) || (stream->_cnt == -1)) {
                stream->_flag |= stream->_cnt ? _IOERR : _IOEOF;
                stream->_cnt = 0;
                return(EOF);
        }

        stream->_cnt--;

        return(0xff & *stream->_ptr++);
}



long solftell_lk(SOLFILE *stream)
{
	long filepos;
	long rdcnt;
	int offset;
	if (stream->_cnt < 0) stream->_cnt = 0;
    if ((filepos = lseek(stream->_file, 0L, SEEK_CUR)) < 0L) return(-1L);
	// offset is how many bytes we have read from our internal buffer.
    offset = stream->_ptr - stream->_base;
	// _cnt = number of bytes left in that buffer, 
    rdcnt = stream->_cnt + (offset);
    filepos -= (long)rdcnt;
    return(filepos + (long)offset);
}
long solftell(SOLFILE *stream)
{
	long x;
	sol_lock(stream);
	x = solftell_lk(stream);
	sol_unlock(stream);
	return x;
}
int sol_freebuf(SOLFILE *stream)
{
	// assume stream is locked
	if (mybuf(stream)) free(stream->_base);
	stream->_flag &= ~(_IOYOURBUF | _IOMYBUF | _IOSETVBUF);
    stream->_bufsiz = 0;
    stream->_ptr = stream->_base = NULL;
    stream->_cnt = 0;
	return TRUE;
}
int solsetvbuf( SOLFILE *stream, char *buffer, int type, size_t size )
{
        int retval=0;   /* assume good return */
		if (stream==NULL) return 0;
        if ( (type != _IONBF) && ((size < 2) || (size > 20000000) ||
             ((type != _IOFBF) && (type != _IOLBF))) )
                return(-1);
        size &= (size_t)~1;
	    sol_lock(stream);

        // Flush the current buffer and free it, if it is ours.
        sol_flush(stream);
        sol_freebuf(stream);

        /*
         * Clear a bunch of bits in stream->_flag (all bits related to
         * buffering and those which used to be in stream2->_flag2). Most
         */
//imsg("solfile flags playing %x %x %x %x %x %x\n",stream->_flag,_IOMYBUF , _IOYOURBUF , _IONBF , _IOSETVBUF , _IOFEOF);
		 stream->_flag &= ~(_IOMYBUF | _IOYOURBUF | _IONBF |
                           _IOSETVBUF |
                           _IOFEOF );
//imsg("solfile flags playing %x %x %x %x %x %x\n",stream->_flag,_IOMYBUF , _IOYOURBUF , _IONBF , _IOSETVBUF , _IOFEOF);

        /*
         * CASE 1: No Buffering.
         */

        if ( buffer == NULL ) {
                if ( (buffer = malloc(size)) == NULL ) {
                        retval = -1;
                        goto done;
                }
                stream->_flag |= _IOMYBUF | _IOSETVBUF;
        } else { // buffer is from user;
                stream->_flag |= _IOYOURBUF | _IOSETVBUF;
        }

        stream->_bufsiz = size;
        stream->_ptr = stream->_base = buffer;
        stream->_cnt = 0;
done:
		sol_unlock(stream);
        return(retval);
}

static int ntmp;
char *sol_tmpname(void)
{
	char *s;
	char *t;
	char fname[BFSZ];
	sol_mutex_wait();
#ifdef WIN32
	t = getenv("TEMP");
	if (t==NULL) t = "c:\\temp";
	sprintf(fname,"%s\\sol_%d_%d_%d",t,th_pid(),th_id(),ntmp++);
#else
	sprintf(fname,"/var/tmp/sol_%d_%d_%d",th_pid(),th_id(),ntmp++);
#endif
	s =  strdup(fname);
	sol_mutex_release();
	return s;
}
SOLFILE *soltmpfile(void)
{
	SOLFILE *f=NULL;
	char *name;
	name = sol_tmpname();
	f = solfopen(name,"w+b");
	if (f==NULL) return f;
	f->_tmpfname = name;
	return f;
}
void solclearerr(SOLFILE *stream)
{
    sol_lock(stream);
    stream->_flag &= ~(_IOERR|_IOEOF);
    sol_unlock(stream);
	return ;
}

#endif

