/*  Part of XPCE --- The SWI-Prolog GUI toolkit

    Author:        Jan Wielemaker and Anjo Anjewierden
    E-mail:        jan@swi.psy.uva.nl
    WWW:           http://www.swi.psy.uva.nl/projects/xpce/
    Copyright (c)  1985-2024, University of Amsterdam
			      CWI, Amsterdam
			      SWI-Prolog Solutions b.v.
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions
    are met:

    1. Redistributions of source code must retain the above copyright
       notice, this list of conditions and the following disclaimer.

    2. Redistributions in binary form must reproduce the above copyright
       notice, this list of conditions and the following disclaimer in
       the documentation and/or other materials provided with the
       distribution.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    POSSIBILITY OF SUCH DAMAGE.
*/

#include <md.h>				/* get HAVE_'s */
#if defined(HAVE_SOCKET) || defined(HAVE_WINSOCK)

#include <sys/types.h>
#include <memory.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef HAVE_WINSOCK
#include "mswinsock.h"
#include <ws2tcpip.h>
#include <io.h>
#include <fcntl.h>
#else /*HAVE_WINSOCK*/

#define HAVE_SYS_UN_H 1
#define UNIX_DOMAIN_SOCKETS 1
#include <sys/socket.h>			/* must be first to avoid send() */
#include <netdb.h>			/* conflict ... */
#include <netinet/in.h>
#ifdef HAVE_SYS_SOCKETVAR_H
#include <sys/socketvar.h>
#endif
#include <errno.h>
extern int errno;
#undef NAME_MAX				/* conflict */

#define SOCKET int
#ifndef HAVE_SOCKLEN_T
#define socklen_t size_t
#endif
#endif /*WINSOCK*/

#define create PCEcreate		/* avoid conflict */

#include <h/kernel.h>
#include <h/unix.h>
#include <h/interface.h>
#if defined(HAVE_SYS_TIME_H) && !defined(__USE_W32_SOCKETS)
#include <sys/time.h>
#endif
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#include <signal.h>

#define MAX_UN_ADDRESS_LEN (sizeof(struct sockaddr_un) - sizeof(short))

#ifndef INVALID_SOCKET
#define INVALID_SOCKET -1
#endif

static status	closeSocket(Socket);

#ifdef HAVE_WINSOCK
#include <msw/mswin.h>
#define NO_WINERR 1			/* there really isn't a way !!!! */
#ifdef NO_WINERR

typedef struct
{ int	 id;
  char  *description;
} wsock_err;

static wsock_err wsock_err_list[] =
{ { WSAEACCES, "Permission denied" },
  { WSAEADDRINUSE, "Address already in use" },
  { WSAEADDRNOTAVAIL, "Cannot assign requested address" },
  { WSAEAFNOSUPPORT, "Address family not supported by protocol family" },
  { WSAEALREADY, "Operation already in progress" },
  { WSAECONNABORTED, "Software caused connection abort" },
  { WSAECONNREFUSED, "Connection refused" },
  { WSAECONNRESET, "Connection reset by peer" },
  { WSAEDESTADDRREQ, "Destination address required" },
  { WSAEFAULT, "Bad address" },
  { WSAEHOSTDOWN, "Host is down" },
  { WSAEHOSTUNREACH, "No route to host" },
  { WSAEINPROGRESS, "Operation now in progress" },
  { WSAEINTR, "Interrupted function call" },
  { WSAEINVAL, "Invalid argument" },
  { WSAEISCONN, "Socket is already connected" },
  { WSAEMFILE, "Too many open files" },
  { WSAEMSGSIZE, "Message too long" },
  { WSAENETDOWN, "Network is down" },
  { WSAENETRESET, "Network dropped connection on reset" },
  { WSAENETUNREACH, "Network is unreachable" },
  { WSAENOBUFS, "No buffer space available" },
  { WSAENOPROTOOPT, "Bad protocol option" },
  { WSAENOTCONN, "Socket is not connected" },
  { WSAENOTSOCK, "Socket operation on non-socket" },
  { WSAEOPNOTSUPP, "Operation not supported" },
  { WSAEPFNOSUPPORT, "Protocol family not supported" },
  { WSAEPROCLIM, "Too many processes" },
  { WSAEPROTONOSUPPORT, "Protocol not supported" },
  { WSAEPROTOTYPE, "Protocol wrong type for socket" },
  { WSAESHUTDOWN, "Cannot send after socket shutdown" },
  { WSAESOCKTNOSUPPORT, "Socket type not supported" },
  { WSAETIMEDOUT, "Connection timed out" },
  { WSAEWOULDBLOCK, "Resource temporarily unavailable" },
  { WSAEDISCON, "Graceful shutdown in progress" },
  { 0, NULL }
};
#endif /*NO_WINERR*/


Name
SockError(void)
{
#ifdef NO_WINERR
  int err = WSAGetLastError();
  wsock_err *e = wsock_err_list;

  if ( !err )
    return (Name) NIL;

  for( ; e->description; e++ )
  { if ( e->id == err )
      return CtoName(e->description);
  }

  return WinStrError(err);
#else
  return WinStrError(WSAGetLastError());
#endif
}

#define SocketHandle(s) ((SOCKET)((s)->ws_ref))

#else /*HAVE_WINSOCK*/
#define SocketHandle(s) ((s->rdfd))
#define SockError() getOsErrorPce(PCE)
#endif /*HAVE_WINSOCK*/

static Chain	SocketChain;		/* Available open sockets */

static void
closeAllSockets(int status)
{ Socket s;

  for_chain(SocketChain, s, closeSocket(s));

#ifdef WINSOCK
  WSACleanup();
#endif
}


static void
setupSockets(void)
{ static int initialised = 0;

  if ( !initialised )
  {
#ifdef HAVE_WINSOCK
    WSADATA data;
    WORD wversion = MAKEWORD(2, 0);

    if ( WSAStartup(wversion, &data) != 0 )
      errorPce(NIL, NAME_socket, NAME_initialise, SockError());

    DEBUG(NAME_socket,
	  Cprintf("WSAStartup(): wVersion = %d.%d, wHighVersion = %d.%d\n",
		  data.wVersion >> 8, data.wVersion & 0xff,
		  data.wHighVersion >> 8, data.wHighVersion & 0xff);
	  Cprintf("Description: %s\n", data.szDescription);
	  Cprintf("Status:      %s\n", data.szSystemStatus);
	 );
#endif

    at_pce_exit(closeAllSockets, ATEXIT_FIFO);

    initialised++;
  }
}


static status
initialiseSocket(Socket s, Any address, Name domain)
{ setupSockets();

  initialiseStream((Stream) s, NIL, NIL, NIL, DEFAULT);
#ifdef HAVE_WINSOCK
  s->ws_ref = (WsRef) INVALID_SOCKET;
#endif

  if ( isDefault(domain) )
  { if ( instanceOfObject(address, ClassFile) )
      domain = NAME_unix;
    else if ( instanceOfObject(address, ClassTuple) || isInteger(address) )
      domain = NAME_inet;
    else
      return errorPce(s, NAME_noDomain);
  }

  assign(s, domain,	    domain);
  assign(s, address,	    address);
  assign(s, status,	    NAME_idle);

  succeed;
}


static void
registerSocket(Socket s)		/* do not influence GC */
{ unsigned long flags = s->flags;
  unsigned long refs  = s->references;

  appendChain(SocketChain, s);
  s->flags      = flags;
  s->references = refs;
}


static void
unregisterSocket(Socket s)
{ unsigned long flags = s->flags;
  unsigned long refs  = s->references;

  addCodeReference(s);			/* avoid drop-out */
  deleteChain(SocketChain, s);

  s->flags      = flags;
  s->references = refs;
}


static status
registerClientSocket(Socket s, Socket client)
{ unsigned long flags = s->flags;
  unsigned long refs  = s->references;

  appendChain(s->clients, client);
  assign(client, master, s);
  s->flags      = flags;
  s->references = refs;

  succeed;
}


static status
unregisterClientSocket(Socket s, Socket client)
{ unsigned long flags = s->flags;
  unsigned long refs  = s->references;

  addCodeReference(s);			/* avoid drop-out */
  if ( notNil(s->clients) )
    deleteChain(s->clients, client);
  assign(client, master, NIL);
  s->flags      = flags;
  s->references = refs;

  succeed;
}


static status
unlinkSocket(Socket s)
{ return closeSocket(s);
}


static status
cloneSocket(Socket s, Socket clone)
{ clonePceSlots(s, clone);

  clone->rdfd = clone->wrfd = -1;
#ifdef HAVE_WINSOCK
  clone->ws_ref = (WsRef)INVALID_SOCKET;
#else
  clone->ws_ref = 0;
#endif
  clone->input_buffer = NULL;
  clone->input_allocated = s->input_p = 0;

  succeed;
}

		 /*******************************
		 *	     PRINT NAME		*
		 *******************************/

static StringObj
getPrintNameSocket(Socket s)
{ string tmp;
  StringObj sobj;
  Any av[3];
  Name fmt;
  int an;

  av[0] = getClassNameObject(s);

  if ( instanceOfObject(s->address, ClassTuple) )
  { Tuple t = (Tuple) s->address;

    av[1] = t->first;
    av[2] = t->second;
    an = 3;
    fmt = CtoName("%s(%s:%d)");
  } else
  { av[1] = get(s->address, NAME_printName, EAV);
    an = 2;
    fmt = CtoName("%s(%s)");
  }

  str_writefv(&tmp, (CharArray) fmt, an, av);
  sobj = StringToString(&tmp);
  str_unalloc(&tmp);

  answer(sobj);
}


		 /*******************************
		 *      CREATE/CONNECT/ETC	*
		 *******************************/

static status
createSocket(Socket s)
{ if ( SocketHandle(s) == INVALID_SOCKET )
  { int domain;

    closeSocket(s);

    if ( s->domain == NAME_unix )
    {
#ifdef UNIX_DOMAIN_SOCKETS
      domain = PF_UNIX;
#else
      return errorPce(s, NAME_noSocketDomain, NAME_unix);
#endif
    } else /*if ( s->domain == NAME_inet )*/
      domain = PF_INET;

#ifdef HAVE_WINSOCK
  { SOCKET mss;

    if ( (mss=socket(domain, SOCK_STREAM, 0)) == INVALID_SOCKET )
      return errorPce(s, NAME_socket, NAME_create, SockError());

    s->ws_ref = (WsRef) mss;
  }
#else
    if ( (s->rdfd = s->wrfd = socket(domain, SOCK_STREAM, 0)) < 0 )
      return errorPce(s, NAME_socket, NAME_create, SockError());
#endif
  }

  succeed;
}


#ifdef UNIX_DOMAIN_SOCKETS
static status
unix_address_socket(Socket s, struct sockaddr_un *address, unsigned int *len)
{ Name name = getOsNameFile((FileObj) s->address);
  char *path;

  if ( !name )
    fail;

  path = strName(name);
  address->sun_family = PF_UNIX;
  *len = strlen(path)+1;
  if ( *len > MAX_UN_ADDRESS_LEN )
    return errorPce(s, NAME_socket, NAME_address, CtoName("Name too long"));

  memcpy(address->sun_path, path, *len);
  *len += sizeof(address->sun_family);

  succeed;
}
#endif /*UNIX_DOMAIN_SOCKETS*/


static status
inet_address_socket(Socket s, struct sockaddr_in *address, int *len)
{ memset(address, 0, sizeof(*address));
  *len = sizeof(*address);
  address->sin_family = PF_INET;

  if ( instanceOfObject(s->address, ClassTuple) ) /* client */
  { CharArray hostname;
    Int port;
    Tuple t = s->address;

    if ( !(hostname = checkType(t->first, TypeName, NIL)) )
      return errorPce(t->first, NAME_unexpectedType, TypeName);
    if ( !(port = checkType(t->second, TypeInt, NIL)) )
      return errorPce(t->second, NAME_unexpectedType, TypeInt);

    struct addrinfo hints;
    struct addrinfo *res;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    if ( getaddrinfo(strName(hostname), NULL, &hints, &res) != 0 )
      return errorPce(s, NAME_noHost, hostname);
    assert(res && res->ai_family == AF_INET);
    memcpy(&address->sin_addr,
	   &((struct sockaddr_in*)res->ai_addr)->sin_addr,
	   sizeof(address->sin_addr));
    freeaddrinfo(res);
    address->sin_port = htons((unsigned short)valInt(port));
  } else if ( isInteger(s->address) )	/* server */
  { address->sin_port = htons((unsigned short)valInt(s->address));
    address->sin_addr.s_addr = htonl(INADDR_ANY);
  } else
    return errorPce(s->address, NAME_unexpectedType, CtoType("tuple"));

  succeed;
}


static status
bindSocket(Socket s, BoolObj reuse)
{ int rval;

  TRY(createSocket(s));

#ifdef UNIX_DOMAIN_SOCKETS
  if ( s->domain == NAME_unix )
  { struct sockaddr_un address;
    unsigned int len;
    TRY( unix_address_socket(s, &address, &len) );
    rval = bind(SocketHandle(s), (struct sockaddr *) &address, len);
  } else /*if ( s->domain == NAME_inet )*/
#endif
  { struct sockaddr_in address;
    int len;
    TRY( inet_address_socket(s, &address, &len) );

#if defined(SOL_SOCKET) && defined(SO_REUSEADDR)
    if ( reuse == ON )
    { int status = 1;

      DEBUG(NAME_socket, Cprintf("Setting SO_REUSEADDR\n"));
      if ( setsockopt(SocketHandle(s), SOL_SOCKET, SO_REUSEADDR,
		      (char *) &status, sizeof(int)) < 0 )
	return errorPce(s, NAME_socket, NAME_setsockopt, SockError());
    }
#endif

    if ( (rval = bind(SocketHandle(s), (struct sockaddr *) &address, len))==0 )
    { if ( s->address == ZERO ||
	   (instanceOfObject(s->address, ClassTuple) &&
	    ((Tuple)s->address)->second == ZERO) )
      { struct sockaddr_in addr;
#ifdef __WINDOWS__
	int len = sizeof(addr);
#else
	socklen_t len = sizeof(addr);
#endif

	if ( getsockname(SocketHandle(s), (struct sockaddr *) &addr, &len) )
	{ return errorPce(s, NAME_socket, NAME_getsockname, SockError());
	} else
	{ if ( s->address == ZERO )
	    assign(s, address, toInt(ntohs(addr.sin_port)));
	  else
	    assign((Tuple)s->address, second, toInt(ntohs(addr.sin_port)));
	}
      }
    }
  }

  if ( rval != 0 )
    return errorPce(s, NAME_socket, NAME_bind, SockError());

  succeed;
}


status
acceptSocket(Socket s)
{ SOCKET id2;
  Socket s2;
  Any client_address;

#ifdef UNIX_DOMAIN_SOCKETS
  if ( s->domain == NAME_unix )
  { struct sockaddr_un address;
    socklen_t len = sizeof(address);

    if ( (id2=accept(SocketHandle(s), (struct sockaddr *) &address, &len))<0 )
      errorPce(s, NAME_socket, NAME_accept, SockError());

    client_address = s->address;
  } else /*if ( s->domain == NAME_inet )*/
#endif
  { struct sockaddr_in address;
#ifdef __WINDOWS__
    int len = sizeof(address);
#else
    socklen_t len = sizeof(address);
#endif

    if ( (id2=accept(SocketHandle(s), (struct sockaddr *) &address, &len))<0 )
      errorPce(s, NAME_socket, NAME_accept, SockError());

    { struct hostent *hp;

      if ( (hp = gethostbyaddr((char *)&address.sin_addr,
			       sizeof(address.sin_addr),
			       AF_INET)) )
	client_address = answerObject(ClassTuple,
				      CtoName(hp->h_name),
				      toInt(address.sin_port),
				      EAV);
      else
	client_address = NIL;
    }
  }

  (void)client_address;				/* not used right now */

  if ( !(s2 = get(s, NAME_clone, EAV)) )
    return errorPce(s, NAME_failedToClone);

#ifdef HAVE_WINSOCK
  s2->ws_ref = (WsRef) id2;
  s2->rdfd = s2->wrfd = 0;		/* signal open status */
#else
  s2->rdfd = s2->wrfd = id2;
#endif
  assign(s2, input_message, s->input_message);
  assign(s2, status, NAME_accepted);
  registerClientSocket(s, s2);
  inputStream((Stream) s2, DEFAULT);

  if ( notNil(s->accept_message) )
    forwardReceiverCode(s->accept_message, s, s2, EAV);

  succeed;
}


static status
listenSocket(Socket s, Code accept_message, Int backlog, BoolObj reuse)
{ if ( isDefault(backlog) )
    backlog = toInt(5);

  TRY(bindSocket(s, reuse));

  if ( listen(SocketHandle(s), valInt(backlog)) )
    return errorPce(s, NAME_socket, NAME_listen, SockError());

  assign(s, status, NAME_listen);
  if ( notDefault(accept_message) )
    assign(s, accept_message, accept_message);
  assign(s, clients, newObject(ClassChain, EAV));
  registerSocket(s);

#ifndef RANDOM
#define RANDOM() rand()
#endif

  if ( notNil(s->authority) )
  { Int passwd = toInt(RANDOM());

    TRY(openFile(s->authority, NAME_write, DEFAULT, DEFAULT));

    if ( instanceOfObject(s->address, ClassFile) )
    { send(s->authority, NAME_format, CtoName("unix %s\n%d\n"),
	   getAbsolutePathFile(s->address));
    } else if ( instanceOfObject(s->address, ClassTuple) )
    { Tuple t = s->address;

      send(s->authority, NAME_format, CtoName("inet %s:%d\n%d\n"),
	   t->first, t->second, passwd, EAV);
    } else
    { send(s->authority, NAME_format, CtoName("inet %s:%d\n%d\n"),
	   getHostnamePce(PCE), s->address, passwd, EAV);
    }

    TRY(closeFile(s->authority));
  }

  openDisplay(CurrentDisplay(NIL));
  ws_listen_socket(s);

  succeed;
}


static status
connectSocket(Socket s)
{ int rval;

  if ( s->status == NAME_connected )
    succeed;

  TRY(createSocket(s));

#ifdef UNIX_DOMAIN_SOCKETS
  if ( s->domain == NAME_unix )
  { struct sockaddr_un address;
    unsigned int len;
    TRY( unix_address_socket(s, &address, &len) );
    rval = connect(SocketHandle(s), (struct sockaddr *) &address, len);
  } else /*if ( s->domain == NAME_inet )*/
#endif
  { struct sockaddr_in address;
    int len;
    TRY( inet_address_socket(s, &address, &len) );
    rval = connect(SocketHandle(s), (struct sockaddr *) &address, len);
  }

  if ( rval )
    return errorPce(s, NAME_socket, NAME_connect, SockError());

#ifdef HAVE_WINSOCK
  s->rdfd = s->wrfd = 0;		/* signal open status */
#endif

  assign(s, status, NAME_connected);
  registerSocket(s);

  openDisplay(CurrentDisplay(NIL));
  inputStream((Stream)s, DEFAULT);

  succeed;
}


static Any
getPeerNameSocket(Socket s)
{
#ifdef UNIX_DOMAIN_SOCKETS
  if ( s->domain == NAME_unix )
  { struct sockaddr_un *address;
    char buf[MAX_UN_ADDRESS_LEN + sizeof(address->sun_family)];
    socklen_t len;

    address = (struct sockaddr_un *) buf;
    len = sizeof(buf);

    if ( getpeername(SocketHandle(s), (struct sockaddr *) address, &len) < 0 )
    { errorPce(s, NAME_socket, NAME_peerName, SockError());
      fail;
    }

    answer(CtoName(address->sun_path));
  } else /* if ( s->domain = NAME_inet ) */
#endif /*UNIX_DOMAIN_SOCKETS*/
  { struct sockaddr_in address;
#ifdef __WINDOWS__
    int len = sizeof(address);
#else
    socklen_t len = sizeof(address);
#endif
    int port;
    unsigned long addr;
    char aname[3*4+4];

    if ( getpeername(SocketHandle(s), (struct sockaddr *) &address, &len) < 0 )
    { errorPce(s, NAME_socket, NAME_peerName, SockError());
      fail;
    }

    port = address.sin_port;
    addr = htonl(address.sin_addr.s_addr); /* TBD */
    sprintf(aname, "%d.%d.%d.%d",
	    (int)((addr >> 24) & 0xff),
	    (int)((addr >> 16) & 0xff),
	    (int)((addr >>  8) & 0xff),
	    (int) (addr        & 0xff));

    answer(answerObject(ClassTuple, CtoName(aname), toInt(port), EAV));
  }
}


		 /*******************************
		 *	    EXCEPTIONS		*
		 *******************************/

static status
eofSocket(Socket s)
{ if ( s->status == NAME_accepted )
    freeObject(s);

  succeed;
}


static status
brokenPipeSocket(Socket s)
{ succeed;
}


static status
closeSocket(Socket s)
{ closeStream((Stream) s);

  if ( notNil(s->clients) )		/* destroy clients */
  { Socket client;

    for_chain(s->clients, client, closeSocket(client));
  }

  if ( notNil(s->master) )		/* delete client <->master link */
    unregisterClientSocket(s->master, s);

  if ( s->domain == NAME_unix &&	/* remove from file-system */
       s->status == NAME_listen )
    removeFile(s->address);

  assign(s, status, NAME_idle);
  unregisterSocket(s);

  succeed;
}


		 /*******************************
		 *	 CLASS DECLARATION	*
		 *******************************/

/* Type declarations */

static char *T_listen[] =
        { "accept_message=[code]*", "backlog=[{1..5}]", "reuse=[bool]" };
static char *T_initialise[] =
        { "address=file|tuple|int*", "domain=[{unix,inet}]" };

/* Instance Variables */

static vardecl var_socket[] =
{ IV(NAME_address, "file|tuple|int*", IV_GET,
     NAME_address, "Address for the connection end-point"),
  IV(NAME_domain, "{unix,inet}", IV_GET,
     NAME_address, "Domain for the connection"),
  IV(NAME_status, "{idle,listen,accepted,connected}", IV_GET,
     NAME_status, "Status of the associated socket"),
  IV(NAME_acceptMessage, "code*", IV_BOTH,
     NAME_connect, "Message to accept new client"),
  IV(NAME_clients, "chain*", IV_GET,
     NAME_server, "Chain with accepted connections"),
  IV(NAME_master, "socket*", IV_GET,
     NAME_server, "Socket I accepted a connection for"),
  IV(NAME_authority, "file*", IV_GET,
     NAME_authority, "Name of authority-file (if any)")
};

/* Send Methods */

static senddecl send_socket[] =
{ SM(NAME_initialise, 2, T_initialise, initialiseSocket,
     DEFAULT, "Create socket from address and domain"),
  SM(NAME_unlink, 0, NULL, unlinkSocket,
     DEFAULT, "Cleanup socket"),
  SM(NAME_connect, 0, NULL, connectSocket,
     NAME_connect, "Connect with server socket"),
  SM(NAME_listen, 3, T_listen, listenSocket,
     NAME_connect, "Listen for connection requests]"),
  SM(NAME_brokenPipe, 0, NULL, brokenPipeSocket,
     NAME_control, "Attempt to write on broken connection"),
  SM(NAME_endOfFile, 0, NULL, eofSocket,
     NAME_input, "EOF read"),
  SM(NAME_close, 0, NULL, closeSocket,
     NAME_output, "Close communication to socket")
};

/* Get Methods */

static getdecl get_socket[] =
{ GM(NAME_printName, 0, "string", NULL, getPrintNameSocket,
     DEFAULT, "returns <classname>(<address>)"),
  GM(NAME_peerName, 0, "name|tuple", NULL, getPeerNameSocket,
     NAME_address, "Description of the other-sides address")
};

/* Resources */

#define rc_socket NULL
/*
static classvardecl rc_socket[] =
{
};
*/

/* Class Declaration */

static Name socket_termnames[] = { NAME_address };

ClassDecl(socket_decls,
          var_socket, send_socket, get_socket, rc_socket,
          1, socket_termnames,
          "$Rev$");

status
makeClassSocket(Class class)
{ declareClass(class, &socket_decls);

  setCloneFunctionClass(class, cloneSocket);
  cloneStyleVariableClass(class, NAME_clients, NAME_nil);
  cloneStyleVariableClass(class, NAME_master, NAME_nil);

#ifdef HAVE_SOCKET
  featureClass(class, NAME_unixDomain, ON);
#endif
  featureClass(class, NAME_inetDomain, ON);

  SocketChain = globalObject(NAME_openSockets, ClassChain, EAV);

  succeed;
}

#endif /*HAVE_SOCKET*/
