// This may look like C code, but it is really -*- C++ -*-
// 
// <copyright> 
//  
//  Copyright (c) 1996
//  Institute for Information Processing and Computer Supported New Media (IICM), 
//  Graz University of Technology, Austria. 
//  
// </copyright> 
// 
// 
// <file> 
// 
// Name:        rpcbuf.C
// 
// Purpose:     
// 
// Created:     quite a few years in the past by someone I sure dont like.
// 
// Modified:    all the time to fix bugs again and again
// 
// Description: 
// 
// $Id: rpcbuf.C,v 1.10 1997/01/24 13:52:29 gorasche Exp $
// 
// $Log: rpcbuf.C,v $
// Revision 1.10  1997/01/24 13:52:29  gorasche
// some minor changes for WIN32
//
// Revision 1.9  1997/01/22 13:06:45  jfasch
// modified doallocate(), dtor, and the expand_g() to maintain also the
// get area (which seemes to equal the reserve area) in this. formerly,
// it was maintained by the streambuf, hence deallocated there. the prob
// was that (at least with GNU's libio, as of around Jan 1997) the areas
// are new'ed here, and free'd there. this may work under some
// circumstances, but there happened funny things.
//
// 
// </file> 
#include "rpcbuf.h"

#include <sys/types.h>
#include <sys/socket.h>

#ifdef WIN32
#  include <io.h>
#endif
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>

#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

/* hleitner: (16.3.94) -- no zapeof in libg++ 
gorasche: (1.2.96) ditto in MSC */
#if defined(__GNUC__) || defined(WIN32)       
#define zapeof(c) ((c) & 0xFF)
#endif

#ifndef SOMAXCONN
#  define SOMAXCONN 8
#endif

// --------------------------------------------------------------------
const char* rpcbuf :: version1 = "$Id: rpcbuf.C,v 1.10 1997/01/24 13:52:29 gorasche Exp $" ;
Verbose rpcbuf :: verbose ;

// I need a pointer to an iostreamb so I can insert and extract values
// in the length field of RPC requests.  If I don't have a stream, I
// won't allow you to call start_request().

rpcbuf::rpcbuf(iostreamb* mystream) : streambuf(),
    _mystream(mystream),
    _rptr(nil),
    _actualWidth(0),
    _host(nil),
    _port(0),
    _fd(-1),
    _opened(false),
    _close(false),
    _nonblocking(false),
    _verbose(true) {}

// Free the buffer used to store the put area.  The streambuf
// destructor will free the buffer used to store the get area.

// rpcbuf::~rpcbuf() {
//     close();

//     delete pbase();
//     setp(nil, nil);
// }
rpcbuf::~rpcbuf() {
    close();

    delete base() ; 
    setb (nil, 0, false) ; // quite a dummy, I think.

    delete pbase();
    setp(nil, nil);
}

// Return information about the connection.

const char* rpcbuf::host() {
    return _host;
}

int rpcbuf::port() {
    if (!_opened) {
	return 0;
    }

    if (_port) {
	return _port;
    }

    struct sockaddr_in name;
    int name_len = sizeof(name);
    if (getsockname(_fd, (struct sockaddr*)&name, &name_len) < 0) {
	sys_error("rpcbuf::port: getsockname");
	return 0;
    }

    _port = ntohs(name.sin_port);
    return _port;
}

int rpcbuf::fd() {
    return _fd;
}

boolean rpcbuf::opened() {
    return _opened;
}

boolean rpcbuf::nonblocking() {
    return _nonblocking;
}

// Create a socket, bind the socket to a port address, and prepare to
// accept incoming connections.

rpcbuf* rpcbuf::listen(int port) {
    struct sockaddr_in name;
    ::memset (&name, 0, sizeof(name)) ;
    name.sin_family = AF_INET;
    name.sin_port = htons(port);
    name.sin_addr.s_addr = htonl(INADDR_ANY);

    if (_opened) {
	error("rpcbuf::listen: already using a file number");
	return nil;
    }

    int fd = socket(AF_INET, SOCK_STREAM, 0);
#ifdef WIN32
  	if (fd==INVALID_SOCKET) {
#else
    if (fd < 0) {
#endif
	sys_error("rpcbuf::listen: socket");
	return nil;
    }

    // reuse address of socket

    int optval = 1;                        // kandrews  08 Mar 94
    int optlen = sizeof(int);

    if ( setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, optlen) == -1) {     // kandrews
	sys_error("rpcbuf::listen: setsockopt");
#ifdef WIN32
        closesocket( fd);
        int ok=WSAGetLastError(); 
        if (ok < 0)
           perror("rpcbuf::listen: close");
#else
        ::close(fd);
#endif
	return nil;
    }


    if (bind(fd, (struct sockaddr*)&name, sizeof(name)) < 0) {
	sys_error("rpcbuf::listen: bind");
#ifdef WIN32
        closesocket( fd);
        int ok=WSAGetLastError(); 
        if (ok < 0)
           perror("rpcbuf::listen: close");
#else
        ::close(fd);
#endif
	return nil;
    }

    if (::listen(fd, SOMAXCONN) < 0) {
	sys_error("rpcbuf::listen: listen");
#ifdef WIN32
        closesocket( fd);
        int ok=WSAGetLastError(); 
        if (ok < 0)
           perror("rpcbuf::listen: close");
#else
        ::close(fd);
#endif
	return nil;
    }

    _host = nil;
    _port = port;
    _fd = fd;
    _opened = true;
    _close = true;
    _nonblocking = false;
    return this;
}

// Create a socket and make a connection to another socket.  Do
// retries in case the peer has hit its backlog, but not too many
// retries since the error is indistinguishable from there not being a
// peer listening at all.

rpcbuf* rpcbuf::connect(const char* host, int port) {
   DEBUGNL ("rpcbuf::connect()") ;
    struct sockaddr_in name;
    ::memset (&name, 0, sizeof(name)) ;
    name.sin_family = AF_INET;
    name.sin_port = htons(port);

    if (_opened) {
       DEBUGNL ("rpcbuf::connect: already using a file number") ;
       return nil;
    }

    const unsigned long INVALIDADDR = (unsigned long) -1;
    unsigned long hostinetaddr = INVALIDADDR;
    if (isascii(host[0]) && isdigit(host[0])) {
#ifdef AIXV3
	/* cast work-around for AIX */
	hostinetaddr = inet_addr((const int*)host);
#else
	hostinetaddr = inet_addr(host);
#endif
	name.sin_addr.s_addr = hostinetaddr;
    }
    if (hostinetaddr == INVALIDADDR) {
	/* cast to workaround bug in prototype on some systems */
	struct hostent* hp = gethostbyname((char*)host) ;
	if (hp == nil) {
	    error("rpcbuf::connect: gethostbyname: no such host");
	    return nil;
	}
	if (hp->h_addrtype != AF_INET) {
           DEBUGNL ("rpcbuf::connect: gethostbyname: not an Internet host") ;
           return nil;
	}
	::memcpy (&name.sin_addr, hp->h_addr, sizeof(name.sin_addr));
    }

    int fd = -1;
    int retries = 2;
    do {
	fd = ::socket(AF_INET, SOCK_STREAM, 0);
#ifdef WIN32
  if (fd==INVALID_SOCKET) {
#else
	if (fd < 0) {
#endif
           DEBUGNL ("rpcbuf::connect(): ::socket failed") ;
           sys_error("rpcbuf::connect: socket");
           return nil;
	}
	if (::connect(fd, (struct sockaddr*)&name, sizeof(name)) < 0) {
	    if (errno == ECONNREFUSED && retries > 0) {
               // try again since peer's backlog may just be full
#ifdef WIN32
               closesocket( fd);
               int ok=WSAGetLastError(); 
               if (ok < 0)
                  perror("rpcbuf::listen: close");
#else
               ::close(fd);
#endif
               sleep(1);
               continue;
	    } else {
               DEBUGNL ("rpcbuf::connect(): ::connect() failed") ;
               sys_error("rpcbuf::connect(): ::connect()");
#ifdef WIN32
                closesocket( fd);
                int ok=WSAGetLastError(); 
                if (ok < 0)
                   perror("rpcbuf::listen: close");
#else
                ::close(fd);
#endif
                return nil;
	    }
	}
	break;			// the connection succeeded
    } while (retries-- > 0);

    _host = host;
    _port = port;
    _fd = fd;
    _opened = true;
    _close = true;
    _nonblocking = false;
    return this;
}

// Accept an incoming connection, allocate a new file descriptor for
// it, and return the new file descriptor.

rpcbuf* rpcbuf::accept(int& fd) {
    struct sockaddr_in name;
    int name_len = sizeof(name);

    if (!_opened) {
       DEBUGNL ("rpcbuf::accept(): not using a file number yet") ;
       return nil;
    }

    fd = ::accept(_fd, (struct sockaddr*)&name, &name_len);
#ifdef WIN32
  if (fd==INVALID_SOCKET) {
#else
	if (fd < 0) {
#endif
       DEBUGNL ("rpcbuf::accept(): ::accept() failed") ;
       sys_error ("rpcbuf::accept(): ::accept()");
       return nil;
    }

    return this;
}

// Attach the streambuf to a file descriptor.  The streambuf cannot
// close the file descriptor because something else might be using it.

rpcbuf* rpcbuf::attach(int fd) {
    if (_opened) {
       DEBUGNL ("rpcbuf::attach: already using a file number") ;
       return nil;
    }

#ifdef WIN32
  if (fd==INVALID_SOCKET) {
#else
	if (fd < 0) {
#endif
       DEBUGNL ("rpcbuf::attach: cannot use a negative file number") ;
       return nil;
    }

    _host = nil;
    _port = 0;
    _fd = fd;
    _opened = true;
    _close = false;
    _nonblocking = false;
    return this;
}

// Empty the get/put areas, close the file descriptor if nothing else
// might use it, and detach the streambuf from the file descriptor.

rpcbuf* rpcbuf::close() {
    if (!_opened) {
	return nil;
    }

    sync();

    int ok = 0;
    if (_close) {
#ifdef WIN32
       closesocket(_fd);
       ok=WSAGetLastError(); 
#else
       ok=::close(_fd);
#endif
       if (ok < 0) {
          sys_error("rpcbuf::close: close");
       }
    }

    _host = nil;
    _port = 0;
    _fd = -1;
    _opened = false;
    _close = false;
    _nonblocking = false;
    return (ok < 0) ? nil : this;
}

// Set or clear non-blocking I/O on the file descriptor.
#ifdef WIN32
rpcbuf* rpcbuf::nonblocking(boolean nonblocking)
{
  u_long ulParami=(u_long)nonblocking;
  if(!_opened)
  {
     DEBUGNL ("rpcbuf::nonblocking: not using a file number yet") ;
     return nil;
  }

  if(_nonblocking!=nonblocking)
  {
    // set socket state
    if(ioctlsocket(_fd,FIONBIO,(u_long *)&ulParami)==SOCKET_ERROR)
    {
	    sys_error("rpcbuf::nonblocking: F_GETFL fcntl");
	    return nil;
	  }
  }


  _nonblocking = nonblocking;
  return this;
}
#else
rpcbuf* rpcbuf::nonblocking(boolean nonblocking) {
    if (!_opened) {
       DEBUGNL ("rpcbuf::nonblocking: not using a file number yet") ;
       return nil;
    }

    if (_nonblocking != nonblocking) {
	int flags = fcntl(_fd, F_GETFL, 0);
	if (flags < 0) {
	    sys_error("rpcbuf::nonblocking: F_GETFL fcntl");
	    return nil;
	}
	if (nonblocking) {
	    flags |= O_NDELAY;
	} else {
	    flags &= ~O_NDELAY;
	}
	if (fcntl(_fd, F_SETFL, flags) < 0) {
	    sys_error("rpcbuf::nonblocking: F_SETFL fcntl");
	    return nil;
	}
    }

    _nonblocking = nonblocking;
    return this;
}
#endif /*WIN32 */
// Set or clear printing of system error messages.

rpcbuf* rpcbuf::setVerbose(boolean v) {
   verbose = v;
   return this;
}

// Finish the current request, if any, and then start a new request.
// The request begins with a length field, so insert a zero with
// enough padding characters (if using formatted I/O) to overwrite the
// zero later with 2**32 - 1, the largest possible length for a
// request (the length must fit in an int).

const int FIELDWIDTH = 11;	// large enough to hold "2147483647 "

int rpcbuf::start_request() {
    if (!_mystream || !_opened || allocate() == EOF) {
       return EOF;
    }

    finish_request();
    setr(pptr());

    const int length = 0;
    mystream().width(FIELDWIDTH);
    mystream() << length;
    _actualWidth = pptr() - rptr();

    return 0;
}

// Finish the current request by inserting its final length in the
// length field at the beginning of the request (overwriting the old
// value).  The length of the request includes its own length field.

void rpcbuf::finish_request() {
    int length = pptr() - rptr();
    if (rptr() && length > 0) {
	pbump(-length);
	mystream().width(FIELDWIDTH);
	mystream() << length;
	if (_actualWidth != pptr() - rptr()) {
           DEBUGNL ("rpcbuf::finish_request(): length field\'s width changed") ;
	}
	pbump(length - (pptr() - rptr()));
    }
    setr(nil);
}

// Calculate the width of the length field (_actualWidth) and check
// that all of the length field is in the buffer.  If so, extract the
// length, move the get pointer back to the beginning of the request,
// and return 0 if all of the request is in the buffer.  Otherwise,
// return EOF.  By checking for EOF, the caller avoids extracting a
// request before it's completely buffered.  The caller must call
// select() to wait for new input and call rpcbuf::underflow() to
// enqueue it until the complete request is buffered.

int rpcbuf::read_request() {
    if (!_mystream) {
       DEBUGNL ("rpcbuf::read_request(): !_mystream") ;
       return EOF;
    }

    if (!_actualWidth) {
	char* orig = pptr();
	const int length = 0;
	mystream().width(FIELDWIDTH);
	mystream() << length;
	_actualWidth = pptr() - orig;
	pbump(orig - pptr());
    }

    int navail = in_avail();
    if (navail < _actualWidth) {
       // do not DEBUGNL since this is a common case
       // DEBUGNL ("rpcbuf::read_request(): navail < _actualWidth") ;
       return EOF;
    }

    char* orig = gptr();
    int length = 0;
    mystream() >> length;
    gbump(orig - gptr()); // reset gptr()

    if (length <= 0) {
       DEBUGNL ("rpcbuf::read_request(): zero or negative length") ;
       return EOF;
    }

    // make the get area large enough to hold twice the request
    // size (shouldn't simply the size be enough? ++++)
    if (length > ebuf() - eback() && !expand_g(length * 2)) {
       DEBUGNL ("rpcbuf::read_request(): out of memory") ;
       return EOF;
    }

    if (navail < length) {
       DEBUGNL ("rpcbuf::read_request(): navail < length") ;
       return EOF;
    } else {
       return 0;
    }
}

// Finish the current RPC request if there's nothing to append to it,
// thus allowing flush to send the current RPC request.  Send any
// outgoing data using a loop to safeguard against partial writes.
// Shift any still incomplete RPC request to the beginning of the put
// area to make room for more data.  Append the overflow char if any.

int rpcbuf::overflow(int c) {
    if (!_opened || allocate() == EOF) {
	return EOF;
    }

    // c == EOF means ostream::flush() has been called. have to finish the
    // current request (insert the length field) if any.
    if (c == EOF) {
	finish_request();
    }

//     // jfasch 20.3.1995: what if the request does *not* start at pbase()? Might
//     // be the case if a prior request was not terminated by flush(), but by a new
//     // request. Then the old request remains in the put area as regular stream
//     // data (finish_request() does neither ostream::flush() nor ostream::sync().
//     if (rptr() >= pbase() && pptr() >= epptr() && !expand_p()) {

    if (rptr() == pbase() && pptr() >= epptr() && !expand_p()) {
       DEBUGNL ("rpcbuf::overflow(): out of memory") ;
       return EOF;
    }

    // write any data in the put area except for the current request (if any).
    int nwrite = (rptr() >= pbase()) ? rptr() - pbase() : out_waiting();
    int count = 0, blockcount = 30;                                // kandrews
    while (count < nwrite) {
       // jfasch 24 Apr 1995:
       // changed from system write() to a loop to prevent from being
       // damaged by a signal
       int nsent = rpc_write(_fd, pbase() + count, nwrite - count); 
       if (nsent < 0) {             // try every sec. for 30 secs.
#ifdef WIN32
          if (WSAGetLastError()==WSAEWOULDBLOCK && --blockcount)
          {
#else
          if ( errno == EWOULDBLOCK && --blockcount )
          {          // kandrews
#endif
             sleep(1);                                            // kandrews
             continue;                                            // kandrews
          }                                                      // kandrews
          DEBUGNL ("rpcbuf::overflow(): too many non-blocking writes") ;
          sys_error ("rpcbuf::overflow(): write");
          return EOF;
       }
       // jfasch 23 Feb 1995: error only if 30 consecutive (would-)
       // blocking writes occured -->
       blockcount = 30 ;
       // <--
       count += nsent;
    }

    // if there is a request in the put area, and there were any
    // regular data preceding that, copy the request down to the
    // beginning of the area and adjust the request pointer.
    if (rptr() > pbase()) {
       ::memcpy (pbase(), rptr(), pptr() - rptr());
       rbump(-nwrite);
    }

    // adjust the put pointer (should probably be: if (nwrite) ... ++++)
    pbump(-nwrite);

    // append the overflow character
    if (c != EOF) {
	sputc(c);
    }

    return zapeof(c);
}

// Empty the put area before filling the get area in case the input
// depends on the output just flushed.  Move any unread data between
// gptr() and egptr() to the beginning of the get area.  The get area
// may contain unread data under nonblocking I/O because an incomplete
// RPC request is not extracted until the rest of its data arrives.
// Read as much data as available into the free space between egptr()
// and ebuf() (the get area occupies the entire buffer).  Move egptr()
// to the end of the new data.  Return the first unread character.

int rpcbuf::underflow() {
    if (!_opened || allocate() == EOF) {
	return EOF;
    }

    // Empty the put area before filling the get area in case the input depends
    // on the output just flushed.
    if (overflow() == EOF) {
       DEBUGNL ("rpcbuf::underflow(): overflow() faild") ;
       return EOF;
    }

    // copy any unread data to the beginning of the get area to make
    // space for the data to be read below ...
    int nunread = in_avail();
    if (nunread) {
#ifdef WIN32
       return (unsigned char) *gptr();
#else
       memcpy (eback(), gptr(), nunread);
#endif
    }
    // .. and adjust the istream pointers accordingly.
    setg(eback(), eback(), eback() + nunread);

//    int nread = read(_fd, egptr(), ebuf() - egptr());       // kandrews
//    if (nread < 0) {
//	sys_error("rpcbuf::underflow: read");
//	return EOF;
//    }

    // fill the rest of the get area (at least a part of it).
    int nread, blockcount = 30;                                     // kandrews

    // jfasch 24 Apr 1995:
    // changed from system read() to a loop to prevent from being
    // damaged by a signal
    while(0 > (nread = rpc_read(_fd, egptr(), ebuf() - egptr()))) {
#ifdef WIN32
      if(WSAGetLastError()==WSAEWOULDBLOCK && --blockcount) 
      {
#else
      if (errno == EWOULDBLOCK && --blockcount)
      {    // try every sec. for 30 secs.
#endif
	sleep(1);
	continue;
      }
      DEBUGNL ("rpcbuf::underflow(): too many non-blocking ::read()s") ;
      sys_error ("rpcbuf::underflow: read") ;
      return EOF;
    }                                                               // kandrews

    if (nread == 0) {
       return EOF;
    }

    setg(eback(), gptr(), egptr() + nread);
    return zapeof(*gptr());
}

#ifdef WIN32
int rpcbuf::readon() 
{
    if (!_opened || allocate() == EOF) 
        return EOF;

    if (overflow() == EOF) 
        return EOF;

    int nunread = in_avail();
    if (nunread) 
      memcpy (eback(), gptr(), nunread);
    setg(eback(), eback(), eback() + nunread);

    int nread;

   while(0 > (nread = rpc_read(_fd, egptr(), ebuf() - egptr())))
   {
        int err = WSAGetLastError();
        if (err == WSAEWOULDBLOCK) 
        {
            cerr << "EWOULDBLOCK in read";
            Sleep(1);
            continue;
        }
        char buf[80];
        sprintf(buf, "rpcbuf::readon: read (%d) ", err);
        sys_error(buf);
        return EOF;
    }

    if ( (nread == 0) && (nunread==0) )
    {
        setg(0, 0, 0);
        return EOF;
    }

    setg(eback(), gptr(), egptr() + nread);
    return (unsigned char)*gptr();
}
#endif

// Probably called from ios's destructor.  Can't do anything with
// still unread data except discard it, but can flush any outgoing RPC
// requests from the put area.

int rpcbuf::sync() {
/* gpani: (16.3.94) -- gnu has 'normal' streams */
/* gorasche: WinNT too! */
#if (defined(__GNUC__) || defined(WIN32))
     if (out_waiting()) {
	  if (!_opened || allocate() == EOF)
	       return EOF;

	  finish_request();
	  
//           // jfasch 20.3.1995 (for an explanation see overflow())
// 	  if (rptr() >= pbase() && pptr() >= epptr() && !expand_p()) {
          // (not changed)

	  if (rptr() == pbase() && pptr() >= epptr() && !expand_p()) {
             DEBUGNL ("rpcbuf::sync(): out of memory") ;
             return EOF;
	  }
	  
	  int nwrite = (rptr() >= pbase()) ? rptr() - pbase() : out_waiting();
	  int count = 0, blockcount = 30;
	  while (count < nwrite) {
             // jfasch 24 Apr 1995:
             // changed from system write() to a loop to prevent from being
             // damaged by a signal
             int nsent = rpc_write(_fd, pbase() + count, nwrite - count);
             if (nsent < 0) {
#ifdef WIN32
                if(WSAGetLastError()==WSAEWOULDBLOCK && --blockcount) {
#else
                if (errno == EWOULDBLOCK && --blockcount ) {
#endif
                   sleep(1);
                   continue;
                }
                DEBUGNL ("rpcbuf::sync(): too many non-blocking ::write()s") ;
                sys_error("rpcbuf::sync(): write");
                return EOF;
             }
             count += nsent;
	  }

          // if there is a request in the put area, and there were any
          // regular data preceding that, copy the request down to the
          // beginning of the area and adjust the request
          // pointer. (see also in overflow().)
	  if (rptr() > pbase()) {
	       ::memcpy (pbase(), rptr(), pptr() - rptr());
	       rbump(-nwrite);
	  }
	  pbump(-nwrite);
     }
     return 0;
#else
    gbump(in_avail());
    return out_waiting() ? overflow() : 0;
#endif
}

// Can't seek on a socket, but can return the get pointer's current
// position so that the caller can find out how many bytes he read
// since the get pointer's last position (within the same request).

#if (defined(cplusplus_2_1) || defined(WIN32))
streampos rpcbuf::seekoff(streamoff offset, ios::seek_dir dir, int mode) {
#else
streampos rpcbuf::seekoff(streamoff offset, seek_dir dir, int mode) {
#endif
    if (!_opened || !gptr()) {
	return EOF;
    }

    if (offset != 0 || dir != ios::cur || mode != ios::in) {
	return EOF;
    }

    return (streampos)gptr();
}

// Refuse any attempt to set the buffers for storing incoming and
// outgoing RPC requests because we need the ability to dynamically
// expand the buffers' sizes.

streambuf* rpcbuf::setbuf(char*, int) {
    return nil;
}

// Dynamically allocate two separate buffers for storing incoming and
// outgoing RPC requests.  Allocating separate buffers for the get and
// put areas makes it easier to expand either area later if necessary.

// int rpcbuf::doallocate() {
//     const int RPCBUFSIZE = 2032;

//     char* get = new char[RPCBUFSIZE];
//     if (!get) {
// 	error("rpcbuf::doallocate: out of memory");
// 	return EOF;
//     }
//     setb(get, get + RPCBUFSIZE, true);
//     setg(get, get, get);

//     char* put = new char[RPCBUFSIZE];
//     if (!put) {
// 	error("rpcbuf::doallocate: out of memory");
// 	return EOF;
//     }
//     setp(put, put + RPCBUFSIZE);
//     setr(nil);

//     return 0;
// }
int rpcbuf::doallocate() {
    const int RPCBUFSIZE = 2032;

    char* get = new char[RPCBUFSIZE];
    if (!get) {
	error("rpcbuf::doallocate: out of memory");
	return EOF;
    }
    setb(get, get + RPCBUFSIZE, false);
    setg(get, get, get);

    char* put = new char[RPCBUFSIZE];
    if (!put) {
	error("rpcbuf::doallocate: out of memory");
	return EOF;
    }
    setp(put, put + RPCBUFSIZE);
    setr(nil);

    return 0;
}

// Expand the get area to make room for a large incoming request.

boolean rpcbuf::expand_g(int newsize) {
    char* get = new char[newsize];
    if (!get) {
	return false;
    }

    int navail = in_avail();
    ::memcpy (get, gptr(), navail);
// --------- in rpcbuf::doallocate    setb(get, get + RPCBUFSIZE, true);                  // kandrews
// ---------    true means automatically delete when setb( ) or ~streambuf() is called 
//     delete eback();

    // equals the get area, I think. I pass false ("dont delete it")
    // to setb() on every occasion, so I have to do it here.
    delete base() ; 

    setb(get, get + newsize, false); // you see? false!
    setg(get, get, get + navail);

    return true;
}

// Expand the put area to make room for additional data to be appended
// to an outgoing request.

boolean rpcbuf::expand_p() {
    int newsize = (epptr() - pbase()) * 2;
    char* put = new char[newsize];
    if (!put) {
	return false;
    }

//    // jfasch 20.3.1995 (see below): the length of the regular stream data
//    // preceding the current request:
//    int reg_datalen = (rptr() >= pbase()) ?  rptr() - pbase() :  0 ;

    // out_waiting() is the number of chars between pbase() and pptr()
    int nwaiting = out_waiting();
    // copy the old put area (i.e., the interesting bytes) to the new one
    ::memcpy (put, pbase(), nwaiting);
    delete pbase();
    // set pbase() and pptr() to the beginning of the new put area (pbump follows
    // below), and epptr() to the end of it.
    setp(put, put + newsize);
    // adjust pptr()
    pbump(nwaiting);
    // the request begins at the beginning of the put area
    setr(put);

//     // jfasch 20.3.1995: but what if there was some regular stream data preceding
//     // the request in the old put area? (if (reg_datalen) ... ++++)
//     rbump (reg_datalen) ;

    return true;
}

// Print a user error message.

void rpcbuf::error(const char* msg) {
    if (verbose) {
	cerr << msg << "\n";
	cerr.flush();
    }
}

// Print a system error message.

void rpcbuf::sys_error(const char* msg) {
    if (verbose) {
	perror(msg);
    }
}

// gorasche 020496
#ifdef WIN32
int rpcbuf :: rpc_read (int fd, char* buf, int len) {
   int nread ;
   while((nread=::recv(fd,buf,len,0)) < 0 && WSAGetLastError()==WSAEINTR);
   return nread ;
}
int rpcbuf :: rpc_write (int fd, const char* buf, int len) {
   int nwritten ;
   while((nwritten=::send(fd,buf,len,0)) < 0 && WSAGetLastError()==WSAEINTR);
   return nwritten ;
}
#else
// jfasch 24 Apr 1995
int rpcbuf :: rpc_read (int fd, char* buf, int len) {
   int nread ;
   while ((nread = ::read (fd, buf, len)) < 0  &&  ::errno == EINTR) ;
   return nread ;
}
int rpcbuf :: rpc_write (int fd, const char* buf, int len) {
   int nwritten ;
   while ((nwritten = ::write (fd, buf, len)) < 0  &&  ::errno == EINTR) ;
   return nwritten ;
}
#endif
