/**
 * @file tobjstrm.cc
 *
 * Turbo Vision - Version 2.0
 *
 * Copyright (c) 1994 by Borland International
 * All Rights Reserved.
 *
 * Modified by Sergio Sigala <sergio@sigala.it>
 * Modified by Max Okumoto <okumoto@ucsd.edu>
 */

#define Uses_TStreamable
#define Uses_TStreamableClass
#define Uses_TStreamableTypes
#define Uses_TPWrittenObjects
#define Uses_TPReadObjects
#define Uses_pstream
#define Uses_ipstream
#define Uses_opstream
#define Uses_iopstream
#define Uses_ifpstream
#define Uses_ofpstream
#define Uses_fpstream
#include <tvision/tv.h>

#include <fstream>
#include <ios>

#include <assert.h>
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <arpa/inet.h>

extern "C" {
    #include <sys/param.h>
}

const uchar nullStringLen = UCHAR_MAX;

TStreamableClass::TStreamableClass(const char *n, BUILDER b, int d ) :
    name( n ),
    build( b ),
    delta( d )
{
    pstream::initTypes();
    pstream::registerType( this );
}

TStreamableTypes::TStreamableTypes() : TNSSortedCollection( 5, 5 )
{
}

TStreamableTypes::~TStreamableTypes()
{
}

void
TStreamableTypes::registerType(const TStreamableClass *d )
{
    insert( (void *)d );
}

const TStreamableClass *TStreamableTypes::lookup(const char name[] )
{
    ccIndex loc;
    if( search( (void *)name, loc ) )
        return (TStreamableClass *)at( loc );
    else
        return 0;
}

void *
TStreamableTypes::keyOf( void *d )
{
    return (void *)((TStreamableClass *)d)->name;
}

int TStreamableTypes::compare( void *d1, void *d2 )
{
    return strcmp( (char *)d1, (char *)d2 );
}

TPWrittenObjects::TPWrittenObjects() : TNSSortedCollection( 5, 5 ), curId( 0 )
{
}

TPWrittenObjects::~TPWrittenObjects()
{
}

void
TPWrittenObjects::registerObject(const void *adr )
{
    TPWObj *o = new TPWObj( adr, curId++ );
    insert( o );
}

P_id_type TPWrittenObjects::find(const void *d )
{
    ccIndex loc;
    if( search( (void *)d, loc ) )
        return ((TPWObj *)at( loc ))->ident;
    else
        return P_id_notFound;
}

void *
TPWrittenObjects::keyOf( void *d )
{
    return (void *)((TPWObj *)d)->address;
}

int TPWrittenObjects::compare( void *o1, void *o2 )
{
    if( o1 == o2 )
        return 0;
    else if( ((char *)o1)+1 < ((char *)o2)+1 ) // force normalization
        return -1;
    else
        return 1;
}

TPWObj::TPWObj(const void *adr, P_id_type id ) : address( adr ), ident( id )
{
}

TPReadObjects::TPReadObjects() : TNSCollection( 5, 5 ), curId( 0 )
{
}

TPReadObjects::~TPReadObjects()
{
}

void
TPReadObjects::registerObject(const void *adr )
{
    ccIndex loc = insert( (void *)adr );
#ifndef __UNPATCHED
    assert( loc == (ccIndex)curId );     // to be sure that TNSCollection
#else
    assert( loc == curId++ );   // to be sure that TNSCollection
#endif
                                // continues to work the way
                                // it does now...
#ifndef __UNPATCHED
    curId++;  // Move the increment OUTSIDE the assertion.
#endif
}

const void *TPReadObjects::find( P_id_type id )
{
    return at( id );
}

/*---------------------------------------------------------------------------*
 *
 *---------------------------------------------------------------------------*/

/**
 * This form allocates a default buffer.
 */
pstream::pstream()
{
}

/**
 * Destroys the pstream object.
 */
pstream::~pstream()
{
}

/**
 * Creates the associated @ref TStreamableTypes object types. Called by the
 * @ref TStreamableClass constructor.
 */
void
pstream::initTypes()
{
    if (types == 0)
        types = new TStreamableTypes;
}

/**
 * Undocumented.
 */
void
pstream::registerType(TStreamableClass *ts)
{
    types->registerType(ts);
}

/**
 * Sets the given error condition, where StreamableError is defined as
 * follows:
 *
 * <pre>
 * enum StreamableError { peNotRegistered, peInvalidType };
 * </pre>
 */
void
pstream::error(StreamableError)
{
    abort();
}

/**
 * Sets the given error condition, where StreamableError is defined as
 * follows:
 *
 * <pre>
 * enum StreamableError { peNotRegistered, peInvalidType };
 * </pre>
 */
void
pstream::error(StreamableError, const TStreamable&)
{
    abort();
}

/*---------------------------------------------------------------------------*
 *
 *---------------------------------------------------------------------------*/

/**
 * This form creates a buffered ipstream with the given buffer.
 */
ipstream::ipstream(std::streambuf *sb)
    : std::istream(sb)
{
}

/**
 * Destroys the ipstream object.
 */
ipstream::~ipstream()
{
    objs.shouldDelete = False;
    objs.shutDown();
}

/**
 * This form moves the stream's current position to the absolute
 * position given by `pos'.
 */
ipstream &
ipstream::seekg(std::streampos pos)
{
    objs.removeAll();
    std::istream::seekg(pos);
    return *this;
}

/**
 * This form moves to a position relative to the current position by an
 * offset `off' (+ or -) starting at `dir'. Parameter `dir' can be set to:
 *
 * <pre>
 * beg (start of stream)
 *
 * cur (current stream position)
 *
 * end (end of stream)
 * </pre>
 */
ipstream &
ipstream::seekg(std::streamoff off, std::ios::seekdir dir)
{
    objs.removeAll();
    std::istream::seekg(off, dir);
    return *this;
}

/**
 * Returns the word at the current stream position.
 */
uchar
ipstream::readByte()
{
    uchar val;

    read((char *)&val, 1);
    return val;
}

/**
 * Returns the word at the current stream position.
 */
ushort
ipstream::readWord()
{
    /* SS: words are stored in little endian format (LSB first) */
    ushort ret = readByte();
    ret = ret | (readByte() << 8);
    return ret;
}

/**
 * Returns the long at the current stream position.
 */
ulong
ipstream::readLong()
{
    /* SS: ints are stored in little endian format (LSB first) */
    ulong ret = readByte();
    ret = ret | (readByte() << 8);
    ret = ret | (readByte() << 16);
    ret = ret | (readByte() << 24);
    return ret;
}

/**
 * Reads `sz' bytes from current stream position, and writes them to
 * the address given in `data'.
 */
void
ipstream::readBytes(void *data, std::streamsize sz)
{
    read((char *)data, sz);
}

/**
 * Returns a string read from the current stream position.
 */
char *
ipstream::readString()
{
    int ch = get();
    if (ch == EOF) {
	return NULL;
    }

    uchar len = ch;
    if (len == nullStringLen) {
	return NULL;
    }

    char *buf = new char[len+1];

    read(buf, len);
    buf[len] = EOS;

    return buf;
}

/**
 * Returns a string read from the current stream position.
 */
char *
ipstream::readString(char *buf, unsigned maxLen)
{
    assert(buf != NULL);

    int len = get();
    if (len == EOF) {
	return NULL;
    }

    assert(len < maxLen);

    read(buf, len);
    buf[len] = EOS;

    return buf;
}

ipstream &
ipstream::operator>>(char &ch)
{
    int c = get();
    if (c != EOF) {
	ch = (char)c;
    }
    return *this;
}


ipstream &
ipstream::operator>>(signed char &ch)
{
    int c = get();
    if (c != EOF) {
	ch = (signed char)c;
    }
    return *this;
}

ipstream &
ipstream::operator>>(unsigned char &ch)
{
    int c = get();
    if (c != EOF) {
	ch = (unsigned char)c;
    }
    return *this;
}

ipstream &
ipstream::operator>>(signed short &sh)
{
    sh = readWord();
    return *this;
}

ipstream &
ipstream::operator>>(unsigned short &sh)
{
    sh = readWord();
    return *this;
}

ipstream &
ipstream::operator>>(signed int &i)
{
    i = readLong();
    return *this;
}

ipstream &
ipstream::operator>>(unsigned int &i)
{
    i = readLong();
    return *this;
}

ipstream &
ipstream::operator>>(signed long &l)
{
    l = readLong();
    return *this;
}

ipstream &
ipstream::operator>>(unsigned long &l)
{
    l = readLong();
    return *this;
}

ipstream &
ipstream::operator>>(float &f)
{
    read((char *)&f, sizeof(f));
    return *this;
}

ipstream &
ipstream::operator>>(double &d)
{
    read((char *)&d, sizeof(d));
    return *this;
}

ipstream &
ipstream::operator>>(long double &ld)
{
    read((char *)&ld, sizeof(ld));
    return *this;
}

ipstream &
ipstream::operator>>(TStreamable& t)
{
    const TStreamableClass *pc = readPrefix();
    readData(pc, &t);
    readSuffix();
    return *this;
}

ipstream &
ipstream::operator>>(void *&t)
{
    int ch = get();
    switch (ch) {
    case pstream::ptNull:
	{
	t = 0;
	break;
	}
    case pstream::ptIndexed:
	{
	P_id_type index = readWord();
	t = (void *)find(index);
	assert(t != 0);
	break;
	}
    case pstream::ptObject:
	{
	const TStreamableClass *pc = readPrefix();
	t = readData(pc, 0);
	readSuffix();
	break;
	}
    default:
	error(pstream::peInvalidType);
	break;
    }
    return *this;
}

/**
 * Returns the @ref TStreamableClass object corresponding to the class
 * name stored at the current position.
 */
const TStreamableClass *
ipstream::readPrefix()
{
    int ch = get();
    assert(ch == '[');    // don't combine this with the previous line!
                            // We must always do the read, even if we're
                            // not checking assertions

    char name[128];
    readString(name, sizeof name);
    return types->lookup(name);
}

/**
 * Invokes the appropriate read function to read from the stream to the
 * object `mem'. If `mem' is 0, the appropriate build function is called
 * first.
 */
void *
ipstream::readData(const TStreamableClass *c, TStreamable *mem)
{
    if (mem == 0)
        mem = c->build();

    /*
     * Register the actual address of the object, not the address
     * of the TStreamable sub-object
     */
    registerObject((char *)mem - c->delta);
    return mem->read(*this);
}

/**
 * Reads and checks the final byte of an object's name field.
 */
void
ipstream::readSuffix()
{
    int ch = get();

    /*
     * Don't combine this with the previous line!
     * We must always do the write, even if we're
     * not checking assertions
     */
    assert(ch == ']');
}

/**
 * Returns a pointer to the object corresponding to `id'.
 */
const void *
ipstream::find(P_id_type id)
{
    return objs.find(id);
}

/**
 * Registers the class of the object pointed by `adr'.
 */
void
ipstream::registerObject(const void *adr)
{
    objs.registerObject(adr);
}

/*---------------------------------------------------------------------------*
 *
 *---------------------------------------------------------------------------*/

/**
 * This form creates a buffered opstream with the given buffer.
 */
opstream::opstream(std::streambuf *sb)
    : std::ostream(sb)
{
}

/**
 * Destroys the opstream object.
 */
opstream::~opstream()
{
    objs.shutDown();
}

/**
 * This form moves the stream's current position to the absolute
 * position given by `pos'.
 */
opstream &
opstream::seekp(std::streampos pos)
{
#ifndef __UNPATCHED
    objs.freeAll();   // CMF 07.11.92 --- delete the TPWObj's
#endif
    objs.removeAll();
    std::ostream::seekp(pos);
    return *this;
}

/**
 * This form moves to a position relative to the current position by an
 * offset `off' (+ or -) starting at `dir'. Parameter `dir' can be set to:
 *
 * <pre>
 * beg (start of stream)
 *
 * cur (current stream position)
 *
 * end (end of stream)
 * </pre>
 */
opstream &
opstream::seekp(std::streamoff off, std::ios::seekdir dir)
{
#ifndef __UNPATCHED
    objs.freeAll();   // CMF 07.11.92 ... s.a.
#endif
    objs.removeAll();
    std::ostream::seekp(off, dir);
    return *this;
}

/**
 * Writes character `ch' to the stream.
 */
void
opstream::writeByte(uchar ch)
{
    write((char *)&ch, 1);
}

/**
 * Writes `sz' bytes from `data' buffer to the stream.
 */
void
opstream::writeBytes(const void *data, std::streamsize sz)
{
    write((char *)data, sz);
}

/**
 * Writes the word `us' to the stream.
 */
void
opstream::writeWord( ushort sh )
{
    /* SS: words are stored in little endian format (LSB first) */
    writeByte(sh & 0xff);
    writeByte((sh >> 8) & 0xff);
}

/**
 * Writes `str' to the stream (together with a leading length byte).
 */
void
opstream::writeString(const char *str )
{
    if (str == 0 )
        {
        writeByte( nullStringLen );
        return;
        }
    int len = strlen( str );
    writeByte( (uchar)len );
    writeBytes( str, len );
}

opstream &
opstream::operator<<(char ch)
{
    writeByte( ch );
    return *this;
}

opstream &
opstream::operator<<(signed char ch)
{
    writeByte( ch );
    return *this;
}

opstream &
opstream::operator<<(unsigned char ch)
{
    writeByte( ch );
    return *this;
}

opstream &
opstream::operator<<(signed short sh)
{
    writeWord( sh );
    return *this;
}

opstream &
opstream::operator<<(unsigned short sh)
{
    writeWord( sh );
    return *this;
}

opstream &
opstream::operator<<(signed int i)
{
    /* SS: ints are stored in little endian format (LSB first) */
    writeByte(i & 0xff);
    writeByte((i >> 8) & 0xff);
    writeByte((i >> 16) & 0xff);
    writeByte((i >> 24) & 0xff);
    return *this;
}

opstream &
opstream::operator<<(unsigned int i)
{
    /* SS: ints are stored in little endian format (LSB first) */
    writeByte(i & 0xff);
    writeByte((i >> 8) & 0xff);
    writeByte((i >> 16) & 0xff);
    writeByte((i >> 24) & 0xff);
    return *this;
}
opstream &
opstream::operator<<(signed long l)
{
    /* SS: longs are stored in little endian format (LSB first) */
    writeByte(l & 0xff);
    writeByte((l >> 8) & 0xff);
    writeByte((l >> 16) & 0xff);
    writeByte((l >> 24) & 0xff);
    return *this;
}

opstream &
opstream::operator<<(unsigned long l)
{
    /* SS: longs are stored in little endian format (LSB first) */
    writeByte(l & 0xff);
    writeByte((l >> 8) & 0xff);
    writeByte((l >> 16) & 0xff);
    writeByte((l >> 24) & 0xff);
    return *this;
}

opstream &
opstream::operator<<(float f )
{
    writeBytes( &f, sizeof(f) );
    return *this;
}

opstream &
opstream::operator<<(double d )
{
    writeBytes( &d, sizeof(d) );
    return *this;
}

opstream &
opstream::operator<<(long double ld )
{
    writeBytes( &ld, sizeof(ld) );
    return *this;
}

opstream &
opstream::operator<<(TStreamable &t)
{
    writePrefix( t );
    writeData( t );
    writeSuffix( t );
    return *this;
}

opstream &
opstream::operator<<(TStreamable *t)
{
    P_id_type index;
    if (t == 0 )
        writeByte( pstream::ptNull );
    else if ((index = find( t )) != P_id_notFound )
        {
        writeByte( pstream::ptIndexed );
        writeWord( index );
        }
    else
        {
        writeByte( pstream::ptObject );
        *this << *t;
        }
    return *this;
}

/**
 * Writes the class name prefix to the stream.
 *
 * The << operator uses this function to write a prefix and suffix around
 * the data written with @ref writeData(). The prefix/suffix is used to
 * ensure type-safe stream I/O.
 */
void
opstream::writePrefix(const TStreamable& t )
{
    writeByte( '[' );
    writeString( t.streamableName() );
}

/**
 * Writes data to the stream by calling the appropriate class's write
 * member function for the object being written.
 */
void
opstream::writeData( TStreamable& t )
{
    if (types->lookup( t.streamableName() ) == 0 )
        error( peNotRegistered, t );
    else
        {
        registerObject( &t );
        t.write( *this );
        }
}

/**
 * Writes the class name suffix to the stream.
 *
 * The << operator uses this function to write a prefix and suffix around
 * the data written with @ref writeData(). The prefix/suffix is used to
 * ensure type-safe stream I/O.
 */
void
opstream::writeSuffix(const TStreamable &)
{
    writeByte(']');
}

/**
 * Returns the type ID for the object ad address `adr'.
 */
P_id_type
opstream::find(const void *adr)
{
    return objs.find(adr);
}

/**
 * Registers the class of the object pointed by `adr'.
 */
void
opstream::registerObject(const void *adr)
{
    objs.registerObject(adr);
}

/*---------------------------------------------------------------------------*
 *
 *---------------------------------------------------------------------------*/

/**
 * This form creates a buffered iopstream with the given buffer.
 */
iopstream::iopstream(std::streambuf *sb)
    : ipstream(sb), opstream(sb)
{
}

/**
 * Destroys the iopstream object.
 */
iopstream::~iopstream()
{
}

/*---------------------------------------------------------------------------*
 *
 *---------------------------------------------------------------------------*/

/**
 * Creates a buffered ifpstream object.
 */
ifpstream::ifpstream()
    : ipstream(NULL), buf()
{
    init(&buf);
}

/**
 * Creates a buffered ifpstream object. You can open a file and attach it
 * to the stream by specifying the `name' and `omode' arguments.
 */
ifpstream::ifpstream(const char name[], std::ios::openmode omode)
    : ipstream(NULL), buf()
{
    init(&buf);
    open(name, omode);
}

/**
 * Destroys the ifpstream object.
 */
ifpstream::~ifpstream()
{
}

/**
 * Opens the the named file in the given mode (app, ate, in, out, binary,
 * trunc, nocreate, or noreplace) and protection.  The opened file is
 * attached to this stream.
 */
void
ifpstream::open(const char name[], std::ios::openmode omode)
{
    using std::ios;

    if (!buf.open(name, omode | ios::in | ios::binary)) {
	this->setstate(ios_base::failbit);
    }
}

/*---------------------------------------------------------------------------*
 *
 *---------------------------------------------------------------------------*/

/**
 * Creates a buffered ofpstream object.
 */
ofpstream::ofpstream()
    : opstream(NULL), buf()
{
    init(&buf);
}

/**
 * Creates a buffered ofpstream object. You can open a file and attach it
 * to the stream by specifying the `name' and `omode' arguments.
 */
ofpstream::ofpstream(const char name[], std::ios::openmode omode)
    : opstream(NULL), buf()
{
    init(&buf);
    open(name, omode);
}

/**
 * Destroys the ofpstream object.
 */
ofpstream::~ofpstream()
{
}

/**
 * Opens the the named file in the given mode (app, ate, in, out, binary,
 * trunc, nocreate, or noreplace) and protection.  The opened file is
 * attached to this stream. 
 */
void
ofpstream::open(const char name[], std::ios::openmode omode)
{
    using std::ios;

    if (!buf.open(name, omode | ios::out | ios::binary)) {
	this->setstate(ios_base::failbit);
    }
}

/*---------------------------------------------------------------------------*
 *
 *---------------------------------------------------------------------------*/

/**
 * Creates a buffered fpstream object.
 */
fpstream::fpstream()
    : iopstream(NULL), buf()
{
    init(&buf);
}

/**
 * Creates a buffered fpstream object. You can open a file and attach it
 * to the stream by specifying the `name' and `omode' arguments.
 */
fpstream::fpstream(const char name[], std::ios::openmode omode)
    : iopstream(NULL), buf()
{
    init(&buf);
    open(name, omode);
}

/**
 * Destroys the fpstream object.
 */
fpstream::~fpstream()
{
}

/**
 * Opens the named file in the given mode (app, ate, in, out, binary,
 * trunc, nocreate, noreplace) and protection. The opened file is
 * attatched to this stream.
 */
void
fpstream::open(const char name[], std::ios::openmode omode)
{
    using std::ios;

    if (!buf.open(name, omode | ios::in | ios::out | ios::binary)) {
	this->setstate(ios_base::failbit);
    }
}



syntax highlighted by Code2HTML, v. 0.9.1