// $Header: /usr/u/moudgill/project/Socket/RCS/sstream.C,v 1.13 1992/10/02 18:35:17 moudgill Exp moudgill $
// 17th December, 1991. Mayan Moudgill.

static const char rcsid[] = "$Id: sstream.C,v 1.13 1992/10/02 18:35:17 moudgill Exp moudgill $";

#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netdb.h>
#ifdef __linux__
#include <unistd.h>
#ifndef SOMAXCONN
#define SOMAXCONN	8
#endif
#undef SO_LINGER
#else
#include <osfcn.h>
#include <libc.h>
#endif
#include <fcntl.h>
#include <signal.h>
#include <errno.h>

#include "sstream.h"

#ifdef USE_SIG_PF
typedef SIG_PF signal_arg2;
#else
typedef void (*signal_arg2) (int);
#endif

static selset   null_set;

int             sstream::traceindent;
sstream *       sockbuf::streams[FD_SETSIZE];

int                            sstream::showtrace     = 0;
int                            sstream::showerror     = 0;
int                            sstream::showmonitor   = 0;
int                            sstream::showsyscall   = 0;

inline void                    sstream::traceon(char * c)
{
   if( tracecontrol && showtrace ) {
#if defined(SIG_BLOCK) && defined(SIG_SETMASK)
      sigset_t set, oset;
      sigemptyset (&set);
      sigaddset (&set, SIGIO);
      sigaddset (&set, SIGURG);
      sigaddset (&set, SIGPIPE);
      sigprocmask (SIG_BLOCK, &set, &oset);
#else
      int mask = sigblock(sigmask(SIGIO)|sigmask(SIGURG)|sigmask(SIGPIPE));
#endif
      traceindent += 3;
      for(int i=0; i< traceindent; i++) {
         cerr << " ";
      }
      cerr << "ENTERING "<< c <<"\n";
#if defined(SIG_BLOCK) && defined(SIG_SETMASK)
   sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL);
#else
   sigsetmask(mask);
#endif
   }
}

inline void                    sstream::traceoff(char * c)
{
   if( tracecontrol && showtrace) {
#if defined(SIG_BLOCK) && defined(SIG_SETMASK)
      sigset_t set, oset;
      sigemptyset (&set);
      sigaddset (&set, SIGIO);
      sigaddset (&set, SIGURG);
      sigaddset (&set, SIGPIPE);
      sigprocmask (SIG_BLOCK, &set, &oset);
#else
      int mask = sigblock(sigmask(SIGIO)|sigmask(SIGURG)|sigmask(SIGPIPE));
#endif
      for(int i=0; i< traceindent; i++) {
         cerr << " ";
      }
      cerr << "EXITING  "<< c <<"\n";
      traceindent -= 3;
#if defined(SIG_BLOCK) && defined(SIG_SETMASK)
   sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL);
#else
   sigsetmask(mask);
#endif
   }
}

inline void                   sstream::error(int sd, unsigned int bit)
{
   if( errorcontrol && showerror ) {
      cerr << "ERROR : stream #" << sd << " 0x" << ::hex << bit << ::dec
            << " " << errno << "\n";
   }

   if( sd >= 0 && sd < FD_SETSIZE && streams[sd] != 0) {
      sstream& s = *streams[sd];
      s.clear( s.rdstate() | ios::badbit);
      s._serror |= bit;
      s._serrno  = errno;
   }
}

inline void                    sstream::fail(int sd, unsigned int bit)
{
   if( errorcontrol && showerror ) {
      cerr << "FAIL : stream #" << sd << " 0x" << ::hex << bit << ::dec
            << " " << errno << "\n";
   }
   if( sd >= 0 && sd < FD_SETSIZE && streams[sd] != 0) {
      sstream& s = *streams[sd];
      s._serror |= bit;
      s._serrno  = errno;
   }

}

inline void                    sstream::monitor(char * s, int c)
{
    if( monitorcontrol && showmonitor ) {
       cerr << "MONITOR " << s << " " << c << "\n";
    }
}

inline int                     sstream::syscall(int e, char * s)
{
    sstream::traceon(s);
    if( e == -1 ) {
       if( syscallcontrol && showsyscall ) {
           cerr << "SYSCALL " << s << " " << errno << "\n";
           if( errno <= sys_nerr ) {
              cerr << "        " << sys_errlist[errno];
           }
       }
    }
    sstream::traceoff(s);
    return e;
}

selset                         sockbuf::_sockets;
sockbuf *                      sockbuf::handlealloc()
{
   sstream::traceon("sockbuf * sockbuf::handlealloc()");
   if( unbuffered() ) {
      setg( sunbuf, sunbuf+1, sunbuf+1);
   }
   else {
      if( allocate() == EOF ) {
         sstream::traceoff("sockbuf * sockbuf::handlealloc() : could not allocate");
         return 0;
      }
   }
   sstream::traceoff("sockbuf * sockbuf::handlealloc()");
   return this;
}

int                            sockbuf::doallocate()
{
char *   c = new char[2*bufsize];

   sstream::traceon("int sockbuf::doallocate()");
   if( c == 0 ) {
   sstream::traceoff("int sockbuf::doallocate() : new failed");
      return EOF;
   }
   else {
      setb(c, c + 2*bufsize, 1);
      setp(c, c + bufsize);
      setg(c + bufsize, c + 2*bufsize, c + 2*bufsize);
   sstream::traceoff("int sockbuf::doallocate()");
      return 0;
   }
}

int                            sockbuf::overflow(int i )
{
   sstream::traceon("int sockbuf::overflow(int)");
   if( !(_sstate & sstream::openedbit) ) {
      sstream::fail(_sd, sstream::openbit);
      sstream::traceoff("int sockbuf::overflow(int)");
      return EOF;
   }

   if( unbuffered() ) {
      char   c = i;
      if( i != EOF && send(_sd, &c, sizeof(char), 0) == -1 ) {
         sstream::fail(_sd, sstream::sendbit);
         sstream::traceoff("int sockbuf::overflow(int) : unbuffered error");
         return EOF;
      }
      sstream::monitor("sockbuf::overflow() : transmitting ", i);
      sstream::traceoff("int sockbuf::overflow(int) : unbuffered normal");
      return 0;
   }
   if( pptr() > pbase() ) {
      sstream::monitor("sockbuf::overflow() : transmiting #",
	    pptr() - pbase());
      for(char * p = pbase(); p < pptr(); p++) {
         sstream::monitor("sockbuf:overflow() : transmiting ", *p);
      }
      if( send(_sd, pbase(), pptr() - pbase(), 0) == -1 ) {
         sstream::fail(_sd, sstream::sendbit);
         sstream::traceoff("int sockbuf::overflow(int) : buffered error");
         return EOF;
      }
      setp( pbase(), epptr() );
   }
   if( i != EOF ) {
      if( sputc(i) == EOF ) {
         sstream::fail(_sd, sstream::otherbit);
         sstream::traceoff("int sockbuf::overflow(int) : buffered error");
         return EOF;
      }
      sstream::monitor("sockbuf::overflow() : inserting ", *pptr());
   }
   sstream::traceoff("int sockbuf::overflow(int) : buffered normal");
   return 0;
}

int                            sockbuf::underflow()
{
char   buf[bufsize];

   sstream::traceon("int sockbuf::underflow()");
   if( !(_sstate & sstream::openedbit) ) {
      sstream::fail(_sd, sstream::openbit);
      sstream::traceoff("int sockbuf::underflow()");
      return EOF;
   }

   if( unbuffered() ) {
      setg(sunbuf, sunbuf, sunbuf+1);
      if( recv(_sd, sunbuf, sizeof(char), 0) == -1 ) {
         sstream::fail(_sd, sstream::recvbit);
         sstream::traceoff("int sockbuf::underflow() : unbuffered error");
         return EOF;
      }
      sstream::monitor("sockbuf::underflow() : recieving ", sunbuf[0]);
      sstream::traceoff("int sockbuf::underflow() : unbuffered normal");
      return sunbuf[0];
   }
   else if( egptr() > gptr() ) {
      sstream::traceoff("int sockbuf::underflow() : buffered full");
      return *gptr();
   }
   else {
      int      n;
      if( (n= recv(_sd, buf, bufsize, 0)) == -1) {
         sstream::fail(_sd, sstream::recvbit);
         sstream::traceoff("int sockbuf::underflow() : buffered error");
         return EOF;
      }
      sstream::monitor("sockbuf::underflow() : recieving #", n);
      char *   p = gptr() - n;
      int      i;
      for( i = 0; i < n; i++) {
         sstream::monitor("sockbuf::underflow() : recieving ", buf[i]);
         *p++ = buf[i];
      }
      gbump(-n);

      sstream::traceoff("int sockbuf::underflow() : buffered normal");
      return *gptr();
   }
}

streambuf *                    sockbuf::setbuf(char * p, int l)
{

   sstream::traceon("streambuf * sockbuf::setbuf(char *, int)");
   if( p == 0 || l <= 0) {
      unbuffered(1);
   sstream::traceoff("streambuf * sockbuf::setbuf(char *, int)");
      return this;
   }
   else {
      int   h = l/2;
      setb(p, p + l, 0);
      setp(p, p +h);
      setg(p + h, p + l, p + l);
      unbuffered(0);
   sstream::traceoff("streambuf * sockbuf::setbuf(char *, int)");
      return this;
   }
}

int                            sockbuf::sync()
{
char   buf[bufsize];
long   toread;

   if( !unbuffered() ) {
      sstream::syscall(ioctl(_sd, FIONREAD, &toread),
	    "sockbuf::sync():ioctl(FIONREAD)");

      while(toread > 0) {
      int  n;
	 if( (n= recv(_sd, buf, bufsize, 0)) == -1) {
	    sstream::fail(_sd, sstream::recvbit);
	    sstream::traceoff("int sockbuf::sync() : buffered error");
	    return EOF;
	 }
	 sstream::monitor("sockbuf::sync() : recieving #", n);
      char *   p = gptr() - n;
      int      i;
	 for( i = 0; i < n; i++) {
	    sstream::monitor("sockbuf::sync() : recieving ", buf[i]);
	    *p++ = buf[i];
	 }
	 gbump(-n);

	 sstream::syscall(ioctl(_sd, FIONREAD, &toread),
		  "sockbuf::sync():ioctl(FIONREAD)");
      }
   }

   if( pptr() > pbase() ) {
      return overflow(EOF);
   }

   return 0;
}

sockbuf *                      sockbuf::open(int port)
{
sockaddr_in   addr;
char          hostname[MAXHOSTNAMELEN+1];
hostent *     hostinfo;
int           opt;

   sstream::traceon("sockbuf * sockbuf::open(int)");
   if( _sstate & sstream::openedbit ) {
      sstream::fail(_sd, sstream::openbit);
      sstream::traceoff("sockbuf * sockbuf::open(int)");
      return 0;
   }
   if( (_sd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ) {
      sstream::fail(_sd, sstream::socketbit);
      sstream::traceoff("sockbuf * sockbuf::open(int)");
      return 0;
   }
   
   opt = 1;
   sstream::syscall(setsockopt(_sd, SOL_SOCKET, SO_REUSEADDR,
	 (char *) &opt, sizeof(int)),
	 "sockbuf::open():setsockopt(SO_REUSEADDR)");

   opt = 1;
   sstream::syscall(setsockopt(_sd, SOL_SOCKET, SO_KEEPALIVE,
	 (char *) &opt, sizeof(int)),
	 "sockbuf::open():setsockopt(SO_KEEPALIVE)");

   gethostname(hostname, MAXHOSTNAMELEN);
   if( (hostinfo = gethostbyname(hostname)) == 0 ) {
      _myaddr = INADDR_ANY;
   }
   else {
      _myaddr = ntohl(((in_addr *)(hostinfo->h_addr))->s_addr);
   }
   _myport = port;

   addr.sin_addr.s_addr = INADDR_ANY;
   addr.sin_port       = htons(_myport);
   addr.sin_family     = AF_INET;

   if( bind(_sd, (sockaddr *) &addr, sizeof(addr)) ) {
      sstream::fail(_sd, sstream::bindbit);
      close();
      sstream::traceoff("sockbuf * sockbuf::open(int)");
      return 0;
   }

   if( listen(_sd, SOMAXCONN) ) {
      sstream::fail(_sd, sstream::listenbit);
      close();
      sstream::traceoff("sockbuf * sockbuf::open(int)");
      return 0;
   }

   if( handlealloc() == 0 ) {
      sstream::fail(_sd, sstream::allocbit);
      close();
      sstream::traceoff("sockbuf * sockbuf::open(int)");
      return 0;
   }
   
   _sstate |= sstream::openedbit | sstream::serverbit;
   streams[_sd] = _stream;
   _sockets += _sd;

   sstream::traceoff("sockbuf * sockbuf::open(int)");
   return this;
}

sockbuf *                      sockbuf::open(char * toname, int port)
{
sockaddr_in   addr;
char          hostname[MAXHOSTNAMELEN+1];
hostent *     hostinfo;
int           opt;

   sstream::traceon("sockbuf * sockbuf::open(char *, int)");
   if( _sstate & sstream::openedbit ) {
      sstream::fail(_sd, sstream::openbit);
      sstream::traceoff("sockbuf * sockbuf::open(char *, int)");
      return 0;
   }
   if( (_sd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ) {
      sstream::fail(_sd, sstream::socketbit);
      sstream::traceoff("sockbuf * sockbuf::open(char *, int)");
      return 0;
   }
   
   opt = 1;
   sstream::syscall(setsockopt(_sd, SOL_SOCKET, SO_REUSEADDR,
	 (char *) &opt, sizeof(int)),
	 "sockbuf::open():setsockopt(SO_REUSEADDR)");

   opt = 1;
   sstream::syscall(setsockopt(_sd, SOL_SOCKET, SO_KEEPALIVE,
	 (char *) &opt, sizeof(int)),
	 "sockbuf::open():setsockopt(SO_KEEPALIVE)");

   gethostname(hostname, MAXHOSTNAMELEN);
   if( (hostinfo = gethostbyname(hostname)) == 0 ) {
      _myaddr = INADDR_ANY;
   }
   else {
      _myaddr = ntohl(((in_addr *)(hostinfo->h_addr))->s_addr);
   }

   if( (hostinfo = gethostbyname(toname)) == 0 ) {
      _sstate |= sstream::addrbit;
   sstream::traceoff("sockbuf * sockbuf::open(char *, int)");
      return 0;
   }
   _toaddr = ntohl(((in_addr *)(hostinfo->h_addr))->s_addr);
   _toport = port;

   addr.sin_addr.s_addr = htonl(_toaddr);
   addr.sin_port       = htons(_toport);
   addr.sin_family     = AF_INET;
   if( connect(_sd, (sockaddr *) &addr, sizeof(addr)) == -1 ) {
      sstream::fail(_sd, sstream::connectbit);
      sstream::traceoff("sockbuf * sockbuf::open(char *, int)");
      return 0;
   }

   if( handlealloc() == 0 ) {
      sstream::fail(_sd, sstream::allocbit);
      close();
      sstream::traceoff("sockbuf * sockbuf::open(char *, int)");
      return 0;
   }
   
   _sstate |= sstream::openedbit | sstream::clientbit;
   streams[_sd] = _stream;
   _sockets += _sd;

   sstream::traceoff("sockbuf * sockbuf::open(char *, int)");
   return this;
} 

sockbuf *                      sockbuf::accept(sockbuf& ser)
{
sockaddr_in   addr;
int           len= sizeof(addr);

   sstream::traceon("sockbuf * sockbuf::accept(sockbuf&)");
   if( !(ser._sstate & sstream::openedbit) ||
        !(ser._sstate & sstream::serverbit) ||
        (_sstate & sstream::openedbit) ) {
      sstream::fail(_sd, sstream::openbit);
      sstream::traceoff("sockbuf * sockbuf::accept(sockbuf&)");
      return 0;
   }

   if( (_sd = ::accept(ser._sd, (sockaddr *) &addr, &len)) == -1 ) {
      sstream::fail(_sd, sstream::acceptbit);
      sstream::traceoff("sockbuf * sockbuf::accept(sockbuf&)");
      return 0;
   }
   _toaddr = ntohl(addr.sin_addr.s_addr);
   _toport = ntohs(addr.sin_port);
   _myaddr = ser._myaddr;
   _server = ser._sd;

   if( handlealloc() == 0 ) {
      sstream::fail(_sd, sstream::allocbit);
      close();
      sstream::traceoff("sockbuf * sockbuf::accept(sockbuf&) : handlealloc failed");
      return 0;
   }
   
   _sstate |= sstream::openedbit | sstream::serverdupbit;
   streams[_sd] = _stream;
   _sockets += _sd;
   
   sstream::traceoff("sockbuf * sockbuf::accept(sockbuf&)");
   return this;
}

sockbuf *                      sockbuf::close()
{
char     buf[bufsize];
   
   sstream::traceon("sockbuf * sockbuf::close()");
   if( !(_sstate & sstream::openedbit) ) {
//    sstream::fail(_sd, sstream::openbit);
      sstream::traceoff("sockbuf * sockbuf::close()");
      return 0;
   }

   iohandler(0);
   urghandler(0);
   pipehandler(0);

   _sockets -= _sd;

   _stream  = 0;
   _myaddr  = 0;
   _myport  = 0;
   _toaddr  = 0;
   _toport  = 0;
   
   long   toread;
   sstream::syscall(ioctl(_sd, FIONREAD, &toread),
         "sockbuf::close():ioctl(FIONREAD)");
   while(toread > 0) {
      sstream::syscall(recv(_sd, buf, bufsize, 0), "sockbuf::close():recv()");
      sstream::syscall(ioctl(_sd, FIONREAD, &toread),
            "sockbuf::close():ioctl(FIONREAD)");
   }

#ifdef apollo
   _sstate(oobinlinebit);
#else
   _sstate = 0;
#endif
      /*
      setb(c, c + 2*bufsize, 1);
      setp(c, c + bufsize);
      setg(c + bufsize, c + 2*bufsize, c + 2*bufsize);
      */

   setg( eback(), egptr(), egptr());
   setp( pbase(), epptr());

   if( shutdown(_sd, 2) == -1) {
      sstream::fail(_sd, sstream::closebit);
      sstream::traceoff("sockbuf * sockbuf::close()");
      return 0;
    }

   streams[_sd] = 0;
   _sd = EOF;
   sstream::traceoff("sockbuf * sockbuf::close()");
   return this;
}

                               sockbuf::~sockbuf()
{
   sstream::traceon("sockbuf::~sockbuf()");
   close();
   sstream::traceoff("sockbuf::~sockbuf()");
}

signalhandlerfn *              sockbuf::iouser = 0;
void                           sockbuf::iomanager(int)
{
selset    r = _sockets;
int       i;
sstream * s;
seliter   I;

   sstream::traceon("void sockbuf::iomanager()");

   if( sstream::syscall(selpoll(r, null_set, null_set),
	 "sockbuf:iomanager():selpoll()") == -1 ) {
      sstream::traceoff("void sockbuf::iomanager()");
      return;
   }

   for( i = I(r); !I; i = ++I) {
      s = streams[i];
      if( s->_sstate & sstream::trapiobit ) {
         int   toread;
         sstream::syscall(ioctl(s->_sd, FIONREAD, &toread),
	       "sockbuf::iomanager():ioctl(FIONREAD)");
         if( toread > 0 ) {
	    if( s->_sstate & sstream::oobinlinebit ) {
	       int atmark;
	       sstream::syscall(ioctl(s->_sd, SIOCATMARK, &atmark),
	          "sockbuf::iomanager():ioctl(SIOCATMARK)");
	       if( atmark == 0) {
                  s->_iohandler( *s, toread);
	       }
	    }
	    else {
                s->_iohandler( *s, toread);
	    }
         }
         else if( s->_sstate & sstream::serverbit) {
            s->_iohandler( *s, 0);
         }
      }
      else if( iouser != 0 ) {
         iouser(SIGIO);
      }
   }
   sstream::traceoff("void sockbuf::iomanager()");
}

iohandlerfn *                  sockbuf::iohandler(iohandlerfn * fn)
{
static          count = 0;
iohandlerfn *   old = 0;
int             grp;
#if defined(SIG_BLOCK) && defined(SIG_SETMASK)
struct sigaction action;
#endif

   sstream::traceon("iohandlerfn * sockbuf::iohandler(iohandlerfn)");
   if( fn == 0 ) {
      if( _sstate & sstream::trapiobit ) {
         count--;
         old = _iohandler;
         _sstate &= ~sstream::trapiobit;
         grp = 0;
         sstream::syscall(ioctl(_sd, FIOASYNC, &grp),
	       "sockbuf::iohandler():ioctl(FIOASYNC)");
         if( !(_sstate & sstream::trapurgbit ) ) {
            grp = 0;
            sstream::syscall(ioctl(_sd, SIOCSPGRP, &grp),
	       "sockbuf::iohandler():ioctl(SIOCSPGRP)");
         }
         if( count == 0 ) {
            if( iouser == 0 ) {
#if defined(SIG_BLOCK) && defined(SIG_SETMASK)
		action.sa_handler = SIG_DFL;
		sigemptyset (&action.sa_mask);
		action.sa_flags = 0;
		(void) sigaction (SIGIO, &action, NULL);
#else
		(void) signal(SIGIO, SIG_DFL);
#endif
            }
            else {
#if defined(SIG_BLOCK) && defined(SIG_SETMASK)
		action.sa_handler = iouser;
		sigemptyset (&action.sa_mask);
		action.sa_flags = 0;
		(void) sigaction (SIGIO, &action, NULL);
#else
		(void) signal(SIGIO, (signal_arg2)iouser);
#endif
            }
         }
      }
   }
   else {
      if( _sstate & sstream::trapiobit ) {
        old = _iohandler;
        _iohandler = fn;
      }
      else {
         count++;
         _iohandler = fn;
         _sstate |= sstream::trapiobit;
         if( !(_sstate & sstream::trapurgbit ) ) {
            grp = getpid();
            sstream::syscall(ioctl(_sd, SIOCSPGRP, &grp),
	       "sockbuf::iohandler():ioctl(SIOCSPGRP)");
         }
         grp = 1;
         sstream::syscall(ioctl(_sd, FIOASYNC, &grp),
	       "sockbuf::iohandler():ioctl(FIOASYNC)");
         if( count == 1) {
#if defined(SIG_BLOCK) && defined(SIG_SETMASK)
	    struct sigaction oaction;
	    action.sa_handler = sockbuf::iomanager;
	    sigemptyset (&action.sa_mask);
	    action.sa_flags = 0;
	    iouser = (signalhandlerfn *)
		((sigaction (SIGIO, &action, &oaction) == -1)
		? SIG_ERR : oaction.sa_handler);
#else
            iouser = (signalhandlerfn *) signal(SIGIO, (signal_arg2)sockbuf::iomanager);
#endif
         }
      }
   }
   sstream::traceoff("iohandlerfn * sockbuf::iohandler(iohandlerfn)");
   return old;
}

signalhandlerfn *              sockbuf::urguser = 0;
void                           sockbuf::urgmanager(int)
{
selset    e = _sockets;
int       i;
sstream * s;
char      c;
seliter   I;

   sstream::traceon("void sockbuf::urgmanager(int)");

   if( sstream::syscall(selpoll(null_set, null_set, e),
	 "sockbuf::urgmanager():selpoll()") == -1 ) {
      sstream::traceoff("void sockbuf::urgmanager(int)");
      return;
   }
   for( i = I(e); !I; i = ++I) {
      s = streams[i];

      if( s->_sstate & sstream::trapurgbit) {
	 if( s->_sstate & sstream::oobinlinebit ) {
            if( sstream::syscall(recv(i, &c, sizeof(char), 0),
	          "sockbuf::urgmanager():recv(0)") != -1 ) {
               sstream::monitor("recieving urg ", c);
               s->_urghandler( *s, c);
	    }
            else {
               if( errno != EINVAL ) {
                  sstream::fail(s->_sd, sstream::recvbit);
               }
            }
	 }
         else {
	    if( sstream::syscall(recv(i, &c, sizeof(char), MSG_OOB),
	          "sockbuf::urgmanager():recv(MSG_OOB)") != -1 ) {
               sstream::monitor("recieving urg OOB ", c);
               s->_urghandler( *s, c);
	    }
            else {
               if( errno != EINVAL ) {
                  sstream::fail(s->_sd, sstream::recvbit);
               }
            }
         }
      }
      else if( urguser != 0 ) {
         urguser(SIGURG);
      }
   }
   sstream::traceoff("void sockbuf::urgmanager(int)");
}

urghandlerfn *                  sockbuf::urghandler(urghandlerfn * fn)
{
static          count = 0;
urghandlerfn *  old = 0;
int             grp;      
#if defined(SIG_BLOCK) && defined(SIG_SETMASK)
struct sigaction action;
#endif

   sstream::traceon("urghandlerfn * sockbuf::urghandler(urghandlerfn * fn)");
   if( fn == 0 ) {
      if( _sstate & sstream::trapurgbit ) {
         count--;
         old = _urghandler;
         _sstate &= ~sstream::trapurgbit;
         if( !(_sstate & sstream::trapiobit )  ) {
            grp = 0;
            sstream::syscall(ioctl(_sd, SIOCSPGRP, &grp),
	       "sockbuf::urghandler():ioctl(SIOCSPGRP)");
         }
         if( count == 0 ) {
            if( urguser == 0 ) {
#if defined(SIG_BLOCK) && defined(SIG_SETMASK)
		action.sa_handler = SIG_DFL;
		sigemptyset (&action.sa_mask);
		action.sa_flags = 0;
		(void) sigaction (SIGURG, &action, NULL);
#else
		(void) signal(SIGURG, SIG_DFL);
#endif
            }
            else {
#if defined(SIG_BLOCK) && defined(SIG_SETMASK)
		action.sa_handler = urguser;
		sigemptyset (&action.sa_mask);
		action.sa_flags = 0;
		(void) sigaction (SIGURG, &action, NULL);
#else
		(void) signal(SIGURG, (signal_arg2)urguser);
#endif
            }
         }
      }
   }
   else {
      if( _sstate & sstream::trapurgbit) {
        old = _urghandler;
        _urghandler = fn;
      }
      else {
         count++;
         _urghandler = fn;
         _sstate |= sstream::trapurgbit;
         if( !(_sstate & sstream::trapiobit )  ) {
            grp = getpid();
            sstream::syscall(ioctl(_sd, SIOCSPGRP, &grp),
	       "sockbuf::urghandler():ioctl(SIOCSPGRP)");
         }
         if( count == 1 ) {
#if defined(SIG_BLOCK) && defined(SIG_SETMASK)
	    struct sigaction oaction;
	    action.sa_handler = sockbuf::urgmanager;
	    sigemptyset (&action.sa_mask);
	    action.sa_flags = 0;
	    urguser = (signalhandlerfn *)
		((sigaction (SIGURG, &action, &oaction) == -1)
		? SIG_ERR : oaction.sa_handler);
#else
            urguser = (signalhandlerfn *) signal(SIGURG, (signal_arg2)sockbuf::urgmanager);
#endif
         }
      }
   }
   sstream::traceoff("urghandlerfn * sockbuf::urghandler(urghandlerfn * fn)");
   return old;
}

signalhandlerfn *              sockbuf::pipeuser = 0;
void                           sockbuf::pipemanager(int)
{
selset    w = _sockets;
int       i;
sstream * s;
seliter   I;
   
   sstream::traceon("void sockbuf::pipemanager(int)");
   if( sstream::syscall(selpoll(null_set, w, null_set),
	 "sockbuf:pipemanager():selpoll()") == -1 ) {
      sstream::traceoff("void sockbuf::pipemanager(int)");
      return;
   }

   for( i = I(w); !I; i = ++I) {
      s = streams[i];
      if( s->_sstate & sstream::trappipebit) {
         s->_pipehandler(*s);
      }
      else if( pipeuser != 0 ) {
         pipeuser(SIGPIPE);
      }
      else {
         sstream::fail(s->_sd, sstream::pipebit);
         s->clear( ios::badbit | s->rdstate());
      }
   }

   sstream::traceoff("void sockbuf::pipemanager(int)");
}

pipehandlerfn *                  sockbuf::pipehandler(pipehandlerfn * fn)
{
static                   count = 0;
pipehandlerfn *            old = 0;
#if defined(SIG_BLOCK) && defined(SIG_SETMASK)
struct sigaction action;
#endif

   sstream::traceon("pipehandlerfn * sockbuf::pipehandler(pipehandlerfn * fn)");
   if( fn == 0 ) {
      if( _sstate & sstream::trappipebit ) {
         count--;
         old = _pipehandler;
         _sstate &= ~sstream::trappipebit;
         if( count == 0 ) {
            if( pipeuser == 0 ) {
#if defined(SIG_BLOCK) && defined(SIG_SETMASK)
		action.sa_handler = SIG_DFL;
		sigemptyset (&action.sa_mask);
		action.sa_flags = 0;
		(void) sigaction (SIGPIPE, &action, NULL);
#else
		signal(SIGPIPE, SIG_DFL);
#endif
            }
            else {
#if defined(SIG_BLOCK) && defined(SIG_SETMASK)
		action.sa_handler = pipeuser;
		sigemptyset (&action.sa_mask);
		action.sa_flags = 0;
		(void) sigaction (SIGPIPE, &action, NULL);
#else
		signal(SIGPIPE, (signal_arg2)pipeuser);
#endif
            }
         }
      }
   }
   else {
      if( _sstate & sstream::trappipebit) {
        old = _pipehandler;
        _pipehandler = fn;
      }
      else {
         count++;
         _pipehandler = fn;
         _sstate |= sstream::trappipebit;
         if( count == 1 ) {
#if defined(SIG_BLOCK) && defined(SIG_SETMASK)
	    struct sigaction oaction;
	    action.sa_handler = sockbuf::pipemanager;
	    sigemptyset (&action.sa_mask);
	    action.sa_flags = 0;
	    pipeuser = (signalhandlerfn *)
		((sigaction (SIGPIPE, &action, &oaction) == -1)
		? SIG_ERR : oaction.sa_handler);
#else
            pipeuser =(signalhandlerfn *) signal(SIGPIPE, (signal_arg2)sockbuf::pipemanager);
#endif
         }
      }
   }
   sstream::traceoff("pipehandlerfn * sockbuf::pipehandler(pipehandlerfn * fn)");
   return old;
}

char *                         sockbuf::host()
{
static char c[1];
in_addr     addr;
hostent *   ent;

   addr.s_addr        = _myaddr;
   ent = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET);
   if( ent ) {
      return ent->h_name;
   }
   else {
      c[0] = 0;
      return c;
   }
}

char *                           sockbuf::tohost()
{
static char c[1];
in_addr     addr;
hostent *   ent;

   addr.s_addr = _toaddr;
   ent  = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET);
   if( ent ) {
      return ent->h_name;
   }
   else {
      c[0] = 0;
      return c;
   }
}

unsigned                         sockbuf::sstate(unsigned m)
{
unsigned   old = _sstate;
int        f;
#ifdef SO_LINGER
linger     l;
#endif

   sstream::traceon("unsigned sockbuf::sstate(unsigned m)");
   if( (old & sstream::noblockbit) && !(m & sstream::noblockbit) ) {
      _sstate &= ~sstream::noblockbit;
      f = 0;
      sstream::syscall(ioctl(_sd, FIONBIO, &f),
	    "sockbuf::sstate():ioctl(FIONBIO)");
   }
   else if( (m & sstream::noblockbit) && !(old & sstream::noblockbit) ) {
      _sstate |= sstream::noblockbit;
      f = 1;
      sstream::syscall(ioctl(_sd, FIONBIO, &f),
	    "sockbuf::sstate():ioctl(FIONBIO)");
   }
#ifdef SO_LINGER
   if( (old & sstream::nolingerbit) && !(m & sstream::nolingerbit) ) {
      _sstate &= ~sstream::nolingerbit;
      l.l_onoff  = 0;
      l.l_linger = 0;
      sstream::syscall(setsockopt(_sd, SOL_SOCKET, SO_LINGER,
	    (char *)&l, sizeof(l)),
	    "sockbuf::sstate():setsockopt(SO_LINGER)");
   }
   else if( (m & sstream::nolingerbit) && !(old & sstream::nolingerbit) ) {
      _sstate |= sstream::nolingerbit;
      l.l_onoff  = 1;
      l.l_linger = 0;
      sstream::syscall(setsockopt(_sd, SOL_SOCKET, SO_LINGER,
	    (char *)&l, sizeof(l)),
	    "sockbuf::sstate():setsockopt(SO_LINGER)");
   }
#endif
   if( (old & sstream::oobinlinebit) && !(m & sstream::oobinlinebit) ) {
      _sstate &= ~sstream::oobinlinebit;
      f = 0;
      sstream::syscall(setsockopt(_sd, SOL_SOCKET, SO_OOBINLINE,
	    (char *)&f, sizeof(f)),
	    "sockbuf::sstate():setsockopt(SO_OOBINLINE)");
   }
   else if( (m & sstream::oobinlinebit) && !(old & sstream::oobinlinebit) ) {
      _sstate |= sstream::oobinlinebit;
      f = 1;
      sstream::syscall(setsockopt(_sd, SOL_SOCKET, SO_OOBINLINE,
	    (char *)&f, sizeof(f)),
	    "sockbuf::sstate():setsockopt(SO_OOBINLINE)");
   }
   if( (old & sstream::noreuseaddrbit) && !(m & sstream::noreuseaddrbit) ) {
      _sstate &= ~sstream::noreuseaddrbit;
      f = 1;
      sstream::syscall(setsockopt(_sd, SOL_SOCKET, SO_REUSEADDR,
	    (char *)&f, sizeof(f)),
	    "sockbuf::sstate():setsockopt(SO_REUSEADDR)");
   }
   else if( (m & sstream::noreuseaddrbit) && !(old & sstream::noreuseaddrbit) ) {
      _sstate |= sstream::noreuseaddrbit;
      f = 0;
      sstream::syscall(setsockopt(_sd, SOL_SOCKET, SO_REUSEADDR,
	    (char *)&f, sizeof(f)),
	    "sockbuf::sstate():setsockopt(SO_REUSEADDR)");
   }
   if( (old & sstream::nokeepalivebit) && !(m & sstream::nokeepalivebit) ) {
      _sstate &= ~sstream::nokeepalivebit;
      f = 1;
      sstream::syscall(setsockopt(_sd, SOL_SOCKET, SO_KEEPALIVE,
	    (char *)&f, sizeof(f)),
	    "sockbuf::sstate():setsockopt(SO_KEEPALIVE)");
   }
   else if( (m & sstream::nokeepalivebit) && !(old & sstream::nokeepalivebit) ) {
      _sstate |= sstream::nokeepalivebit;
      f = 0;
      sstream::syscall(setsockopt(_sd, SOL_SOCKET, SO_KEEPALIVE,
	    (char *)&f, sizeof(f)),
	    "sockbuf::sstate():setsockopt(SO_KEEPALIVE)");
   }
   if( (old & sstream::unitbufferbit) && !(m & sstream::unitbufferbit) ) {
      _sstate &= ~sstream::unitbufferbit;
      _stream->unsetf(ios::unitbuf);
   }
   else if( (m & sstream::unitbufferbit) && !(old & sstream::unitbufferbit) ) {
      _sstate |= sstream::unitbufferbit;
      _stream->setf(ios::unitbuf);
   }
   sstream::traceoff("unsigned sockbuf::sstate(unsigned m)");
   return old;
}



#ifndef __GNUC__
static ostream&  osendfn(ostream& o, char c)
{
sockbuf * s = (sockbuf *)(o.rdbuf());

   sstream::traceon("ostream& osendfn(ostream&, char)");
   if( send(s->sd(), &c, sizeof(char), MSG_OOB) == -1 ) {
      sstream::fail(s->sd(), sstream::sendbit);
   }
   sstream::traceoff("ostream& osendfn(ostream&, char)");
   return o;
}

OMANIP(char)     osend(char c)
{
   return OMANIP(char)(osendfn, c);
}

static istream&  orecvfn(istream& i, char * c)
{
sockbuf * s = (sockbuf *)(i.rdbuf());
   sstream::traceon("istream&  orecvfn(istream&, char *)");
   if( recv(s->sd(), c, sizeof(char), MSG_OOB) == -1 ) {
      sstream::fail(s->sd(), sstream::recvbit);
   }
   sstream::traceoff("istream&  orecvfn(istream&, char *)");
   return i;
}

IMANIP(charptr)   orecv(char &c)
{
    return IMANIP(charptr)(orecvfn, &c);
}
#else
static ostream&  osendfn(ostream& o, char c)
{
sockbuf * s = (sockbuf *)(o.rdbuf());

   sstream::traceon("ostream& osendfn(ostream&, char)");
   if( send(s->sd(), &c, sizeof(char), MSG_OOB) == -1 ) {
      sstream::fail(s->sd(), sstream::sendbit);
   }
   sstream::traceoff("ostream& osendfn(ostream&, char)");
   return o;
}

oapp<char>    osend(osendfn);

static istream&  orecvfn(istream& i, char& c)
{
sockbuf * s = (sockbuf *)(i.rdbuf());
   sstream::traceon("istream&  orecvfn(istream&, char *)");
   if( recv(s->sd(), &c, sizeof(char), MSG_OOB) == -1 ) {
      sstream::fail(s->sd(), sstream::recvbit);
   }
   sstream::traceoff("istream&  orecvfn(istream&, char *)");
   return i;
}

iapp<char&>   orecv(orecvfn);

#endif
