// This may look like C code, but it is really -*- C++ -*-
// 
// <copyright> 
//  
//  Copyright (c) 1994
//  Institute for Information Processing and Computer Supported New Media (IICM), 
//  Graz University of Technology, Austria. 
//  
// </copyright> 
// 
// 
// <file> 
// 
// Name:        socket.C
// 
// Purpose:     
// 
// Created:     4 May 95   Joerg Faschingbauer
// 
// Modified:    
// 
// Description: 
// 
// $Id: socket.C,v 1.13 1997/02/12 13:13:41 gorasche Exp $
//
// $Log: socket.C,v $
// Revision 1.13  1997/02/12 13:13:41  gorasche
// retry send and recv on non-blocking socket when WSAEWOULDBLOCK
//
// Revision 1.12  1997/01/29 15:36:05  gorasche
// changes for Win32
// - overloaded read, write
// - ::errno changed to errno
//
// Revision 1.11  1996/12/17 18:09:03  jfasch
// bug fix:
// Socket::Socket(int fd) left the flags uninitialized
//
// Revision 1.10  1996/10/03 15:59:07  jfasch
// adaptions due to moving it from the previous location in DcCommon to HgUtilities
//
// Revision 1.9  1996/08/20 13:24:19  jfasch
// memset
//
// Revision 1.8  1996/07/22 08:18:19  jfasch
// *** empty log message ***
//
// Revision 1.7  1996/07/08 13:16:36  jfasch
// *** empty log message ***
//
// Revision 1.6  1996/06/12 11:30:34  jfasch
// no real changes
//
// 
// </file> 
#include "socket.h"

#include "new.h"

#include "hgunistd.h"
#include "assert.h"

#include <string.h>
#include <fcntl.h>

#include <sys/socket.h>

#ifndef WIN32
#include <sys/un.h> /* UNIX domain sockets */
#endif



#ifndef VERBOSE
#  define VERBOSE
#endif
#include "verbose.h"



// --------------------------------------------------------------------
const char* Socket :: version1 = "$Id: socket.C,v 1.13 1997/02/12 13:13:41 gorasche Exp $" ;

Socket :: Socket()
: blocking_(true),
  listening_(false) {}

Socket :: Socket (int fd)
: blocking_(true),
  listening_(false) {
   attach (fd) ;
}

Socket :: ~Socket() {
   if (! closed())
      close() ;
}

bool Socket :: close() {
#ifdef WIN32
  DEBUGNL ("Socket::close()") ;
  if(closed())
    return true ;

  DEBUGNL ("Socket::close(): closing") ;
  if (::closesocket(fd()))
  {
     int wsaerrno=WSAGetLastError();
     set_errno_(wsaerrno) ;
     return false ;
  }
  set_fd_(-1) ;
  blocking_=false;
  listening_=false;
  return true;
#else

   if (HgFile::close()) {
      blocking_ = false ;
      listening_ = false ;
      return true ;
   }
   return false ;
#endif
}

void Socket :: attach (int thefd) {
   HgFile::attach (thefd) ;
   blocking_ = check_blocking_(fd()) ;
   // check if listening ? +++++
   // assert blocking vs listening ? +++++
}

bool Socket :: blocking (bool b) {
   if (blocking_ == b)
      return true ;
   
   if (! set_blocking_(fd(), b))
      return false ;

   blocking_ = b ;
   return true ;
}

bool Socket :: accept (int& newfd) {
   hgassert (fd()>=0, "Socket::accept(): not using a file number yet") ;
   hgassert (get_listening_(), "Socket::accept(): not listening") ;
   
   // my socket may either be in the internet or in the UNIX domain.
   // here we take as address a sockaddr_un, which is guaranteed to be larger than
   // the other address structures. (we throw it away anyhow.)
#ifdef WIN32
   static struct sockaddr addr ;
#else
   static struct sockaddr_un addr ;
#endif
   static int addrlen = sizeof (addr) ;
   ::memset (&addr, 0, sizeof(addr)) ;
   
   newfd = accept_((sockaddr*)&addr, &addrlen) ;
   return (newfd < 0) ? false : true ;
}

bool Socket :: accept (SocketPtr& sp) {
   int newfd ;
   bool rv = accept (newfd) ;
   if (rv)
      sp = SocketPtr (HGNEW (Socket (newfd))) ;
   else
      sp = SocketPtr() ;
   return rv ;
}

void Socket :: set_listening_(bool b) {
   hgassert (fd()>=0, "Socket::set_listening_(): not using a file number yet") ;
   listening_ = b ;
}

int Socket :: accept_(sockaddr* addr, int* addrlen) {
   hgassert (fd()>=0, "Socket::accept_(sockaddr&,int&): not using a file number yet") ;
   hgassert (get_listening_(), "Socket::accept_(sockaddr&,int&): not listening") ;

   // fix a "bad return value and errno bug" in ::accept() on some
   // exotic systems (as there are: linux, solaris, ...:-). they say
   // strange things when they should simply say EMFILE or ENFILE.
#ifndef WIN32
   int dupfd = ::dup (fd()) ;
   if (dupfd < 0) {
      set_errno_(::errno) ;
      perror_("Socket::accept_(sockaddr&,int&): ::dup()") ;
      return dupfd ;
   }
   else 
      ::close (dupfd) ;
#endif /* WIN32 */
   
   int newfd = ::accept (fd(), addr, addrlen) ;
   if (newfd < 0) {
      set_errno_(errno) ;
      perror_("Socket::accept_(sockaddr&,int&): ::accept()") ;
   }
   return newfd ;
}

bool Socket :: check_blocking_(int fd) {
   int rv = ::get_blocking (fd) ;
   if (rv < 0) {
      perror_("Socket::check_blocking_(): fcntl() get flags") ;
      hgassert (false, "Socket::check_blocking_(): could not get blocking state of socket") ;
      return false ; // satisfy compiler
   }
   else if (rv == 0)
      return false ;
   else 
      return true ;
}

bool Socket :: set_blocking_(int fd, bool b) {
   int rv = ::set_blocking (fd, b) ;
   if (rv < 0) {
      perror_("Socket::set_blocking_(): ::fcntl() error") ;
      hgassert (false, "Socket::set_blocking_(): ::fcntl() error") ;
      return false ; // satisfy compiler
   }
   else {
      hgassert (rv==0, "Socket::set_blocking_(): invalid return value") ;
      return true ;
   }
}

#ifdef WIN32
int Socket::write(const char* buf, int nbytes)
{
   hgassert (fd()>=0, "Socket::write(): invalid file number") ;
   int nwritten ;
   int iError;
   while((nwritten=::send(fd(),buf,nbytes,0)) < 0 && ((iError=WSAGetLastError())==WSAEINTR||iError==WSAEWOULDBLOCK));
   if (nwritten < 0)
   {
      set_errno_(iError);
   }
   return nwritten;
}

int Socket::read(char* buf, int nbytes)
{
   hgassert (fd()>=0, "Socket::read(): invalid file number") ;
   int nread ;
   int iError;
   while((nread=::recv(fd(),buf,nbytes,0)) < 0 && ((iError=WSAGetLastError())==WSAEINTR||iError==WSAEWOULDBLOCK));
   if (nread < 0)
   {
      set_errno_(iError);
   }
   return nread;
}
#endif
