static const char* version_id =
  "$Id: SocketBase.cc,v 1.9 1999/08/16 21:55:30 v2kea318 Exp $";

// $Log: SocketBase.cc,v $
// Revision 1.9  1999/08/16  21:55:30  v2kea318
// Use a MutexRep object rather than a Mutex in the constructor.
//
// Revision 1.8  1999/08/12  19:24:37  v2kea318
// Do not embed C++ style comments (//) inside C style comments (/* */).
//
// Revision 1.7  1999/08/06  21:14:54  v2kea318
// Put mutex protection around the call to gethostbyname in the
// constructor.  This is to prevent other threads from changing the
// value of the data before we have a chance to copy it.
//
// Revision 1.6  1999/07/30  20:44:12  v2kea318
// Converted the code to G++ compliance.
//
// Revision 1.5  1999/07/02  15:45:07  v2kea318
// Ensure we have a NULL terminated string in cpstring.
//
// Revision 1.4  1999/04/27  14:55:56  v2kea318
// Added the HostEnt methods.
//
// Revision 1.3  1999/04/26  14:22:13  v2kea318
// Store a copy of the struct hostent data.
//
// Revision 1.2  1999/04/23  19:52:03  v2kea318
// Fixed version.  Appears to work right.
//
// Revision 1.1  1999/04/23  18:55:01  v2kea318
// Initial revision
//
//***************************************************************************

#include "SocketBase.h"
#include <cerrno>
#include "MutexRep.h"

extern char* sys_errlist[];
static MutexRep mutex;

//***************************************************************************

SocketBase::SocketBase(const string& host, int p)
{
  struct hostent* thp;

  /* Get the host info.  Put the code in a block, to force the mutex
     to be released after we copy the hostent data. */
  {
      MutexRep mr(mutex);
      if ((thp = gethostbyname(host.c_str())) == NULL) 
	  throw UnknownHost(host);
      hp.copy(thp);
  }
  
  // start building the sa object.
  sa.sin_port = p;
  sa.sin_family = hp.data()->h_addrtype;
}

//***************************************************************************

void SocketBase::build()
{
  // build the socket.
  if ((sock = socket(hp.data()->h_addrtype, SOCK_STREAM, 0)) < 0) 
    throw SocketException(sys_errlist[errno]);
}

//***************************************************************************

string SocketBase::gethostname() const
{
  return hp.data()->h_name;
}

//***************************************************************************

int SocketBase::getport() const
{
  return sa.sin_port;
}

//***************************************************************************

static size_t nelements(char** a)
{
  size_t cnt = 0;
  while (*a++) cnt++;
  return cnt;
}

static char* cpstring(char* s, int fixed_len = -1)
{
  size_t len = (fixed_len < 0) ? strlen(s) : fixed_len;
  char* cp = new char [ len + 1 ];
  memcpy(cp, s, len);
  cp[len] = '\0';
  return cp;
}

static void cparray(char**& cp, char** s, int fixed_len = -1)
{
  size_t nels = nelements(s);
  cp = new char* [ nels + 1 ];
  for (size_t cnt = 0; cnt < nels; cnt++)
    cp[cnt] = cpstring(s[cnt], fixed_len);
  cp[nels] = NULL;
}

void SocketBase::HostEnt::copy(const struct hostent* hp)
{
  // allocate memory.
  ptr = Pointer<struct hostent>(new struct hostent);
  
  // copy the constants.
  ptr->h_length   = hp->h_length;
  ptr->h_addrtype = hp->h_addrtype;
  
  // copy the hname.
  ptr->h_name = cpstring(hp->h_name);
  
  // handle the aliases.
  cparray(ptr->h_aliases, hp->h_aliases);

  // handle the address list.  Use the fixed size from the h_length field.
  cparray(ptr->h_addr_list, hp->h_addr_list, hp->h_length);
}	

//***************************************************************************

static void clean_s_array(char** p)
{
  char** s = p;
  while (*s) delete [] *s++;
  delete [] p;
}

void SocketBase::HostEnt::cleanup()
{
  if (ptr) {
    // the name is a simply string.
    delete [] ptr->h_name; ptr->h_name = NULL;

    // the alias list is an array of strings.
    if (ptr->h_aliases) clean_s_array(ptr->h_aliases);
    ptr->h_aliases = NULL;

    // the address list is an array of strings.
    if (ptr->h_addr_list) clean_s_array(ptr->h_addr_list);
    ptr->h_addr_list = NULL;
  }
}

