/*  
**  stream.h		C++ Runtime
**
**  Copyright (C)	Microway, Inc. 1987 - 1994.
**			P.O. BOX 79
**			Kingston, MA. 02364
**			(508) 746 - 7341 Voice
**			(508) 746 - 4678 FAX
**			e-mail tech@microway.com
**
**  This program is the property of Microway, Inc, its contents are
**  proprietary information and no part of it is to be disclosed to anyone
**  except employees of Microway, Inc., or as agreed in writing by the
**  President of Microway, Inc.
*/

#ifndef __STREAM_H
# define __STREAM_H

/* Compatability defs common to new and old */
# define _FORMBUFSIZE	1024		// Maximum output for any of these...

extern "C++" {			/* Use new 2.0 names, even in 1.2 */

extern char *rom(int, int=0, int uc=1); // formatted roman numerals
extern long romtol(const char *);	// unformat roman numerals

extern char *dec(long, int=0);		// formatted decimal output
extern char *oct(long, int=0);		// formatted octal
extern char *hex(long, int=0,int uc=0); // formatted hex (if uc non-zero UPPER C
extern char *uns(unsigned long, int=0); // formatted unsigned decimal

extern char *chr(int, int=0);		// character output: Kludge: 0=>no char
extern char *str(const char *, int=0);	// string output:

extern char *fdbl(double, int w=0, int p=6);	// floating output:w=field width
	    // p=decimal points
extern char *edbl(double, int w=0, int p=6);	// scientific notation output
extern char *gdbl(double, int w=0, int p=6);	// whatever seems nicest

extern char *form( const char * ...);	// sprintf formatting

# ifdef __mih
/* In the 2.0 library there is a stream.h which just includes useful files */
#  include <iostream.h>
#  include <fstream.h>
#  include <ststream.h>
#  include <iomanip.h>
static const int _good = (ios::goodbit);
static const int _eof = (ios::eofbit);
static const int _fail = (ios::failbit);
static const int _bad = (ios::badbit);
static const int input = (ios::in);
static const int output = (ios::out);
static const int append = (ios::app);
# else

#  include <stdio.h>
    /* make sure stdio.h defines _flsbuf */

#  ifndef BUFSIZE
#   define BUFSIZE 1024
#  endif

enum state_value { _good=0, _eof=1, _fail=2, _bad=4 };
enum open_mode { input=0, output=1, append=2 };

struct streambuf {
    char *base; 	    //Start of buffer
    char *pptr; 	    //Next free character (put ptr, end of filled buf)
    char *gptr; 	    //Next filled character (get ptr)
    char *eptr; 	    // one off from end of buffer
    char alloc; 	    // Allocated by new?
    FILE *fp;		    // What's this for? stdio compatability? what?

      // Empty a (put) buffer
      // Return EOF on error
    virtual overflow(int c=EOF);

      // Fill (get) a buffer
      // return EOF on error/eof, otherwise next character
    virtual underflow();

    void stossc()
	{ if (gptr++ >= pptr) underflow(); }

    int sbumpc()		// Read the current character and advance
	{ return( (gptr >= pptr && underflow()==EOF)? EOF : *(unsigned char *)(gptr++) ); }

    int snextc()		// Get the next character
	{ return( (++gptr >= pptr) ? underflow() : *(unsigned char *)gptr ); }

    inline int sgetc()		// get the current character
	{ return( (gptr>=pptr) ? underflow() : *(unsigned char *)gptr ); }

    inline void sputbackc(char c)
	{ if ( gptr > base ) *--gptr = c; }

    inline sputc(int c=EOF)
	{ if ( fp==NULL ) return(( pptr<eptr )? *(pptr++)=c : overflow(c&0xff));
	  else return( putc(c,fp)); }

    inline streambuf *setbuf( char *p, int len, int count=0 ) {
	base = p;
	gptr = p;
	pptr = p+count;
	eptr = p+len;
	return this;
    }

    int doallocate();		    // allocate some space for the buffer
    inline int allocate(){ return base==0 ? doallocate() : 0; }

    inline streambuf() { base = gptr = pptr = eptr = 0; alloc = 0; fp = 0; }
    inline streambuf(char* p, int l) { setbuf(p,l); alloc = 0; fp=0; }
    inline ~streambuf() { if (base && alloc) delete base; }
};

extern "C" int close(int);

struct filebuf : public streambuf {	// For files
    int fd;				// unix file#
    char opened;			// Has this filebuf been opened?

    int overflow( int c=EOF );		// virtuals replacing those in
    int underflow();			//	streambuf

    filebuf *open( char *, open_mode ); // open, obviously
    int close() { int i=opened; if (i) { overflow(); if ( fp!=NULL) fclose(fp); ::close(fd);} opened=0; return i; }

    filebuf()				    { opened=0; fd= -1; }
    filebuf(FILE *p)			    { opened=1; fp=p; }
    filebuf(int f)			    { opened=1; fd= f; }
    filebuf(int f, char *p, int l ) : (p,l) { opened=1; fd=f; }
    ~filebuf()				    { close(); }
};

// A useful kludge: sending an input stream to a whitespace object eats
// whitespace
struct whitespace {};


class istream;

class ostream {
    streambuf *bp;
    short state;
public:
    friend istream;

    ostream &operator<<(const char *);
    ostream &operator<<(long double v) { return (*this << gdbl(v)); }
    ostream &operator<<(double v) { return (*this << gdbl(v)); }
    ostream &operator<<(float v) { return (*this << gdbl((double) v)); }
    ostream &operator<<(long v) { return (*this << dec(v)); }
    ostream &operator<<(int v) { return (*this << dec(v)); }
    ostream &operator<<(short v) { return (*this << dec(v)); }
    ostream &operator<<(char v) { return (*this << dec(v)); }
    ostream &operator<<(unsigned long v) { return (*this << uns(v)); }
    ostream &operator<<(unsigned int v) { return (*this << uns(v)); }
    ostream &operator<<(unsigned short v) { return (*this << uns(v)); }
    ostream &operator<<(unsigned char v) { return (*this << uns(v)); }
    ostream &operator<<(streambuf &);

    ostream &put(char c) { bp->sputc(c); return( *this ); }
    ostream &flush() { bp->overflow(); return( *this ); };

    operator void *() { return( state!=_good? NULL : this ); }
    int operator !() { return( state!=_good ); }
    int eof() { return( state&_eof ); }
    int fail() { return( state!=_good ); }
    int bad() { return( state>=_fail ); }
    int good() { return( state==_good ); }
    void clear(state_value i=_good) { state = i; }
    int rdstate() { return( state ); }
    char *bufptr() { return( bp->base ); }

    ostream( streambuf *s ) { bp=s; state=_good; }
    ostream( int fd ) { bp = new filebuf(fd); state = _good; }
    ostream( FILE *fp ) { bp = new filebuf(fp); state = _good; }
    ostream( int size, char *b ) {
	state = _good;
	bp = new streambuf();
	if ( b==NULL ) b=new char[size];
	bp->setbuf(b,size);
    }

    ~ostream() { flush(); }
};

inline ostream &operator<<(ostream &t,ostream & (*m)(ostream&))    { return m(t); }
ostream &endl(ostream &);
ostream &flush(ostream &);

class istream {
    streambuf *bp;
    ostream *tied_to;
    short state;
    char skipws;	    // Whether to skip white space or not
public:
    friend ostream;
    friend void eatwhite(istream &);

    int skip(int s) { int os=skipws; skipws=s; return(os); }
    void tiecheck() { if ( tied_to!=NULL ) tied_to->flush(); }

    istream &operator>>(char *);	//string
    istream &operator>>(char &);	//character
    istream &operator>>(unsigned char &c)	{return *this>> *((char *) &c);}
    istream &operator>>(signed char &c) 	{return *this>> *((char *) &c);}
    istream &operator>>(int &);
    istream &operator>>(unsigned &u)		{return *this>>*((int *) &u);}
    istream &operator>>(short &s) { int t; *this>>t; s=t; return(*this);}
    istream &operator>>(unsigned short &us) { int t; *this>>t; us=t; return(*this);}
    istream &operator>>(long &l)		{return *this>>*((int *) &l);}
    istream &operator>>(unsigned long &ul)	{return *this>>*((int *) &ul);}
					// Assumes sizeof(int)==sizeof(long)
    istream &operator>>(double &d);	// If we ever get a real long double
					// reverse these three lines.
    istream &operator>>(long double &ld) { double t; *this>>t; ld=t; return(*this);}
    istream &operator>>(float &f) { double t; *this>>t; f=t; return(*this);}
    istream &operator>>(streambuf &);
    istream &operator>>(whitespace &) { eatwhite(*this); return(*this); }
    /*istream &operator>>(common &);		/* ???????????????? */

    /*
	Does not eat white space
    */
    istream &get(char *, int, char='\n');	// Eat a string
    istream &get(streambuf &, char='\n');
    istream &get(char &c) {
	tiecheck();
	if ( state==_good ) {
	    int tc = bp->sbumpc();
	    if ( tc == EOF ) state = _eof;
	    else c = tc;
	}
    return( *this );
    }

    istream &putback(char c) { bp->sputbackc(c); return( *this );}
    ostream *tie(ostream *t) { ostream *ot=tied_to; tied_to=t; return(ot);}

    operator void *() { return( state!=_good? NULL : this ); }
    int operator !() { return( state!=_good ); }
    int eof() { return( state&_eof ); }
    int fail() { return( state!=_good ); }
    int bad() { return( state>=_fail ); }
    int good() { return( state==_good ); }
    void clear(state_value i=_good) { state = i; }
    int rdstate() { return( state ); }
    char *bufptr() { return( bp->base ); }

    istream(streambuf *s, int skip=1, ostream *t=NULL ) {
	state=_good;
	skipws = skip;
	bp = s;
	tied_to = t;
    }
    istream(int size, char *p, int skip=1 ) {
	state = _good;
	skipws = skip;
	bp = new streambuf;
	if ( p==NULL ) p = new char[size];
	bp->setbuf(p, size, size );
	tied_to = NULL;
    }
    istream(int fd, int skip=1, ostream *t=NULL ) {
	state = _good;
	skipws = skip;
	bp = new filebuf(fd);
	tied_to = t;
    }
};

extern struct whitespace WS;	    // Globally available whitespace object

extern istream cin;
extern ostream cout;
extern ostream cerr;
# endif
}
#endif
