// 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: 
// 
// 
// </file> 
#include "socket.h"

#include <hyperg/utils/hgunistd.h>
#include <hyperg/hyperg/assert.h>

// both for perror() on the various platforms
#include <stdio.h>
#include <errno.h>

#include <fcntl.h>

#include <sys/socket.h>
#include <sys/un.h> /* UNIX domain sockets */



#define VERBOSE
#include <hyperg/hyperg/verbose.h>



// --------------------------------------------------------------------
Socket :: Socket()
: File(),
  blocking_(true),
  listening_(false) {}

Socket :: Socket (int fd, boolean close) {
   attach (fd, close) ;
}

boolean Socket :: close() {
   if (File::close()) {
      blocking_ = false ;
      listening_ = false ;
      return true ;
   }
   return false ;
}

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

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

   blocking_ = b ;
   return true ;
}

boolean 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.)
   static struct sockaddr_un addr ;
   static int addrlen = sizeof (addr) ;
   
   newfd = accept_((sockaddr*)&addr, &addrlen) ;
   return (newfd < 0) ? false : true ;
}

boolean Socket :: accept (SocketPtr& sp) {
   int newfd ;
   boolean rv = accept (newfd) ;
   if (rv)
      sp = SocketPtr (new Socket (newfd, true)) ;
   else
      sp = SocketPtr() ;
   return rv ;
}

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") ;
   
   int newfd = ::accept (fd(), addr, addrlen) ;
   if (newfd < 0) {
      set_errno_(::errno) ;
#ifdef VERBOSE_SOCKET      
      ::perror ("Socket::accept(): ::accept()") ;
#endif
   }
   return newfd ;
}

boolean Socket :: check_blocking_(int fd) {
   int rv = ::get_blocking (fd) ;
   if (rv < 0) {
#ifdef VERBOSE_SOCKET
      ::perror ("Socket::check_blocking_(): fcntl() get flags") ;
#endif
      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 ;
}

boolean Socket :: set_blocking_(int fd, boolean b) {
   int rv = ::set_blocking (fd, b) ;
   if (rv < 0) {
#ifdef VERBOSE_SOCKET
      ::perror ("Socket::set_blocking_(): ::fcntl() error") ;
#endif
      hgassert (false, "Socket::set_blocking_(): ::fcntl() error") ;
      return false ; // satisfy compiler
   }
   else {
      hgassert (rv==0, "Socket::set_blocking_(): invalid return value") ;
      return true ;
   }
}
