summaryrefslogtreecommitdiff
path: root/comm
diff options
context:
space:
mode:
authorchatty1993-04-07 11:50:31 +0000
committerchatty1993-04-07 11:50:31 +0000
commitba066c34dde204aa192d03a23a81356374d93731 (patch)
tree39391f6235d2cf8a59a0634ac5ea430cdd21f5d4 /comm
parent05ab076e1c2a9ca16472f9a6b47b8d22914b3783 (diff)
downloadivy-league-ba066c34dde204aa192d03a23a81356374d93731.zip
ivy-league-ba066c34dde204aa192d03a23a81356374d93731.tar.gz
ivy-league-ba066c34dde204aa192d03a23a81356374d93731.tar.bz2
ivy-league-ba066c34dde204aa192d03a23a81356374d93731.tar.xz
Initial revision
Diffstat (limited to 'comm')
-rw-r--r--comm/Address.cc350
-rw-r--r--comm/Address.h118
-rw-r--r--comm/Channel.cc402
-rw-r--r--comm/Channel.h114
-rw-r--r--comm/Datagram.cc166
-rw-r--r--comm/Datagram.h40
-rw-r--r--comm/Imakefile99
-rw-r--r--comm/Message.cc98
-rw-r--r--comm/Message.h32
-rw-r--r--comm/MsgBuffer.cc707
-rw-r--r--comm/MsgBuffer.h112
-rw-r--r--comm/MsgStream.cc506
-rw-r--r--comm/MsgStream.h70
-rw-r--r--comm/Multiplexer.cc463
-rw-r--r--comm/Multiplexer.h83
-rw-r--r--comm/OLD/Agent.cc386
-rw-r--r--comm/OLD/Agent.h82
-rw-r--r--comm/OLD/Event.cc121
-rw-r--r--comm/OLD/Event.h92
-rw-r--r--comm/OLD/PortServer.cc410
-rw-r--r--comm/OLD/PortServer.h48
-rw-r--r--comm/OLD/PortServerReq.cc60
-rw-r--r--comm/OLD/PortServerReq.h45
-rw-r--r--comm/OLD/ReqMgr.cc184
-rw-r--r--comm/OLD/ReqMgr.h81
-rw-r--r--comm/OLD/ReqMgr.l95
-rw-r--r--comm/OLD/ReqMgr.y285
-rw-r--r--comm/OLD/Server.cc395
-rw-r--r--comm/OLD/Server.h77
-rw-r--r--comm/OLD/Service.cc328
-rw-r--r--comm/OLD/Service.h100
-rw-r--r--comm/OLD/SimpleMessage.cc47
-rw-r--r--comm/OLD/SimpleMessage.h23
-rw-r--r--comm/OLD/TextStream.cc1030
-rw-r--r--comm/OLD/TextStream.h157
-rw-r--r--comm/OLD/dgram.cc717
-rw-r--r--comm/OLD/dgram.h102
-rw-r--r--comm/OLD/portserv.cc653
-rw-r--r--comm/OLD/porttest.cc72
-rw-r--r--comm/OLD/reqgen.cc34
-rw-r--r--comm/Socket.cc274
-rw-r--r--comm/Socket.h60
-rw-r--r--comm/Stream.cc124
-rw-r--r--comm/Stream.h33
-rw-r--r--comm/TimeOut.cc69
-rw-r--r--comm/TimeOut.h32
-rw-r--r--comm/doc.main396
-rw-r--r--comm/error.cc261
-rw-r--r--comm/error.h58
-rw-r--r--comm/global.h28
-rw-r--r--comm/version.h75
51 files changed, 10394 insertions, 0 deletions
diff --git a/comm/Address.cc b/comm/Address.cc
new file mode 100644
index 0000000..789a370
--- /dev/null
+++ b/comm/Address.cc
@@ -0,0 +1,350 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Addresses
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "Address.h"
+#include "error.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <memory.h>
+#include <netdb.h>
+
+/*?class UchAddress
+This class is a virtual base class: no objects of this class are ever created.
+It implements the Unix type \typ{^{struct sockaddr}}, used for creating sockets.
+Addresses are used mainly as arguments of constructors for the class
+\typ{UchSocket} and its derived classes.
+All its member functions are virtual.
+These functions exist for all the derived classes but are only described here.
+An address can be valid or invalid, depending on the constructor being able to create the address or not.
+There are currently two derived classes, for Unix domain addresses and Internet domain addresses.
+
+The class \typ{^{pUchAddress}} implements smart pointers to addresses.
+Smart pointers behave like pointers but they manage a reference count on the
+pointed to objects for an automatic reclaim of dynamically allocated objects.
+?*/
+
+/*?
+Construct an invalid address.
+?*/
+UchAddress :: UchAddress ()
+: Valid (FALSE)
+{
+}
+
+/*?nodoc?*/
+UchAddress :: ~UchAddress ()
+{
+}
+
+// virtual functions for class address
+//
+
+/*?
+Return one of \var{AF_UNSPEC}, \var{AF_UNIX} or \var{AF_INET}.
+Other values may be defined if supported by the system.
+?*/
+int
+UchAddress :: Family ()
+{
+ return AF_UNSPEC;
+}
+
+/*?nextdoc?*/
+int
+UchAddress :: Length ()
+{
+ return 0;
+}
+
+/*?
+Return the address structure and its length.
+\typ{^{SockAddr}} is a typedef for \typ{^{struct sockaddr}}.
+?*/
+SockAddr*
+UchAddress :: GetSockAddr ()
+{
+ return 0;
+}
+
+/*?nodoc?*/
+char*
+UchAddress :: StrRepr (char* buf)
+{
+ strcpy (buf, "virtual address !");
+ return buf;
+}
+
+#ifdef UNIX_SOCK
+
+// constructors for Unix addresses
+// a Unix domain address is just a filename
+// -> some problems: check for access, when to unlink it ?
+// to do : generate name from a template (for uniqueness)
+//
+
+/*?nodoc?*/
+UchUnixAddress :: UchUnixAddress ()
+: UchAddress ()
+{
+ // nothing
+}
+
+/*?
+Create a Unix domain address associated to file \var{filename}.
+The file is not unlinked when the address is destroyed;
+it should be unlinked by the application whenever the last socket using this address is closed.
+?*/
+UchUnixAddress :: UchUnixAddress (const char* filename)
+: UchAddress ()
+{
+ Addr.sun_family = AF_UNIX;
+ strcpy (Addr.sun_path, filename);
+ Valid = TRUE; // should check access to file ???
+ // unlink if already there ?
+}
+
+/*?nodoc?*/
+UchUnixAddress :: ~UchUnixAddress ()
+{
+ // how to unlink the file ??
+}
+
+/*?nodoc?*/
+int
+UchUnixAddress :: Family ()
+{
+ return AF_UNIX;
+}
+
+/*?nodoc?*/
+int
+UchUnixAddress :: Length ()
+{
+ if (Valid)
+ return (Addr.sun_path + strlen (Addr.sun_path)) - (char*) &Addr;
+ return 0;
+}
+
+/*?nodoc?*/
+SockAddr*
+UchUnixAddress :: GetSockAddr ()
+{
+ return (SockAddr*) &Addr;
+}
+
+/*?nodoc?*/
+char*
+UchUnixAddress :: StrRepr (char* buf)
+{
+ sprintf (buf, "%s", Addr.sun_path);
+ return buf;
+}
+
+#endif /* UNIX_SOCK */
+
+// constructors for inet addresses
+// inet address specified by:
+// hostname / port# (ANY if hostname is NIL, LOOPBACK if hostname is "")
+// hostid / port# (predefined hostids ANYADDR, ...)
+// todo:
+// hostname / service name ??
+//
+/*?nodoc?*/
+UchInetAddress :: UchInetAddress ()
+: UchAddress ()
+{
+ // nothing
+}
+
+/*?
+Construct an inet address, from a hostname and a port number.
+The address is valid only if the host is valid.
+If \var{host} is the empty string, the loopback host is used;
+if it is the nil pointer, the wildcard is used.
+If the port number is 0, it will be allocated by the system.
+?*/
+UchInetAddress :: UchInetAddress (const char* name, sword port)
+: UchAddress ()
+{
+ struct hostent *host;
+
+ if (name && *name) {
+ host = gethostbyname (name);
+ if (! host)
+ return;
+ memcpy (&Addr.sin_addr, host->h_addr, host->h_length);
+ } else
+ Addr.sin_addr.s_addr = name ? INADDR_LOOPBACK : INADDR_ANY;
+ Addr.sin_family = AF_INET;
+ Addr.sin_port = htons (port);
+ Valid = TRUE;
+}
+
+/*?
+Construct an inet address, from a host id (internet address) and a port number.
+If the port number is 0, it is assigned by the system.
+The host ids \var{^{ANYADDR}}, \var{^{LOOPBACK}} and \var{^{BROADCAST}} are predefined.
+\var{ANYADDR} is used to receive messages from anywhere.
+\var{LOOPBACK} is the local host address.
+\var{BROADCAST} makes it possible to send data to all the hosts of a local area network.
+?*/
+UchInetAddress :: UchInetAddress (lword host, sword port)
+: UchAddress ()
+{
+ Addr.sin_family = AF_INET;
+ Addr.sin_port = htons (port);
+ Addr.sin_addr.s_addr = host;
+ Valid = TRUE;
+}
+
+/*?nodoc?*/
+UchInetAddress :: ~UchInetAddress ()
+{
+}
+
+/*?nodoc?*/
+int
+UchInetAddress :: Family ()
+{
+ return AF_INET;
+}
+
+/*?nodoc?*/
+int
+UchInetAddress :: Length ()
+{
+ return sizeof (Addr);
+}
+
+/*?nodoc?*/
+SockAddr*
+UchInetAddress :: GetSockAddr ()
+{
+ return (SockAddr*) &Addr;
+}
+
+/*?nodoc?*/
+char*
+UchInetAddress :: StrRepr (char* buf)
+{
+ struct hostent *host;
+ char *hname;
+ unsigned long saddr;
+
+ saddr = Addr.sin_addr.s_addr;
+ host = gethostbyaddr ((char*) &saddr, sizeof (long), AF_INET);
+ if (host)
+ hname = host->h_name;
+ else
+ if (saddr == INADDR_ANY)
+ hname = "ANY";
+ else if (saddr == INADDR_LOOPBACK)
+ hname = "LOOPBACK";
+ else if (saddr == INADDR_BROADCAST)
+ hname = "BROADCAST";
+ else
+ hname = "???";
+ sprintf (buf, "%s::%d", hname, ntohs (Addr.sin_port));
+ return buf;
+}
+
+/*?
+This is a global function (static member of class \typ{UchAddress}).
+It creates an object of a derived class of \typ{UchAddress} from a generic address
+(thus is cannot be replaced by a constructor).
+A generic address is the following union of address structures
+(it is typically returned by system calls like \fun{recvfrom}):
+\begin{ccode}
+typedef union {
+ struct sockaddr sa; // default
+ struct sockaddr_un su; // Unix
+ struct sockaddr_in si; // inet
+} GEN_ADDR;
+\end{ccode}
+?*/
+UchAddress*
+UchAddress :: Decode (GEN_ADDR* addr, int alen)
+{
+ switch (addr->sa.sa_family) {
+#ifdef UNIX_SOCK
+ case AF_UNIX :
+ addr->su.sun_path [alen] = 0;
+ return new UchUnixAddress (addr->su.sun_path);
+#endif
+ case AF_INET :
+ return new UchInetAddress (addr->si.sin_addr.s_addr, addr->si.sin_port);
+
+ default :
+ return 0;
+ }
+}
+
+/*?
+This is a global function (static member of class \typ{UchInetAddress}.
+It returns the internet address of the local host.
+This is different from the predefined address \var{LOOPBACK}:
+\var{LOOPBACK} is the same constant on each machine, so that it cannot
+be communicated across the network.
+The value returned by \fun{LoopBack} is the unique internet address of the local host.
+Thus it can be communicated to another host across the network.
+?*/
+lword
+UchInetAddress :: LoopBack ()
+{
+ static lword loopback = 0;
+
+ if (loopback)
+ return loopback;
+
+ struct hostent *host;
+ char name [128];
+
+ gethostname (name, sizeof (name));
+ host = gethostbyname (name);
+ if (! host) {
+ Error (ErrFatal, "address", "cannot get address of local host");
+ return 0;
+ }
+ return loopback = * ((lword *) host->h_addr);
+}
+
+#ifdef DOC
+// fake entries for inline functions
+
+/*?
+Return TRUE if the address is valid.
+?*/
+bool
+UchAddress :: IsValid ()
+{}
+
+/*?
+Return the host number of the address.
+?*/
+lword
+UchInetAddress :: Host ()
+{}
+
+/*?
+Return the port number of the address.
+?*/
+sword
+UchInetAddress :: Port ()
+{}
+
+#endif /* DOC */
+
diff --git a/comm/Address.h b/comm/Address.h
new file mode 100644
index 0000000..b220339
--- /dev/null
+++ b/comm/Address.h
@@ -0,0 +1,118 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Addresses
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#ifndef Address_H_
+#define Address_H_
+
+#include "cplus_bugs.h"
+#include "global.h"
+#include "ccu/SmartPointer.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#ifdef UNIX_SOCK
+#include <sys/un.h>
+#endif
+
+#ifndef AF_UNIX
+#define AF_UNIX AF_UNSPEC
+#endif
+
+
+typedef struct sockaddr SockAddr;
+
+/* this union is useful for adresses returned by system calls */
+
+typedef union {
+ struct sockaddr sa; // default
+#ifdef UNIX_SOCK
+ struct sockaddr_un su; // unix
+#endif
+ struct sockaddr_in si; // inet
+} GEN_ADDR;
+
+
+class UchAddress : public CcuSmartData {
+protected:
+ bool Valid;
+
+public:
+ UchAddress ();
+ ~UchAddress ();
+
+ bool IsValid () const { return Valid; }
+virtual int Family (); // AF_UNSPEC, AF_UNIX, AF_INET
+virtual int Length ();
+virtual SockAddr* GetSockAddr ();
+virtual char* StrRepr (char* buf);
+
+static UchAddress* Decode (GEN_ADDR*, int);
+};
+
+PointerClass (pUchAddress, UchAddress)
+
+class UchInetAddress : public UchAddress {
+protected:
+ struct sockaddr_in Addr;
+
+public:
+ UchInetAddress ();
+ UchInetAddress (lword, sword = 0); // hostid, port#
+ UchInetAddress (const char*, sword = 0); // hostname, port#
+ ~UchInetAddress ();
+
+ int Family ();
+ int Length ();
+ SockAddr* GetSockAddr ();
+inline sword Port () { return Addr.sin_port; }
+inline lword Host () { return Addr.sin_addr.s_addr; }
+ char* StrRepr (char* buf);
+
+static lword LoopBack ();
+};
+
+#ifndef INADDR_LOOPBACK
+#define INADDR_LOOPBACK (INET_ADDR::LoopBack ())
+#endif
+
+#define ANYADDR ((lword) INADDR_ANY)
+#define LOOPBACK ((lword) INADDR_LOOPBACK)
+#define BROADCAST ((lword) INADDR_BROADCAST)
+
+
+
+#ifdef UNIX_SOCK
+
+class UchUnixAddress : public UchAddress {
+protected:
+ struct sockaddr_un Addr;
+
+public:
+ UchUnixAddress ();
+ UchUnixAddress (const char*);
+ ~UchUnixAddress ();
+
+ int Family ();
+ int Length ();
+ SockAddr* GetSockAddr ();
+ char* StrRepr (char* buf);
+};
+
+#endif /* UNIX_ADDR */
+
+
+#endif /* Address_H_ */
+
diff --git a/comm/Channel.cc b/comm/Channel.cc
new file mode 100644
index 0000000..b8178aa
--- /dev/null
+++ b/comm/Channel.cc
@@ -0,0 +1,402 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * File descriptors, channels
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "Channel.h"
+#include "MsgBuffer.h"
+#include "error.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/socket.h>
+
+CH_HANDLER UchChannel::ReadHandler = 0;
+CH_HANDLER UchChannel::WriteHandler = 0;
+
+//
+// this section implements refcounts for file descriptors, so that they
+// are closed only when the last occurence is deleted
+//
+static int Refs [NFILE];
+static int Inited = 0;
+
+/*?nodoc?*/
+void
+_AddRef (int fd)
+{
+ if (fd < 0)
+ return;
+ if (! Inited) {
+ Inited = 1;
+ for (int i = 0; i < NFILE; i++)
+ Refs [i] = 0;
+ Refs [0] = Refs [1] = Refs [2] = 1;
+ }
+ Refs [fd]++;
+#ifdef SMART_DEBUG
+ dbgprintf ("AddRef %d -> %d\n", fd, Refs [fd]);
+#endif
+#ifdef _TRACE2
+ _TRACE2 (fd, Refs [fd]);
+#endif
+}
+
+/*?nodoc?*/
+void
+_DelRef (int fd)
+{
+ if (fd < 0)
+ return;
+ if (--Refs [fd] == 0)
+ close (fd);
+#ifdef SMART_DEBUG
+ dbgprintf ("DelRef %d -> %d\n", fd, Refs [fd]);
+#endif
+#ifdef _TRACE2
+ _TRACE2 (fd, Refs [fd]);
+#endif
+}
+
+/*?nodoc?*/
+char*
+UchFilDes :: StrRepr (char* buf)
+{
+ sprintf (buf, "%d", Fd);
+ return buf;
+}
+
+/*?class UchFilDes
+This class implements objects representing Unix file descriptors.
+When the associated file descriptor is -1, the \typ{UchFilDes} is said to be closed,
+else it is said to be opened.
+Because several objects of this class can refer to the same file descriptor,
+the destructor closes the file descriptor only if it is the last object referring to this file descriptor.
+This is implemented with the help of a reference count, the overloading of \fun{operator =}
+and the definition of the constructor \fun{UchFilDes(UchFilDes &)}.
+All member functions described here are inline for maximum efficiency.
+?*/
+
+#ifdef DOC
+
+/*?
+Construct a closed file descriptor.
+?*/
+UchFilDes :: UchFilDes ()
+{ }
+
+/*?
+Construct an open file descriptor for the Unix file descriptor \var{fd}.
+In particular, this makes it possible to pass an integer where a \typ{UchFilDes} is normally expected.
+This is most useful when using Unix file descriptors.
+?*/
+UchFilDes :: UchFilDes (int fd)
+{ }
+
+/*?
+This conversion operator makes it possible to pass a \typ{UchFilDes} argument where
+an integer is expected. This is most useful for system calls.
+?*/
+int
+UchFilDes :: operator int ()
+{ }
+
+/*?
+Return the Unix file descriptor, or -1 if it is closed.
+?*/
+int
+UchFilDes :: FilDes ()
+{ }
+
+/*?
+Open the object on file descriptor \var{fd}.
+If it was already opened, it is closed first.
+?*/
+void
+UchFilDes :: Open (int fd)
+{ }
+
+/*?
+Close a file descriptor. If it is opened, this actually closes the file descriptor
+only if it was the last reference to it.
+\fun{Close} is automatically called by the destructor.
+?*/
+void
+UchFilDes :: Close ()
+{ }
+
+/*?
+Return TRUE if the object is opened, FALSE else.
+?*/
+bool
+UchFilDes :: Opened ()
+{ }
+
+/*?
+Read at most \var{n} bytes into buffer \var{b} and return the number of bytes actually read.
+This is the Unix \fun{read} system call.
+For efficiency, this function does not test whether the file descriptor is opened.
+?*/
+int
+UchFilDes :: Read (byte* b, int n)
+{ }
+
+/*?
+Write at most \var{n} bytes of buffer \var{b} and return the number of bytes actually written.
+This is the Unix \fun{write} system call.
+For efficiency, this function does not test whether the file descriptor is opened.
+?*/
+int
+UchFilDes :: Write (byte* b, int n)
+{ }
+
+#endif /* DOC */
+
+/*?class UchChannel
+A \typ{UchChannel} is basically a file descriptor, with more strict semantics than a \typ{UchFilDes}.
+A channel is immutable: once initialized, it cannot change its file descriptor.
+It has a mode, of type \typ{^{IOMODE}}, with possible values
+\var{IONone}, \var{IORead}, \var{IOWrite}, \var{IOReadWrite},
+\var{IOSelect}, \var{IOReadSelect}, \var{IOWriteSelect}, \var{IOAll}.
+\index{IOMODE :: IONone}\index{IOMODE :: IORead}\index{IOMODE :: IOWrite}\index{IOMODE :: IOReadWrite}\index{IOMODE :: IOSelect}\index{IOMODE :: IOReadSelect}\index{IOMODE :: IOWriteSelect}\index{IOMODE :: IOAll}
+Before being initialized, a UchChannel.has mode \var{IONone} and is said to be closed.
+
+A UchChannel.has a number of virtual functions.
+The destructor is virtual; this is useful for classes that store pointers to objects
+of a class derived from \typ{UchChannel} (like in the class \typ{UchSocket}).
+The functions \fun{HandleSelect}, \fun{HandleRead} and \fun{HandleWrite}
+are intended to give a channel the ability to automatically handle input/output,
+for instance for use with the \fun{select} system call.
+Such capabilities are used and thus illustrated in class \typ{UchMultiplexer}.
+
+The class \typ{^{pUchChannel}} implements smart pointers to channels.
+Smart pointers behave like pointers but they manage a reference count on the
+pointed to objects for an automatic reclaim of dynamically allocated objects.
+This is very useful especially in the class \typ{UchMultiplexer} because arrays of
+(smart) pointers to buffers are used.
+?*/
+
+/*?
+Construct a closed channel.
+?*/
+UchChannel :: UchChannel ()
+: Fd (),
+ Mode (IONone)
+{
+}
+
+/*?
+Construct an open channel on file descriptor \var{fd} with mode \var{io}.
+?*/
+UchChannel :: UchChannel (int fd, IOMODE io)
+: Fd (fd),
+ Mode (io)
+{
+}
+
+/*?nodoc?*/
+UchChannel :: UchChannel (const UchChannel& ch)
+: Fd (ch.Fd),
+ Mode (ch.Mode)
+{
+}
+
+#ifdef DOC
+/*?
+Open the channel on file descriptor \var{fd}.
+A channel can only be opened once, and it cannot be closed.
+This function is useful when the channel was created with the default contructor,
+in order to associate a file descriptor to it.
+Because a channel is immutable, this can be done only once.
+?*/
+void
+UchChannel :: Open (int fd)
+{ }
+
+/*?nextdoc?*/
+IOMODE
+UchChannel :: IOMode ()
+{
+}
+
+/*?
+Return/set the mode of the channel.
+?*/
+void
+UchChannel :: SetMode (IOMODE io)
+{ }
+
+#endif /* DOC */
+
+
+/*?nodoc?*/
+UchChannel :: ~UchChannel ()
+{
+ // nothing special here
+}
+
+/*?nodoc?*/
+UchChannel*
+UchChannel :: Copy () const
+{
+ return new UchChannel (*this);
+}
+
+/*?nextdoc?*/
+void
+UchChannel :: AddNotify (UchMultiplexer&)
+{
+}
+
+/*?
+These virtual functions are called whenever a channel is added to (resp. removed from)
+a multiplexer. The default implementation does nothing.
+?*/
+void
+UchChannel :: RemoveNotify (UchMultiplexer&)
+{
+}
+
+/*?nextdoc?*/
+void
+UchChannel :: HandleWrite ()
+{
+ if (! UchChannel::WriteHandler)
+ Error (ErrFatal, "UchChannel::HandleWrite", ErrorTable [ErrShouldImplement]);
+ else
+ (* UchChannel::WriteHandler) (this);
+}
+
+/*?
+These virtual functions are called by a \typ{UchMultiplexer} when data can be written
+to the channel or read from the channel.
+They are are normally redefined in derived classes.
+The default implementation is the following:
+if the static members \var{UchChannel::ReadHandler} (resp. \var{UchChannel::WriteHandler}) is set,
+it is called by \fun{HandleRead} (resp. \fun{HandleWrite});
+else an error message is output.
+These members are public so that they can be assigned directly at any time.
+By default they are not set.
+The type of these static members is \typ{^{CH_HANDLER}}:
+pointer to void function with one argument of type \typ{UchChannel*}.
+?*/
+void
+UchChannel :: HandleRead ()
+{
+ if (! UchChannel::ReadHandler)
+ Error (ErrFatal, "UchChannel::HandleRead", ErrorTable [ErrShouldImplement]);
+ else
+ (* UchChannel::ReadHandler) (this);
+}
+
+/*?
+This virtual function is called by a \typ{UchMultiplexer} before making a \fun{select} call.
+It is intended to handle any background task or buffered input/output associated to the channel.
+If it returns TRUE, the channel set scan functions will return before performing the select call.
+See the class \typ{UchMultiplexer} for more details.
+The default implementation does nothing but returning FALSE.
+?*/
+bool
+UchChannel :: HandleSelect ()
+{
+ return FALSE;
+}
+
+#ifdef DOC
+
+/*?nextdoc?*/
+int
+UchChannel :: Read (byte* b, int n)
+{ }
+
+/*?
+The Unix \fun{read} and \fun{write} system calls, as in class \typ{UchFilDes}.
+?*/
+int
+UchChannel :: Write (byte* b, int n)
+{ }
+
+#endif /* DOC */
+
+/*?nextdoc?*/
+int
+UchChannel :: Read (UchMsgBuffer& b)
+{
+ int l = b.FreeLength ();
+ if (! l)
+ return -2;
+ int n = read (Fd, (char*) b.Free (), l);
+ if (n > 0)
+ b.More (n);
+ return n;
+}
+
+/*?
+The Unix \fun{read} and \fun{write} system calls applied to a \typ{UchMsgBuffer}.
+They return the number of bytes transferred, -1 if a system error occurred,
+-2 if the buffer is empty when writing or full when reading.
+?*/
+int
+UchChannel :: Write (UchMsgBuffer& b)
+{
+ int l = b.BufLength ();
+ if (! l)
+ return -2;
+ int n = write (Fd, (const char*) b.Buffer (), l);
+ if (n > 0)
+ b.Flush (n);
+ return n;
+}
+
+/*?nextdoc?*/
+bool
+UchChannel :: ReadBuffer (UchMsgBuffer& b)
+{
+ int n;
+ errno = 0;
+ while ((n = Read (b)) > 0);
+ return bool (n == -2);
+}
+
+/*?
+These functions repeatedly call \fun{Read} or \fun{Write} until the whole buffer is
+transferred or a system error occurred. In case of an error, they return FALSE, else TRUE.
+?*/
+bool
+UchChannel :: WriteBuffer (UchMsgBuffer& b)
+{
+ int n;
+ errno = 0;
+ while ((n = Write (b)) > 0);
+ return bool (n == -2);
+}
+
+/*?
+This function is intended for sockets that accept connections.
+It returns a dynamically allocated new channel that is opened on the accepted connection.
+If it fails, it returns 0.
+?*/
+UchChannel*
+UchChannel :: Accept ()
+{
+ int fd;
+
+ errno = 0;
+ if (Fd < 0)
+ return (UchChannel*) 0;
+
+ if ((fd = accept (Fd, 0, 0)) < 0)
+ return (UchChannel*) 0;
+
+ return new UchChannel (fd);
+}
+
diff --git a/comm/Channel.h b/comm/Channel.h
new file mode 100644
index 0000000..302d05d
--- /dev/null
+++ b/comm/Channel.h
@@ -0,0 +1,114 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * File descriptors, channels
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#ifndef Channel_H_
+#define Channel_H_
+
+#include "cplus_bugs.h"
+#include "global.h"
+#include "ccu/SmartPointer.h"
+
+// we need read and write system calls
+#include <unistd.h>
+
+class UchMsgBuffer;
+
+#ifndef NFILE
+#define NFILE 20
+#endif
+
+enum IOMODE { IONone = 0, IORead = 1, IOWrite = 2,
+ IOReadWrite = 3, IOSelect = 4, IOReadSelect = 5,
+ IOWriteSelect = 6, IOAll = 7 };
+
+// Declarations for inlines
+//
+extern void _AddRef (int);
+extern void _DelRef (int);
+
+extern char* StrReprBuf;
+
+class UchFilDes {
+protected:
+ int Fd;
+public:
+inline UchFilDes () : Fd (-1) { }
+inline UchFilDes (const UchFilDes& fd) { _AddRef (Fd = fd.Fd); }
+inline UchFilDes (int fd) { _AddRef (Fd = fd); }
+inline ~UchFilDes () { _DelRef (Fd); }
+
+inline operator int () const { return Fd; }
+inline UchFilDes& operator = (const UchFilDes& fd) { _DelRef (Fd); Fd = fd.Fd; _AddRef (Fd); return *this; }
+inline int FilDes () { return Fd; }
+inline void Open (int fd) { _DelRef (Fd); _AddRef (Fd = fd); }
+inline void Close () { _DelRef (Fd); Fd = -1; }
+inline bool Opened () { return bool (Fd >= 0); }
+inline int Read (byte* b, int n) { return read (Fd, (char*) b, n); }
+inline int Write (byte* b, int n) { return write (Fd, (const char*) b, n); }
+ char* StrRepr (char* = StrReprBuf);
+};
+
+// This class defines channels, ie file descriptors with handlers for read/write.
+// The file descriptor of a channel is immutable: once created, it cannot change;
+// this is crucial for channel sets, for instance.
+// However, because Copy and operator= are defined,immutability can be
+// potentially bypassed...
+// UchChannel also derives from CcuSmartData, for building smart pointers
+//
+class UchChannel;
+typedef void (* CH_HANDLER) (UchChannel *);
+class UchMultiplexer;
+
+class UchChannel : public CcuSmartData {
+friend class UchMultiplexer;
+protected:
+ UchFilDes Fd;
+ IOMODE Mode;
+
+ void AddNotify (UchMultiplexer&);
+ void RemoveNotify (UchMultiplexer&);
+
+public:
+ UchChannel ();
+ UchChannel (int, IOMODE = IOReadWrite);
+ UchChannel (const UchChannel& ch);
+ ~UchChannel ();
+
+inline void Open (int fd) { if (Fd < 0) Fd.Open (fd); }
+inline IOMODE IOMode () const { return Mode; }
+inline void SetMode (IOMODE io) { Mode = io; }
+ int Read (UchMsgBuffer&);
+ int Write (UchMsgBuffer&);
+ bool ReadBuffer (UchMsgBuffer&);
+ bool WriteBuffer (UchMsgBuffer&);
+ UchChannel* Accept ();
+virtual UchChannel* Copy () const;
+virtual void HandleWrite ();
+virtual void HandleRead ();
+virtual bool HandleSelect ();
+
+inline int FilDes () const { return Fd; }
+inline int Read (byte* b, int n) { return read (Fd, (char*) b, n); }
+inline int Write (byte* b, int n) { return write (Fd, (const char*) b, n); }
+inline operator int () { return Fd; }
+inline char* StrRepr (char* s = StrReprBuf) { return Fd.StrRepr (s); }
+
+static CH_HANDLER ReadHandler;
+static CH_HANDLER WriteHandler;
+};
+
+PointerClass (pUchChannel, UchChannel)
+
+#endif /* Channel_H_ */
+
diff --git a/comm/Datagram.cc b/comm/Datagram.cc
new file mode 100644
index 0000000..b88deb5
--- /dev/null
+++ b/comm/Datagram.cc
@@ -0,0 +1,166 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Datagrams
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "Datagram.h"
+#include "MsgBuffer.h"
+#include <stdlib.h>
+#include <sys/socket.h>
+
+/*?class UchDatagram
+A datagram socket can send to and receive from any other datagram socket,
+unless it is connected.
+Thus, establishing a datagram connection is simple.
+
+UchDatagram sockets are not reliable: messages can be lost, duplicated, or be received in a different order.
+They keep the message boundaries: when \var{n} bytes are written, you will read at most \var{n} bytes;
+but if you ask to read less than \var{n} bytes, then the end of the message will be lost.
+
+When a datagram socket is not connected, you must provide an address when you send a message;
+when a message is read, the address of the sender can be retrieved (with function \fun{From}).
+When a datagram socket is connected, messages can only be sent to and read from the
+connected address. The \fun{Read} and \fun{Write} calls can be used in this case.
+
+Before any data can be sent or received, the socket must be set up with \fun{Setup}.
+?*/
+
+/*?nextdoc?*/
+UchDatagram :: UchDatagram ()
+: UchSocket (),
+ FAddr (0)
+{
+}
+
+/*?
+These constructors are similar to those of the class \typ{UchSocket}.
+?*/
+UchDatagram :: UchDatagram (UchAddress* bound, UchAddress* connected)
+: UchSocket (bound, connected),
+ FAddr (0)
+{
+}
+
+/*?nodoc?*/
+UchDatagram :: UchDatagram (const UchDatagram& d)
+: UchSocket (d),
+ FAddr (d.FAddr)
+{
+}
+
+/*?nodoc?*/
+UchDatagram :: ~UchDatagram ()
+{
+}
+
+#ifdef DOC
+/*?
+Return the address of the sender of the last received message.
+?*/
+UchAddress*
+UchDatagram :: From ()
+{ }
+
+#endif /* DOC */
+
+/*?nodoc?*/
+UchChannel*
+UchDatagram :: Copy () const
+{
+ return new UchDatagram (*this);
+}
+
+/*?nodoc?*/
+int
+UchDatagram :: SockType ()
+{
+ return SOCK_DGRAM;
+}
+
+/*?
+Send \var{len} bytes of \var{buf} on the datagram, to a destination address \var{to}.
+Return the number of bytes actually transferred, or -1 if a system error occurred.
+If the datagram is connected, you must use \fun{Write} instead.
+?*/
+int
+UchDatagram :: Send (byte* buf, int len, UchAddress& to)
+{
+ return sendto (FilDes (), (char*) buf, len, 0, to.GetSockAddr (), to.Length ());
+}
+
+/*?
+Receive at most \var{len} bytes into \var{buf}.
+Return the number of bytes actually transferred, or -1 if a system error occurred.
+The address of the sender can be retrieved with \fun{From}.
+If the socket is connected, you must use \fun{Read} instead.
+?*/
+int
+UchDatagram :: Receive (byte* buf, int len)
+{
+ GEN_ADDR addr;
+ int alen = sizeof (addr);
+ int ret;
+
+ ret = recvfrom (FilDes (), (char*) buf, len, 0, &addr.sa, &alen);
+ if (ret < 0)
+ return ret;
+
+ FAddr = UchAddress::Decode (&addr, alen);
+ return ret;
+}
+
+/*?
+This is equivalent to \fun{Send(buf, len, From ())}:
+it sends \fun{len} bytes to the sender of the last received message.
+If there is no such sender, it returns -1.
+?*/
+int
+UchDatagram :: Reply (byte* buf, int len)
+{
+ if (! FAddr)
+ return -1;
+ return sendto (FilDes (), (char*) buf, len, 0, FAddr->GetSockAddr (), FAddr->Length ());
+}
+
+/*?nextdoc?*/
+int
+UchDatagram :: Send (UchMsgBuffer& buf, UchAddress& to, bool peek)
+{
+ int n = Send (buf.Buffer (), buf.BufLength (), to);
+ if (! peek)
+ buf.Flush (n);
+ return n;
+}
+
+/*?nextdoc?*/
+int
+UchDatagram :: Receive (UchMsgBuffer& buf)
+{
+ int n = Receive (buf.Free (), buf.FreeLength ());
+ if (n > 0)
+ buf.More (n);
+ return n;
+}
+
+/*?
+The same functions but with a \typ{UchMsgBuffer} argument instead of a byte pointer and size.
+As usual, if \var{peek} is TRUE the buffer is not flushed.
+?*/
+int
+UchDatagram :: Reply (UchMsgBuffer& buf, bool peek)
+{
+ int n = Reply (buf.Buffer (), buf.BufLength ());
+ if (! peek)
+ buf.Flush (n);
+ return n;
+}
+
diff --git a/comm/Datagram.h b/comm/Datagram.h
new file mode 100644
index 0000000..3ac6b36
--- /dev/null
+++ b/comm/Datagram.h
@@ -0,0 +1,40 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Datagrams
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#ifndef Datagram_H_
+#define Datagram_H_
+
+#include "cplus_bugs.h"
+#include "Socket.h"
+
+class UchDatagram : public UchSocket {
+protected:
+ pUchAddress FAddr;
+public:
+ UchDatagram ();
+ UchDatagram (UchAddress*, UchAddress*);
+ ~UchDatagram ();
+ UchDatagram (const UchDatagram& d);
+ UchChannel* Copy () const;
+ int SockType ();
+ int Send (byte*, int, UchAddress&);
+ int Receive (byte*, int);
+inline UchAddress* From () { return FAddr; }
+ int Reply (byte*, int);
+ int Send (UchMsgBuffer& b, UchAddress& a, bool = FALSE);
+ int Receive (UchMsgBuffer& b);
+ int Reply (UchMsgBuffer& b, bool = FALSE);
+};
+
+#endif /* Datagram_H_ */
diff --git a/comm/Imakefile b/comm/Imakefile
new file mode 100644
index 0000000..ee86832
--- /dev/null
+++ b/comm/Imakefile
@@ -0,0 +1,99 @@
+#
+# The Unix Channel
+#
+# by Michel Beaudouin-Lafon
+#
+# Copyright 1990-1993
+# Laboratoire de Recherche en Informatique (LRI)
+#
+# Imakefile
+#
+# $Id$
+# $CurLog$
+#
+
+ CXXFLAGS = $(CXXOPTIONS) -I. -I$(LOCINCL) -DUNIX_SOCK
+
+ cc = $(CXXSUFFIX)
+ SRC = error.$(cc) MsgBuffer.$(cc) Message.$(cc) Channel.$(cc) \
+ TimeOut.$(cc) Multiplexer.$(cc) Address.$(cc) \
+ Socket.$(cc) Datagram.$(cc) Stream.$(cc) MsgStream.$(cc) dgram.$(cc) \
+ Event.$(cc) Server.$(cc) Service.$(cc) Agent.$(cc) TextStream.$(cc) \
+ PortServer.$(cc) PortServerReq.$(cc)
+
+ HDR = error.h MsgBuffer.h Message.h Channel.h TimeOut.h Multiplexer.h \
+ Address.h Socket.h Datagram.h Stream.h MsgStream.h dgram.h \
+ Event.h Server.h Service.h Agent.h TextStream.h PortServer.h
+
+
+ UCHOBJ = error.o MsgBuffer.o Message.o Channel.o TimeOut.o Multiplexer.o \
+ Address.o Socket.o Datagram.o Stream.o MsgStream.o dgram.o \
+ Event.o Server.o Service.o Agent.o TextStream.o \
+ PortServer.o PortServerReq.o
+
+ CHANOBJ = error.o MsgBuffer.o Message.o Channel.o Multiplexer.o
+
+ UCHHDR = version.h \
+ $(LOCINCL)/ccu/SmartPointer.h $(LOCINCL)/ccu/List.h \
+ $(LOCINCL)/ccu/bool.h \
+ $(LOCINCL)/ccu/word.h $(LOCINCL)/ccu/IdTable.h \
+ $(LOCINCL)/ccu/String.h \
+ $(LOCINCL)/ccu/Time.h $(LOCINCL)/ccu/Timer.h\
+ global.h error.h MsgBuffer.h Message.h \
+ Channel.h Multiplexer.h TimeOut.h \
+ Address.h Socket.h Datagram.h Stream.h MsgStream.h dgram.h \
+ Event.h Server.h Service.h Agent.h TextStream.h PortServer.h
+
+ CHANHDR = version.h \
+ $(LOCINCL)/ccu/bool.h \
+ $(LOCINCL)/ccu/word.h \
+ $(LOCINCL)/ccu/SmartPointer.h $(LOCINCL)/ccu/List.h \
+ $(LOCINCL)/ccu/Time.h $(LOCINCL)/ccu/Timer.h\
+ global.h error.h MsgBuffer.h Message.h \
+ Channel.h Multiplexer.h
+
+ LLIB = $(LOCLIB)/libCcu.a
+
+ LEX = lex
+ YACC = yacc # or bison -y
+
+ DOC = ../../DOC/UCH
+ DISTRDOC = ../../DOC.distr
+
+CxxRule ()
+
+# default target is 'chan' or 'chan comm'
+all : UchTarget
+
+comm : $(LOCLIB)/libUch.a $(LOCBIN)/portserv $(LOCBIN)/porttest $(LOCINCL)/uch.h
+
+chan : $(LOCLIB)/libChan.a $(LOCINCL)/chan.h
+
+local : libUch.a libChan.a uch.h chan.h portserv porttest
+
+LibraryTarget (Uch, $(UCHOBJ))
+LibraryTarget (Chan, $(CHANOBJ))
+
+YaccTarget (ReqMgr,ReqMgr.yacc)
+LexTarget (ReqMgr,ReqMgr.lex)
+ReqMgr.lex.o: ReqMgr.lex.cc ReqMgr.yacc.h
+
+ProgramTarget (portserv, portserv.o, libUch.a)
+ProgramTarget (porttest, porttest.o, libUch.a)
+ProgramTarget (test, test.o, libUch.a)
+ProgramTarget (reqgen, reqgen.o ReqMgr.o ReqMgr.yacc.o ReqMgr.lex.o, )
+
+GenHeaderTarget (uch.h, $(UCHHDR))
+GenHeaderTarget (chan.h, $(CHANHDR))
+
+CopyLibsTarget ($(LOCLIB),Uch)
+CopyLibsTarget ($(LOCLIB),Chan)
+
+CopyTarget ($(LOCINCL)/uch.h, uch.h)
+CopyTarget ($(LOCINCL)/chan.h, chan.h)
+CopyTarget ($(LOCBIN)/portserv, portserv)
+CopyTarget ($(LOCBIN)/porttest, porttest)
+
+DocRule ("The Unix Channel")
+TeXRule ()
+DistrDocRule (UCH)
diff --git a/comm/Message.cc b/comm/Message.cc
new file mode 100644
index 0000000..54cd771
--- /dev/null
+++ b/comm/Message.cc
@@ -0,0 +1,98 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Messages
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "cplus_bugs.h"
+
+#include "Message.h"
+
+/*?class UchMessage
+\typ{UchMessage} is the virtual base class for messages.
+Each subclass must redefine the virtual functions \fun{WriteTo} and \fun{ReadFrom}.
+Messages can be added to buffers and can be retrieved from buffers.
+?*/
+
+/*?nodoc?*/
+UchMessage :: UchMessage ()
+{
+ // nothing
+}
+
+/*?nodoc?*/
+UchMessage :: ~UchMessage ()
+{
+}
+
+/*?
+This virtual function must write a message into a buffer.
+Usually, the implementation of this function in a derived class starts by calling
+the same function of its base class, because a derived class inherits the fields
+of its base class and adds its own fields.
+Data is usually appended to the buffer by the \fun{UchMsgBuffer::Append} functions or operator <<.
+In class \typ{UchMessage}, this function does nothing.
+?*/
+void
+UchMessage :: WriteTo (UchMsgBuffer&)
+{
+ // nothing
+}
+
+/*?
+This virtual function must extract data from the buffer to create the message.
+At most \var{len} bytes should be read from the buffer.
+Usually, the implementation of this function in a derived class starts by calling
+the same function of its base class, because a derived class inherits the fields
+of its base class and adds its own fields.
+Data is usually retrieved from the buffer by the \fun{UchMsgBuffer::Get} functions or operator >>.
+In class \typ{UchMessage}, this function does nothing.
+?*/
+void
+UchMessage :: ReadFrom (UchMsgBuffer&, lword)
+{
+}
+
+/*?
+This virtual function can be redefined in derived classes to support the handling of messages
+after they have been loaded from a buffer. The function \typ{MyClient}::\fun{NewMessage}
+may then look like:
+\begin{ccode}
+void
+MyClient :: NewMessage (UchMsgBuffer& buffer, bool ask)
+{
+ MyRequest* m = 0;
+ lword type;
+ buffer.MsgPeek (&type);
+ switch (type) {
+ case MyFirstReqType:
+ m = new MyFirstRequest;
+ break;
+ case ...
+ ...
+ }
+ if (m) {
+ buffer.Get (m);
+ m->Activate ();
+ return TRUE;
+ }
+ return FALSE;
+}
+\end{ccode}
+However, this virtual function is only provided as a facility and is not used
+by \uch. Redefining it is not mandatory.
+?*/
+void
+UchMessage :: Activate ()
+{
+}
+
+
diff --git a/comm/Message.h b/comm/Message.h
new file mode 100644
index 0000000..a254f1e
--- /dev/null
+++ b/comm/Message.h
@@ -0,0 +1,32 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Messages
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#ifndef Message_H_
+#define Message_H_
+
+#include "global.h"
+
+class UchMsgBuffer;
+
+class UchMessage {
+public:
+ UchMessage ();
+virtual ~UchMessage ();
+virtual void WriteTo (UchMsgBuffer&);
+virtual void ReadFrom (UchMsgBuffer&, lword);
+virtual void Activate ();
+};
+
+#endif /* Message_H_ */
+
diff --git a/comm/MsgBuffer.cc b/comm/MsgBuffer.cc
new file mode 100644
index 0000000..0aefd6c
--- /dev/null
+++ b/comm/MsgBuffer.cc
@@ -0,0 +1,707 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Messages, buffers
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "MsgBuffer.h"
+#include "Message.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+
+// management of buffers
+// The buffer is said to be allocated if Begin is non zero.
+// There is a special constructor that builds an allocated buffer with Begin = 0 :
+// UchMsgBuffer (UchMsgBuffer&, int len)
+// This buffer can only be read from and flushed
+// This is useful for creating a fake buffer that is actually a pointer to
+// a subpart of the original one. Thus, the new buffer can be safely read from
+// without eating too far away
+//
+
+/*?class UchMsgBuffer
+A \typ{UchMsgBuffer} is an array of bytes of a given size that can be partially filled.
+The buffer may grow whenever bytes are appended to it.
+The initial size, the increments and the maximum sizes can be defined.
+If the buffer exceeds its maximal size, it will be deleted when it becomes empty
+in order to give it a smaller size.
+
+Data can be appended and extracted from the buffer by \fun{operator <<} and \fun{operator >>}.
+When extracting data, the buffer may not contain enough bytes.
+We say that a ``get error'' has occurred and the buffer remembers it until it is modified
+by the functions \fun{Append} or \fun{More}, or by \fun{operator <<}.
+
+The class \typ{^{pUchMsgBuffer}} implements smart pointers to buffers.
+Smart pointers behave like pointers but they manage a reference count on the
+pointed to objects for an automatic reclaim of dynamically allocated objects.
+
+\fig{buffer}{Some operations on a buffer.}
+?*/
+
+static int padding [] = { 0, 1, 2, 3};
+inline int pad (int sz) { return sz + padding [sz & 03]; }
+
+/*?
+Construct an empty buffer.
+The buffer will be allocated with a default size whenever data is appended to it.
+This default size is currently 128 bytes.
+?*/
+UchMsgBuffer :: UchMsgBuffer ()
+{
+ Begin = Start = Stop = End = 0;
+ GetErr = FALSE;
+ GrowSize = MinSize = 128;
+ MaxSize = 0;
+}
+
+/*?
+Construct a buffer with \var{sz} bytes.
+?*/
+UchMsgBuffer :: UchMsgBuffer (int sz)
+{
+ MinSize = GrowSize = sz = pad (sz);
+ Begin = Start = Stop = new byte [sz];
+ End = Begin + sz;
+ GetErr = FALSE;
+ MaxSize = 0;
+}
+
+/*?
+Construct a buffer with minimum \var{sz} bytes, growing by \var{grow} bytes,
+until a maximum size of \var{max} bytes.
+?*/
+UchMsgBuffer :: UchMsgBuffer (int sz, int grow, int max)
+{
+ MinSize = sz = pad (sz);
+ Begin = Start = Stop = new byte [sz];
+ End = Begin + sz;
+ GetErr = FALSE;
+ GrowSize = grow;
+ MaxSize = max;
+}
+
+/*?
+Construct a so called fake buffer, i.e. a buffer whose contents is made of the
+first \var{l} characters of buffer \var{b} (or the whole buffer if \var{l} is zero).
+The contents of the buffers is actually shared until the fake buffer is destructed
+or data is appended to it.
+Fake buffers are mainly used to extract data without exceeding a given size and
+without making a copy.
+Note that the contents of the fake buffer can be overwritten if you read and then append to
+the original buffer.
+The rule of thumb is to avoid modifying the original buffer while there is a fake buffer on it.
+?*/
+UchMsgBuffer :: UchMsgBuffer (const UchMsgBuffer& b, int l)
+{
+ Begin = 0;
+ Start = b.Start;
+ if (l && Start + l <= b.Stop)
+ End = Stop = Start + l;
+ else
+ End = Stop = b.Stop;
+ GetErr = FALSE;
+ GrowSize = b.GrowSize;
+ MinSize = b.MinSize;
+ MaxSize = b.MaxSize;
+}
+
+/*?nodoc?*/
+UchMsgBuffer :: ~UchMsgBuffer ()
+{
+ Delete ();
+}
+
+/*?
+Delete a buffer, i.e. free its internal byte buffer.
+The buffer can still be used after this function has been called (it will reallocate the byte buffer).
+The destructor calls this function.
+?*/
+void
+UchMsgBuffer :: Delete ()
+{
+ if (Begin)
+ delete Begin;
+ Begin = Start = Stop = End = 0;
+}
+
+/*?
+Insure that a UchMsgBuffer.has at least \var{sz} free bytes.
+The buffer may be reallocated with a new size to provide the necessary space.
+?*/
+void
+UchMsgBuffer :: NeedSize (int sz)
+{
+ // brand new buffer
+ if (! Begin) {
+ if (End) // ! Begin && End => fake buffer
+ return;
+ if ((sz = pad (sz)) < MinSize)
+ sz = MinSize;
+ Begin = Start = Stop = new byte [sz];
+ End = Begin + sz;
+ return;
+ }
+
+ // if enough space, do nothing
+ if (End - Stop >= sz)
+ return;
+
+ // try to compact
+ int l = Stop - Start;
+ if (l && (Start - Begin) + (End - Stop) >= sz) {
+ memcpy (Begin, Start, l);
+ Start = Begin;
+ Stop = Start + l;
+ return;
+ }
+
+ // sigh! allocate new
+ sz = pad (sz + ((l < GrowSize) ? GrowSize : l));
+ byte* b = new byte [sz];
+ if (l)
+ memcpy (b, Start, l);
+ delete Begin;
+ Begin = Start = b;
+ Stop = Start + l;
+ End = Begin + sz;
+}
+
+/*?
+Discard the \var{n} first bytes of a buffer, or the whole buffer if \var{n} is negative.
+If the buffer becomes empty and had exceeded its maximum size, it is reset.
+?*/
+void
+UchMsgBuffer :: Flush (int n)
+{
+//printf ("UchMsgBuffer %x : Flush (%d)\n", this, n);
+ if (! Begin)
+ if (! End) // ! Begin && End => fake buffer
+ return;
+ if (n < 0 || Start + n >= Stop) {
+ Start = Stop = Begin;
+ // free if too big
+ if (MaxSize > 0 && End - Start >= MaxSize)
+ Delete ();
+ } else
+ Start += n;
+//printf ("flushed\n");
+}
+
+/*?
+Flush the buffer until \var{p}.
+This can be used when the UchMsgBuffer.has been accessed through \fun{Buffer ()}
+and you want to flush until a given position in the buffer.
+?*/
+void
+UchMsgBuffer :: Flush (byte* p)
+{
+ if (p >= Start && p <= Stop)
+ Flush (p - Start);
+}
+
+/*?
+Set the minimum, increment and maximum size of a buffer (in bytes).
+If an argument is negative, the corresponding size is not changed.
+If an argument is zero, a default value is used:
+128 for minimum and increment, 0 for maximum.
+Sizes are rounded to the nearest multiple of 4.
+If the maximum size is 0, there is no upper bound on the size of the buffer.
+Note that the buffer can always grow as big as necessary to hold the appended data;
+the maximum size is only used to reallocate the buffer whenever it becomes empty.
+?*/
+void
+UchMsgBuffer :: SetSizes (int min, int grow, int max)
+{
+ if (min >= 0)
+ MinSize = pad (min);
+ if (grow >= 0)
+ GrowSize = pad (grow);
+ if (max >= 0)
+ MaxSize = pad (grow);
+ if (min == 0)
+ MinSize = 128;
+ if (grow == 0)
+ GrowSize = 128;
+}
+
+//----- append operations
+//
+
+/*?
+Append the \var{n} first bytes of \var{buf} to the buffer.
+All append functions call \fun{NeedSize} so that appending always succeeds.
+?*/
+void
+UchMsgBuffer :: Append (const byte* buf, int n)
+{
+ GetErr = FALSE;
+ if (! Begin || End - Stop < n)
+ NeedSize (n);
+ memcpy (Stop, buf, n);
+ Stop += n;
+}
+
+/*?nextdoc?*/
+void
+UchMsgBuffer :: Append (byte b)
+{
+ GetErr = FALSE;
+ if (! Begin || End - Stop < 1)
+ NeedSize (1);
+ *Stop++ = b;
+}
+
+#ifdef DOC
+
+/*?nextdoc?*/
+void
+UchMsgBuffer :: Append (char c)
+{ }
+
+/*?nextdoc?*/
+void
+UchMsgBuffer :: Append (lword l)
+{ }
+
+/*?
+Append a short word, a long word, a byte or a character to a buffer.
+The buffer is extended as necessary.
+?*/
+void
+UchMsgBuffer :: Append (sword s)
+{ }
+
+#endif /* DOC */
+
+/*?
+Append a string to a buffer.
+If the second argument is \var{TRUE}, the terminating null byte is appended, else it is not.
+If the string is the null pointer, nothing is appended to the buffer.
+?*/
+void
+UchMsgBuffer :: Append (const char* s, bool nullbyte)
+{
+ if (! s)
+ return;
+ GetErr = FALSE;
+ int l = strlen (s) + (nullbyte ? 1 : 0);
+ if (! Begin || End - Stop < l)
+ NeedSize (l);
+ memcpy (Stop, s, l);
+ Stop += l;
+}
+
+/*?
+Append a message \var{msg} to the buffer.
+The virtual function \fun{WriteTo} of the buffer is called.
+The length of the message is automatically computed and prepended to the message itself.
+?*/
+void
+UchMsgBuffer :: Append (UchMessage& msg)
+{
+ GetErr = FALSE;
+
+ // reserve space for length
+ if (! Begin || End - Stop < lwsize)
+ NeedSize (lwsize);
+ // save current position (saving Start is wrong because it may move)
+ lword l = Stop - Start;
+ // skip space for length (this means that the length includes the 4 bytes of the length itself)
+ Stop += lwsize;
+
+ // append message to buffer
+ msg.WriteTo (*this);
+
+ // now patch the buffer ...
+ // retrieve position
+ byte* p = Start + l;
+ // compute length
+ lword len = (Stop - Start) - l;
+ // and patch
+ memcpy (p, &len, lwsize);
+}
+
+//----- get operations
+//
+
+/*?
+Extract exactly \var{n} bytes from the buffer and copy them into \var{b}.
+Return TRUE if the bytes were extracted, else return FALSE and mark the buffer with the get error flag.
+If \var{peek} is TRUE, the buffer is not flushed after extracting the data
+(i.e. the extracted data is still in the buffer).
+?*/
+bool
+UchMsgBuffer :: Get (byte* b, int n, bool peek)
+{
+//printf ("UchMsgBuffer %x : Get %d bytes\n", this, n);
+ if (GetErr)
+ return FALSE;
+ if (Stop - Start < n) {
+ GetErr = TRUE;
+//printf ("Get failed : %d bytes\n", Stop - Start);
+ return FALSE;
+ }
+ memcpy (b, Start, n);
+ if (! peek)
+ Flush (n);
+ return TRUE;
+}
+
+/*?nextdoc?*/
+bool
+UchMsgBuffer :: Get (byte* b, bool peek)
+{
+//printf ("UchMsgBuffer %x : Get byte\n", this);
+ if (GetErr)
+ return FALSE;
+ if (Stop - Start < 1) {
+ GetErr = TRUE;
+//printf ("Get failed : %d bytes\n", Stop - Start);
+ return FALSE;
+ }
+ *b = *Start;
+ if (! peek)
+ Flush (1);
+ return TRUE;
+}
+
+#ifdef DOC
+
+/*?nextdoc?*/
+bool
+UchMsgBuffer :: Get (char* c, bool peek)
+{
+}
+
+/*?nextdoc?*/
+bool
+UchMsgBuffer :: Get (sword* s, bool peek)
+{ }
+
+/*?
+Extract a long word, a short word, a byte, or a character from the buffer into the argument.
+Return TRUE if the data was actually extracted, else return FALSE and mark the buffer with the get error flag.
+If \var{peek} is TRUE, the buffer is not flushed after extracting the data.
+?*/
+bool
+UchMsgBuffer :: Get (lword* l, bool peek)
+{
+}
+
+#endif /* DOC */
+
+/*?nextdoc?*/
+int
+UchMsgBuffer :: Get (char* str, int n, char delim, bool peek)
+{
+ char ds [2];
+ ds [0] = delim;
+ ds [1] = 0;
+ return Get (str, n, ds, peek);
+}
+
+/*?
+Get at most \var{n} characters from the buffer into \var{str}.
+If \var{n} is negative, no limit other than the size of the buffer
+is imposed.
+If \var{delim} is non null, it is a delimiter or a string of delimiters:
+the buffer is transferred until a delimiter is encountered.
+A null character always terminates the transfer.
+If \var{peek} is TRUE, the buffer is not flushed after extracting the data.
+The number of characters actually transferred is returned.
+\var{str} is always terminated by a null character.
+?*/
+int
+UchMsgBuffer :: Get (char* str, int n, const char* delim, bool peek)
+{
+ if (GetErr)
+ return 0;
+ if (n < 0 || Stop - Start < n)
+ n = Stop - Start;
+ if (! n)
+ return 0;
+
+ int i = 0;
+ char*q = str;
+ for (char* p = (char*) Start; *p && n; n--, i++) {
+ if (delim) {
+ for (const char *s = delim; *s; s++)
+ if (*s == *p)
+ break;
+ if (*s)
+ break;
+ }
+ *q++ = *p++;
+ }
+ *q = '\0';
+ if (! peek)
+ Flush (i);
+ return i;
+}
+
+/*?
+Get a message from the buffer.
+The message must be prefixed by its length, as done by \fun{Append(UchMessage*)}.
+The virtual function \fun{ReadFrom} of the message is called to convert the buffer into a message.
+If there is not enough data in the buffer, FALSE is returned.
+If \fun{ReadFrom} reads past the end of the message,
+FALSE is returned and the get error flag is set.
+?*/
+bool
+UchMsgBuffer :: Get (UchMessage* msg)
+{
+ if (GetErr || (Stop - Start < lwsize))
+ return FALSE;
+
+ // get length
+ lword l = Stop - Start;
+ lword len = 0;
+ memcpy ((byte*) &len, Start, lwsize);
+
+ // check that buffer is big enough
+ if (l < len)
+ return FALSE;
+
+ // read from buffer
+ Start += lwsize;
+ msg->ReadFrom (*this, len);
+
+ // align to length
+ lword rl = l - (Stop - Start);
+ if (rl == len) {
+ return TRUE;
+ } else
+ if (rl < len) {
+ Flush ((int) len - (int) rl);
+ return TRUE;
+ } else {
+ GetErr = TRUE;
+ return FALSE;
+ }
+}
+
+//------ peek operations
+
+/*?nextdoc?*/
+bool
+UchMsgBuffer :: Peek (byte* b, lword offset)
+{
+ if (Stop - Start < offset + 1)
+ return FALSE;
+ *b = *(Start + offset);
+ return TRUE;
+}
+
+/*?nextdoc?*/
+bool
+UchMsgBuffer :: Peek (sword* sw, lword offset)
+{
+ if (Stop - Start < offset + swsize)
+ return FALSE;
+ memcpy (sw, Start + offset, swsize);
+ return TRUE;
+}
+
+/*?nextdoc?*/
+bool
+UchMsgBuffer :: Peek (lword* lw, lword offset)
+{
+ if (Stop - Start < offset + lwsize)
+ return FALSE;
+ memcpy (lw, Start + offset, lwsize);
+ return TRUE;
+}
+
+/*?
+These functions extract a byte, a short word, a long word, a byte string
+at \var{offset} bytes from the start of the buffer.
+They return FALSE if the data to peek falls outside the buffer, else TRUE.
+?*/
+bool
+UchMsgBuffer :: Peek (int sz, byte* b, lword offset)
+{
+ if (Stop - Start < offset + sz)
+ return FALSE;
+ memcpy (b, Start + offset, sz);
+ return TRUE;
+}
+
+#ifdef DOC
+
+/*?nextdoc?*/
+bool
+UchMsgBuffer :: MsgPeek (byte* b, lword offset)
+{ }
+
+/*?nextdoc?*/
+bool
+UchMsgBuffer :: MsgPeek (sword* sw, lword offset)
+{ }
+
+/*?nextdoc?*/
+bool
+UchMsgBuffer :: MsgPeek (lword* lw, lword offset)
+{ }
+
+/*?
+These function are similar to the \fun{Peek} functions;
+they must be used when the buffer contains a message:
+a message is always prefixed by its length, thus changing the origin of the offset.
+Note that the length itself can be extracted with \fun{Peek(lword*, 0)}.
+?*/
+bool
+UchMsgBuffer :: MsgPeek (int sz, byte* b, lword offset)
+{ }
+
+#endif /* DOC */
+
+//----- miscellaneous
+//
+
+/*?
+Extend the buffer's contents by \var{n} bytes.
+This function is useful when data has been appended to the buffer without
+using one of the \fun{Append} functions.
+For instance, to read \var{n} bytes from a Unix file descriptor into a buffer,
+you would do:
+\begin{ccode}
+b.NeedSize (n);
+int nb = read (fd, (char*) Free (), n);
+if (nb > 0)
+ b.More (n);
+\end{ccode}
+?*/
+void
+UchMsgBuffer :: More (int n)
+{
+ GetErr = FALSE;
+ if (End) // Begin == 0 for fake buffers
+ Stop += n;
+}
+
+/*?
+Discard the \var{n} last bytes of the buffer.
+This is useful to remove erroneous data that has just been appended to the buffer.
+This is in some sense the reverse operation of \fun{More}.
+?*/
+void
+UchMsgBuffer :: Discard (int n)
+{
+ if (! Begin)
+ if (! End) // ! Begin && End => fake buffer
+ return;
+ if (Stop - n <= Start) {
+ Start = Stop = Begin;
+ // free if too big
+ if (MaxSize > 0 && End - Start >= MaxSize)
+ Delete ();
+ } else
+ Stop -= n;
+
+}
+
+#ifdef DOC
+// fake entries for inline functions
+
+/*?nextdoc?*/
+byte*
+UchMsgBuffer :: Buffer ()
+{ }
+
+/*?
+Return the address of the beginning of the buffer and the length of the buffer.
+This is used in conjunction with \fun{Flush} to extract data from the buffer, as in the following example:
+\begin{ccode}
+byte* p = b.Buffer ();
+int n = b.BufLength ();
+n = write (fd, (const char*) p, n);
+if (n > 0)
+ b.Flush (n);
+\end{ccode}
+?*/
+int
+UchMsgBuffer :: BufLength ()
+{ }
+
+/*?nextdoc?*/
+byte*
+UchMsgBuffer :: Free ()
+{ }
+
+/*?
+Return the address of the first free byte in the buffer and the number of free bytes.
+This may be used in conjunction with \fun{NeedSize} and \fun{More}
+to append data to the buffer.
+?*/
+int
+UchMsgBuffer :: FreeLength ()
+{ }
+
+/*?
+Grow the buffer if its free size is less than its grow size.
+This function can be called when a buffer is full.
+?*/
+void
+UchMsgBuffer :: Grow ()
+{ }
+
+/*?
+This operator is equivalent to the \fun{Append} operations.
+The type of the data can be a long word, a short word, a byte, a character, or a string;
+for strings, the null character is included.
+The operator returns the buffer so that several items can be added.
+?*/
+UchMsgBuffer&
+UchMsgBuffer :: operator << (type data)
+{ }
+
+/*?nextdoc?*/
+UchMsgBuffer&
+UchMsgBuffer :: operator >> (type* data)
+{ }
+
+/*?
+This operator is equivalent to the \fun{Get} operations.
+The type of the data can be a long word, a short word, a byte, or a character.
+The operator returns the buffer so that several items can be read.
+Errors can be tested by the functions \fun{Error} and \fun{Ok}.
+?*/
+UchMsgBuffer&
+UchMsgBuffer :: operator >> (type& data)
+{ }
+
+/*?nextdoc?*/
+bool
+UchMsgBuffer :: Error ()
+{ }
+
+/*?
+Test the state of the buffer, as set by the get functions.
+\fun{Error} returns TRUE if a get error has occurred.
+\fun{Ok} is the negation of \fun{Error}.
+?*/
+bool
+UchMsgBuffer :: Ok ()
+{ }
+
+/*?
+Reset the error flag.
+The error flag is automatically reset by the functions \fun{Append} and \fun{More},
+and by \fun{operator <<}.
+?*/
+void
+UchMsgBuffer :: ResetError ()
+{ }
+
+#endif /* DOC */
+
diff --git a/comm/MsgBuffer.h b/comm/MsgBuffer.h
new file mode 100644
index 0000000..0f6a55c
--- /dev/null
+++ b/comm/MsgBuffer.h
@@ -0,0 +1,112 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Messages, buffers
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#ifndef Buffer_H_
+#define Buffer_H_
+
+#include "cplus_bugs.h"
+#include "global.h"
+#include "ccu/SmartPointer.h"
+
+class UchMessage;
+
+// Buffer is the buffer itself, End points after its last byte
+// Start and Stop limit its content
+// UchMsgBuffer derives from CcuSmartData to be able to have smart pointers (pUchMsgBuffer)
+// a buffer grows and shrinks automatically
+//
+class UchMsgBuffer : public CcuSmartData {
+protected:
+ byte* Begin;
+ byte* Start;
+ byte* Stop;
+ byte* End;
+ bool GetErr;
+ int GrowSize, MaxSize, MinSize;
+
+public:
+ UchMsgBuffer ();
+ UchMsgBuffer (int);
+ UchMsgBuffer (int, int, int);
+ UchMsgBuffer (const UchMsgBuffer&, int = 0); // construct a fake buffer
+ ~UchMsgBuffer ();
+
+ void Delete ();
+ void NeedSize (int n);
+ void Grow () { NeedSize (GrowSize); }
+ void Flush (int n = -1);
+ void Flush (byte*);
+ void SetSizes (int, int, int);
+
+ void Append (const byte*, int);
+ void Append (byte);
+ void Append (char c) { Append ((byte) c); }
+ void Append (sword s) { Append ((const byte*) &s, swsize); }
+ void Append (lword l) { Append ((const byte*) &l, lwsize); }
+ void Append (const char*, bool = TRUE);
+ void Append (UchMessage&);
+
+ bool Get (byte*, int, bool = FALSE);
+ bool Get (byte*, bool = FALSE);
+ bool Get (char* c, bool peek = FALSE) { return Get ((byte*) c, peek); }
+ bool Get (lword* l, bool peek = FALSE) { return Get ((byte*) l, lwsize, peek); }
+ bool Get (sword* s, bool peek = FALSE) { return Get ((byte*) s, swsize, peek); }
+ int Get (char*, int, char, bool = FALSE);
+ int Get (char*, int, const char* = 0, bool = FALSE);
+ bool Get (UchMessage*);
+
+ bool Peek (byte*, lword = 0);
+ bool Peek (sword*, lword = 0);
+ bool Peek (lword*, lword = 0);
+ bool Peek (int, byte*, lword = 0);
+ bool MsgPeek (byte* b, lword o = 0) { return Peek (b, o+lwsize); }
+ bool MsgPeek (sword* sw, lword o = 0) { return Peek (sw, o+lwsize); }
+ bool MsgPeek (lword* lw, lword o = 0) { return Peek (lw, o+lwsize); }
+ bool MsgPeek (int sz, byte* b, lword o = 0) { return Peek (sz, b, o+lwsize); }
+
+ byte* Buffer () { return Start; }
+ int BufLength () { return Stop - Start; }
+ byte* Free () { return Stop; }
+ int FreeLength () { return End - Stop; }
+ void More (int);
+ void Discard (int);
+
+ bool Error () { return GetErr; }
+ bool Ok () { return GetErr ? FALSE : TRUE; }
+ void ResetError () { GetErr = FALSE; }
+
+ UchMsgBuffer& operator << (lword l) { Append (l); return *this; }
+ UchMsgBuffer& operator << (sword s) { Append (s); return *this; }
+ UchMsgBuffer& operator << (byte b) { Append (b); return *this; }
+ UchMsgBuffer& operator << (char c) { Append (c); return *this; }
+ UchMsgBuffer& operator << (const char* s) { Append (s, TRUE); return *this; }
+
+ UchMsgBuffer& operator >> (lword& l) { Get (&l); return *this; }
+ UchMsgBuffer& operator >> (sword& s) { Get (&s); return *this; }
+ UchMsgBuffer& operator >> (byte& b) { Get ((byte*) &b, FALSE); return *this; }
+ UchMsgBuffer& operator >> (char& c) { Get ((byte*) &c, FALSE); return *this; }
+ UchMsgBuffer& operator >> (char* s) { Get (s, -1); return *this; }
+
+#ifdef DOC
+ UchMsgBuffer& operator << (type data);
+ UchMsgBuffer& operator >> (type& data);
+ UchMsgBuffer& operator >> (type* data);
+#endif
+
+friend UchMsgBuffer& DbgPrint (UchMsgBuffer&, int);
+};
+
+PointerClass (pUchMsgBuffer, UchMsgBuffer)
+
+#endif /* Buffer_H_ */
diff --git a/comm/MsgStream.cc b/comm/MsgStream.cc
new file mode 100644
index 0000000..07cb5c1
--- /dev/null
+++ b/comm/MsgStream.cc
@@ -0,0 +1,506 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * UchMessage streams
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "MsgStream.h"
+#include "Message.h"
+#include "error.h"
+
+// #define DEBUG
+
+#ifdef DEBUG
+#include <stdio.h>
+#define DBG(inst) inst
+#else
+#define DBG(inst)
+#endif
+
+/*?class UchMsgStream
+An object of class \typ{UchMsgStream} is a stream that sends and receives messages:
+we call it a message stream.
+
+Messages are sent with the function \fun{Send}.
+Output messages are normally buffered to increase performance, but synchronous mode is available.
+The output buffer can be flushed by the application, or by the stream itself if needed
+(for instance when the buffer is full, or when a synchronous communication is needed).
+
+Incoming messages are handled by the virtual function \fun{HandleRead} of class \typ{UchChannel}:
+it is redefined so that incoming bytes are packed into messages.
+When a full message is available, \fun{HandleRead} calls the virtual
+function \fun{NewMessage}. In the class \typ{UchMsgStream}, this virtual function does
+nothing but discarding the message.
+
+Messages that need an answer are handled in the following way:
+the sender calls the function\fun{Ask}, and is blocked until the answer is received;
+any incoming messages are stored for later processing.
+When the answer arrives, \fun{Ask} calls the virtual function \fun{ConvertAnswer}.
+The returned value of \fun{ConvertAnswer} is then returned by \fun{Ask},
+thus returning to the application the reply to its question.
+
+On the receiver's side, the following happens:
+when a message sent with \fun{Ask} is received, \fun{NewMessage} is called as usual,
+but an argument indicates that it is a question that needs an answer.
+The receiver must then use \fun{Reply} to send its answer.
+Messages can be sent before replying, but it is not possible to send a question
+on a message stream that is waiting for an answer:
+this would result in a deadlock since the other party is already waiting for an answer
+and is buffering other incoming messages.
+
+The default output mode is buffered (i.e. asynchronous).
+The buffered output is automatically flushed when a question is sent,
+or when the output buffer is full, or explicitly with \fun{Flush}, or with the second argument of \fun{Send}.
+The output mode can be switched to a synchronous mode where each message is sent immediately.
+% It can also be switched to a locked synchronous mode: this is a synchronous mode where
+% the sender waits for each message to be processed before proceeding its execution.
+% This is mainly useful for debugging purposes.
+
+\medskip
+To use a message stream, a program needs to derive the class \typ{UchMsgStream}
+in order to redefine the virtual functions \fun{NewMessage} and \fun{ConvertAnswer}.
+\fun{NewMessage} treats the incoming message, and will probably call \fun{Send} and \fun{Ask}.
+The top level of the program needs just call \fun{HandleRead} in a forever loop.
+
+Most of the time, a program will use several message streams
+(for instance to manage several clients).
+In this case a channel set is the best way to implement the application:
+the definitions in the class \typ{UchMsgStream} of the functions
+\fun{HandleRead}, \fun{HandleWrite} and \fun{HandleSelect} make it easy
+to use this class in combination with the class \typ{UchMultiplexer}.
+The virtual function \fun{HandeWrite} of channels is redefined to flush the output buffer.
+The virtual function \fun{HandleSelect} of channels is redefined to handle the incoming messages
+that were buffered while waiting for an answer.
+As for the single stream situation, you need just derive the class \fun{UchMsgStream} to
+redefine the virtual functions \fun{NewMessage} and \fun{ConvertAnswer}.
+?*/
+
+// ---- no byte swapping ...
+// ---- no locked sync mode ...
+
+/*?nextdoc?*/
+UchMsgStream :: UchMsgStream ()
+: UchStream (), InBuffer (), OutBuffer (), Buffered ()
+{
+ OutSize = 128;
+ State = WAITING;
+ BufferedMessages = FALSE;
+ WaitingForAnswer = FALSE;
+ WaitingReply = FALSE;
+ Sync = FALSE;
+}
+
+/*?
+These constructors are similar to those of class \typ{UchSocket}.
+?*/
+UchMsgStream :: UchMsgStream (UchAddress* bindTo, UchAddress* connectTo)
+: UchStream (bindTo, connectTo), InBuffer (), OutBuffer (), Buffered ()
+{
+ OutSize = 128;
+ State = WAITING;
+ BufferedMessages = FALSE;
+ WaitingForAnswer = FALSE;
+ WaitingReply = FALSE;
+ Sync = FALSE;
+}
+
+// *** this copy constructor might be automatically generated
+/*?nodoc?*/
+UchMsgStream :: UchMsgStream (const UchMsgStream& ms)
+: UchStream (*(UchMsgStream*)&ms), InBuffer (), OutBuffer (), Buffered (*(UchMsgBuffer*)&ms.Buffered)
+{
+ OutSize = ms.OutSize;
+ State = ms.State;
+ BufferedMessages = ms.BufferedMessages;
+ WaitingForAnswer = ms.WaitingForAnswer;
+ WaitingReply = ms.WaitingReply;
+ Sync = ms.Sync;
+}
+
+/*?nextdoc?*/
+void
+UchMsgStream :: InputBuffer (int min, int grow, int max)
+{
+ InBuffer.SetSizes (min, grow, max);
+}
+
+/*?
+Set the input and output buffer sizes.
+Default sizes are used if these functions are not called.
+?*/
+void
+UchMsgStream :: OutputBuffer (int min, int grow, int max)
+{
+ InBuffer.SetSizes (min, grow, max);
+ OutSize = max;
+}
+
+/*?nodoc?*/
+UchMsgStream :: ~UchMsgStream ()
+{
+ Flush ();
+ InBuffer.Delete ();
+ OutBuffer.Delete ();
+ Buffered.Delete ();
+}
+
+/*?nodoc?*/
+UchChannel*
+UchMsgStream :: Copy () const
+{
+ return new UchMsgStream (*this);
+}
+
+
+// read stream into input buffer
+// delete the stream when an oef is received
+// return the number of bytes read
+//
+/*?hidden?*/
+int
+UchMsgStream :: ReadInput ()
+{
+ if (! InBuffer.BufLength ())
+ InBuffer.NeedSize (128);
+ int n = Read (InBuffer);
+ if (n <= 0) {
+ if (n == -2) {
+//printf ("ReadInput: growing\n", n);
+ InBuffer.Grow ();
+ n = Read (InBuffer);
+ }
+//printf ("ReadInput: %d bytes\n", n);
+ if (n < 0)
+ SysError (ErrWarn, "UchMsgStream::HandleRead");
+ if (n <= 0)
+ Delete ();
+ }
+ return n;
+}
+
+/*?
+This virtual function is called when an end of file is read,
+meaning that the communication is terminated.
+It is also called if an error occurs while reading.
+You can check the value of the global variable \var{errno}:
+if it is non zero, \fun{Delete} was called because of an error.
+By default this function does nothing.
+?*/
+void
+UchMsgStream :: Delete ()
+{
+ // nothing
+}
+
+// process the input buffer
+// waitAnswer indicates whether we are waiting for an answer
+// this functions uses a very simple automaton:
+// WAITING: nothing in the buffer
+// GOT_TYPE: read the 1 byte header mark of a message
+// MSG / ASK / ANS for messages, questions, answers
+// SYNC / ASYNC / OK for sync management
+// GOT_LENGTH: read the 4 byte header fo a message
+// DONE: a full message is in the buffer
+//
+// WaitingReply is true if a question has been received and Reply has not been called yet
+// WaitingForAnswer is true when Ask has been called and the answer is not yet there
+//
+/*?hidden?*/
+void
+UchMsgStream :: ProcessInput (UchMsgBuffer& buf, bool waitAnswer)
+{
+DBG (printf ("ProcessInput: ");)
+ for (;;) {
+ switch (State) {
+ case WAITING:
+DBG (printf ("WAITING\n");)
+ buf.Get ((byte*) &InType);
+DBG (if (buf.Error ()) printf (" buf empty\n");)
+ if (buf.Error ())
+ return;
+ WaitingReply = FALSE;
+ switch (InType) {
+ case ASK :
+DBG (printf (" waiting reply\n");)
+ WaitingReply = TRUE;
+ State = GOT_TYPE;
+ break;
+ case ANS :
+DBG (printf (" answer\n");)
+ case MSG :
+DBG (if (InType != ANS) printf (" msg\n");)
+DBG (printf (" got type\n");)
+ State = GOT_TYPE;
+ break;
+ case SYNC :
+ case ASYNC :
+ case OK :
+ default :
+DBG (printf (" unknown !!\n");)
+ State = WAITING;
+ }
+ if (State != GOT_TYPE)
+ break;
+ // fallthrough
+
+ case GOT_TYPE:
+DBG (printf ("GOT_TYPE\n");)
+ if (! buf.Peek ((lword*) &InLength))
+ return;
+ buf.NeedSize ((int) InLength - buf.BufLength ());
+ State = GOT_LENGTH;
+ // fallthrough
+
+ case GOT_LENGTH:
+DBG (printf ("GOT_LENGTH\n");)
+ if (buf.BufLength () < InLength)
+ return;
+ State = DONE;
+ // fallthrough
+
+ case DONE:
+DBG (printf ("DONE\n");)
+ if (waitAnswer) {
+ if (InType == ANS) {
+DBG (printf ("got answer\n");)
+ WaitingForAnswer = FALSE;
+ // answer still in the buffer
+ // the answer is converted and
+ // the buffer is flushed in Ask
+ return;
+ } else {
+ // store incoming message in a separate buffer
+ BufferedMessages = TRUE;
+ Buffered.Append (InType);
+ Buffered.Append (buf.Buffer (), InLength);
+DBG (printf ("buffering\n");)
+ }
+ } else {
+ if (InType == MSG || InType == ASK) {
+DBG (printf ("new message\n");)
+ // pass a fake buffer to the handler
+ UchMsgBuffer fake (buf, InLength);
+ if (! NewMessage (fake, WaitingReply))
+ return;
+ // *** this return breaks the assumption that
+ // *** ProcessInput empties the buffer.
+ // *** this is assumed in HandleRead/HandleSelect
+ // *** because BufferedMessages is reset to FALSE;
+ }
+DBG (else printf ("discarding\n");)
+ }
+ buf.Flush (InLength);
+ State = WAITING;
+ // fallthrough
+ }
+ }
+}
+
+/*?nodoc?*/
+void
+UchMsgStream :: HandleRead ()
+{
+ if (BufferedMessages) {
+ ProcessInput (Buffered, FALSE);
+ BufferedMessages = FALSE;
+ }
+ int n = ReadInput ();
+ if (n <= 0)
+ return;
+ ProcessInput (InBuffer, FALSE);
+}
+
+/*?nodoc?*/
+void
+UchMsgStream :: HandleWrite ()
+{
+ Flush ();
+}
+
+/*?nodoc?*/
+bool
+UchMsgStream :: HandleSelect ()
+{
+ if (BufferedMessages) {
+ ProcessInput (Buffered, FALSE);
+ BufferedMessages = FALSE;
+ }
+ return FALSE;
+}
+
+
+/*?
+This virtual function is called whenever a complete message is in the buffer.
+The buffer contains exactly one message, so that you can use \com{buf.Get(&msg)}
+to extract the message from the buffer.
+\var{ask} is TRUE if the message was sent with \fun{Ask}.
+In this case an answer must be sent back with \fun{Reply}.
+Messages can be sent before replying; they will be buffered by the receiver for later processing;
+thus, you cannot use \fun{Ask} before replying.
+\fun{NewMessage} function must return TRUE if it handled the message, else FALSE.
+If it returns FALSE, it will be called again with the same arguments next time data arrives on this channel.
+In the class \typ{UchMsgStream} this function does nothing and returns TRUE;
+if \var{ask} is TRUE, it replies immediately with an empty message.
+?*/
+bool
+UchMsgStream :: NewMessage (UchMsgBuffer&, bool ask)
+{
+ if (ask) {
+ UchMessage dummy;
+
+ Reply (dummy);
+ }
+ return TRUE;
+}
+
+/*?
+Send a message.
+If \var{flush} is TRUE, the output buffer will be flushed.
+This also happens when the message stream is in synchronous mode,
+or if the output UchMsgBuffer.has exceeded its flush size (see \fun{FlushSize}).
+?*/
+void
+UchMsgStream :: Send (UchMessage& msg, bool flush)
+{
+ OutBuffer.Append ((byte) MSG);
+ OutBuffer.Append (msg);
+ if (flush || Sync || OutBuffer.BufLength () >= OutSize)
+ Flush ();
+}
+
+/*?
+Send a message and wait for an answer.
+Incoming messages that are received while waiting for the answer are kept for later processing.
+The answer message is returned by calling the virtual function \fun{ConvertAnswer}.
+?*/
+void*
+UchMsgStream :: Ask (UchMessage& msg)
+{
+ if (WaitingReply) {
+ Error (ErrWarn, "UchMsgStream::Ask", "cannot ask before replying");
+ return 0;
+ }
+ OutBuffer.Append ((byte) ASK);
+ OutBuffer.Append (msg);
+ Flush ();
+ WaitingForAnswer = TRUE;
+
+ do {
+#ifdef DEBUG
+ printf ("waiting for answer\n");
+#endif
+ int n = ReadInput ();
+#ifdef DEBUG
+ printf (" got %d bytes\n", n);
+#endif
+ if (n <= 0)
+ return 0;
+ ProcessInput (InBuffer, TRUE);
+ } while (WaitingForAnswer);
+
+ // ProcessInput did not flush so that we can convert the answer
+ // *** If ProcessInput would return a void*, we could do all this stuff in it ...
+ UchMsgBuffer fake (InBuffer, InLength);
+ void* res = ConvertAnswer (fake);
+ InBuffer.Flush (InLength);
+ State = WAITING;
+ return res;
+}
+
+/*?
+This function must be used instead of \fun{Send} to send a reply to a message sent by \fun{Ask}.
+?*/
+void
+UchMsgStream :: Reply (UchMessage& msg)
+{
+ if (! WaitingReply) {
+ Error (ErrWarn, "UchMsgStream::Reply", "out of phase reply discarded");
+ return;
+ }
+ OutBuffer.Append ((byte) ANS);
+ OutBuffer.Append (msg);
+ Flush ();
+ WaitingReply = FALSE;
+}
+
+
+/*?
+This function is called by \fun{Ask} when the answer is in the buffer,
+in order to convert it into an object usable by the application.
+?*/
+void*
+UchMsgStream :: ConvertAnswer (UchMsgBuffer&)
+{
+ return 0;
+}
+
+/*?
+Flush the output buffer.
+This function is called automatically when the buffer exceeds its flush size,
+or after each message when this stream is in synchronous mode.
+It is also called from \fun{Ask} to wait for the answer.
+?*/
+void
+UchMsgStream :: Flush ()
+{
+ if (! OutBuffer.BufLength ())
+ return;
+ int n = Write (OutBuffer);
+ OutBuffer.Flush (n);
+}
+
+/*?
+Send a buffer containing a message.
+If \var{flush} is TRUE, the output buffer will be flushed.
+This also happens when the message stream is in synchronous mode,
+or if the output UchMsgBuffer.has exceeded its flush size (see \fun{FlushSize}).
+The buffer {\em must} contain a converted message.
+This can be used for instance from inside \fun{NewMessage} to resend
+the incoming message to another client, without having to convert
+the buffer to a message.
+?*/
+void
+UchMsgStream :: Send (UchMsgBuffer& buf, bool flush)
+{
+ OutBuffer.Append ((byte) MSG);
+ OutBuffer.Append (buf.Buffer (), buf.BufLength ());
+ if (flush || Sync || OutBuffer.BufLength () >= OutSize)
+ Flush ();
+}
+
+#ifdef DOC
+/*?
+This function defines the size of the output buffer that triggers automatic flushing
+in asynchronous mode. By default the flush size is the maximum size of the
+output buffer. As a consequence, it is changed by \fun{OutBuffer}.
+?*/
+void
+UchMsgStream :: FlushSize (int n)
+{ }
+
+/*?nextdoc?*/
+void
+UchMsgStream :: SetSyncMode (bool s)
+{ }
+
+/*?
+A message stream can be in synchronous or asynchronous mode.
+In asynchronous mode output is buffered while in synchronous mode it is not.
+Synchronous mode is usually less efficient than asynchronous mode
+because it makes more system calls to transfer data;
+however synchronous mode can be useful for debugging applications.
+?*/
+bool
+UchMsgStream :: GetSyncMode ()
+{ }
+
+#endif /* DOC */
+
diff --git a/comm/MsgStream.h b/comm/MsgStream.h
new file mode 100644
index 0000000..c2b8e1f
--- /dev/null
+++ b/comm/MsgStream.h
@@ -0,0 +1,70 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * UchMessage streams
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#ifndef MsgStream_H_
+#define MsgStream_H_
+
+#include "Stream.h"
+#include "ccu/SmartPointer.h"
+#include "MsgBuffer.h"
+class UchMessage;
+
+class UchMsgStream : public UchStream {
+private:
+
+protected:
+ UchMsgBuffer InBuffer;
+ int InSize;
+ UchMsgBuffer OutBuffer;
+ int OutSize;
+enum STATE { WAITING, GOT_TYPE, GOT_LENGTH, DONE};
+enum TYPE { MSG = 1, ASK, ANS, SYNC, ASYNC, OK };
+ STATE State;
+ bool BufferedMessages;
+ UchMsgBuffer Buffered;
+ bool WaitingForAnswer;
+ bool WaitingReply;
+ int InLength;
+ byte InType;
+ bool Sync;
+
+ void ProcessInput (UchMsgBuffer&, bool);
+ int ReadInput ();
+public:
+ UchMsgStream ();
+ UchMsgStream (const UchMsgStream&);
+ UchMsgStream (UchAddress*, UchAddress*);
+ ~UchMsgStream ();
+
+ void InputBuffer (int min, int grow, int max);
+ void OutputBuffer (int min, int grow, int max);
+inline void FlushSize (int n) { OutSize = n; }
+ UchChannel* Copy () const;
+ void HandleRead ();
+ void HandleWrite ();
+ bool HandleSelect ();
+inline bool GetSyncMode () { return Sync; }
+inline void SetSyncMode (bool s) { Sync = s; Flush (); }
+virtual bool NewMessage (UchMsgBuffer&, bool);
+virtual void* ConvertAnswer (UchMsgBuffer&);
+virtual void Delete ();
+ void Send (UchMessage&, bool = FALSE);
+ void* Ask (UchMessage&);
+ void Reply (UchMessage&);
+ void Flush ();
+ void Send (UchMsgBuffer&, bool = FALSE);
+};
+
+#endif /* MsgStream_H_ */
+
diff --git a/comm/Multiplexer.cc b/comm/Multiplexer.cc
new file mode 100644
index 0000000..497e041
--- /dev/null
+++ b/comm/Multiplexer.cc
@@ -0,0 +1,463 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Channel sets, or multiplexers
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "Multiplexer.h"
+#include "TimeOut.h"
+
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+
+#if defined(__hpux) && !defined(__GNUG__)
+#define FD_SET_TYPE(x) ((int*) x)
+#else
+#define FD_SET_TYPE(x) (x)
+#endif
+
+extern int errno;
+
+#ifndef FD_SET
+#ifndef NFDBITS
+#define FD_SET(n, p) ((p)->fds_bits[0] |= (1 << (n)))
+#define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1 << (n)))
+#define FD_ISSET(n, p) ((p)->fds_bits[0] & (1 << (n)))
+#define FD_ZERO(p) ((p)->fds_bits[0] = 0)
+#else
+#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
+#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
+#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
+#define FD_ZERO(p) memset((char *)(p), 0, sizeof(*(p)))
+#endif
+#endif
+
+
+/*?class UchMultiplexer
+An object of class \typ{UchMultiplexer} is a set of channels.
+Some of the channels in the set are active, others can be inactive.
+A channel set can be scanned so that the functions \fun{HandleRead}
+and \fun{HandleWrite} are called for each active channel that is ready to read or write.
+The functions \fun{HandleSelect} of each channel is called before actually scanning
+so that each channel can perform a background task or buffer input/output.
+This provides a level of abstraction over the Unix \fun{select} system call.
+Note that the functions that take an integer file descriptor as argument can be passed
+a \typ{FILDES} or a \typ{UchChannel} because the corresponding conversion operators are defined.
+
+The class \typ{^{pCHAN_SET}} implements smart pointers to channel sets.
+Smart pointers behave like pointers but they manage a reference count on the
+pointed to objects for an automatic reclaim of dynamically allocated objects.
+?*/
+
+// constructor: initialize states
+//
+
+/*?
+Create an empty channel set.
+?*/
+UchMultiplexer :: UchMultiplexer ()
+: Channels (new pUchChannel [NFILE]),
+ Timers (),
+ ReadCount (0),
+ WriteCount (0),
+ SelectCount (0),
+ Looping (FALSE)
+{
+ FD_ZERO (&ReadMask);
+ FD_ZERO (&WriteMask);
+ FD_ZERO (&SelectMask);
+}
+
+/*?nodoc?*/
+UchMultiplexer :: ~UchMultiplexer ()
+{
+#ifdef CPLUS_BUG4
+ delete [NFILE] Channels;
+#else
+ delete [] Channels;
+#endif
+ Channels = 0;
+}
+
+/*?
+Add a channel to the set.
+Note that the argument is a pointer to the channel; no copy is made by this function.
+If a channel with the same file descriptor as the one being added is already in the set,
+the old channel is first removed, thus calling \fun{RemoveNotify} for that channel.
+The virtual function \fun{AddNotify} is called before actually adding the channel
+to the set.
+?*/
+void
+UchMultiplexer :: Add (UchChannel* chan)
+{
+ int fd = chan->FilDes ();
+ if (fd < 0)
+ return;
+ pUchChannel ochan = Channels [fd];
+ if (ochan)
+ Remove (fd);
+ chan->AddNotify (*this);
+ AddNotify (chan);
+ Channels [fd] = chan;
+ SetMasks (fd, chan->IOMode ());
+}
+
+pUchChannel NIL_CHAN (0);
+
+/*?
+Remove a channel from the set.
+The virtual function \fun{RemoveNotify} is called before actually removing the channel
+from the set.
+?*/
+void
+UchMultiplexer :: Remove (int fd)
+{
+ if (fd < 0)
+ return;
+ UchChannel* ch = Channels [fd];
+ if (ch) {
+ ch->RemoveNotify (*this);
+ RemoveNotify (ch);
+ SetMasks (fd, IONone);
+ Channels [fd] = 0;
+ }
+}
+
+/*?
+Remove all channels from this channel set.
+This function calls \fun{Remove} for all channels of the set.
+This is a way of exiting from \fun{LoopScan}.
+?*/
+void
+UchMultiplexer :: RemoveAll ()
+{
+ for (int fd = 0; fd < NFILE; fd++)
+ Remove (fd);
+}
+
+/*?
+Change the mode of a channel in the set.
+Mode \var{IONone} makes the channel inactive (without removing it).
+The virtual function \fun{SetModeNotify} is called before actually changing
+the mode of the channel.
+?*/
+void
+UchMultiplexer :: SetMode (int fd, IOMODE mode)
+{
+ if (fd < 0)
+ return;
+
+ SetModeNotify (Channels [fd], mode);
+ SetMasks (fd, mode);
+ Channels [fd] -> SetMode (mode);
+}
+
+
+// update the masks when channel fd changes its mode
+//
+/*?hidden?*/
+void
+UchMultiplexer :: SetMasks (int fd, IOMODE mode)
+{
+ if (mode & IORead) {
+ if (! FD_ISSET (fd, &ReadMask)) {
+ ReadCount ++;
+ FD_SET (fd, &ReadMask);
+ }
+ } else {
+ if (FD_ISSET (fd, &ReadMask)) {
+ FD_CLR (fd, &ReadMask);
+ ReadCount --;
+ }
+ }
+
+ if (mode & IOWrite) {
+ if (! FD_ISSET (fd, &WriteMask)) {
+ WriteCount ++;
+ FD_SET (fd, &WriteMask);
+ }
+ } else {
+ if (FD_ISSET (fd, &WriteMask)) {
+ FD_CLR (fd, &WriteMask);
+ WriteCount --;
+ }
+ }
+
+ if (mode & IOSelect) {
+ if (! FD_ISSET (fd, &SelectMask)) {
+ SelectCount ++;
+ FD_SET (fd, &SelectMask);
+ }
+ } else {
+ if (FD_ISSET (fd, &SelectMask)) {
+ FD_CLR (fd, &SelectMask);
+ SelectCount --;
+ }
+ }
+
+}
+
+/*?nextdoc?*/
+void
+UchMultiplexer :: AddNotify (UchChannel*)
+{
+ // nothing
+}
+
+/*?nextdoc?*/
+void
+UchMultiplexer :: RemoveNotify (UchChannel*)
+{
+ // nothing
+}
+
+/*?
+This function calls the select handler of each channel of the set in select mode.
+It returns TRUE as soon as one select handler returns true,
+else it returns FALSE when the select handlers have been called.
+?*/
+bool
+UchMultiplexer :: HandleSelect ()
+{
+ int fd, nfd;
+
+ if (fd0 >= NFILE)
+ fd0 = 0;
+ for (fd = fd0++, nfd = SelectCount; nfd; (fd < NFILE) ? fd++ : (fd = 0)) {
+ if (! FD_ISSET (fd, &SelectMask))
+ continue;
+ if (Channels [fd] -> HandleSelect ())
+ return TRUE;
+ nfd--;
+ if (! Looping)
+ return FALSE;
+ }
+ return FALSE;
+}
+
+/*?
+These virtual functions are called by the functions \fun{Add}, \fun{Remove}
+(and thus \fun{RemoveAll}) and \fun{SetMode}.
+They provide hooks for application that need to take special action when the
+set changes. They are called before the corresponding action is actually
+carried out.
+By default these functions do nothing.
+?*/
+void
+UchMultiplexer :: SetModeNotify (UchChannel*, IOMODE)
+{
+ // nothing
+}
+
+/*?nextdoc?*/
+int
+UchMultiplexer :: Scan (bool nointr, bool poll)
+{
+ fd_set rmsk, wmsk;
+ int nfd, ret = -1;
+ register int fd;
+ struct timeval tout;
+ struct timeval* timeout = 0;
+
+ if (poll) {
+ timeout = &tout;
+ tout.tv_sec = tout.tv_usec = 0;
+ }
+
+ while (ret <= 0) {
+ CcuCoreTimer::Fire (&Timers);
+
+ if (ReadCount == 0 && WriteCount == 0 && SelectCount == 0)
+ return 0;
+
+ for (fd = 0, nfd = SelectCount; nfd; fd++) {
+ if (! FD_ISSET (fd, &SelectMask))
+ continue;
+ UchChannel* ch = Channels [fd];
+ if (ch && ch->HandleSelect ())
+ return -2;
+ nfd--;
+ }
+
+//printf ("Scan: read %x, write %x, TimeOut %d", ReadMask, WriteMask, TimeOut);
+ rmsk = ReadMask;
+ wmsk = WriteMask;
+ if (!poll && TimeOut != -1) {
+ CcuTimeStamp now;
+ Millisecond delay = TimeOut - now;
+//printf (", timeout in %d ms", delay);
+ tout.tv_sec = delay / 1000;
+ tout.tv_usec = 1000 * (delay % 1000);
+ timeout = &tout;
+ }
+//printf ("\n");
+
+ ret = select (NFILE, FD_SET_TYPE(&rmsk) /*read*/, FD_SET_TYPE(&wmsk) /*write*/, 0 /*except*/, timeout);
+ if (ret < 0 || ret == 0 && poll) {
+//printf ("select failed: %d, errno %d\n", ret, errno);
+ if (nointr && ret == -1 && errno == EINTR)
+ continue;
+ return ret;
+ }
+ }
+
+ for (fd = 0, nfd = ret; nfd; fd++) {
+//printf ("Scan: handle %d: r %d, w %d\n", fd, FD_ISSET (fd, &rmsk), FD_ISSET (fd, &wmsk));
+ if (FD_ISSET (fd, &wmsk)) {
+ nfd --;
+ UchChannel* ch = Channels [fd];
+ if (ch)
+ ch->HandleWrite ();
+ }
+ if (FD_ISSET (fd, &rmsk)) {
+ nfd--;
+ UchChannel* ch = Channels [fd];
+ if (ch)
+ ch->HandleRead ();
+ }
+ }
+
+ return ret;
+}
+
+/*?
+Scan the channels in the set and call the channel's handlers.
+First the select handler (\fun{UchChannel::HandleSelect}) of each channel with mode
+\var{IOSelect} is called.
+If it returns TRUE, \fun{Scan} exits immediately, while \fun{LoopScan} loops immediately.
+If no select handler returns TRUE, the system call \fun{select} is used to poll or
+wait for the channels that are ready.
+When the select returns normally, the write handlers \fun{HandleWrite} of the
+channels in mode \var{IOWrite} that are ready to write are called, and
+the read handlers (\fun{HandleRead}) of the channels in mode \fun{IORead}
+that are ready to read are called.
+If \var{nointr} is TRUE, ignore the interrupted system calls, else return an error
+whenever the \fun{select} system call is interrupted.
+If \var{poll} is TRUE, the call is non-blocking, else it is blocking.
+\fun{Scan} calls \fun{select} only once;
+it returns -2 if a select handler returns, it returns 0 if the channel set has no active channels,
+else it returns the return code of \fun{select}.
+\fun{LoopScan} calls \fun{select} repeatedly until \var{LoopEnd} is called, or
+an error occurs, the channel set has no more active channel
+This can occur because the select, read or write handler of a channel may remove
+the channel or change its mode.
+This is usually done when the read handler detects an end of file.
+\fun{LoopScan} returns 0 when there are no more active channel in the set,
+it returns -1 when an error occured (the code is in \var{errno}),
+and it returns 1 if \fun{LoopEnd} was called.
+?*/
+int
+UchMultiplexer :: LoopScan (bool nointr)
+{
+ fd_set rmsk, wmsk;
+ register int nfd, fd;
+ Looping = TRUE;
+
+ for (fd0 = 0; Looping; fd0 < NFILE ? fd0++ : (fd0 = 0)) {
+ CcuCoreTimer::Fire (&Timers);
+
+ if (ReadCount == 0 && WriteCount == 0 && SelectCount == 0)
+ return 0;
+
+ for (fd = fd0, nfd = SelectCount; nfd; (fd < NFILE) ? fd++ : (fd = 0)) {
+ if (! FD_ISSET (fd, &SelectMask))
+ continue;
+ UchChannel* ch = Channels [fd];
+ if (ch && ch->HandleSelect ())
+ break;
+ nfd--;
+ if (! Looping)
+ return 1;
+ }
+ if (nfd) // a handler returned TRUE.
+ continue;
+
+ rmsk = ReadMask;
+ wmsk = WriteMask;
+ struct timeval tv;
+ struct timeval* timeout = 0;
+ if (TimeOut != -1) {
+ CcuTimeStamp now;
+ Millisecond delay = TimeOut - now;
+ tv.tv_sec = delay / 1000;
+ tv.tv_usec = 1000 * (delay % 1000);
+ timeout = &tv;
+ }
+ nfd = select (NFILE, FD_SET_TYPE(&rmsk) /*read*/, FD_SET_TYPE(&wmsk) /*write*/, 0 /*except*/, timeout);
+ if (nfd < 0) {
+ if (nointr && nfd == -1 && errno == EINTR)
+ continue;
+ return nfd;
+ }
+
+ for (fd = fd0; nfd; (fd < NFILE) ? fd++ : (fd = 0)) {
+ if (FD_ISSET (fd, &wmsk)) {
+ nfd--;
+ Channels [fd] -> HandleWrite ();
+ if (! Looping)
+ return 1;
+ }
+ if (FD_ISSET (fd, &rmsk)) {
+ nfd--;
+ Channels [fd] -> HandleRead ();
+ if (! Looping)
+ return 1;
+ }
+ }
+ }
+ return 1;
+}
+
+// string repr
+//
+/*?nodoc?*/
+char*
+UchMultiplexer :: StrRepr (char* buf)
+{
+ sprintf (buf, "R:%ux W:%ux", ReadMask, WriteMask);
+ return buf;
+}
+
+#ifdef DOC
+// fake entries for inline functions
+
+/*?
+This array operator returns a (smart) pointer to the channel corresponding to file descriptor \var{fd}.
+If there is no such channel in this channel set, the operator returns NIL.
+?*/
+pUchChannel
+UchMultiplexer :: operator [] (int fd)
+{ }
+
+/*?
+Add a channel to the set.
+Here the argument is a reference, and a dynamically allocated copy of it is actually added.
+Compare with previous function.
+?*/
+void
+UchMultiplexer :: Add (const UchChannel& ch)
+{ }
+
+/*?
+This function makes \fun{LoopScan} exit immediately.
+Thus it must be called from within a channel's handler.
+?*/
+void
+UchMultiplexer :: LoopEnd ()
+{ }
+
+#endif /* DOC */
+
diff --git a/comm/Multiplexer.h b/comm/Multiplexer.h
new file mode 100644
index 0000000..0c30bfd
--- /dev/null
+++ b/comm/Multiplexer.h
@@ -0,0 +1,83 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Channel sets, or multiplexers
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#ifndef Multiplexer_H_
+#define Multiplexer_H_
+
+#include "Channel.h"
+#include <sys/types.h>
+#include "ccu/Timer.h"
+
+extern char* StrReprBuf;
+
+// This class defines channel sets for multiplexing i/o
+// Because of the coercion defined for FILDES -> int,
+// most arguments of type ints can be FILDES, UchChannel, ...
+// An array of pointers to channels is kept in the object;
+// we use smart pointers to handle deletion cleanly.
+// The masks for reading/writing are kept consistent with the IOMode of
+// the channels, as long as it is not changed by a direct access to the channel
+// operator[] returns a pointer and cannot be used as an lhs of an assignment.
+//
+class UchMultiplexer : public CcuSmartData {
+friend class UchBaseTimeOut;
+
+protected:
+ pUchChannel* Channels;
+ CcuTimerSet Timers;
+ fd_set ReadMask;
+ fd_set WriteMask;
+ fd_set SelectMask;
+ short ReadCount;
+ short WriteCount;
+ short SelectCount;
+ bool Looping;
+ Millisecond TimeOut;
+ int fd0; // used by HandleSelect and LoopScan
+
+ void SetMasks (int, IOMODE);
+inline CcuTimerSet* GetTimerSet () { return &Timers; }
+inline void SetTimeOut (Millisecond t) { TimeOut = t; }
+inline void SuppressTimeOut () {TimeOut = -1; }
+
+public:
+ UchMultiplexer ();
+ ~UchMultiplexer ();
+
+inline pUchChannel operator [] (int fd) { if (fd < 0) return pUchChannel (0); else return Channels [fd]; }
+
+ void Add (UchChannel*);
+inline void Add (const UchChannel& ch) { Add (ch.Copy ()); }
+ void Remove (int);
+ void RemoveAll ();
+ void SetMode (int, IOMODE);
+
+ bool HandleSelect ();
+virtual int Scan (bool nointr = TRUE, bool poll = FALSE);
+virtual int LoopScan (bool nointr = TRUE);
+inline void LoopEnd () { Looping = FALSE; }
+
+ char* StrRepr (char* = StrReprBuf);
+
+protected:
+/*? public ?*/
+virtual void AddNotify (UchChannel*);
+virtual void RemoveNotify (UchChannel*);
+virtual void SetModeNotify (UchChannel*, IOMODE);
+};
+
+PointerClass (pUchMultiplexer, UchMultiplexer)
+
+#endif /* Multiplexer_H_ */
+
diff --git a/comm/OLD/Agent.cc b/comm/OLD/Agent.cc
new file mode 100644
index 0000000..3e4bf8a
--- /dev/null
+++ b/comm/OLD/Agent.cc
@@ -0,0 +1,386 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Agents, by Stephane Chatty
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "Agent.h"
+
+/*?class UchAgent
+The class \typ{UchAgent} derives from \typ{UchStream}.
+It is associated to a channel set to monitor the connected agents in parallel.
+Whenever a new agent connects, the virtual function \fun{HandleNew} is called.
+This function must return a pointer to an object of class \typ{UchRemoteAgent}.
+
+Because the agents may be on different machines with different architectures,
+the byte swapping is always done by the ``server'' (ie. the agent that did not initiate
+the communication), by convention. (NOTE: byte swapping is not currently implemented)
+?*/
+
+/*?class UchRemoteAgent
+Class \typ{UchRemoteAgent} derives from \typ{UchMsgStream}.
+It is still an abstract class because the virtual functions \fun{NewMessage} and \fun{ConvertAnswer}
+of \typ{UchMsgStream} are not defined in \typ{UchRemoteAgent}.
+Instances of \fun{UchRemoteAgent} only know that they may belong to an instance of \typ{UchAgent}.
+Thus the virtual function \fun{Delete} is redefined to achieve a clean removal from the server's list.
+?*/
+
+/*?
+Construct an empty server port.
+?*/
+UchAgent :: UchAgent ()
+: UchStream (),
+ RemoteAgents (),
+ ChanSet (0)
+{
+}
+
+/*?nodoc?*/
+UchAgent :: UchAgent (const UchAgent& sp)
+: UchStream (sp),
+ RemoteAgents (),
+ ChanSet (0)
+{
+}
+
+/*?
+Construct a server port bound to address \var{a}.
+When using Internet domain addresses, \var{a} should refer to
+the wildcard address so that clients can connect from any machine.
+This can be done with the instruction \com{new UchInetAddress(ANYADDR)}.
+?*/
+UchAgent :: UchAgent (UchAddress* a)
+: UchStream (a, 0),
+ RemoteAgents (),
+ ChanSet (0)
+{
+}
+
+/*?nodoc?*/
+UchAgent :: ~UchAgent ()
+{
+ UchRemoteAgent* cl = 0;
+
+ while (cl = RemoteAgents.First ())
+ cl->Delete ();
+ ChanSet = 0; // will delete it if necessary
+}
+
+/*?nodoc?*/
+UchChannel*
+UchAgent :: Copy () const
+{
+ return new UchAgent (*this);
+}
+
+/*?
+Remove a client from the set of clients connected to the server.
+This is normally done automatically whenever the client disconnects itself,
+as a side effect of destroying the client.
+?*/
+void
+UchAgent :: RemoveRemoteAgent (UchRemoteAgent* cl)
+{
+ if (RemoteAgents.Remove (cl)) {
+ cl->MyLocalAgent = 0;
+ HandleRemove (cl);
+ } else
+ Error (ErrWarn, "UchAgent::RemoveRemoteAgent", "not owned by this server");
+}
+
+/*?
+Bind the socket and listen to it.
+Initialize the channel set of the server to \var{cs}, or create a new one if \var{cs} is 0.
+The channel set is used by the server to read and process messages from its clients:
+whenever a client connects to the server, an instance of \typ{UchRemoteAgent} is added to the channel set.
+Whenever data is readable from a client, it is read in an input buffer and processed as soon
+as a full message is received.
+Whenever a client closes the connection, it is removed from the channel set of the server.
+This function returns TRUE if all went well, else it calls \fun{SysError} and returns FALSE.
+You may want to pass your own channel set if you are already multiplexing
+i/o on that channel set. For instance, you may have several \typ{UchAgent} objects
+in your application.
+A smart pointer to the channel set is actually kept in the server, so that it can be shared
+securely.
+?*/
+bool
+UchAgent :: Setup (UchMultiplexer* cs)
+{
+ if (Listen () < 0) {
+ SysError (ErrWarn, "UchAgent::Setup");
+ return FALSE;
+ }
+ SetMode (IORead);
+ if (!cs)
+ cs = new UchMultiplexer;
+ ChanSet = cs;
+ ChanSet->Add (this);
+
+ return TRUE;
+}
+
+/*?
+This function removes the agent's registration channel from its channel set.
+This has the effect of ignoring any incoming connections.
+This function will also make \fun{Run} exit if there is no other
+channel in the channel set.
+Unless you added your own channels to this channel set,
+this will be the case if there is no remote agent currently connected.
+This is the only way to exit properly from \fun{Run}.
+?*/
+void
+UchAgent :: Unlisten ()
+{
+ if (ChanSet)
+ ChanSet->Remove (*this);
+}
+
+
+/*?nodoc?*/
+void
+UchAgent :: HandleRead ()
+{
+// cannot redefine Accept to return UchRemoteAgent*, so this does not work :
+// UchRemoteAgent* cl = Accept ();
+// need to delete the channel created by Accept, so this does not work either :
+// UchRemoteAgent* cl = new UchRemoteAgent (Accept ());
+// so we do this (less elegant ...) :
+ UchChannel* ch = Accept ();
+ if (! ch) {
+ SysError (ErrWarn, "UchAgent::HandleRead: Accept");
+ return;
+ }
+ UchRemoteAgent* ra = new UchRemoteAgent (this, ch);
+ delete ch;
+
+ ra->SetMode (IOReadSelect);
+ RemoteAgents.Append (ra);
+ if (ChanSet)
+ ChanSet->Add (ra);
+}
+
+/*?
+This virtual function is called whenever a new agent connects to this one.
+?*/
+void
+UchAgent :: HandleNew (UchRemoteAgent*)
+{
+}
+
+/*?
+This virtual function is called whenever a remote agent is removed (\fun{RemoveRemoteAgent}).
+It is a hook for the application to take whatever action; the default action is to do nothing.
+When this function is called, the client is already removed from the client list of the server.
+?*/
+void
+UchAgent :: HandleRemove (UchRemoteAgent*)
+{
+}
+
+// default error functions
+//
+/*?nextdoc?*/
+bool
+UchAgent :: SysError (errtype how, const char* who, int excl1, int excl2)
+{
+ return ::SysError (how, who, excl1, excl2);
+}
+
+/*?
+Each server port class can have its own error handling routines.
+Their default action is to call the global functions \fun{Error} and \fun{SysError}.
+They can be called from the following functions:
+\fun{Error} can be called from
+\fun{RemoveRemoteAgent} when the client does not belong to this server;
+\fun{SysError} can be called from
+\fun{Setup} when the socket could not be setup correctly,
+and from \fun{HandleRead}
+when the connection could not be accepted.
+?*/
+void
+UchAgent :: Error (errtype how, const char* who, const char* what)
+{
+ ::Error (how, who, what);
+}
+
+/*?
+Setup the server if necessary, then scan and process its channel set
+by calling \fun{LoopScan} for it.
+To have a server active, you need at least to initialize it (and thus know its address),
+and then run it.
+If you have added your own channels to the channel set of the server,
+their \fun{HandleRead} and \fun{HandleWrite} functions will be called normally by \fun{Run}.
+?*/
+void
+UchAgent :: Run ()
+{
+ if (! ChanSet)
+ if (! Setup ())
+ Error (ErrFatal, "UchAgent::Run", "could not setup");
+ if (ChanSet)
+ ChanSet->LoopScan ();
+}
+
+/*?
+Send a message to all the agents currently connected to this one.
+If \var{flush} is TRUE, the output buffer of each remote agent is flushed.
+?*/
+void
+UchAgent :: Broadcast (UchMessage& msg, bool flush)
+{
+ CcuListIterOf <UchRemoteAgent> li (RemoteAgents);
+ while (++li)
+ (*li)->Send (msg, flush);
+}
+
+/*?
+Send a message to all the agents currently connected to this one, except \var{exclude}.
+If \var{flush} is TRUE, the output buffer of each client is flushed.
+?*/
+void
+UchAgent :: Broadcast (UchMessage& msg, UchRemoteAgent* excl, bool flush)
+{
+ CcuListIterOf <UchRemoteAgent> li (RemoteAgents);
+ while (++li)
+ if (*li != excl)
+ (*li)->Send (msg, flush);
+}
+
+/*?nextdoc?*/
+void
+UchAgent :: Broadcast (UchMsgBuffer& buf, bool flush)
+{
+ CcuListIterOf <UchRemoteAgent> li (RemoteAgents);
+ while (++li)
+ (*li)->Send (buf, flush);
+}
+
+/*?
+These functions are similar to the previous ones, except that they take
+a buffer instead of a message. The buffer {\em must} contain a converted message.
+It is more efficient to broadcast a buffer than a message because there is less
+message conversion overhead.
+?*/
+void
+UchAgent :: Broadcast (UchMsgBuffer& buf, UchRemoteAgent* excl, bool flush)
+{
+ CcuListIterOf <UchRemoteAgent> li (RemoteAgents);
+ while (++li)
+ if (*li != excl)
+ (*li)->Send (buf, flush);
+}
+
+#ifdef DOC
+//fake entries for inline functions
+
+/*?
+Return the channel set used by the server.
+This is useful if you want to add your own channels to the channel set.
+?*/
+UchMultiplexer*
+UchAgent :: GetChanSet ()
+{
+}
+#endif
+
+UchRemoteAgent*
+UchAgent :: Contact (UchAddress* a)
+{
+ if (!a)
+ return 0;
+ UchRemoteAgent* ra = new UchRemoteAgent (this, a);
+ if (ra)
+ ChanSet->Add (ra);
+ return ra;
+}
+
+/*?nodoc?*/
+UchRemoteAgent :: UchRemoteAgent (UchAgent* a)
+: UchMsgStream (),
+ MyLocalAgent (a)
+{
+}
+
+/*?nodoc?*/
+UchRemoteAgent :: UchRemoteAgent (const UchRemoteAgent& cl)
+: UchMsgStream (cl),
+ MyLocalAgent (cl.MyLocalAgent)
+{
+}
+
+/*?
+Construct a new agent connected to this one on channel \var{ch}.
+?*/
+UchRemoteAgent :: UchRemoteAgent (UchAgent* a, UchChannel* ch)
+: UchMsgStream (),
+ MyLocalAgent (a)
+{
+ UchChannel::Open (ch->FilDes ());
+}
+
+UchRemoteAgent :: UchRemoteAgent (UchAgent* a, UchAddress* addr)
+: UchMsgStream (addr, 0),
+ MyLocalAgent (a)
+{
+}
+
+
+/*?nodoc?*/
+UchRemoteAgent :: ~UchRemoteAgent ()
+{
+ if (MyLocalAgent) {
+ UchAgent* s = MyLocalAgent;
+ s->Error (ErrWarn, "~UchRemoteAgent", "remote agent still connected; deleting anyway ...\n");
+ s->RemoveRemoteAgent (this);
+ if (s->ChanSet)
+ s->ChanSet->Remove (*this); // calls the destructor if no more refs
+ }
+}
+
+/*?nodoc?*/
+UchChannel*
+UchRemoteAgent :: Copy () const
+{
+ return new UchRemoteAgent (*this);
+}
+
+/*?
+This function must be used to delete a remote agent explicitly.
+It is not safe to use the operator delete.
+This function is called when an end of file is read from the client;
+this means that you usually do not need to call it.
+?*/
+void
+UchRemoteAgent :: Delete ()
+{
+ if (MyLocalAgent) {
+ UchAgent* s = MyLocalAgent;
+ s->RemoveRemoteAgent (this);
+ if (s->ChanSet)
+ s->ChanSet->Remove (*this); // calls the destructor if no more refs
+ }
+}
+
+
+
+#ifdef DOC
+//fake entries for inline functions
+
+/*?
+Return the server corresponding to a given client.
+?*/
+UchAgent*
+UchRemoteAgent :: GetAgent ()
+{
+}
+
+#endif /* DOC */
+
diff --git a/comm/OLD/Agent.h b/comm/OLD/Agent.h
new file mode 100644
index 0000000..e7db419
--- /dev/null
+++ b/comm/OLD/Agent.h
@@ -0,0 +1,82 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Agents, by Stephane Chatty
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#ifndef Agent_H_
+#define Agent_H_
+
+#include "ccu/List.h"
+#include "MsgStream.h"
+#include "Multiplexer.h"
+#include "error.h"
+
+class UchRemoteAgent;
+
+class UchAgent : public UchStream {
+friend class UchRemoteAgent;
+
+protected:
+ CcuListOf <UchRemoteAgent> RemoteAgents;
+ pUchMultiplexer ChanSet;
+
+ void HandleRead ();
+
+public:
+ UchAgent ();
+ UchAgent (const UchAgent&);
+ UchAgent (UchAddress*);
+ ~UchAgent ();
+
+ UchChannel* Copy () const;
+
+ bool Setup (UchMultiplexer* cs = 0);
+inline UchMultiplexer* GetMultiplexer () { return ChanSet; }
+
+ UchRemoteAgent* Contact (UchAddress*);
+ void RemoveRemoteAgent (UchRemoteAgent*);
+ void Run ();
+ void Unlisten ();
+
+ void Broadcast (UchMessage&, bool = FALSE);
+ void Broadcast (UchMessage&, UchRemoteAgent*, bool = FALSE);
+ void Broadcast (UchMsgBuffer&, bool = FALSE);
+ void Broadcast (UchMsgBuffer&, UchRemoteAgent*, bool = FALSE);
+
+virtual void HandleNew (UchRemoteAgent*);
+virtual void HandleRemove (UchRemoteAgent*);
+virtual bool SysError (errtype, const char*, int = 0, int = 0);
+virtual void Error (errtype, const char*, const char*);
+};
+
+
+class UchRemoteAgent : public UchMsgStream {
+friend class UchAgent;
+
+protected:
+ UchAgent *MyLocalAgent;
+
+public:
+ UchRemoteAgent (UchAgent*);
+ UchRemoteAgent (const UchRemoteAgent& cl);
+ UchRemoteAgent (UchAgent*, UchAddress*);
+ UchRemoteAgent (UchAgent*, UchChannel* ch);
+ ~UchRemoteAgent ();
+
+ UchChannel* Copy () const;
+ void Delete ();
+
+inline UchAgent* GetLocalContact () { return MyLocalAgent; }
+};
+
+
+#endif /* Agent_H_ */
diff --git a/comm/OLD/Event.cc b/comm/OLD/Event.cc
new file mode 100644
index 0000000..5fdef7c
--- /dev/null
+++ b/comm/OLD/Event.cc
@@ -0,0 +1,121 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1993
+ * Centre d'Etudes de la Navigation Aerienne (CENA)
+ *
+ * Events, by Stephane Chatty
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "Event.h"
+#include <memory.h>
+
+UchEventFeatureList :: UchEventFeatureList (const UchEventFeatureList& base, int nbf, UchEventFeature* f)
+: CcuListOf <UchEventFeature> (base)
+{
+ Load (nbf, f);
+}
+
+UchEventFeatureList :: UchEventFeatureList (int nbf, UchEventFeature* f)
+: CcuListOf <UchEventFeature> ()
+{
+ Load (nbf, f);
+}
+
+void
+UchEventFeatureList :: Load (int nbf, UchEventFeature* f)
+{
+ while (nbf-- > 0)
+ Append (f++);
+}
+
+UchEventFeatureList :: ~UchEventFeatureList ()
+{
+}
+
+
+CcuListOf <UchEventType>* UchEventType::AllTypes = 0;
+
+void
+UchEventType :: ClassInit ()
+{
+ AllTypes = new CcuListOf <UchEventType>;
+}
+
+UchEventType :: UchEventType (const char* name, int nbf, UchEventFeature f [])
+: Name (name),
+#if 0
+ Features (),
+#endif
+ NbFeatures (nbf),
+ FeaturesOffsets (new int [nbf])
+{
+ if (!AllTypes)
+ ClassInit ();
+ AllTypes->Append (this);
+ int offset = 0;
+ for (int i = 0; i < nbf; ++i) {
+ offset += f[i].Size;
+ FeaturesOffsets [i] = offset;
+ }
+}
+
+UchEventType :: UchEventType (const char* name, const UchEventFeatureList& fl)
+: Name (name),
+#if 0
+ Features (),
+#endif
+ NbFeatures (fl.Length ()),
+ FeaturesOffsets (new int [NbFeatures])
+{
+ if (!AllTypes)
+ ClassInit ();
+ AllTypes->Append (this);
+ int offset = 0;
+ int i = 0;
+ CcuListIterOf <UchEventFeature> f = fl;
+ while (++f) {
+ offset += (*f)->Size;
+ FeaturesOffsets [i] = offset;
+ ++i;
+ }
+}
+
+UchEventType :: ~UchEventType ()
+{
+ AllTypes->Remove (this);
+ delete [] FeaturesOffsets;
+}
+
+UchEvent :: UchEvent (const UchEventType* t)
+: Type (*t)
+{
+ Data = new char [t->GetTotalSize ()];
+}
+
+UchEvent :: ~UchEvent ()
+{
+ delete [] Data;
+}
+
+void
+UchEvent :: SetFeature (int i, const void* data)
+{
+ int offset = Type.GetOffset (i);
+ int size = Type.GetSize (i);
+ memcpy (Data+offset, data, size);
+}
+
+void
+UchEvent :: GetFeature (int i, void* data)
+{
+ int offset = Type.GetOffset (i);
+ int size = Type.GetSize (i);
+ memcpy (data, Data+offset, size);
+}
+
diff --git a/comm/OLD/Event.h b/comm/OLD/Event.h
new file mode 100644
index 0000000..27f3907
--- /dev/null
+++ b/comm/OLD/Event.h
@@ -0,0 +1,92 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1993
+ * Centre d'Etudes de la Navigation Aerienne (CENA)
+ *
+ * Events, by Stephane Chatty
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#ifndef UchEvent_H_
+#define UchEvent_H_
+
+#include "cplus_bugs.h"
+#include "global.h"
+#include "ccu/String.h"
+#include "ccu/List.h"
+
+#if 0
+class UchEventFeature {
+protected:
+ CcuString Name;
+ UchEventFeatureType& Type;
+
+public:
+inline UchEventFeature (const char* n, UchEventFeatureType& t) : Name (n), Type (t) {}
+inline ~UchEventFeature () {}
+};
+#endif
+
+struct UchEventFeature {
+ const char* Name;
+ int Size;
+};
+
+
+class UchEventFeatureList : public CcuListOf <UchEventFeature> {
+private:
+ void Load (int, UchEventFeature*);
+
+public:
+ UchEventFeatureList (int, UchEventFeature*);
+ UchEventFeatureList (const UchEventFeatureList&, int, UchEventFeature*);
+ ~UchEventFeatureList ();
+};
+
+
+class UchEventType {
+private:
+static void ClassInit ();
+
+protected:
+static CcuListOf <UchEventType>* AllTypes;
+ CcuString Name;
+#if 0
+ CcuListOf <UchEventFeature> Features;
+#endif
+ int NbFeatures;
+ int* FeaturesOffsets;
+
+public:
+ UchEventType (const char*, int, UchEventFeature []);
+ UchEventType (const char*, const UchEventFeatureList&);
+ ~UchEventType ();
+inline int operator == (const UchEventType& evt) const { return (this == &evt); }
+inline int GetTotalSize () const { return FeaturesOffsets [NbFeatures - 1]; }
+inline int GetOffset (int i) const { return i ? FeaturesOffsets [i-1] : 0; }
+inline int GetSize (int i) const { return FeaturesOffsets [i] - (i ? FeaturesOffsets [i-1] : 0); }
+#if 0
+inline void AddFeature (const char* fn, UchEventFeatureType& t) { Features->Append (new UchEventFeature (fn, t); }
+inline void RemoveFeature () { }
+#endif
+};
+
+class UchEvent {
+protected:
+ const UchEventType& Type;
+ char* Data;
+
+public:
+ UchEvent (const UchEventType*);
+ ~UchEvent ();
+ void SetFeature (int, const void*);
+ void GetFeature (int, void*);
+inline const UchEventType& GetType () const { return Type; }
+};
+
+#endif /* UchEvent_H_ */
diff --git a/comm/OLD/PortServer.cc b/comm/OLD/PortServer.cc
new file mode 100644
index 0000000..db4b020
--- /dev/null
+++ b/comm/OLD/PortServer.cc
@@ -0,0 +1,410 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Port server: client side
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "PortServer.h"
+#include "PortServerReq.h"
+#include "error.h"
+#include "Datagram.h"
+#include "ccu/Timer.h"
+
+#include <stdlib.h>
+#include <string.h>
+// sometimes, cuserid is declared in stdio.h ...
+#include <stdio.h>
+#include <netdb.h>
+
+/*?class UchPortServer
+The class \typ{UchPortServer} described here implements the communication with the port server.
+Both servers and clients need to use this class.
+The server registers its address with a well-known key in the port server,
+while a client uses that key to retrieve the server's address.
+?*/
+
+/*?
+Construct a link to the port server, given a service name and a host name.
+The service name must be present in the service database
+(usually the file \com{/etc/services} or a yellow pages map).
+The host name identifies the machine on which the port server lives.
+If it is the null pointer, the loopback host is assumed.
+?*/
+UchPortServer :: UchPortServer (const char* serv, const char* host)
+: Name ()
+{
+ /* get service name */
+ struct servent* service = getservbyname (serv, 0);
+ sword portno;
+ if (service) {
+ portno = ntohs (service->s_port);
+ } else {
+ const char* port = getenv (serv);
+ if (port)
+ portno = atoi (port);
+ else
+ Error (ErrFatal, "getservbyname", "service not available");
+ }
+
+ /* open datagram socket */
+ if (host)
+ Serv = new UchDatagram (0, new UchInetAddress (host, portno));
+ else
+ Serv = new UchDatagram (0, new UchInetAddress (LOOPBACK, portno));
+ if (!Serv->Setup ())
+ SysError (ErrFatal, "Setup");
+ Name = serv;
+}
+
+/*?nodoc?*/
+UchPortServer :: ~UchPortServer ()
+{
+ delete Serv;
+ Serv = 0;
+}
+
+static void
+SendRequest (UchDatagram* to, UchPortServerReq req)
+{
+ UchMsgBuffer buf (256);
+ buf.Append (req);
+ to->Write (buf);
+}
+
+/*?
+Register a server address in the port server.
+The actual key that is stored in the port server is computed by \fun{MakeKey(key)}.
+This function is to be used by servers that want to register themselves.
+If the host field of \var{addr} is \var{LOOPBACK} or \var{ANYADDR}, the actual host id
+is registered instead. This makes it possible to retrieve a valid address from another host.
+\var{id} is used as an identication key when the server asks to be removed.
+If it is 0, the process number of the server (\fun{getpid()}) is used.
+?*/
+void
+UchPortServer :: Register (const char* key, UchInetAddress& addr, int id)
+{
+ long host = addr.Host ();
+ if (host == LOOPBACK || host == ANYADDR)
+ host = UchInetAddress::LoopBack ();
+ if (! id)
+ id = getpid ();
+ UchPortServerReq req (PortServRegister, host, addr.Port (), id, MakeKey (key));
+ SendRequest (Serv, req);
+}
+
+/*?
+Remove a registered service from the port server.
+This function should be called by a server when it exits.
+\var{id} authenticates the server so that any other process is unlikely
+to be able to remove the service.
+If it is 0, the process number of the server (\fun{getpid()}) is used.
+If a server fails to remove itself, the association will stay forever in the port server,
+unless it is overwritten by the registration of the same key.
+?*/
+void
+UchPortServer :: Remove (const char* key, UchInetAddress& addr, int id)
+{
+ long host = addr.Host ();
+ if (host == LOOPBACK || host == ANYADDR)
+ host = UchInetAddress :: LoopBack ();
+ if (! id)
+ id = getpid ();
+ UchPortServerReq req (PortServRemove, host, addr.Port (), id, MakeKey (key));
+ SendRequest (Serv, req);
+}
+
+#if 0
+static jmp_buf JmpTimeOut;
+
+static void
+Timeout (int)
+{
+ longjmp (JmpTimeOut, 1);
+}
+#else
+static bool expired = FALSE;
+static void
+Expire (Millisecond)
+{
+ expired = TRUE;
+}
+#endif
+
+/*?
+Get a server address from the port server.
+Wait for \var{timeout} seconds before giving up:
+the port server may not respond because it is not present (the communication uses datagrams).
+0 is returned if the function failed (because of the timeout or because
+the given key is not registered);
+else a newly allocated address is returned.
+If the host field of the retrieved address is the same as the host id,
+it is replaced by the loopback host.
+This function is to be used by clients to retrieve the address of the server they want to connect to.
+?*/
+UchInetAddress*
+UchPortServer :: Inquire (const char* key, int timeout)
+{
+ UchPortServerReq req (PortServInquire, 0, 0, 0, MakeKey (key));
+ SendRequest (Serv, req);
+ UchMsgBuffer buf (256);
+#if 0
+ if (setjmp (JmpTimeOut)) {
+ alarm (0);
+ Error (ErrWarn, "UchPortServer::Inquire", "timeout");
+ return 0;
+ }
+ signal (SIGALRM, Timeout);
+ alarm (timeout);
+ int n = Serv->Receive (buf);
+ alarm (0);
+#else
+ expired = FALSE;
+ CcuTimer timer (timeout * 1000, Expire);
+ int n = Serv->Receive (buf);
+ if (expired) {
+ Error (ErrWarn, "UchPortServer::Inquire", "timeout");
+ return 0;
+ }
+#endif
+ if (n <= 0) {
+ SysError (ErrWarn, "Inquire");
+ return 0;
+ }
+ if (! buf.Get (&req)) {
+ Error (ErrWarn, "UchPortServer::Inquire", "could not read answer");
+ return 0;
+ }
+ switch (req.Type) {
+ case PortServAnswer :
+ return new UchInetAddress (req.Host == UchInetAddress::LoopBack () ? LOOPBACK : req.Host, req.Port);
+ case PortServFail :
+ return 0;
+ }
+ Error (ErrWarn, "UchPortServer::Inquire", "unexpected answer type");
+ return 0;
+}
+
+/*?
+Retrieve all entries in the port server that match a given key.
+The key is a regular expression in the form of \com{ed(1)}.
+For each match, the function \fun{f} is called with four arguments:
+the matched string, its associated host and port numbers, and the original key.
+If it returns 0, \fun{Match} returns immediately.
+The type \typ{^{PortServMatchFun}} is declared like this:\\
+\com{typedef int (* PortServMatchFun) (const char*, lword, sword, const char*)}\\
+\fun{Match} waits for \var{timeout} seconds before giving up while waiting an answer:
+the port server may not respond because it is not present (the communication uses datagrams).
+The function returns the total number of expected answers (it can be 0), or -1 if an error occurs.
+?*/
+int
+UchPortServer :: Match (const char* key, PortServMatchFun f, int timeout)
+{
+ int nb = 0;
+ int call = 1;
+
+ UchPortServerReq req (PortServMatch, 0, 0, 0, key);
+ SendRequest (Serv, req);
+ UchMsgBuffer buf (1024);
+ for (;;) {
+ buf.Flush ();
+#if 0
+ if (setjmp (JmpTimeOut)) {
+ alarm (0);
+ Error (ErrWarn, "UchPortServer::Match", "timeout");
+ return 0;
+ }
+ signal (SIGALRM, Timeout);
+ alarm (timeout);
+ int n = Serv->Receive (buf);
+ alarm (0);
+#else
+ expired = FALSE;
+ CcuTimer timer (timeout * 1000, Expire);
+ int n = Serv->Receive (buf);
+ if (expired) {
+ Error (ErrWarn, "UchPortServer::Match", "timeout");
+ return 0;
+ }
+#endif
+ if (n <= 0) {
+ SysError (ErrWarn, "Match");
+ break;
+ }
+ if (! buf.Get (&req)) {
+ Error (ErrWarn, "Match", "could not read message");
+ continue;
+ }
+ switch (req.Type) {
+ case PortServMatch:
+ if (call)
+ call = f (req.Key, req.Host, req.Port, key);
+ nb++;
+ break;
+ case PortServEndMatch:
+ if (nb != req.Ident)
+ Error (ErrWarn, "Match", "lost answers");
+ return req.Ident;
+ default:
+ Error (ErrWarn, "Match", "unexpected answer type");
+ return -1;
+ }
+ }
+ return nb;
+}
+
+/*?
+Ask the port server to dump its state.
+This is not normally used unless for debugging.
+Note that this is different from the save feature of the port server.
+\fun{Dump} uses a different formats, and always uses the standard output of the port server.
+?*/
+void
+UchPortServer :: Dump ()
+{
+ UchPortServerReq req (PortServDump);
+ SendRequest (Serv, req);
+}
+
+/*?
+Ask the port server to quit.
+If the port server was configured to save its state, it does so before exiting.
+NOTE: because the state cannot be reloaded from a file, this function
+is dangerous: it makes the port server loose its associations.
+?*/
+void
+UchPortServer :: Quit ()
+{
+ UchPortServerReq req (PortServQuit);
+ SendRequest (Serv, req);
+}
+
+/*?
+Build the actual key that will be stored in the port server.
+The key is made of a set of components normally separated by colons.
+Each component can be a literal string or a meta-character:
+\com{\%h} for hostname, \com{\%s} for service name, \com{\%u} for user name.
+A sample key is ``\com{\%s:\%u:foo}''.
+\com{\%u} and \com{\%h} are useful when a given service needs a different server,
+and thus a different association in the port server,
+for different users and/or hosts.
+\com{\%s} is useful if the same port server is used for different services.
+?*/
+char*
+UchPortServer :: MakeKey (const char* key)
+{
+ static char buffer [256];
+ char* p = buffer;
+
+ for (const char* q = key; *q; q++) {
+ if (*q == '%') {
+ switch (*++q) {
+ case 'h' :
+ if (gethostname (p, sizeof (buffer) - (p - buffer)) < 0) {
+ SysError (ErrWarn, "UchPortServer::MakeKey");
+ strcpy (p, "???");
+ }
+ break;
+ case 'u' : {
+ char* l = getlogin ();
+ if (! l)
+ l = cuserid (0);
+ if (! l) {
+ Error (ErrWarn, "UchPortServer::MakeKey", "getlogin failed");
+ strcpy (p, "???");
+ }
+ strcpy (p, l);
+ }
+ break;
+ case 's' :
+ strcpy (p, Name);
+ break;
+ default :
+ *p++ = '%';
+ *p++ = *q;
+ *p = 0;
+ break;
+ }
+ p += strlen (p);
+ } else
+ *p++ = *q;
+ }
+ *p = 0;
+
+ return buffer;
+}
+
+#ifdef DOC
+/*?nextdoc?*/
+void
+PortServerRegister (const char* service, const char* name, UchAddress* addr)
+{ }
+#endif /* DOC */
+
+/*?
+This global function can be used for simple registration to the portserver.
+The key used is ``\com{\%s:\%u:name}'', and the default identifier is the process number.
+It connects to the local port server.
+This function is overloaded for an easy use with \fun{UchAddress::GetAddr}
+because it returns an \typ{UchAddress*}.
+Be sure however that the address is an internet address (and not a Unix address for instance).
+?*/
+void
+PortServerRegister (const char* service, const char* name, UchInetAddress& addr)
+{
+ UchPortServer ps (service);
+ char key [256];
+ strcpy (key, "%s:%u:");
+ strcat (key, name);
+ ps.Register (key, addr, getpid ());
+}
+
+#ifdef DOC
+/*?nextdoc?*/
+void
+PortServerRemove (const char* service, const char* name, UchAddress* addr)
+{
+}
+#endif /* DOC */
+
+/*?
+This global function can be used for a simple removal from the portserver.
+The key used is ``\com{\%s:\%u:name}'', and the default identifier is the process number.
+It connects to the local port server.
+This function is overloaded for an easy use with \fun{UchAddress::GetAddr}
+because it returns an \typ{UchAddress*}.
+Be sure however that the address is an internet address (and not a Unix address for instance).
+?*/
+void
+PortServerRemove (const char* service, const char* name, UchInetAddress& addr)
+{
+ UchPortServer ps (service);
+ char key [256];
+ strcpy (key, "%s:%u:");
+ strcat (key, name);
+ ps.Remove (key, addr, getpid ());
+}
+
+/*?
+This global function can be used for a simple inquiry to the portserver.
+The key used is ``\com{\%s:\%u:name}''.
+It connects to the port server on the given host (default is local).
+?*/
+UchInetAddress*
+PortServerInquire (const char* service, const char* name, const char* host)
+{
+ UchPortServer ps (service, (host && *host) ? host : 0);
+ char key [256];
+ strcpy (key, "%s:%u:");
+ strcat (key, name);
+ return ps.Inquire (key);
+}
+
+
diff --git a/comm/OLD/PortServer.h b/comm/OLD/PortServer.h
new file mode 100644
index 0000000..1cd8ff5
--- /dev/null
+++ b/comm/OLD/PortServer.h
@@ -0,0 +1,48 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Port server : client side
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#ifndef PortServer_H_
+#define PortServer_H_
+
+#include "Socket.h"
+#include "ccu/String.h"
+class UchDatagram;
+
+typedef int (* PortServMatchFun) (const char*, lword, sword, const char*);
+
+class UchPortServer {
+protected:
+ UchDatagram* Serv;
+ CcuString Name;
+
+public:
+ UchPortServer (const char*, const char* = 0);
+ ~UchPortServer ();
+
+ void Register (const char*, UchInetAddress&, int = 0);
+ void Remove (const char*, UchInetAddress&, int = 0);
+ UchInetAddress* Inquire (const char*, int = 10);
+ int Match (const char*, PortServMatchFun, int = 10);
+ void Dump ();
+ void Quit ();
+ char* MakeKey (const char*);
+};
+
+extern void PortServerRegister (const char*, const char*, UchInetAddress&);
+extern void PortServerRemove (const char*, const char*, UchInetAddress&);
+inline void PortServerRegister (const char* s, const char* h, UchAddress* a) { PortServerRegister (s, h, * ((UchInetAddress*) a)); }
+inline void PortServerRemove (const char* s, const char* h, UchAddress* a) { PortServerRemove (s, h, * ((UchInetAddress*) a)); }
+extern UchInetAddress* PortServerInquire (const char*, const char*, const char* = 0);
+
+#endif /* PortServer_H_ */
diff --git a/comm/OLD/PortServerReq.cc b/comm/OLD/PortServerReq.cc
new file mode 100644
index 0000000..5628408
--- /dev/null
+++ b/comm/OLD/PortServerReq.cc
@@ -0,0 +1,60 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Port server requests
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "PortServerReq.h"
+
+UchPortServerReq :: UchPortServerReq ()
+: UchMessage (),
+ Type (0),
+ Host (0),
+ Port (0),
+ Ident (0),
+ Key (0)
+{
+}
+
+UchPortServerReq :: UchPortServerReq (sword t)
+: Type (t),
+ Host (0),
+ Port (0),
+ Ident (0),
+ Key (0)
+{
+}
+
+UchPortServerReq :: UchPortServerReq (sword t, lword h, sword p, lword i, const char* k)
+: Type (t),
+ Host (h),
+ Port (p),
+ Ident (i),
+ Key (k)
+{
+}
+
+UchPortServerReq :: ~UchPortServerReq ()
+{
+}
+
+void
+UchPortServerReq :: ReadFrom (UchMsgBuffer& b, lword)
+{
+ b >> Type >> Host >> Port >> Ident;
+ Key = b.BufLength () ? (const char*) b.Buffer () : 0;
+}
+
+void
+UchPortServerReq :: WriteTo (UchMsgBuffer& b)
+{
+ b << Type << Host << Port << Ident << (char*) Key;
+}
diff --git a/comm/OLD/PortServerReq.h b/comm/OLD/PortServerReq.h
new file mode 100644
index 0000000..febde80
--- /dev/null
+++ b/comm/OLD/PortServerReq.h
@@ -0,0 +1,45 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Port server requests
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#ifndef PortServerReq_H_
+#define PortServerReq_H_
+
+#include "MsgBuffer.h"
+#include "Message.h"
+
+enum PortServMessages {
+ PortServRegister, PortServRemove, PortServInquire,
+ PortServMatch, PortServEndMatch, PortServAnswer,
+ PortServFail, PortServDump, PortServQuit
+};
+
+class UchPortServerReq : public UchMessage {
+public:
+ sword Type;
+ lword Host;
+ sword Port;
+ const char* Key;
+ lword Ident;
+
+ UchPortServerReq ();
+ UchPortServerReq (sword);
+ UchPortServerReq (sword, lword, sword, lword, const char* = 0);
+ ~UchPortServerReq ();
+
+inline void SetKey (const char* k) { Key = k; }
+ void ReadFrom (UchMsgBuffer&, lword);
+ void WriteTo (UchMsgBuffer&);
+};
+
+#endif /* PortServerReq_H_ */
diff --git a/comm/OLD/ReqMgr.cc b/comm/OLD/ReqMgr.cc
new file mode 100644
index 0000000..3850723
--- /dev/null
+++ b/comm/OLD/ReqMgr.cc
@@ -0,0 +1,184 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1993
+ * Centre d'Etudes de la Navigation Aerienne (CENA)
+ *
+ * Request management, by Stephane Chatty
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "ReqMgr.h"
+#include <stdio.h>
+#include <fstream.h>
+#include <fcntl.h>
+
+UchReqMgr :: UchReqMgr ()
+: Name ()
+{
+}
+
+UchReqMgr :: ~UchReqMgr ()
+{
+}
+
+void
+UchReqMgr :: SetName (const char* n)
+{
+ if (Name)
+ fprintf (stderr, "client type already set to %s\n", (const char*) Name);
+ else
+ Name = n;
+}
+
+void
+UchReqMgr :: DumpHeader (const char* file)
+{
+ ofstream f (file, ios::out);
+ if (!f) {
+ extern int errno;
+ fprintf (stderr, "can't write to %s: %s\n", file, sys_errlist [errno]);
+ return;
+ }
+ f << "/*\n *\tRequests for clients " << Name << "\n";
+ f << " *\n *\tThis file was generated by reqgen - do not edit\n*/\n\n";
+ f << "#ifndef " << Name << "Req_H_\n";
+ f << "#define " << Name << "Req_H_\n\n";
+ f << "#include <uch.h>\n\n";
+
+ CcuListIterOf <RequestType> req (Requests);
+ while (++req) {
+ (*req)->DumpHeader (f);
+ }
+ f << "#endif\t/* " << Name << "Req_H_ */\n";
+}
+
+
+void
+UchReqMgr :: DumpSource (const char* file)
+{
+ ofstream f (file, ios::out);
+ if (!f) {
+ extern int errno;
+ fprintf (stderr, "can't write to %s: %s\n", file, sys_errlist [errno]);
+ return;
+ }
+
+ f << "/*\n *\tRequests for clients " << Name << "\n";
+ f << " *\n *\tThis file was generated by reqgen - do not edit\n*/\n\n";
+ f << "#include \"" << file << ".h\"\n\n";
+
+ CcuListIterOf <RequestType> req (Requests);
+ while (++req) {
+ (*req)->DumpSource (f);
+ }
+}
+
+
+void
+RequestType :: DumpHeader (ofstream& f)
+{
+ f << "class " << Name << " : public UchMessage {\nprotected:\n";
+
+ CcuListIterOf <RequestField> fields (Fields);
+ while (++fields) {
+ RequestField* field = *fields;
+ f << "\t" << field->GetImpl () << "\t" << field->GetName () << ";\n";
+ }
+
+ f << "public:\n\t\t" << Name << " ();\n";
+
+ CcuListIterOf <RequestConstructor> constr (Constructors);
+ while (++constr) {
+ f << "\t\t" << Name << " (";
+ CcuListIterOf <RequestField> fields ((*constr)->GetParameters ());
+ int first = 1;
+ while (++fields) {
+ if (first)
+ first = 0;
+ else
+ f << ", ";
+ f << (*fields)->GetType ();
+ }
+ f << ");\n";
+ }
+ f << "\t\t~" << Name << " ();\n";
+ f << "\tvoid\tWriteTo (UchMsgBuffer&);\n";
+ f << "\tvoid\tReadFrom (UchMsgBuffer&, lword);\n";
+ f << "\tvoid\tActivate ();\n";
+
+ CcuListIterOf <RequestField> getter (Getters);
+ while (++getter) {
+ RequestField* field = *getter;
+ f << "inline\t" << field->GetType () << "\tGet" << field->GetName ()
+ << " () const { return (" << field->GetType () << ") " << field->GetName () << "; }\n";
+ }
+
+ CcuListIterOf <RequestField> setter (Setters);
+ while (++setter) {
+ RequestField* field = *setter;
+ f << "inline\tvoid\tSet" << field->GetName () << " (" << field->GetType ()
+ << " f) { " << field->GetName () << " = (" << field->GetImpl () << ") f; }\n";
+ }
+
+ f << "};\n\n";
+}
+
+void
+RequestType :: DumpSource (ofstream& f)
+{
+ f << Name << " :: " << Name << " ()\n: UchMessage ()\n{\n}\n\n";
+
+ CcuListIterOf <RequestConstructor> constr (Constructors);
+ while (++constr) {
+ f << Name << " :: " << Name << " (";
+ CcuListIterOf <RequestField> fields ((*constr)->GetParameters ());
+ int i = 0;
+ while (++fields) {
+ if (i > 0)
+ f << ", ";
+ f << (*fields)->GetType () << " i" << i;
+ ++i;
+ }
+ f << ")\n: UchMessage ()";
+ fields.Reset ();
+ i = 0;
+ while (++fields) {
+ f << ",\n " << (*fields)->GetName () << " (i" << i << ")";
+ ++i;
+ }
+ f << "\n{\n}\n\n";
+ }
+
+ f << Name << " :: ~" << Name << " ()\n{\n}\n\n";
+
+ f << "#ifdef SERVER\n\n";
+
+ f << "void\n" << Name << " :: ReadFrom (UchMsgBuffer& b, lword l)\n{\n";
+ f << "\tb";
+ CcuListIterOf <RequestField> field (Fields);
+ while (++field) {
+ f << " >> " << (*field)->GetName ();
+ }
+ f << ";\n";
+ f << "}\n\n";
+
+ f << "#endif\t/* SERVER */\n\n";
+ f << "#ifdef CLIENT\n\n";
+
+ f << "void\n" << Name << " :: " << "WriteTo (UchMsgBuffer& b)\n{\n";
+ f << "\tb";
+ field.Reset ();
+ while (++field) {
+ f << " << " << (*field)->GetName ();
+ }
+ f << ";\n";
+ f << "}\n\n";
+
+ f << "#endif\t/* CLIENT */\n";
+}
+
diff --git a/comm/OLD/ReqMgr.h b/comm/OLD/ReqMgr.h
new file mode 100644
index 0000000..1b02196
--- /dev/null
+++ b/comm/OLD/ReqMgr.h
@@ -0,0 +1,81 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1993
+ * Centre d'Etudes de la Navigation Aerienne (CENA)
+ *
+ * Request management, by Stephane Chatty
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#ifndef ReqMgr_H_
+#define ReqMgr_H_
+
+#include "ccu/String.h"
+#include "ccu/List.h"
+class ofstream;
+
+class RequestField {
+protected:
+ CcuString Name;
+ CcuString Type;
+ CcuString Impl;
+public:
+inline RequestField (const char* n, const char* t, const char* i) : Name (n), Type (t), Impl (i) {}
+inline const char* GetName () const { return Name; }
+inline const char* GetType () const { return Type; }
+inline const char* GetImpl () const { return Impl; }
+};
+
+class RequestConstructor {
+friend int yyparse ();
+protected:
+ CcuListOf <RequestField> Params;
+public:
+inline void AddParameter (RequestField& f) { Params.Append (&f); }
+inline const CcuListOf <RequestField>& GetParameters () const { return Params; }
+};
+
+class RequestType {
+friend int yyparse ();
+protected:
+ CcuString Name;
+ CcuListOf <RequestField> Fields;
+ CcuListOf <RequestField> Getters;
+ CcuListOf <RequestField> Setters;
+ CcuListOf <RequestConstructor> Constructors;
+
+public:
+inline RequestType (const char* n) : Name (n) { }
+inline void AddField (RequestField* f) { Fields.Append (f); }
+inline void AddConstructor (RequestConstructor* f) { Constructors.Append (f); }
+inline const char* GetName () const { return Name; }
+ void Dump (ofstream&);
+ void DumpHeader (ofstream&);
+ void DumpSource (ofstream&);
+};
+
+
+class UchReqMgr {
+friend int yyparse ();
+
+protected:
+ CcuString Name;
+ CcuListOf <RequestType> Requests;
+ void SetName (const char*);
+inline void Add (RequestType* t) { Requests.Append (t); }
+
+public:
+ UchReqMgr ();
+ ~UchReqMgr ();
+ void Read (const char*);
+ void Dump (const char*);
+ void DumpHeader (const char*);
+ void DumpSource (const char*);
+};
+
+#endif /* ReqMgr_H_ */
diff --git a/comm/OLD/ReqMgr.l b/comm/OLD/ReqMgr.l
new file mode 100644
index 0000000..bad288c
--- /dev/null
+++ b/comm/OLD/ReqMgr.l
@@ -0,0 +1,95 @@
+%{
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1993
+ * Centre d'Etudes de la Navigation Aerienne (CENA)
+ *
+ * Request management, by Stephane Chatty
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "ccu/HashTable.h"
+#include "ccu/String.h"
+#include "ReqMgr.yacc.h" // produced from the parser file with '-d' option of yacc
+
+extern int tee (int);
+#define RETURN(x) return (tee(x))
+
+extern "C" {
+ int atoi (const char *);
+ void exit (int);
+ int read (int, char*, int);
+}
+
+
+CcuDictionnary* ResWords;
+int LineNo = 1;
+static char IdBuf [1024];
+
+void
+LexInit ()
+{
+ if (!ResWords) {
+ ResWords = new CcuDictionnary (10);
+ (*ResWords)["client"] = (void*) (Y_CLIENT);
+ (*ResWords)["request"] = (void*) (Y_REQUEST);
+ (*ResWords)["getters"] = (void*) (Y_GETTERS);
+ (*ResWords)["setters"] = (void*) (Y_SETTERS);
+ (*ResWords)["const"] = (void*) (Y_CONST);
+// (*ResWords)[""] = (void*) (Y_);
+ }
+ LineNo = 1;
+
+#ifdef FLEX_SCANNER
+static void yyrestart (FILE*);
+ yyrestart (0);
+#endif
+}
+
+%}
+
+Int [-]?[0-9]+
+Ident [a-zA-Z_][a-zA-Z_0-9]*
+nl [\n]
+sp0 [ \t]*
+sp1 [ \t]+
+LocHeader \"{Ident}.h\"
+GlobHeader \<Ident.h\>
+
+%%
+
+[,();{}*] RETURN (yytext[0]);
+
+\%.*\n { /* comments */
+ ++LineNo;
+ }
+
+{sp1} {}
+
+{nl} {
+ ++LineNo;
+ }
+
+-> { RETURN (Y_YIELDS); }
+
+#include
+{Ident} {
+ CcuHashCell* c;
+ if (c = ResWords->Get (yytext)) {
+ RETURN(int (c->GetInfo ()));
+ } else {
+ yylval.string = NewString (yytext);
+ RETURN (Y_ID);
+ }
+ }
+
+. {
+ RETURN (-1);
+ }
diff --git a/comm/OLD/ReqMgr.y b/comm/OLD/ReqMgr.y
new file mode 100644
index 0000000..4c29f00
--- /dev/null
+++ b/comm/OLD/ReqMgr.y
@@ -0,0 +1,285 @@
+%{
+
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1993
+ * Centre d'Etudes de la Navigation Aerienne (CENA)
+ *
+ * Request management, by Stephane Chatty
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "ccu/HashTable.h"
+extern "C" {
+ int yyerror (const char*);
+ int yyparse();
+ int yywrap ();
+ int yylex();
+}
+
+/* This comes from the lexical analyzer */
+extern CcuDictionnary* ResWords;
+
+static void error (const char*, ...);
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "ReqMgr.h"
+
+static UchReqMgr* CurMgr;
+static RequestType* CurRequest;
+static CcuListOf <RequestField>* CurFieldList;
+static CcuDictionnaryOf <RequestField> Fields (16);
+
+static void AddFieldToReq (const char*, const char*, const char*, const char*, const char*);
+static void AddFieldToList (const char*);
+
+%}
+
+%union {
+ int integer;
+ const char *string;
+}
+
+%token <integer> Y_CLIENT Y_REQUEST Y_YIELDS Y_GETTERS Y_SETTERS Y_CONST
+%token <string> Y_ID
+%type <string> opt_star
+
+%%
+
+file:
+ /* empty */
+ | file decl
+ ;
+
+decl:
+ client
+ | request
+ ;
+
+client:
+ Y_CLIENT Y_ID ';'
+ {
+ CurMgr->SetName ($2);
+ }
+ ;
+
+request:
+ Y_REQUEST Y_ID
+ {
+ CurRequest = new RequestType ($2);
+ CurMgr->Add (CurRequest);
+ Fields.Clear ();
+ }
+ '{' request_entries '}' ';'
+ ;
+
+request_entries:
+ /* empty */
+ | request_entries request_entry
+ ;
+
+request_entry:
+ field
+ | getters
+ | setters
+ | constructor
+ ;
+
+field:
+ Y_CONST Y_ID opt_star Y_ID Y_YIELDS Y_ID ';'
+ {
+ AddFieldToReq ("const", $2, $3, $4, $6);
+ }
+ | Y_ID opt_star Y_ID Y_YIELDS Y_ID ';'
+ {
+ AddFieldToReq (0, $1, $2, $3, $5);
+ }
+ ;
+
+opt_star:
+ /* empty */ { $$ = 0; }
+ | '*' { $$ = "*"; }
+ ;
+
+getters:
+ Y_GETTERS
+ {
+ CurFieldList = &CurRequest->Getters;
+ }
+ '(' id_list ')' ';'
+ ;
+
+setters:
+ Y_SETTERS
+ {
+ CurFieldList = &CurRequest->Setters;
+ }
+ '(' id_list ')' ';'
+ ;
+
+constructor:
+ Y_ID
+ {
+ if (strcmp ($1, CurRequest->GetName ()) != 0)
+ fprintf (stderr, "unknown name %s in request type %s. Considering as constructor.\n",
+ $1, CurRequest->GetName ());
+ RequestConstructor* c = new RequestConstructor;
+ CurRequest->AddConstructor (c);
+ CurFieldList = &c->Params;
+ }
+ '(' id_list ')' ';'
+ ;
+
+id_list:
+ /* empty */
+ | Y_ID { AddFieldToList ($1); }
+ | id_list ',' Y_ID { AddFieldToList ($3); }
+ ;
+
+%%
+#include <stdio.h>
+
+static void
+AddFieldToReq (const char* c, const char* t, const char* s, const char* id, const char* impl)
+{
+ int found;
+ CcuHashCellOf <RequestField>* hc = Fields.Add (id, &found);
+ if (found) {
+ fprintf (stderr, "warning: duplicate field %s in request %s. Ignoring. \n",
+ id, CurRequest->GetName ());
+ } else {
+ char buf [128];
+ if (c || s) {
+ sprintf (buf, "%s %s %s", (c ? c : ""), t, (s ? s : ""));
+ t = buf;
+ }
+ RequestField* f = new RequestField (id, t, impl);
+ hc->SetInfo (f);
+ CurRequest->AddField (f);
+ }
+}
+
+static void
+AddFieldToList (const char* name)
+{
+ RequestField* f = Fields [name];
+ if (!f) {
+ fprintf (stderr, "warning: unknown field %s in request %s\n",
+ name, CurRequest->GetName ());
+ } else {
+ CurFieldList->Append (f);
+ }
+}
+
+int _DoTee = 0;
+
+int
+tee (int x)
+{
+ if (_DoTee) {
+ char *s;
+ switch (x) {
+ case '(':
+ s = "(";
+ break;
+ case ')':
+ s = ")";
+ break;
+ case '*':
+ s = "*";
+ break;
+ case ';':
+ s = ";";
+ break;
+ case ',':
+ s = ",";
+ break;
+ case '{':
+ s = "}";
+ break;
+ case '\n':
+ s = "\n";
+ break;
+ case Y_ID:
+ s = "ID";
+ break;
+ case Y_CLIENT:
+ s = "CLIENT";
+ break;
+ case Y_REQUEST:
+ s = "REQUEST";
+ break;
+ case Y_YIELDS:
+ s = "->";
+ break;
+ case Y_GETTERS:
+ s = "GETTERS";
+ break;
+ case Y_SETTERS:
+ s = "SETTERS";
+ break;
+/* case Y_:
+ s = "";
+ break;
+*/
+ default :
+ s = "???";
+ break;
+ }
+ printf ("%s ", s);
+ }
+ return x;
+}
+
+int
+yyerror (const char *s)
+{
+ error ("%s\n",s);
+ return (0);
+}
+
+int
+yywrap ()
+{
+ return 1;
+}
+
+void
+UchReqMgr :: Read (const char* file)
+{
+ extern void LexInit ();
+ extern FILE *yyin;
+ LexInit ();
+ if (!(yyin = fopen (file,"r"))) {
+ char err [1024];
+ sprintf (err, "Cannot open %s", file);
+ yyerror (err);
+ return;
+ }
+ CurMgr = this;
+ if (yyparse ())
+ fprintf (stderr, "\nParsing failed on %s\n", file);
+ fclose (yyin);
+}
+
+extern int LineNo;
+
+static void
+error (const char* s, ...)
+{
+ va_list ap;
+ va_start(ap, s);
+
+ fprintf (stderr, "Error at line %d: ", LineNo);
+ vfprintf (stderr, s, ap);
+ exit (0);
+}
diff --git a/comm/OLD/Server.cc b/comm/OLD/Server.cc
new file mode 100644
index 0000000..a2e908b
--- /dev/null
+++ b/comm/OLD/Server.cc
@@ -0,0 +1,395 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Servers
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "Server.h"
+
+/*?class UchServer
+The class \typ{UchServer} derives from \typ{UchStream}.
+It is associated to a channel set to monitor the connected clients in parallel.
+Whenever a new client connects to the server, the virtual function \fun{HandleNew} is called.
+This function must return a pointer to an object of class \typ{UchClient} (or a derived class of \typ{UchClient}).
+
+Because the server and the clients may be on different machines with different architectures,
+the byte swapping is always done by the server, by convention. (NOTE: byte swapping is not currently implemented)
+?*/
+
+/*?class UchClient
+Class \typ{UchClient} derives from \typ{UchMsgStream}.
+It is still an abstract class because the virtual functions \fun{NewMessage} and \fun{ConvertAnswer}
+of \typ{UchMsgStream} are not defined in \typ{UchClient}.
+Instances of \fun{UchClient} only know that they may belong to an instance of \typ{UchServer}.
+Thus the virtual function \fun{Delete} is redefined to achieve a clean removal from the server's list.
+?*/
+
+// server ports
+// constructors,
+// destructor
+// copy
+//
+
+/*?
+Construct an empty server port.
+?*/
+UchServer :: UchServer ()
+: UchStream (),
+ Clients (),
+ ChanSet (0)
+{
+}
+
+/*?nodoc?*/
+UchServer :: UchServer (const UchServer& sp)
+: UchStream (sp),
+ Clients (),
+ ChanSet (0)
+{
+}
+
+/*?
+Construct a server port bound to address \var{a}.
+When using Internet domain addresses, \var{a} should refer to
+the wildcard address so that clients can connect from any machine.
+This can be done with the instruction \com{new UchInetAddress(ANYADDR)}.
+?*/
+UchServer :: UchServer (UchAddress* a)
+: UchStream (a, 0),
+ Clients (),
+ ChanSet (0)
+{
+}
+
+/*?nodoc?*/
+UchServer :: ~UchServer ()
+{
+ UchClient* cl = 0;
+
+ while (cl = Clients.First ())
+ cl->Delete ();
+ ChanSet = 0; // will delete it if necessary
+}
+
+/*?nodoc?*/
+UchChannel*
+UchServer :: Copy () const
+{
+ return new UchServer (*this);
+}
+
+/*?
+Remove a client from the set of clients connected to the server.
+This is normally done automatically whenever the client disconnects itself,
+as a side effect of destroying the client.
+?*/
+void
+UchServer :: RemoveClient (UchClient* cl)
+{
+ if (Clients.Remove (cl)) {
+ cl->MyServer = 0;
+ HandleRemove (cl);
+ } else
+ Error (ErrWarn, "UchServer::RemoveClient", "not owned by this server");
+}
+
+/*?
+Bind the socket and listen to it.
+Initialize the channel set of the server to \var{cs}, or create a new one if \var{cs} is 0.
+The channel set is used by the server to read and process messages from its clients:
+whenever a client connects to the server, an instance of \typ{UchClient} is added to the channel set.
+Whenever data is readable from a client, it is read in an input buffer and processed as soon
+as a full message is received.
+Whenever a client closes the connection, it is removed from the channel set of the server.
+This function returns TRUE if all went well, else it calls \fun{SysError} and returns FALSE.
+You may want to pass your own channel set if you are already multiplexing
+i/o on that channel set. For instance, you may have several \typ{UchServer} objects
+in your application.
+A smart pointer to the channel set is actually kept in the server, so that it can be shared
+securely.
+?*/
+bool
+UchServer :: Setup (UchMultiplexer* cs)
+{
+ if (Listen () < 0) {
+ SysError (ErrWarn, "UchServer::Setup");
+ return FALSE;
+ }
+ SetMode (IORead);
+ if (! cs)
+ cs = new UchMultiplexer;
+ ChanSet = cs;
+ ChanSet->Add (this);
+
+ return TRUE;
+}
+
+/*?
+This function removes the server from its channel set.
+This has the effect of ignoring any incoming connections.
+This function will also make \fun{Run} exit, if there is no other channel in the channel set of the server.
+Unless you added your own channels to this channel set,
+this will be the case if there is no client currently connected.
+This is the only way to exit properly from \fun{Run}.
+?*/
+void
+UchServer :: Unlisten ()
+{
+ if (ChanSet)
+ ChanSet->Remove (*this);
+}
+
+
+// handle connections on server port
+//
+
+/*?nodoc?*/
+void
+UchServer :: HandleRead ()
+{
+// cannot redefine Accept to return UchClient*, so this does not work :
+// UchClient* cl = Accept ();
+// need to delete the channel created by Accept, so this does not work either :
+// UchClient* cl = new UchClient (Accept ());
+// so we do this (less elegant ...) :
+ UchChannel* ch = Accept ();
+ if (! ch) {
+ SysError (ErrWarn, "UchServer::HandleRead: Accept");
+ return;
+ }
+ UchClient* cl = HandleNew (ch);
+ delete ch;
+
+ cl->SetMode (IOReadSelect);
+ cl->SetOwner (this);
+ Clients.Append (cl);
+ if (ChanSet)
+ ChanSet->Add (cl);
+}
+
+// Default virtual function for handling new connections
+// If a class MY_CLIENT is derived from UchClient,
+// a class MY_SERVER must be derived from UchServer,
+// with MY_SERVER::HandleNew (UchChannel* ch)
+// doing at least a return new MY_CLIENT (ch)
+//
+/*?
+This virtual function is called whenever a new client connects to the server.
+The default action is to return \com{new UchClient(ch)}.
+This needs to be redefined only if you create a derived class of \typ{UchClient},
+say \typ{MY_CLIENT}, in which case it should be redefined in a derived class
+of \typ{UchServer} to return \com{new MY_CLIENT(ch)}.
+?*/
+UchClient*
+UchServer :: HandleNew (UchChannel* ch)
+{
+ return new UchClient (ch);
+}
+
+/*?
+This virtual function is called whenever a client is removed (\fun{RemoveClient}).
+It is a hook for the application to take whatever action; the default action is to do nothing.
+When this function is called, the client is already removed from the client list of the server.
+?*/
+void
+UchServer :: HandleRemove (UchClient*)
+{
+ // nothing
+}
+
+// default error functions
+//
+/*?nextdoc?*/
+bool
+UchServer :: SysError (errtype how, const char* who, int excl1, int excl2)
+{
+ return ::SysError (how, who, excl1, excl2);
+}
+
+/*?
+Each server port class can have its own error handling routines.
+Their default action is to call the global functions \fun{Error} and \fun{SysError}.
+They can be called from the following functions:
+\fun{Error} can be called from
+\fun{RemoveClient} when the client does not belong to this server;
+\fun{SysError} can be called from
+\fun{Setup} when the socket could not be setup correctly,
+and from \fun{HandleRead}
+when the connection could not be accepted.
+?*/
+void
+UchServer :: Error (errtype how, const char* who, const char* what)
+{
+ ::Error (how, who, what);
+}
+
+/*?
+Setup the server if necessary, then scan and process its channel set
+by calling \fun{LoopScan} for it.
+To have a server active, you need at least to initialize it (and thus know its address),
+and then run it.
+If you have added your own channels to the channel set of the server,
+their \fun{HandleRead} and \fun{HandleWrite} functions will be called normally by \fun{Run}.
+?*/
+void
+UchServer :: Run ()
+{
+ if (! ChanSet)
+ if (! Setup ())
+ Error (ErrFatal, "UchServer::Run", "could not setup");
+ if (ChanSet)
+ ChanSet->LoopScan ();
+}
+
+/*?
+Send a message to all clients currently connected to this server.
+If \var{flush} is TRUE, the output buffer of each client is flushed.
+?*/
+void
+UchServer :: Broadcast (UchMessage& msg, bool flush)
+{
+ CcuListIterOf <UchClient> li (Clients);
+ while (++li)
+ (*li)->Send (msg, flush);
+}
+
+/*?
+Send a message to all clients currently connected to this server, except \var{exclude}.
+If \var{flush} is TRUE, the output buffer of each client is flushed.
+?*/
+void
+UchServer :: Broadcast (UchMessage& msg, UchClient* excl, bool flush)
+{
+ CcuListIterOf <UchClient> li (Clients);
+ while (++li)
+ if (*li != excl)
+ (*li)->Send (msg, flush);
+}
+
+/*?nextdoc?*/
+void
+UchServer :: Broadcast (UchMsgBuffer& buf, bool flush)
+{
+ CcuListIterOf <UchClient> li (Clients);
+ while (++li)
+ (*li)->Send (buf, flush);
+}
+
+/*?
+These functions are similar to the previous ones, except that they take
+a buffer instead of a message. The buffer {\em must} contain a converted message.
+It is more efficient to broadcast a buffer than a message because there is less
+message conversion overhead.
+?*/
+void
+UchServer :: Broadcast (UchMsgBuffer& buf, UchClient* excl, bool flush)
+{
+ CcuListIterOf <UchClient> li (Clients);
+ while (++li)
+ if (*li != excl)
+ (*li)->Send (buf, flush);
+}
+
+// clients:
+//
+
+/*?nodoc?*/
+UchClient :: UchClient ()
+: UchMsgStream (),
+ MyServer (0)
+{
+}
+
+/*?nodoc?*/
+UchClient :: UchClient (const UchClient& cl)
+: UchMsgStream (cl),
+ MyServer (0)
+{
+}
+
+/*?
+Construct a new client connected to the server on channel \var{ch}.
+?*/
+UchClient :: UchClient (UchChannel* ch)
+: UchMsgStream (),
+ MyServer (0)
+{
+ UchChannel::Open (ch->FilDes ());
+}
+
+/*?nodoc?*/
+UchClient :: ~UchClient ()
+{
+ if (MyServer) {
+ UchServer* s = MyServer;
+ s->Error (ErrWarn, "~UchClient", "client still in a server; deleting anyway ...\n");
+ s->RemoveClient (this);
+ if (s->ChanSet)
+ s->ChanSet->Remove (*this); // calls the destructor if no more refs
+ }
+}
+
+/*?nodoc?*/
+UchChannel*
+UchClient :: Copy () const
+{
+ return new UchClient (*this);
+}
+
+/*?
+This function must be used to delete a client explicitly.
+It is not safe to use the operator delete.
+This function is called when an end of file is read from the client;
+this means that you usually do not need to call it.
+?*/
+void
+UchClient :: Delete ()
+{
+ if (MyServer) {
+ UchServer* s = MyServer;
+ s->RemoveClient (this);
+ if (s->ChanSet)
+ s->ChanSet->Remove (*this); // calls the destructor if no more refs
+ }
+}
+
+/*?nodoc?*/
+void
+UchClient :: SetOwner (UchServer* serv)
+{
+ if (MyServer)
+ serv->Error (ErrFatal, "UchClient::SetOwner", "already owned");
+ else
+ MyServer = serv;
+}
+
+#ifdef DOC
+//fake entries for inline functions
+
+/*?
+Return the channel set used by the server.
+This is useful if you want to add your own channels to the channel set.
+?*/
+UchMultiplexer*
+UchServer :: GetChanSet ()
+{
+}
+
+/*?
+Return the server corresponding to a given client.
+?*/
+UchServer*
+UchClient :: GetServer ()
+{
+}
+
+#endif /* DOC */
+
diff --git a/comm/OLD/Server.h b/comm/OLD/Server.h
new file mode 100644
index 0000000..7f8c597
--- /dev/null
+++ b/comm/OLD/Server.h
@@ -0,0 +1,77 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Servers
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#ifndef Server_H_
+#define Server_H_
+
+#include "ccu/List.h"
+#include "MsgStream.h"
+#include "Multiplexer.h"
+#include "error.h"
+
+class UchServer;
+
+class UchClient : public UchMsgStream {
+friend class UchServer;
+
+protected:
+ UchServer *MyServer;
+
+public:
+ UchClient ();
+ UchClient (const UchClient& cl);
+ UchClient (UchChannel* ch);
+ ~UchClient ();
+
+ UchChannel* Copy () const;
+ void Delete ();
+
+inline UchServer* GetServer () { return MyServer; }
+ void SetOwner (UchServer*);
+};
+
+class UchServer : public UchStream {
+friend class UchClient;
+
+protected:
+ CcuListOf <UchClient> Clients;
+ pUchMultiplexer ChanSet;
+
+public:
+ UchServer ();
+ UchServer (const UchServer&);
+ UchServer (UchAddress*);
+ ~UchServer ();
+
+ UchChannel* Copy () const;
+ void HandleRead ();
+
+ bool Setup (UchMultiplexer* cs = 0);
+inline UchMultiplexer* GetMultiplexer () { return ChanSet; }
+ void RemoveClient (UchClient*);
+ void Run ();
+ void Unlisten ();
+ void Broadcast (UchMessage&, bool = FALSE);
+ void Broadcast (UchMessage&, UchClient*, bool = FALSE);
+ void Broadcast (UchMsgBuffer&, bool = FALSE);
+ void Broadcast (UchMsgBuffer&, UchClient*, bool = FALSE);
+// called by HandleRead when a new client connects: must build up a client from a channel
+virtual UchClient* HandleNew (UchChannel*);
+virtual void HandleRemove (UchClient*);
+virtual bool SysError (errtype, const char*, int = 0, int = 0);
+virtual void Error (errtype, const char*, const char*);
+};
+
+
+#endif /* Server_H_ */
diff --git a/comm/OLD/Service.cc b/comm/OLD/Service.cc
new file mode 100644
index 0000000..618214b
--- /dev/null
+++ b/comm/OLD/Service.cc
@@ -0,0 +1,328 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Client side: services
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "Service.h"
+
+/*?class UchService
+An object of class \typ{UchService} (derived from \typ{UchMsgStream}) exists in a client process
+to represent the server it is connected to.
+
+The class \typ{UchService} is virtual: you must create subclasses that redefine
+at least \fun{NewMessage} from class \fun{UchMsgStream}, and if necessary \fun{ConvertAnswer}.
+\fun{NewMessage} should decipher the incoming message, transform it into an event,
+and put in into the event queue with \fun{PutEvent}.
+It can also handle non event messages (for instance errors).
+?*/
+
+/*?class UchEvtMsgQueue
+An event queue is a linked list of events.
+Events are normally appended to the end of the queue and extracted from the beginning.
+?*/
+
+/*?nodoc?*/
+UchEvtMsgQueue :: UchEvtMsgQueue ()
+: CcuSmartData (),
+ Queue ()
+{
+}
+
+/*?nodoc?*/
+UchEvtMsgQueue :: ~UchEvtMsgQueue ()
+{
+}
+
+#ifdef DOC
+/*?
+Append an event to the queue.
+?*/
+void
+UchEvtMsgQueue :: Put (UchEventMsg* msg)
+{
+}
+
+/*?
+Put an event back in the queue (therefore it becomes the first event of the queue).
+?*/
+void
+UchEvtMsgQueue :: PutBack (UchEventMsg* msg)
+{
+}
+
+/*?
+Return the first event from the queue and remove it.
+?*/
+UchEventMsg*
+UchEvtMsgQueue :: Get ()
+{
+}
+
+/*?
+Return the first event from the queue without removing it.
+?*/
+UchEventMsg*
+UchEvtMsgQueue :: Peek ()
+{
+}
+
+#endif /* DOC */
+
+
+/*?
+Construct an empty service.
+?*/
+UchService :: UchService ()
+: UchMsgStream (),
+ EvQueue (0)
+{
+}
+
+/*?
+Construct a service connected to address \var{a}.
+?*/
+UchService :: UchService (UchAddress* a)
+: UchMsgStream (0, a),
+ EvQueue (0)
+{
+}
+
+/*?nodoc?*/
+UchService :: UchService (const UchService& s)
+: UchMsgStream (s),
+ EvQueue (s.EvQueue)
+{
+}
+
+/*?nodoc?*/
+UchService :: ~UchService ()
+{
+ EvQueue = 0; // deletes it
+}
+
+/*?nodoc?*/
+UchChannel*
+UchService :: Copy () const
+{
+ return new UchService (*this);
+}
+
+/*?
+Set the event queue to be used to store the incoming events.
+If not set, a default event queue is created.
+This function is intended to share a queue between several servers.
+?*/
+void
+UchService :: SetEvQueue (UchEvtMsgQueue* evq)
+{
+ EvQueue = evq;
+}
+
+/*?nextdoc?*/
+UchEventMsg*
+UchService :: PeekEvent (bool wait)
+{
+ Flush ();
+ if (!EvQueue)
+ EvQueue = new UchEvtMsgQueue;
+ UchEventMsg* ev = EvQueue->Peek ();
+ if (ev || ! wait)
+ return ev;
+ while (! ev) {
+ HandleRead ();
+ ev = EvQueue->Peek ();
+ }
+ return ev;
+}
+
+/*?
+These functions flush the output buffer, and check the events already in the queue.
+If there is at least one, it is returned;
+\fun{GetEvent} also removes it from the event queue.
+If the event queue is empty and \var{wait} is TRUE, the function blocks until an event arrives,
+else it returns 0 without blocking.
+These functions also create the event queue if it was not set with \fun{SetEvQueue}.
+?*/
+UchEventMsg*
+UchService :: GetEvent (bool wait)
+{
+ Flush ();
+ if (!EvQueue)
+ EvQueue = new UchEvtMsgQueue;
+ UchEventMsg* ev = EvQueue->Get ();
+ if (ev || ! wait)
+ return ev;
+ while (! ev) {
+ HandleRead ();
+ ev = EvQueue->Get ();
+ }
+ return ev;
+}
+
+/*?nextdoc?*/
+void
+UchService :: PutEvent (UchEventMsg* ev)
+{
+ if (! EvQueue)
+ EvQueue = new UchEvtMsgQueue;
+ ev->From = this;
+ EvQueue->Put (ev);
+}
+
+/*?
+These functions are similar to the functions \fun{Put} and \fun{PutBack}
+on the event queue of the server.
+They create the event queue if it was not set with \fun{SetEvQueue},
+and set the event's server.
+?*/
+void
+UchService :: PutBackEvent (UchEventMsg* ev)
+{
+ if (! EvQueue)
+ EvQueue = new UchEvtMsgQueue;
+ ev->From = this;
+ EvQueue->PutBack (ev);
+}
+
+
+/*?class UchEventMsg
+This class derives from \typ{UchMessage}, so it inherits the usual virtual functions
+\fun{ReadFrom} and \fun{WriteTo} that must be redefined in each derived class.
+An event is created when a client receives an asynchronous message from its server.
+Events are linked together in event queues.
+Events must derive from this class.
+?*/
+
+#ifdef DOC
+// fake entries for inline functions
+
+/*?
+Construct an event.
+?*/
+UchEventMsg :: UchEventMsg ()
+{
+}
+
+#endif /* DOC */
+
+/*?nodoc?*/
+UchEventMsg :: ~UchEventMsg ()
+{
+}
+
+#ifdef DOC
+
+/*?
+Return the server that sent this event.
+The service is known only if the event was appended to the event queue with \fun{UchService::PutEvent},
+else it is 0.
+?*/
+UchService*
+UchEventMsg :: GetService ()
+{
+}
+
+#endif /* DOC */
+
+/*?class UchGenEvtMsg
+This is a sample derived class of \typ{UchEventMsg}.
+It defines events that contain a pointer to a \typ{UchMessage}.
+This message must be allocated dynamically because it is deleted by the destructor.
+
+The virtual functions \fun{ReadFrom} and \fun{WriteTo} are defined to act upon the message stored in the event.
+
+The following example fetches a word from the input buffer,
+creates a message depending on its value
+(\typ{FOO_MSG} and \typ{BAR_MSG} have been derived from \typ{UchMessage}),
+and transfers the data from the buffer to the event with \fun{Get}.
+
+This piece of code typically appears in the body
+of \fun{NewMessage}:
+\begin{ccode}
+UchGenEvtMsg* ev = new UchGenEvtMsg;
+sword type;
+if (! buf.Peek (&type))
+ return;
+switch (type) {
+ case Foo :
+ ev->SetMsg (new FOO_MSG);
+ break;
+ case Bar :
+ ev->SetMsg (new BAR_MSG);
+ break;
+ ...
+}
+
+if (!buf.Get (ev))
+ // protocol error
+
+PutEvent (ev);
+\end{ccode}
+?*/
+
+#ifdef DOC
+
+/*?nextdoc?*/
+UchGenEvtMsg :: UchGenEvtMsg ()
+{ }
+
+/*?
+Construct a generic event. The second constructor sets its message.
+The message is deleted when the event is destroyed.
+Thus the message must have been allocated dynamically.
+?*/
+UchGenEvtMsg :: UchGenEvtMsg (UchMessage* m)
+{ }
+
+#endif /* DOC */
+
+/*?nodoc?*/
+UchGenEvtMsg :: ~UchGenEvtMsg ()
+{
+ if (Msg)
+ delete Msg;
+}
+
+
+#ifdef DOC
+
+/*?nextdoc?*/
+void
+UchGenEvtMsg :: SetMsg (UchMessage* m)
+{ }
+
+/*?
+Set and get the message associated to the event.
+When setting the value, the previous message of the event (if any) is deleted.
+?*/
+UchMessage*
+UchGenEvtMsg :: GetMsg ()
+{ }
+
+#endif /* DOC */
+
+/*?nodoc?*/
+void
+UchGenEvtMsg :: ReadFrom (UchMsgBuffer& buf, lword l)
+{
+ if (Msg)
+ Msg->ReadFrom (buf, l);
+}
+
+/*?nodoc?*/
+void
+UchGenEvtMsg :: WriteTo (UchMsgBuffer& buf)
+{
+ if (Msg)
+ Msg->WriteTo (buf);
+}
+
diff --git a/comm/OLD/Service.h b/comm/OLD/Service.h
new file mode 100644
index 0000000..b97a6cf
--- /dev/null
+++ b/comm/OLD/Service.h
@@ -0,0 +1,100 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Client side: services
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#ifndef Service_H_
+#define Service_H_
+
+#include "Message.h"
+#include "MsgStream.h"
+#include "error.h"
+#include "cplus_bugs.h"
+#include "ccu/List.h"
+
+class UchService;
+
+// events are messages sent by a server that are linked into an event list
+//
+class UchEventMsg : public UchMessage {
+friend class UchService;
+
+protected:
+ UchMsgStream* From;
+
+public:
+
+inline UchEventMsg () : UchMessage (), From (0) { }
+ ~UchEventMsg ();
+
+inline UchMsgStream* GetSource () { return From; }
+};
+
+
+class UchGenEvtMsg : public UchEventMsg {
+protected:
+ UchMessage* Msg;
+
+public:
+inline UchGenEvtMsg () : UchEventMsg () { Msg = 0; }
+inline UchGenEvtMsg (UchMessage* m) : UchEventMsg () { Msg = m; }
+ ~UchGenEvtMsg ();
+
+ void WriteTo (UchMsgBuffer&);
+ void ReadFrom (UchMsgBuffer&, lword);
+inline void SetMsg (UchMessage* m) { if (Msg) delete Msg; Msg = m; }
+inline UchMessage* GetMsg () { return Msg; }
+};
+
+// class for storing and retrieving events
+//
+class UchEvtMsgQueue : public CcuSmartData {
+
+protected:
+ CcuListOf <UchEventMsg> Queue;
+
+public:
+ UchEvtMsgQueue ();
+ ~UchEvtMsgQueue ();
+
+inline void Put (UchEventMsg* m) { Queue.Append (m); }
+inline UchEventMsg* Peek () { return Queue.First (); }
+inline UchEventMsg* Get () { return Queue.RemoveFirst (); }
+inline void PutBack (UchEventMsg* m) { Queue.Prepend (m); }
+inline operator const CcuListOf <UchEventMsg>& () const { return Queue; }
+};
+
+PointerClass (pUchEvtMsgQueue, UchEvtMsgQueue)
+
+// UchService is the client's view of a server
+// we use smart pointers so that the event queue can be shared
+//
+class UchService : public UchMsgStream {
+protected:
+ pUchEvtMsgQueue EvQueue;
+
+public:
+ UchService ();
+ UchService (UchAddress*);
+ UchService (const UchService&);
+ ~UchService ();
+
+ UchChannel* Copy () const;
+
+ void SetEvQueue (UchEvtMsgQueue*);
+ UchEventMsg* PeekEvent (bool = TRUE);
+ UchEventMsg* GetEvent (bool = TRUE);
+ void PutEvent (UchEventMsg*);
+ void PutBackEvent (UchEventMsg*);
+};
+
+#endif /* Service_H_ */
diff --git a/comm/OLD/SimpleMessage.cc b/comm/OLD/SimpleMessage.cc
new file mode 100644
index 0000000..1346d22
--- /dev/null
+++ b/comm/OLD/SimpleMessage.cc
@@ -0,0 +1,47 @@
+#ifdef DOC
+// fake entries for inline functions
+
+/*?class UchSimpleMessage
+A simple UchMessage.has a single public field \var{Data} of type \typ{lword}.
+?*/
+
+/*?
+Construct a simple message with data 0.
+?*/
+UchSimpleMessage :: UchSimpleMessage ()
+{}
+
+/*?
+Construct a simple message with data \var{d}.
+?*/
+UchSimpleMessage :: UchSimpleMessage (lword d)
+{}
+
+/*?class UchStringMessage
+A string UchMessage.has a public field \var{Data} of type \typ{char*}.
+It is designed to carry a null-terminated string.
+?*/
+
+/*?
+Construct a simple message with data 0.
+?*/
+UchStringMessage :: UchStringMessage ()
+{}
+
+/*?
+Construct a string message with type data \var{d}.
+The string is not copied.
+?*/
+UchStringMessage :: UchStringMessage (char* d = 0)
+{}
+
+/*?
+Change the string of the message and set it to \var{d}.
+The string is not copied.
+?*/
+void
+UchStringMessage :: SetData (char* d)
+{}
+
+#endif /* DOC */
+
diff --git a/comm/OLD/SimpleMessage.h b/comm/OLD/SimpleMessage.h
new file mode 100644
index 0000000..b4a00ce
--- /dev/null
+++ b/comm/OLD/SimpleMessage.h
@@ -0,0 +1,23 @@
+
+class UchSimpleMessage : public UchMessage {
+public:
+ lword Data;
+
+ UchSimpleMessage () { Data = 0; }
+ UchSimpleMessage (lword d) { Data = d; }
+
+ void WriteTo (UchMsgBuffer& b) { b << Data; }
+ void ReadFrom (UchMsgBuffer& b, lword) { b >> Data; }
+};
+
+class UchStringMessage : public UchMessage {
+public:
+ char* Data;
+
+ UchStringMessage () { Data = 0; }
+ UchStringMessage (char* d) { Data = d; }
+
+ void WriteTo (UchMsgBuffer& b) { b << Data; }
+ void ReadFrom (UchMsgBuffer& b, lword l) { Data = (char*) b.Buffer (); b.Flush ((int) l); }
+ void SetData (char* d) { Data = d; }
+};
diff --git a/comm/OLD/TextStream.cc b/comm/OLD/TextStream.cc
new file mode 100644
index 0000000..a6d12d3
--- /dev/null
+++ b/comm/OLD/TextStream.cc
@@ -0,0 +1,1030 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Text streams
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "TextStream.h"
+#include "TimeOut.h"
+#include "PortServer.h"
+#include "Multiplexer.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+// list is a set of words
+// the separator must be the first and last char of the string:
+// e.g. :yes:no: /foo/bar/toto/
+// word is a word to be looked up in the list.
+// we use the same kind of comparison as in cmpname, provided that the words
+// in the list are capitalized.
+// windex returns 0 if the word was not found, otherwise its index in the list.
+int
+windex (const char* list, const char* word)
+{
+ if (! list || ! word)
+ return 0;
+ char sep = *list;
+ if (! sep)
+ return 0;
+ register const char* p;
+ register const char* w = word;
+ int ind = 1;
+ for (p = list+1; *p; p++) {
+ if (w) { // so far the word matches
+ if (!*w && *p == sep) // found !
+ return ind;
+ if (*w == *p) // still ok
+ w++;
+ else if (isupper (*p)) {
+ if (*w == '-' || *w == '_')
+ w++;
+ if (*p == *w || tolower (*p) == *w)
+ w++;
+ else
+ w = 0;
+ } else // not this item
+ w = 0;
+ } else {
+ if (*p == sep) { // start new item
+ ind++;
+ w = word;
+ }
+ }
+ }
+ return 0;
+}
+
+// compare two names
+// an uppercase letter can match the same uppercase letter,
+// or an optional hyphen or underscore followed by the lowercase letter.
+// if the name starts with an uppercase, it matches the lowercase
+// FooBar == foo-bar, foo_bar, foobar
+// returns like strcmp
+int
+cmpname (register const char* s1, register const char* s2)
+{
+ for (; *s1 && *s2; s1++, s2++) {
+ if (*s1 == *s2)
+ continue;
+ if (isupper (*s1)) {
+ if (*s2 == '-' || *s2 == '_')
+ s2++;
+ if (*s1 == *s2 || tolower (*s1) == *s2)
+ continue;
+ else
+ break;
+ } else if (isupper (*s2)) {
+ if (*s1 == '-' || *s1 == '_')
+ s1++;
+ if (*s2 == *s1 || tolower (*s2) == *s1)
+ continue;
+ else
+ break;
+ } else
+ break;
+ }
+
+ if (*s1 == '\0' && *s2 == '\0')
+ return 0;
+ if (*s1 == '\0')
+ return -1;
+ if (*s2 == '\0')
+ return 1;
+ if (tolower (*s1) < tolower (*s2))
+ return -1;
+ return 1;
+}
+
+// sprintf a message (up to 5 args) and return the formatted string
+// the address of a static string is returned
+const char*
+stringf (const char* fmt, const void* a1, const void* a2, const void* a3, const void* a4, const void* a5)
+{
+ static char msg [1024];
+ sprintf (msg, fmt, a1, a2, a3, a4, a5);
+ return msg;
+}
+
+
+void
+UchTextWord :: SetVal (const char* val)
+{
+ // if Sval is null, then it's an integer, whose value is in Ival
+ // if Sval is non null, then it's a string, whose value is in Sval
+ Ival = atoi (val);
+ if (Ival == 0 && strcmp (val, "0") != 0)
+ Sval = val; // word is a string: atoi returned 0 and val is not "0"
+ else
+ Sval = 0; // word is an integer
+}
+
+const char*
+UchTextWord :: GetQuotes () const
+{
+ if (! Sval)
+ return ""; /* no quotes */
+ if (! *Sval)
+ return "\'\'"; /* empty string */
+ if (index (Sval, ' ') == 0)
+ return ""; /* no quotes */
+ if (index (Sval, '\'') == 0)
+ return "\'\'"; /* 'hello' */
+ if (index (Sval, '\"') == 0)
+ return "\"\""; /* "hello" */
+ return 0; /* cannot quote */
+}
+
+
+void
+UchTextLine :: NewWord (const char* s, int i)
+{
+ if (Num >= Max) {
+ if (Max == 0) {
+ Max = 16;
+ Words = new UchTextWord [Max];
+ } else {
+ UchTextWord* old = Words;
+ Words = new UchTextWord [Max *= 2];
+ UchTextWord* po = old;
+ UchTextWord* pn = Words;
+ for (int i = 0; i < Num; i++)
+ *pn++ = *po++;
+ delete [] old;
+ }
+ }
+ if (s)
+ Words [Num++].SetVal (s);
+ else
+ Words [Num++].SetVal (i);
+}
+
+UchTextLine :: UchTextLine ()
+: Num (0),
+ Max (0),
+ Words (0)
+{
+}
+
+UchTextLine :: ~UchTextLine ()
+{
+ if (Words)
+ delete [] Words;
+}
+
+void
+UchTextLine :: AddTrailer (const UchTextLine& line, int first)
+{
+ for (; first < line.Num; first++) {
+ UchTextWord& word = line [first];
+ NewWord (word, word);
+ }
+}
+
+bool
+UchTextLine :: Parse (char* line)
+{
+ enum WHERE { IN_SPACE, IN_WORD, IN_QUOTE };
+ char quote = '\0';
+ register char* s = line;
+ register char* word = line;
+ register WHERE where = IN_SPACE;
+
+ // split a line into words
+ // a word is a sequence of non blank characters,
+ // or a quoted string (' or ").
+ // '(', ')' and ',' are also ignored to allow for function like notation
+ for (; *s; s++) {
+ switch (where) {
+ case IN_WORD:
+ if (index (" \t(),", *s)) {
+ *s = '\0';
+ where = IN_SPACE;
+ AddWord (word);
+ }
+ break;
+ case IN_QUOTE:
+ if (*s == quote) {
+ *s = '\0';
+ where = IN_SPACE;
+ AddWord (word);
+ }
+ break;
+ case IN_SPACE:
+ if (*s == '\'' || *s == '\"') {
+ quote = *s;
+ *s = '\0';
+ where = IN_QUOTE;
+ word = s+1;
+ } else
+ if (index (" \t(),", *s)) {
+ *s = '\0';
+ } else {
+ where = IN_WORD;
+ word = s;
+ }
+ break;
+ }
+ }
+ if (where != IN_SPACE)
+ AddWord (word);
+ if (where == IN_QUOTE)
+ return FALSE;
+ return TRUE;
+}
+
+// decompile line into buffer
+// if the line does not fit in the buffer, it ends with '...'
+// the return value is dest, or a pointer to a static string "..."
+// if dest or len is 0.
+char*
+UchTextLine :: Unparse (char* dest, int len) const
+{
+ UchTextWord* pa = Words;
+ char* p = dest;
+ char* lim = dest + len -1;
+
+ if (! dest || ! len)
+ return "...";
+
+ for (int i = 0; i < Num; i++, pa++) {
+ // convert arg to string
+ const char* sa;
+ const char* quotes = "";
+ if (pa->IsInt ()) {
+ char num [32];
+ sprintf (num, "%d", int (*pa));
+ sa = num;
+ } else {
+ sa = *pa;
+ quotes = pa->GetQuotes ();
+ if (! quotes) {
+ fprintf (stderr, "UchTextLine::Unparse, cannot quote word\n");
+ quotes = "";
+ sa = "(unquotable)";
+ }
+ }
+ // try to append
+ if (p + strlen (sa) + strlen (quotes) < lim) {
+ if (*quotes)
+ *p++ = quotes [0];
+ strcpy (p, sa);
+ p += strlen (p);
+ if (*quotes)
+ *p++ = quotes [1];
+ } else {
+ if (*quotes)
+ *p++ = quotes [0];
+ strncpy (p, sa, (lim - p) - 3);
+ strcpy (lim -3, "...");
+ p = lim;
+ break;
+ }
+ // separator with next one
+ if (i < Num -1 && p < lim)
+ *p++ = ' ';
+ }
+ *p = '\0';
+ return dest;
+}
+
+// unparse the line in a buffer.
+// the unparsed string is _not_ terminated by a null
+// the return value is a pointer to the beginning of the string in the buffer
+char*
+UchTextLine :: Unparse (UchMsgBuffer* buf) const
+{
+ UchTextWord* pa = Words;
+ char* start = (char*) buf->Buffer ();
+
+ for (int i = 0; i < Num; i++, pa++) {
+ // convert arg to string and append it
+ const char* sa;
+ const char* quotes = "";
+ if (pa->IsInt ()) {
+ char num [32];
+ sprintf (num, "%d", int (*pa));
+ sa = num;
+ } else {
+ sa = *pa;
+ quotes = pa->GetQuotes ();
+ if (! quotes) {
+ fprintf (stderr, "UchTextLine::Unparse, cannot quote word <%s>\n", sa);
+ quotes = "";
+ sa = "(unquotable)";
+ }
+ }
+ if (*quotes)
+ buf->Append (quotes [0]);
+ buf->Append (sa, FALSE /*no newline*/);
+ if (*quotes)
+ buf->Append (quotes [1]);
+ // separator with next one
+ if (i < Num -1)
+ buf->Append (' ');
+ }
+ return start;
+}
+
+bool
+UchTextLine :: Match (const char* cmdname, const char* profile) const
+{
+ if (Num <= 0)
+ return FALSE; // no word on line
+ const char* myname = Words [0];
+ if (! myname || cmpname (myname, cmdname) != 0)
+ return FALSE; // wrong command name
+
+ // check the profile, if it is non null.
+ // each letter in the profile corresponds to one argument:
+ // 'i' for integer, 's' for string, 'b' for bool.
+ // the value for a boolean can be an integer (0=false, otherwise true)
+ // or a string: yes, y, true, t, on, no, n, false, f, off
+ // if the last character of the profile is '+', then the extra arguments
+ // are not checked.
+ // a null profile is identical to the profile "+".
+ // an empty profile ("") describes a command with no argument.
+ // *** we'll probably add features for repeated arguments,
+ // *** enums (mapping strings to ints), etc.
+ if (! profile)
+ return TRUE;
+ const char* p = profile;
+ for (int i = 1; i < Num; i++) {
+ switch (*p) {
+ case 'i':
+ if (! Words [i].IsInt ())
+ return FALSE;
+ break;
+ case 's':
+ if (! Words [i].IsString ())
+ return FALSE;
+ break;
+ case 'b':
+ {
+ if (Words [i].IsInt ())
+ break;
+ const char* sval = Words [i];
+ if (windex (":Yes:Y:True:T:On:", sval)) {
+ Words[i].SetVal (1);
+ break;
+ } else if (windex (":No:N:False:F:Off:", sval)) {
+ Words[i].SetVal (0);
+ break;
+ }
+ return FALSE;
+ }
+ case '+':
+ if (*(p+1)) // not at end of profile
+ fprintf (stderr, "UchTextLine::Match, invalid command profile: <%s>\n", profile);
+ return TRUE;
+ case '\0':
+ // profile shorter than arg list
+ return FALSE;
+ default:
+ fprintf (stderr, "UchTextLine::Match, invalid command profile: <%s>\n", profile);
+
+ return FALSE;
+ }
+ p++;
+ }
+ if (*p)
+ return FALSE; // arg list shorter than profile
+ return TRUE;
+}
+
+// return -1 if word is not in the line, otherwise return its index
+int
+UchTextLine :: Index (const char* word) const
+{
+ UchTextWord* w = Words;
+ int i = 0;
+
+ for (; i < Num; i++, w++)
+ if (w->IsString () && strcmp (*w, word) == 0)
+ return i;
+ return -1;
+}
+
+// return -1 if value is not in the line, otherwise return its index
+int
+UchTextLine :: Index (int value) const
+{
+ UchTextWord* w = Words;
+ int i = 0;
+
+ for (; i < Num; i++, w++)
+ if (w->IsInt () && *w == value)
+ return i;
+ return -1;
+}
+
+//---------------- constants
+
+// we start with a few quick retries
+// AbandonRetries doubles the time and decrement the retries until 1
+// 2 secs * 5 = 10 secs
+// 4 secs * 4 = 16 secs
+// 8 secs * 3 = 24 secs
+// 16 secs * 2 = 32 secs
+// 32 secs * 1 = 32 secs
+// total = 15 retries in 114 secs ~ 2 minutes
+// we want this to be significantly longer than the REG_TIME of the port server
+
+#define RETRY_TIME 2
+#define MAX_RETRIES 5
+
+/*?
+Construct a \typ{UchTextStream}.
+?*/
+UchTextStream :: UchTextStream ()
+: UchStream (),
+ InBuffer (256),
+ OutBuffer (256),
+ MyMpx (0)
+{
+}
+
+/*?
+Protected destructor.
+\fun{Close} must be used instead to properly close down a \typ{UchTextStream}
+?*/
+UchTextStream :: ~UchTextStream ()
+{
+ // nothing
+}
+
+/*?
+This virtual function is called by \fun{HandleRead} when an end of file
+is read (the argument is then \var{TRUE}), or when an error occured while
+reading (the argument is then \var{FALSE}).
+The default action is to call \fun{Close}.
+?*/
+void
+UchTextStream :: Closing (bool)
+{
+ Close ();
+}
+
+void
+UchTextStream :: AddNotify (UchMultiplexer& m)
+{
+ MyMpx = &m;
+}
+
+void
+UchTextStream :: RemoveNotify (UchMultiplexer& m)
+{
+ if (MyMpx == &m)
+ MyMpx = 0;
+}
+
+/*?
+Read and decode input. When a complete line (terminated either by
+a newline or cr-newline) is in the input buffer, it is parsed.
+If parsing is successful, \fun{Execute} is called with the line as argument.
+If \fun{Execute} returns \var{isCmdUnknown}, \fun{TryPredefined} is called
+to decode the predefined commands.
+Finally, \fun{ProcessCmdResult} is called and the line is flushed from
+the input buffer.
+Note that since the parsed line passed to \fun{Execute} contains pointers
+to the input buffer, \fun{Execute} {\em must not} keep copies of these
+pointers, but copy the contents instead.
+?*/
+void
+UchTextStream :: HandleRead ()
+{
+ InBuffer.NeedSize (128);
+ int n = Read (InBuffer);
+ if (n <= 0) {
+ Closing (n < 0 ? FALSE : TRUE);
+ return;
+ }
+
+ // scan buffer for complete commands
+ char* start = (char*) InBuffer.Buffer ();
+ char* stop = start + InBuffer.BufLength ();
+ for (char* p = start; p < stop; p++) {
+ if (*p == '\n') {
+ *p = '\0';
+ // check cr-nl sequence
+ if (p > start && *(p-1) == '\r')
+ *(p-1) = '\0';
+ // parse line, lookup command
+ UchTextLine line;
+ cmd_res res = isCmdUnknown;
+ if (line.Parse (start)) {
+ if (line.NumWords () == 0)
+ res = isCmdOk;
+ else
+ res = Execute (line);
+ if (res == isCmdUnknown)
+ res = TryPredefined (line);
+ } else
+ res = isCmdSyntax;
+ ProcessCmdResult (res, line);
+ // reset for scanning rest of buffer
+ start = p+1;
+ InBuffer.Flush ((byte*) start);
+ }
+ }
+}
+
+/*?
+Process the result \var{res} of the execution of the line \var{line}.
+If \var{res} is one of \var{isCmdSyntax}, \var{isCmdUnknown}, \var{isCmdError},
+a warning message is issued.
+If \var{res} is \var{isCmdClose} (resp. \var{isCmdQuit}), the virtual function
+\fun{Close} (resp. \fun{Quit}) is called.
+If \var{res} is \var{isCmdTerminate} (resp. \var{isCmdAbort}),
+the global function \fun{MpxTerminate} (resp. \fun{MpxAbort}) is called.
+If \var{res} is \var{isCmdExit}, \fun{exit(1)} is called.
+Finally, if \var{res} is \var{isCmdOk}, nothing happens.
+?*/
+void
+UchTextStream :: ProcessCmdResult (cmd_res res, const UchTextLine& line)
+{
+ const char* msg = 0;
+ switch (res) {
+ case isCmdSyntax:
+ msg = "syntax error in <%s>";
+ break;
+ case isCmdUnknown:
+ msg = "unknown command: <%s>";
+ break;
+ case isCmdOk:
+ return;
+ case isCmdError:
+ msg = "error while executing command <%s>";
+ break;
+ case isCmdClose:
+ // like eof (but smarter for the session)
+ Close ();
+ return;
+ case isCmdQuit:
+ //quit application
+ Quit ();
+ return;
+ case isCmdTerminate:
+ // terminate the multiplexer
+ if (MyMpx)
+ MyMpx->LoopEnd ();
+ return;
+ case isCmdAbort:
+ // abort the multiplexer
+// MpxAbort ();
+ return;
+ case isCmdExit:
+ exit (1);
+ }
+ if (msg) {
+ char buf [64];
+ line.Unparse (buf, sizeof (buf));
+ fprintf (stderr, "UchTextStream::ProcessCmdResult\n", msg, buf);
+ }
+}
+
+/*?
+If \var{line} is one of the words
+\com{Close}, \com{Quit}, \com{Terminate}, \com{Abort}, \com{Exit},
+the corresponding code is returned (\var{isCmdClose}, etc.),
+otherwise, \var{isCmdUnknown} is returned.
+?*/
+UchTextStream::cmd_res
+UchTextStream :: TryPredefined (const UchTextLine& line)
+{
+ const char* cname = line [0];
+
+ if (line.NumWords () != 1)
+ return isCmdUnknown;
+ switch (windex (":Close:Quit:Terminate:Abort:Exit:", cname)) {
+ case 1: return isCmdClose;
+ case 2: return isCmdQuit;
+ case 3: return isCmdTerminate;
+ case 4: return isCmdAbort;
+ case 5: return isCmdExit;
+ }
+ return isCmdUnknown;
+}
+
+/*?
+This virtual function is called by the members that send commands on the stream.
+By defaults, it writes the output buffer on the stream.
+Derived classes can redefine this function.
+?*/
+void
+UchTextStream :: DoSend ()
+{
+ Write (OutBuffer);
+}
+
+/*?
+This virtual function is called by \fun{ProcessCmdResult} when a \com{close}
+command has been received.
+It can be called directly by the application to close down the connection
+(remember that the destructor is private and cannot be called directly).
+By default it calls \fun{MpxRemoveChannel} to unregister this stream from the
+multiplexer.
+This will trigger deletion of the stream is the multiplexer holds the last
+smart pointer to it.
+Derived classes can redefine this function, but the redefinition
+should call the default implementation.
+?*/
+void
+UchTextStream :: Close ()
+{
+ MyMpx->Remove (*this);
+}
+
+/*?
+This virtual function is called by \fun{ProcessCmdResult} when a \com{quit}
+command has been received.
+By default is calls \fun{MpxTerminate}.
+Derived classes can redefine this function if they want to terminate more
+gracefully.
+?*/
+void
+UchTextStream :: Quit ()
+{
+ MyMpx->LoopEnd ();
+}
+
+//---------------- ServiceStarter
+
+class UchServiceStarter : public UchBaseTimeOut {
+friend class UchTextService;
+
+protected:
+ UchTextService* Service;
+ int Retries;
+ int Max;
+
+public:
+ UchServiceStarter (UchTextService*, int, int);
+ void Handle (Millisecond);
+};
+
+UchServiceStarter :: UchServiceStarter (UchTextService* s, int retry, int maxr)
+: UchBaseTimeOut (*s->GetMultiplexer (), retry * 1000),
+ Service (s),
+ Retries (0),
+ Max (maxr)
+{
+}
+
+void
+UchServiceStarter :: Handle (Millisecond)
+{
+ /* UchServiceStarters are not timers triggered on signals.
+ For that reason, Handle is allowed to be complex. */
+
+ if (! Service)
+ return;
+
+ fprintf (stderr, "ServiceStarter::TimeOut, attempting restart\n");
+ if (Service->GetStatus () == UchTextService::isRunning
+ || Service->Restart () == UchTextService::isRunning) {
+ Stop ();
+ Service->Starter = 0;
+ delete this;
+ return;
+ }
+
+ if (++Retries >= Max) {
+ Stop ();
+ if (Max <= 1) {
+ fprintf (stderr, "ServiceStarter::TimeOut, abandoning restart\n");
+ Service->Starter = 0;
+ Service->AbandonRestart ();
+ delete this;
+ } else {
+ Max--;
+ Retries = 0;
+ ChangePeriod (GetPeriod () * 2);
+ Restart ();
+ }
+ }
+}
+
+
+/*?
+Construct an empty \var{UchTextService}.
+When using this constructor, the application must call \fun{Init} at a later
+time in order to define the server to connect to.
+The initial state of the service is \var{isLost}, since the actual
+connection to the server is not established yet.
+?*/
+UchTextService :: UchTextService ()
+: UchTextStream (),
+ StatusFlag (isLost),
+ Starter (0),
+ Closed (FALSE),
+ User (),
+ Service (),
+ Host ()
+{
+}
+
+/*?
+Construct a \var{UchTextService} connected to server \var{s} on host \var{h}.
+This is equivalent to the empty constructor followed by a call to \fun{Init}.
+?*/
+UchTextService :: UchTextService (const char* s, const char* h)
+: UchTextStream (),
+ StatusFlag (isLost),
+ Starter (0),
+ Closed (FALSE),
+ User (),
+ Service (),
+ Host ()
+{
+ Init (s, h);
+}
+
+/*?
+The (protected) destructor.
+Call \fun{Close} or \fun{CloseNow} to close down the connection and
+delete the service.
+?*/
+UchTextService :: ~UchTextService ()
+{
+ if (Starter)
+ delete Starter;
+}
+
+/*?
+This protected virtual function is called by \fun{UchTextStream::HandleRead}
+when an end-of-file is read or when a read failed.
+It calls \var{LostServer} so that an attempt to reconnect to the server
+can be made.
+?*/
+void
+UchTextService :: Closing (bool)
+{
+ fprintf (stderr, "UchTextService::Closing, lost server\n");
+ StatusFlag = isLost;
+ LostServer ();
+}
+
+/*?
+Attempt to connect to the server.
+If the address of the server cannot be retrieved from the port server,
+this function sets the state of the service to \var{isUnavailable}
+and returns.
+If an address is obtained from the port server, this function tries to connect
+to that address.
+If this fails, it sets the state to \var{isError} and returns.
+Otherwise, it sets the state to \var{isRunning} and calls the virtual
+function \fun{GotServer}.
+The service is automatically registered with the multiplexer
+(by calling \fun{MpxAddChannel} with mode \var{IONone})
+the first time the connection is established.
+?*/
+UchTextService::status
+UchTextService :: Restart ()
+{
+ UchInetAddress* addr = PortServerInquire ("portserv", Service, Host);
+ if (! addr) {
+ fprintf (stderr, "UchTextService::Restart, no address in port server\n");
+ return StatusFlag = isUnavailable;
+ }
+ ConnectTo (addr);
+
+ bool wasopened = Fd.Opened ();
+ bool setup = TRUE;
+ if (wasopened) {
+ // we are in trouble because a stream socket can be connected
+ // only once and a channel is immutable...
+ // so we have to fake a reopen
+ int ofd = FilDes ();
+ if (ofd >= 0) {
+ close (ofd);
+ int nfd = socket (ConnectedTo () ->Family (), SockType (), 0);
+ fprintf (stderr, "UchTextService::Restart, reopening socket: %d -> %d\n", (void*) ofd, (void*) nfd);
+ if (nfd < 0) {
+ setup = FALSE;
+ } else if (nfd != ofd) {
+ dup2 (nfd, ofd);
+ close (nfd);
+ }
+ }
+ }
+ if (setup)
+ setup = Setup ();
+ if (! wasopened) {
+ SetMode (IONone);
+ MyMpx->Add (this);
+ }
+ if (! setup) {
+ fprintf (stderr, "UchTextService::Restart, could not setup connection\n");
+ return StatusFlag = isError;
+ }
+
+ StatusFlag = isRunning;
+ if (Starter) // do not log if got server at first attempt
+ fprintf (stderr, "UchTextService::Restart, got server\n");
+ GotServer ();
+ return StatusFlag;
+}
+
+/*?
+This protected function arranges for a connection to be attempted in
+\var{retry_time} seconds.
+If the connection cannot be established after \var{maxr} retries,
+\var{retry_time} is doubled and \var{maxr} is decremented and new attempts
+are made with these new values.
+When the number of retries is null, then the virtual function
+\fun{AbandonRestart} is called and no further attempts are made.
+If \var{retry_time} is 0, any pending attempts are cancelled.
+If \var{retry_time} or var{maxr} is negative, suitable default values are used.
+?*/
+void
+UchTextService :: AutoStart (int retry_time, int maxr)
+{
+ if (retry_time < 0)
+ retry_time = RETRY_TIME;
+ if (maxr < 0)
+ maxr = MAX_RETRIES;
+
+ if (retry_time == 0) { // cancel auto-start
+ if (Starter) {
+ delete Starter;
+ Starter = 0;
+ }
+ return;
+ }
+ if (Starter) {
+ Starter->ChangePeriod (retry_time);
+ Starter->Retries = 0;
+ Starter->Max = maxr;
+ } else if (MyMpx) {
+ Starter = new UchServiceStarter (this, retry_time, maxr);
+ } else
+ fprintf (stderr, "UchTextService::AutoStart, no multiplexer for time-out\n");
+}
+
+/*?
+This protected virtual function is called when the connection to the server
+has been broken.
+By default, it resets the mode of the channel to \var{IONone} so that the
+multiplexer does not attempt to read from the server.
+Derived classes can redefine this function, but the redefinition should
+call the default implementation.
+?*/
+void
+UchTextService :: LostServer ()
+{
+ MyMpx->SetMode (*this, IONone);
+ AutoStart ();
+}
+
+/*?
+This protected virtual function is called when the connection to the server
+has been established.
+By default, it sets the mode of the channel to \var{IORead} so that the
+multiplexer can monitor the channel.
+If the output buffer is not empty, it sends it to the server by calling
+\fun{DoSend}.
+Derived classes can redefine this function, but the redefinition should
+call the default implementation.
+?*/
+void
+UchTextService :: GotServer ()
+{
+ MyMpx->SetMode (*this, IORead);
+ if (OutBuffer.BufLength ())
+ DoSend ();
+}
+
+/*?
+This protected virtual function is called when the connection cannot
+be restarted (see \fun{AutoStart}).
+By default it calls \fun{CloseNow} which should ultimately destroy the object.
+A derived class can redefine this function to take whatever clean-up action
+is required.
+?*/
+void
+UchTextService :: AbandonRestart ()
+{
+ CloseNow ();
+}
+
+/*?
+This implementation of the protected virtual function \fun{UchTextStream::Execute}
+always return \var{isCmdOk}.
+This is suitable for the global function \var{TellServer}.
+Derived classes generally do redefine this function.
+?*/
+UchTextStream::cmd_res
+UchTextService :: Execute (const UchTextLine&)
+{
+ // just discard received request by default
+ // (suitable for TellServer below)
+ return isCmdOk;
+}
+
+/*?
+This implementation of the protected virtual function \fun{UchTextStream::DoSend}
+writes the output buffer only if the state of the connection is
+\var{isRunning}.
+Otherwise, the outgoing requests accumulate in the ouput buffer until the
+connection is established.
+This function also closes down the connection by calling \var{MpxRemoveChannel}
+if \fun{Close} has been called before the connection ws actually established.
+?*/
+void
+UchTextService :: DoSend ()
+{
+ if (StatusFlag == isRunning) {
+ Write (OutBuffer);
+ if (Closed)
+ MyMpx->Remove (*this);
+ }
+}
+
+/*?
+Define the server and host to connect to. This function must be used
+if the object was constructed with the default constructor.
+If the connection to the server cannot be established, \fun{Init} calls
+\var{AutoStart} to attempt the connection later.
+?*/
+void
+UchTextService :: Init (const char* s, const char* h)
+{
+ if (StatusFlag == isRunning)
+ return;
+
+ Service = s;
+ Host = h;
+
+ StatusFlag = Restart ();
+ if (StatusFlag != isRunning)
+ AutoStart ();
+}
+
+/*?
+Return the current retry time for the auto-restart of the connection.
+If the connection is not in auto-restart mode, return -1;
+?*/
+int
+UchTextService :: GetRetryTime ()
+{
+ return Starter ? Starter->GetPeriod () / 1000 : -1;
+}
+
+/*?
+Return the current maximum number of retries for the auto-restart of the connection.
+If the connection is not in auto-restart mode, return -1;
+?*/
+int
+UchTextService :: GetMaxRetries ()
+{
+ return Starter ? Starter->Max : -1;
+}
+
+/*?
+Remove this service from the multiplexer if the connection is established
+or if the output buffer is empty.
+Otherwise, mark the connection as being closed;
+in this case, the output buffer will be sent when the connection is etablished,
+and then the connection will be closed.
+?*/
+void
+UchTextService :: Close ()
+{
+ Closed = TRUE;
+ if (StatusFlag == isRunning || OutBuffer.BufLength () == 0)
+ MyMpx->Remove (*this);
+}
+
+/*?
+Remove this service from the multiplexer, whatever
+the state of the connection is.
+?*/
+void
+UchTextService :: CloseNow ()
+{
+ MyMpx->Remove (*this);
+}
+
+/*?
+This global function provides the simplest interface to send a request \var{req}
+to a server \var{sname} on host \var{host}.
+?*/
+bool
+TellServer (const char* sname, const char* host, const char* req)
+{
+ UchTextService* serv = new UchTextService;
+ serv->Init (sname, host);
+ serv->Send (req);
+ serv->Close ();
+ return TRUE;
+}
diff --git a/comm/OLD/TextStream.h b/comm/OLD/TextStream.h
new file mode 100644
index 0000000..cb43fe3
--- /dev/null
+++ b/comm/OLD/TextStream.h
@@ -0,0 +1,157 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Text streams
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#ifndef TextStream_H_
+#define TextStream_H_
+
+#include "cplus_bugs.h"
+#include "Stream.h"
+#include "MsgBuffer.h"
+#include "ccu/String.h"
+
+class UchTextWord {
+protected:
+ const char* Sval;
+ int Ival;
+public:
+inline UchTextWord () : Sval (0), Ival (0) {}
+inline UchTextWord (const char* s) { SetVal (s); }
+inline UchTextWord (int i) { SetVal (i); }
+ void SetVal (const char*);
+inline void SetVal (int i) { Sval = 0; Ival = i; }
+inline bool IsInt () const { return (Sval == 0) ? TRUE : FALSE; }
+inline bool IsString () const { return (Sval != 0) ? TRUE : FALSE; }
+ const char* GetQuotes () const;
+inline operator int () const { return Ival; }
+inline operator const char* () const { return Sval; }
+};
+
+
+class UchTextLine {
+protected:
+ int Num;
+ int Max;
+ UchTextWord* Words;
+void NewWord (const char* s, int i);
+
+public:
+ UchTextLine ();
+ ~UchTextLine ();
+inline UchTextWord& operator [] (int i) const { return Words [i]; }
+inline int NumWords () const { return Num; }
+inline void AddWord (const char* s) { NewWord (s, 0); }
+inline void AddWord (int i) { NewWord (0, i); }
+ void AddTrailer (const UchTextLine&, int);
+inline UchTextLine& operator << (int i) { AddWord (i); return *this; }
+inline UchTextLine& operator << (const char* s) { AddWord (s); return *this; }
+inline UchTextLine& operator << (const UchTextLine& l) { AddTrailer (l, 0); return *this; }
+ bool Parse (char*);
+ char* Unparse (char* dest, int len) const;
+ char* Unparse (UchMsgBuffer*) const;
+ bool Match (const char*, const char* = 0) const;
+ int Index (const char*) const;
+ int Index (int) const;
+inline bool Contains (const char* w) const { return (Index (w) == -1) ? FALSE : TRUE; }
+inline bool Contains (int i) const { return (Index (i) == -1) ? FALSE : TRUE; }
+inline void Reset () { Num = 0; }
+};
+
+class UchTextStream : public UchStream {
+public:
+enum cmd_res {
+ isCmdSyntax, // syntax error when parsing command
+ isCmdUnknown, // unknown command
+ isCmdOk, // request executed
+ isCmdError, // problem while executing request
+ isCmdClose, // close connection
+ isCmdQuit, // quit server
+ isCmdTerminate, // terminate multiplexer
+ isCmdAbort, // abort multiplexer
+ isCmdExit // call exit
+ };
+protected:
+ UchMsgBuffer InBuffer;
+ UchMsgBuffer OutBuffer;
+ UchMultiplexer* MyMpx;
+
+virtual void Closing (bool);
+ void AddNotify (UchMultiplexer&);
+ void RemoveNotify (UchMultiplexer&);
+ void HandleRead ();
+ void ProcessCmdResult (cmd_res, const UchTextLine&);
+ cmd_res TryPredefined (const UchTextLine&);
+virtual cmd_res Execute (const UchTextLine&) = 0;
+virtual void DoSend ();
+
+public:
+ UchTextStream ();
+ ~UchTextStream ();
+ UchTextStream (const UchTextStream&);
+ UchChannel* Copy () const;
+inline UchMultiplexer* GetMultiplexer () const { return MyMpx; }
+virtual void Close (); // 'close' request
+virtual void Quit (); // 'quit' request
+inline void Append (const char* l) { OutBuffer.Append (l, FALSE); }
+inline void Append (const UchTextLine& l) { l.Unparse (&OutBuffer); OutBuffer.Append ('\n');}
+inline UchTextStream& operator << (const char* l) { Append (l); return *this; }
+inline UchTextStream& operator << (const UchTextLine& l) { Append (l); return *this; }
+inline void Send () { DoSend (); }
+inline void Send (const char* l) { Append (l); DoSend (); }
+inline void Send (const UchTextLine& l) { Append (l); DoSend (); }
+};
+
+class UchTextService : public UchTextStream {
+friend class UchServiceStarter;
+public:
+enum status {
+ isUnavailable, // address not found in port server
+ isError, // could not init connection
+ isRunning, // connection established
+ isLost, // no connection (auto-starting)
+ };
+
+protected:
+ status StatusFlag;
+ UchServiceStarter* Starter;
+ bool Closed;
+ CcuString User;
+ CcuString Service;
+ CcuString Host;
+
+ void Closing (bool);
+ status Restart ();
+ void AutoStart (int = -1, int = -1);
+virtual void LostServer ();
+virtual void GotServer ();
+virtual void AbandonRestart ();
+ cmd_res Execute (const UchTextLine&);
+ void DoSend ();
+
+public:
+ UchTextService (const char*, const char* = 0);
+ UchTextService ();
+ ~UchTextService ();
+ void Init (const char*, const char* = 0);
+ status GetStatus () { return StatusFlag; }
+ int GetRetryTime ();
+ int GetMaxRetries ();
+ void Close (); // close after output buffer emptied
+ void CloseNow (); // close now
+
+};
+
+// the simplest interface to a server.
+extern bool TellServer (const char*, const char*, const char*);
+
+#endif /* TextStream_H_ */
diff --git a/comm/OLD/dgram.cc b/comm/OLD/dgram.cc
new file mode 100644
index 0000000..d6e5c50
--- /dev/null
+++ b/comm/OLD/dgram.cc
@@ -0,0 +1,717 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Reliable datagrams - to be updated
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "MsgBuffer.h"
+#include "dgram.h"
+#include "error.h"
+
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <memory.h>
+
+// needed only if DEBUG is active
+#include <stdio.h>
+#define DEBUG //**//
+bool DGTrace = FALSE;
+
+/*?class UchDGRAM
+The class \typ{UchDGRAM} is derived from \typ{UchDatagram}.
+It adds to a datagram a simple protocol to ensure that all messages sent are received.
+It does not prevent message duplication, neither does he ensure that the messages are
+received in the order they are sent.
+
+Each message is sent along with a tag number. The sender keeps each message it sends
+until it receives an acknowledge for it.
+When a message is received, an acknowledge is sent back.
+Unacknowledged messages are resent at regular intervals.
+If a message is not acknowledged after a given number of retries, it is simply discarded.
+?*/
+
+struct PENDING {
+ UchMsgBuffer* outBuf;
+ pUchAddress toAddr;
+ short retries; // for pending
+ lword id; // for input
+
+ PENDING (UchMsgBuffer* b, UchAddress* a, int r = 0) { outBuf = b; toAddr = a; retries = r; id = 0;}
+ ~PENDING () { delete outBuf; toAddr = 0; }
+};
+
+static void
+DelPending (void* p)
+{
+ delete ((PENDING*) p);
+}
+
+/*?hidden?*/
+void
+UchDGRAM_TIMER :: Handle (Millisecond)
+{
+if (DGTrace) printf ("*** TimeOut ***\n");
+ dgram.Expired ();
+}
+
+/*?nodoc?*/
+UchDGRAM_TIMER :: UchDGRAM_TIMER (UchDGRAM& dg)
+: CcuBaseTimer (0),
+ dgram (dg)
+{
+}
+
+UchDGRAM_TIMER :: ~UchDGRAM_TIMER ()
+{
+}
+
+/*?hidden?*/
+void
+UchDGRAM :: Init ()
+{
+ npending = 0;
+ ninput = 0;
+ fromAddr = 0;
+ retry = 5;
+ resend = FALSE;
+ locked = 0;
+ timeout = 1000;
+ sync = FALSE;
+}
+
+/*?nextdoc?*/
+UchDGRAM :: UchDGRAM ()
+: pending (2), input (), timer (*this)
+{
+ Init ();
+}
+
+/*?
+Same constructors as for the class \typ{UchDatagram};
+?*/
+UchDGRAM :: UchDGRAM (UchAddress* b, UchAddress* c)
+: UchDatagram (b, c), pending (2), input (), timer (*this)
+{
+ Init ();
+}
+
+/*?
+The destructor of \typ{UchDGRAM} discards all pending and input messages.
+A derived class could call \fun{Drain} in its destructor to send all pending messages,
+and it could call \fun{Receive} while \fun{NumInput} is non null.
+?*/
+UchDGRAM :: ~UchDGRAM ()
+{
+ timer.Stop ();
+ if (! npending)
+ return;
+ for (CcuIdIter iter (pending); iter (); ++iter) {
+ RemovePending (iter.CurId ());
+ if (! npending)
+ break;
+ }
+ if (ninput) {
+ CcuListIter li (input);
+ while (++li)
+ DelPending (*li);
+ input.Clear ();
+ }
+}
+
+static const byte DGRAM_ERR = 0;
+static const byte DGRAM_SEND = 1;
+static const byte DGRAM_ACK = 2;
+
+// GetInput reads an incoming message
+// if its an ack, it is processed
+// if it is a normal message, it is stored in the input list
+// CheckInput calls GetInput wile there is something to read
+// WaitInput returns when a message is in the input list
+// Wait waits for the acknowledge of a particular id
+
+/*?hidden?*/
+int
+UchDGRAM :: GetInput (int len)
+{
+ UchMsgBuffer* buf = new UchMsgBuffer (len);
+ int n;
+ while ((n = UchDatagram :: Receive (*buf)) == -1 && errno == EINTR)
+DEBUG SysError (ErrWarn, "UchDGRAM::GetInput")
+ ;
+ if (n < 0) {
+ SysError (ErrWarn, "UchDGRAM::GetInput");
+ delete buf;
+ return DGRAM_ERR;
+ }
+DEBUG if (DGTrace) printf ("UchDGRAM :: GetInput : received %d bytes\n", buf->BufLength ());
+
+ lword id;
+ buf->Get (&id);
+ byte s;
+ buf->Get (&s);
+DEBUG if (DGTrace) printf ("UchDGRAM :: GetInput : id %x byte %d\n", id, s);
+
+ if (s == DGRAM_ACK) {
+DEBUG if (DGTrace) printf ("UchDGRAM :: GetInput : acknowledged %x\n", id);
+ RemovePending (id);
+ delete buf;
+ return DGRAM_ACK;
+ }
+
+ PENDING* p = new PENDING (buf, FAddr);
+ p->id = id;
+ input.Append (p);
+ ninput++;
+ return DGRAM_SEND;
+}
+
+/*?hidden?*/
+bool
+UchDGRAM :: CheckInput ()
+{
+ // get any pending messages
+ for (;;) {
+ lword np;
+ if (ioctl (FilDes (), FIONREAD, (char*) &np) < 0)
+ return FALSE;
+DEBUG if (DGTrace) if (np) printf ("UchDGRAM :: CheckInput : FIONREAD says %d\n", np);
+ if (np == 0)
+ break;
+ if (GetInput (int (np)) == DGRAM_ERR)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*?hidden?*/
+bool
+UchDGRAM :: WaitInput ()
+{
+ // check the input queue
+ if (ninput)
+ return TRUE;
+
+ // check pending input
+ if (! CheckInput ())
+ return FALSE;
+
+ // block until something
+ while (! ninput)
+ if (GetInput (2048) == DGRAM_ERR) // *** arbitrary max size
+ return FALSE;
+ return TRUE;
+}
+
+/*?hidden?*/
+bool
+UchDGRAM :: Wait (lword id)
+{
+DEBUG if (DGTrace) printf ("UchDGRAM::Wait %x\n", id);
+ bool ret = FALSE;
+ bool loop = TRUE;
+ Lock ();
+
+ PENDING* pend;
+ while (loop) {
+ if (! CheckInput ())
+ break;
+
+ // did we receive the ack ?
+ pend = (PENDING*) pending.Get (id);
+ if (! pend) {
+ ret = TRUE;
+ break;
+ }
+
+ if (resend) {
+DEBUG if (DGTrace) printf ("UchDGRAM::Wait : resending\n");
+ if (pend->retries <= 0) {
+ RemovePending (id);
+ break;
+ }
+
+ int n = SendBuffer (* pend->outBuf, *pend->toAddr);
+ if (n < 0)
+ break;
+ --pend->retries;
+ resend = FALSE;
+ } else
+DEBUG if (DGTrace) printf ("UchDGRAM::Wait : waiting\n"),
+ timer.Wait (); // sets resend to TRUE
+ }
+ Unlock ();
+DEBUG if (DGTrace) printf ("UchDGRAM::Wait : %s\n", ret ? "done" : "failed");
+ return ret;
+}
+
+// SendAck sends an acknowledge
+// Expired is called when the resend timer has expired
+// PrepareToSend returns a buffer with the header set,
+// and adds it to the pending set
+// SendBuffer sends a buffer, handling interrupted sys calls
+// RemovePending removes a pending message from the table
+/*?hidden?*/
+void
+UchDGRAM :: SendAck (lword id, UchAddress& addr)
+{
+ UchMsgBuffer buf;
+ buf.Append (id);
+ buf.Append (DGRAM_ACK);
+ SendBuffer (buf, addr);
+ fromAddr = & addr;
+DEBUG if (DGTrace) printf ("UchDGRAM :: SendAck : acknowledging %x\n", id);
+}
+
+/*?hidden?*/
+void
+UchDGRAM :: Expired ()
+{
+ if (locked)
+ resend = TRUE;
+ else
+ Resend ();
+}
+
+/*?hidden?*/
+UchMsgBuffer*
+UchDGRAM :: PrepareToSend (UchAddress& to, int retries)
+{
+ UchMsgBuffer* obuf = new UchMsgBuffer;
+/*
+UchInetAddress* ia = (UchInetAddress*) &to;
+UchAddress* toCopy = new UchInetAddress (ia->Host (), ia->Port ());
+*/
+ if (retries == 0)
+ retries = retry;
+ PENDING* out = new PENDING (obuf, &to /*toCopy*/, retries);
+ outId = pending.Store (out);
+
+DEBUG if (DGTrace) printf ("UchDGRAM :: PrepareToSend : id %x\n", outId);
+ obuf->Append (outId);
+ obuf->Append (DGRAM_SEND);
+
+ if (! npending) {
+ timer.ChangePeriod (timeout);
+ timer.Restart ();
+ }
+ npending++;
+
+ return obuf;
+}
+
+/*?hidden?*/
+int
+UchDGRAM :: SendBuffer (UchMsgBuffer& buf, UchAddress& addr)
+{
+ int n;
+ while ((n = UchDatagram :: Send (buf, addr, TRUE)) == -1 && errno == EINTR)
+DEBUG SysError (ErrWarn, "UchDGRAM::SendBuffer")
+ ;
+ return n;
+}
+
+/*?hidden?*/
+void
+UchDGRAM :: RemovePending (lword id)
+{
+ PENDING* pend = (PENDING*) pending.Get (id);
+ if (! pend) {
+ Error (ErrWarn, "Receive", "unrecognized ACK");
+ return;
+ }
+ pending.Remove (id);
+ delete pend;
+ npending--;
+ if (npending == 0)
+ timer.Stop ();
+}
+
+//---------------- the public functions
+
+/*?nextdoc?*/
+int
+UchDGRAM :: Send (byte* buf, int len, UchAddress& to, bool ack, int retries)
+{
+DEBUG if (DGTrace) printf ("UchDGRAM :: Send\n");
+ Lock ();
+ UchMsgBuffer* obuf = PrepareToSend (to, retries);
+ obuf->Append (buf, len);
+ int n = SendBuffer (*obuf, to);
+ CheckInput ();
+ int ret = (ack || sync) ? (Wait (outId) ? n : -1) : n;
+ Unlock ();
+ return ret;
+}
+
+/*?nextdoc?*/
+int
+UchDGRAM :: Receive (byte* buf, int len)
+{
+DEBUG if (DGTrace) printf ("UchDGRAM :: Receive\n");
+ if (! WaitInput ())
+ return -1;
+ PENDING* p = (PENDING*) input.RemoveFirst ();
+ ninput--;
+ int n = p->outBuf->BufLength ();
+ if (len < n)
+ n = len;
+ memcpy (buf, p->outBuf->Buffer (), n);
+ SendAck (p->id, *p->toAddr);
+ delete p;
+DEBUG if (DGTrace) printf ("UchDGRAM :: Receive : received %d bytes\n", n);
+ return n;
+}
+
+/*?nextdoc?*/
+int
+UchDGRAM :: Reply (byte* buf, int len, bool ack, int retries)
+{
+ if (! fromAddr)
+ return -1;
+ return Send (buf, len, *fromAddr, ack, retries);
+}
+
+/*?nextdoc?*/
+int
+UchDGRAM :: Send (UchMsgBuffer& buf, UchAddress& to, bool peek, bool ack, int retries)
+{
+DEBUG if (DGTrace) printf ("UchDGRAM :: Send\n");
+ Lock ();
+ UchMsgBuffer* obuf = PrepareToSend (to, retries);
+ obuf->Append (buf.Buffer (), buf.BufLength ());
+ if (! peek)
+ buf.Flush ();
+ int n = SendBuffer (*obuf, to);
+ CheckInput ();
+ int ret = (ack || sync) ? (Wait (outId) ? n : -1) : n;
+ Unlock ();
+ return ret;
+}
+
+/*?nextdoc?*/
+int
+UchDGRAM :: Receive (UchMsgBuffer& buf)
+{
+DEBUG if (DGTrace) printf ("UchDGRAM :: Receive\n");
+ if (! WaitInput ())
+ return -1;
+ PENDING* p = (PENDING*) input.RemoveFirst ();
+ ninput--;
+ int n = p->outBuf->BufLength ();
+ buf.Append (p->outBuf->Buffer (), n);
+ SendAck (p->id, *p->toAddr);
+ delete p;
+DEBUG if (DGTrace) printf ("UchDGRAM :: Receive : received buffer %d bytes\n", n);
+ return n;
+}
+
+/*?
+These functions are similar to the same functions in the class \typ{UchDatagram},
+except that they manage messages acknowledgement.
+\fun{Send} saves the message in a buffer so that it can be resent if no acknowledge
+is received.
+\fun{Receive} acknowledges the received message.
+\fun{Reply} sends the message to the sender of the last received message.
+If \var{ack} is TRUE, the acknowledge of the message is waited for.
+In that case, \fun{Send} returns -1 if the acknowledge is not received.
+If \var{retries} is non zero, it specifies a number of retries for this message
+different from the default retry number of this dgram.
+?*/
+int
+UchDGRAM :: Reply (UchMsgBuffer& buf, bool peek, bool ack, int retries)
+{
+ if (! fromAddr)
+ return -1;
+ return Send (buf, *fromAddr, peek, ack, retries);
+}
+
+/*?nextdoc?*/
+bool
+UchDGRAM :: Send (UchMessage& msg, UchAddress& to, bool ack, int retries)
+{
+DEBUG if (DGTrace) printf ("UchDGRAM :: Send message\n");
+ Lock ();
+ UchMsgBuffer* obuf = PrepareToSend (to, retries);
+ obuf->Append (msg);
+// int l;
+// printf ("outBuffer is %d bytes long\n", l = obuf->BufLength ());
+// for (int i = 0; i < l; i++) printf ("%02x ", obuf->Buffer () [i]);
+// printf ("\n");
+ int n = SendBuffer (*obuf, to);
+ CheckInput ();
+ bool ret;
+ if (ack || sync)
+ ret = Wait (outId) ? TRUE : FALSE;
+ else
+ ret = bool (n != obuf->BufLength ());
+ Unlock ();
+ return ret;
+}
+
+/*?nextdoc?*/
+bool
+UchDGRAM :: Receive (UchMessage* msg)
+{
+DEBUG if (DGTrace) printf ("UchDGRAM :: Receive message\n");
+ if (! WaitInput ())
+ return FALSE;
+ PENDING* p = (PENDING*) input.RemoveFirst ();
+ ninput--;
+// int n;
+// printf ("inBuffer is %d bytes long\n", n = p->outBuf->BufLength ());
+// for (int i = 0; i < n; i++) printf ("%02x ", p->outBuf->Buffer () [i]);
+// printf ("\n");
+ if (! p->outBuf->Get (msg))
+ return FALSE;
+ SendAck (p->id, *p->toAddr);
+ delete p;
+DEBUG if (DGTrace) printf ("UchDGRAM :: Receive : received message\n");
+ return TRUE;
+}
+
+/*?
+These functions are similar to the previous functions except that they take
+a \typ{UchMessage} as argument.
+\fun{Send} converts the message in the output buffer.
+If \var{ack} is TRUE, the acknowledge of the message is waited for.
+In that case, \fun{Send} returns FALSE if the acknowledge is not received.
+If \var{retries} is non zero, it specifies a number of retries for this message
+different from the default retry number of this dgram.
+\fun{Receive} converts the incoming data into the message passed as argument.
+\fun{Reply} sends the message to the sender of the last received message.
+See also the function \fun{NewMessage} to handle incoming messages.
+?*/
+bool
+UchDGRAM :: Reply (UchMessage& msg, bool ack, int retries)
+{
+ if (! fromAddr)
+ return FALSE;
+ return Send (msg, *fromAddr, ack, retries);
+}
+
+//-----------------------
+#if 0
+bool
+UchDGRAM :: Ask (UchMessage& msg, UchAddress& to)
+{
+DEBUG if (DGTrace) printf ("UchDGRAM :: Ask\n");
+ Lock ();
+ UchMsgBuffer* obuf = PrepareToSend (to, DGRAM_ASK *******);
+ obuf->Append (msg);
+ int n = SendBuffer (*obuf, to);
+ CheckInput ();
+ bool ret = Wait (outId) ? TRUE : FALSE;
+ if (ret) {
+ // wait answer
+ for (;;) {
+ if (! WaitInput ())
+ continue;
+ PENDING* pend = (PENDING*) input.Last ();
+ if (pend->toAddr == to.............
+ }
+ }
+ Unlock ();
+ return ret;
+}
+#endif
+//-----------------------
+
+/*?
+Resend all messages for which no acknowledge has been received.
+If a UchMessage.has been resent more than the retry number, it is discarded.
+When successive messages are to be sent to the same address, only the first
+is resent, to avoid connection overflow.
+?*/
+void
+UchDGRAM :: Resend ()
+{
+if (DGTrace) printf ("UchDGRAM::Resend\n");
+ Lock ();
+ if (npending)
+ CheckInput ();
+ if (! npending) {
+ resend = FALSE;
+ Unlock ();
+ return;
+ }
+
+ UchAddress* addr = 0;
+
+ PENDING* pend;
+ for (CcuIdIter iter (pending); pend = (PENDING*) iter (); ++iter) {
+ if (pend->retries <= 0) {
+ // the output buffer contains the leading id and type
+ UchMsgBuffer fake (*pend->outBuf);
+ lword id;
+ byte typ;
+ fake.Get (&id);
+ fake.Get (&typ);
+ if (! DiscardNotify (fake, *pend->toAddr)) {
+ pend->retries = retry; // *** should be controllable ?
+ continue;
+ }
+if (DGTrace) printf ("UchDGRAM::Resend : abandonning %x\n", iter.CurId ());
+ RemovePending (iter.CurId ());
+ if (! npending)
+ break;
+ } else
+ if (addr != (UchAddress*) pend->toAddr) {
+DEBUG printf ("UchDGRAM::Resend : resending %x\n", iter.CurId ());
+ SendBuffer (* pend->outBuf, *pend->toAddr);
+ --pend->retries;
+ addr = pend->toAddr;
+ CheckInput ();
+ }
+DEBUG else printf ("UchDGRAM::Resend : skipping %x\n", iter.CurId ());
+ }
+
+ resend = FALSE;
+ Unlock ();
+}
+
+/*?
+This virtual function is called whenever a pending message is about
+to be discarded because no acknowledge has been received after
+the the default number of retries.
+If it returns FALSE, the message is not discarded and its retry count is reset to zero.
+Returning FALSE is not very social.
+The default action is to return TRUE, thus discarding the pending message.
+?*/
+bool
+UchDGRAM :: DiscardNotify (UchMsgBuffer&, UchAddress&)
+{
+ return TRUE;
+}
+
+/*?
+Set the interval between retries.
+The default value is 1000 (1 second).
+?*/
+void
+UchDGRAM :: SetRetryTime (Millisecond m)
+{
+ timeout = m;
+ if (npending) {
+ timer.ChangePeriod (m);
+ timer.Restart ();
+ }
+}
+
+/*?
+Wait until all acknowledges have been received,
+or until all messages have been discarded.
+?*/
+void
+UchDGRAM :: Drain ()
+{
+ if (npending)
+ CheckInput ();
+ while (npending)
+ timer.Wait ();
+}
+
+/*?
+This virtual function is called by \fun{HandleRead}.
+It must be redefined in a derived class if \fun{HandleRead} is to be used
+(for instance if this \typ{UchDGRAM} is put in a channel set).
+This function should convert the contents of the buffer into a message, and handle it.
+It should return TRUE if the message was correctly handled, else FALSE.
+Note that if it returns FALSE, the message will not be acknowledged, and thus it will
+be resent later.
+The default behaviour of this virtual function is to issue an error message and
+to return TRUE.
+?*/
+bool
+UchDGRAM :: NewMessage (UchMsgBuffer&)
+{
+ Error (ErrWarn, "UchDGRAM :: NewMessage", "should be defined in derived class");
+ return TRUE;
+}
+
+/*?
+This is an instance of the virtual function \fun{HandleRead} of class \typ{UchChannel}.
+It calls the virtual function \fun{NewMessage} when a message is received.
+If \fun{NewMessage} returns TRUE, an acknowledge is sent back.
+This functions also handles incoming acknowledges.
+?*/
+void
+UchDGRAM :: HandleRead ()
+{
+ if (! CheckInput ())
+ return;
+ if (! ninput)
+ return;
+
+DEBUG if (DGTrace) printf (">>UchDGRAM :: HandleRead\n");
+ PENDING* p = (PENDING*) input.RemoveFirst ();
+ ninput--;
+ if (NewMessage (* p->outBuf))
+ SendAck (p->id, *p->toAddr);
+DEBUG if (DGTrace) printf ("<<UchDGRAM :: HandleRead\n");
+}
+
+/*?
+This is an instance of the virtual function \fun{HandleSelect} of class \typ{UchChannel}.
+It calls the virtual function \fun{NewMessage} when a message is in the input buffer.
+If \fun{NewMessage} returns TRUE, an acknowledge is sent back.
+This functions also handles incoming acknowledges.
+?*/
+bool
+UchDGRAM :: HandleSelect ()
+{
+ if (! ninput)
+ return FALSE;
+DEBUG if (DGTrace) printf (">>UchDGRAM :: HandleSelect\n");
+ PENDING* p = (PENDING*) input.RemoveFirst ();
+ ninput--;
+ if (NewMessage (* p->outBuf))
+ SendAck (p->id, *p->toAddr);
+DEBUG if (DGTrace) printf ("<<UchDGRAM :: HandleSelect\n");
+ return TRUE;
+}
+
+#ifdef DOC
+// fake entries for documentation
+
+/*?
+Return the number of pending messages
+?*/
+int
+UchDGRAM :: NumPending ()
+{ }
+
+/*?
+Return the number of waiting input messages
+?*/
+int
+UchDGRAM :: NumInput ()
+{ }
+
+/*?
+Set the number of times a message is resent until it is discarded.
+The default value is 5.
+?*/
+void
+UchDGRAM :: SetRetry (short r)
+{ }
+
+/*?
+Set the synchronous mode of this \typ{UchDGRAM}.
+If \var{s} is TRUE, the \fun{Send} functions always wait for the acknowledge of the messages.
+The default value is FALSE.
+?*/
+void
+UchDGRAM :: SetSync (bool s)
+{ }
+
+#endif /* DOC */
+
diff --git a/comm/OLD/dgram.h b/comm/OLD/dgram.h
new file mode 100644
index 0000000..143687e
--- /dev/null
+++ b/comm/OLD/dgram.h
@@ -0,0 +1,102 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Reliable datagrams - to be updated
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#ifndef _DGRAM_H_
+#define _DGRAM_H_
+
+#include "global.h"
+#include "Datagram.h"
+#include "ccu/IdTable.h"
+#include "ccu/List.h"
+#include "ccu/Timer.h"
+
+class UchDGRAM;
+class UchMessage;
+
+class UchDGRAM_TIMER : public CcuBaseTimer {
+protected:
+ UchDGRAM& dgram;
+ void Handle (Millisecond);
+
+public:
+ UchDGRAM_TIMER (UchDGRAM&);
+ ~UchDGRAM_TIMER ();
+};
+
+class UchDGRAM : public UchDatagram {
+protected:
+ CcuIdTable pending; // pending messages
+ sword npending; // number of pending messages
+ short retry; // retry times
+ CcuList input; // input messages
+ sword ninput; // number of input messages
+ pUchAddress fromAddr; // address of last acknowledged message
+ lword outId; // id of message sent
+ UchDGRAM_TIMER timer; // timer to resend
+ Millisecond timeout; // timeout for timer
+ short locked;
+ bool resend;
+ bool sync;
+
+ void Init ();
+ int GetInput (int);
+ bool CheckInput ();
+ bool WaitInput ();
+
+ void SendAck (lword, UchAddress&);
+ void Expired ();
+ UchMsgBuffer* PrepareToSend (UchAddress&, int retries);
+ int SendBuffer (UchMsgBuffer&, UchAddress&);
+ void RemovePending (lword);
+ bool Wait (lword);
+ void Lock () { locked++; }
+ void Unlock () { if (--locked == 0 && resend) Resend (); }
+
+public:
+ UchDGRAM ();
+ UchDGRAM (UchAddress*, UchAddress*);
+ ~UchDGRAM ();
+
+ int Send (byte*, int, UchAddress&, bool = FALSE, int retries = 0);
+ int Receive (byte*, int);
+ int Reply (byte*, int, bool = FALSE, int retries = 0);
+
+ int Send (UchMsgBuffer&, UchAddress&, bool = FALSE, bool = FALSE, int retries = 0);
+ int Receive (UchMsgBuffer&);
+ int Reply (UchMsgBuffer&, bool = FALSE, bool = FALSE, int retries = 0);
+
+ bool Send (UchMessage&, UchAddress&, bool = FALSE, int retries = 0);
+ bool Receive (UchMessage* msg);
+ bool Reply (UchMessage&, bool = FALSE, int retries = 0);
+
+virtual bool DiscardNotify (UchMsgBuffer&, UchAddress&);
+
+ int NumPending () { return npending; }
+ int NumInput () { return ninput; }
+ void SetRetry (short r) { retry = r; }
+ void SetRetryTime (Millisecond);
+ void SetSync (bool s) { sync = s; if (s) Drain (); }
+
+ void Resend ();
+ void Drain ();
+
+virtual bool NewMessage (UchMsgBuffer&);
+ void HandleRead ();
+ bool HandleSelect ();
+
+friend class UchDGRAM_TIMER;
+};
+
+#endif /* _DGRAM_H_ */
+
diff --git a/comm/OLD/portserv.cc b/comm/OLD/portserv.cc
new file mode 100644
index 0000000..5470fe3
--- /dev/null
+++ b/comm/OLD/portserv.cc
@@ -0,0 +1,653 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Port server
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "PortServerReq.h"
+#include "PortServer.h"
+#include "error.h"
+#include "Datagram.h"
+#include "ccu/List.h"
+#include "ccu/RegExp.h"
+#include "ccu/String.h"
+#include "ccu/Time.h"
+
+#include <stdlib.h>
+#include <sys/file.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <netdb.h>
+
+//---------------- globals
+// if SaveTime > 0, the state is saved to SaveFile
+// SaveTime seconds after a modification
+// if SaveTime < 0, saving is immediate
+// if SaveTime is 0, no save
+// if SaveFileIsFd >= 0, it is taken as output file descr instead of SaveFile
+const char* SaveFile = 0;
+int SaveFileIsFd = -1;
+int SaveTime = 0;
+int Mod = 0;
+int NbEntries = 0;
+bool Trace = FALSE;
+const char* LoadFile;
+
+//---------------- constants
+// if an auto-launch server has not registered for REG_TIME seconds
+// and somebody inquires its address, it is killed and restarted.
+#define REG_TIME 60000
+#define REG_STR "minute"
+
+// if an auto-launch server has been run within the last ALR_TIME seconds
+// and has not yet registered and somebody inquires its address,
+// is is _not_ run.
+#define ALR_TIME 30000
+#define ALR_STR "30 seconds"
+
+struct Serv {
+ CcuString Key;
+ lword Host;
+ sword Port;
+ int Ident;
+ // auto-launch stuff
+ bool AutoLaunch;
+ bool Running;
+ Millisecond LastRun;
+ Millisecond LastRegistered;
+ char* CmdLine; // the line
+ char** RunCmd; // array to pointers to CmdLine (for execvp)
+};
+
+#ifndef CPLUS_BUG19
+CcuListOf <Serv> ServList;
+#else
+CcuList ServList;
+#endif
+
+//---------------- auto-launch stuff
+char**
+Parse (char* line)
+{
+ enum WHERE { IN_SPACE, IN_WORD, IN_QUOTE };
+ char quote = '\0';
+ register char* s = line;
+ register char* word = line;
+ register WHERE where = IN_SPACE;
+ char* words [256];
+ int nw = 0;
+
+ // split a line into words
+ // a word is a sequence of non blank characters,
+ // or a quoted string (' or ").
+ // '(', ')' and ',' are also ignored to allow for function like notation
+ for (; *s; s++) {
+ switch (where) {
+ case IN_WORD:
+ if (index (" \t(),", *s)) {
+ *s = '\0';
+ where = IN_SPACE;
+ if (nw < 255)
+ words [nw++] = word;
+ else
+ return 0;
+ }
+ break;
+ case IN_QUOTE:
+ if (*s == quote) {
+ *s = '\0';
+ where = IN_SPACE;
+ if (nw < 255)
+ words [nw++] = word;
+ else
+ return 0;
+ }
+ break;
+ case IN_SPACE:
+ if (*s == '\'' || *s == '\"') {
+ quote = *s;
+ *s = '\0';
+ where = IN_QUOTE;
+ word = s+1;
+ } else
+ if (index (" \t(),", *s)) {
+ *s = '\0';
+ } else {
+ where = IN_WORD;
+ word = s;
+ }
+ break;
+ }
+ }
+ if (where != IN_SPACE)
+ if (nw < 255)
+ words [nw++] = word;
+ else
+ return 0;
+ if (where == IN_QUOTE)
+ return 0;
+
+ // if no argv specified, argv[0] = executable
+ if (nw == 3)
+ words [nw++] = words [2];
+ // expected format: key dir exec argv
+ if (nw < 4)
+ return 0;
+ // null terminated array for execvp
+ words [nw++] = 0;
+
+ // return a freshly allocated array
+ char** nwords = new char* [nw];
+ for (int i = 0; i < nw; i++)
+ nwords [i] = words [i];
+ return nwords;
+}
+
+void
+AutoLaunch (Serv* s)
+{
+ // s->RunCmd [0] service name
+ // s->RunCmd [1] directory
+ // s->RunCmd [2] executable
+ // s->RunCmd [3 ... ] argv [0 ...]
+
+ CcuTimeStamp now;
+ if (s->LastRun + ALR_TIME > now) {
+// Log ("AutoLaunch", "%s already launched in the past %s", s->RunCmd [0], ALR_STR);
+ return;
+ }
+// Log ("AutoLaunch", "running %s dir=%s, exec=%s", s->RunCmd[0], s->RunCmd[1], s->RunCmd[2]);
+ if (! fork ()) {
+ // close everything but stdio
+ for (int i = 3; i < NFILE; i++)
+ close (i);
+ // detach from controlling terminal
+ setsid ();
+ // change directory
+ if (chdir (s->RunCmd [1]) < 0)
+ SysError (ErrFatal, s->RunCmd [1]);
+ // exec server
+ if (execvp (s->RunCmd [2], s->RunCmd + 3) < 0)
+ SysError (ErrFatal, s->RunCmd [2]);
+ }
+ s->LastRun = now;
+}
+
+void
+LoadConfig ()
+{
+ char* l = getlogin ();
+ if (! l)
+ l = cuserid (0);
+ if (! l)
+ Error (ErrFatal, "LoadConfig", "getlogin failed");
+
+ FILE* f = fopen (LoadFile, "r");
+ if (!f)
+ SysError (ErrFatal, LoadFile);
+ char line [2048];
+ while (fgets (line, sizeof (line), f)) {
+ // empty line or comment
+ if (! line [0] || line [0] == '#')
+ continue;
+
+ // expected format: servername dir exec argv0 ...
+
+ // remove trailing newline and parse
+ line [strlen (line) -1] = 0;
+ char* nline = new char [strlen (line) + 1];
+ strcpy (nline, line);
+ char** words = Parse (nline);
+ if (! words) {
+ Error (ErrWarn, "LoadConfig", "incorrect line ignored in file");
+ delete nline;
+ continue;
+ }
+ Serv* s = new Serv;
+ char key [256]; // portserv:mbl:audioserver
+ sprintf (key, "portserv:%s:%s", l, words [0]);
+ s->Key = key;
+ s->Host = 0;
+ s->Port = 0;
+ s->Ident = 0;
+ s->AutoLaunch = TRUE;
+ s->Running = FALSE;
+ s->LastRun= 0;
+ s->LastRegistered = 0;
+ s->CmdLine = nline;
+ s->RunCmd = words;
+ ServList.Append (s);
+// Log ("LoadConfig", "defined auto-launch server %s dir=%s exec=%s",
+// s->RunCmd [0], s->RunCmd [1], s->RunCmd [2]);
+ }
+ fclose (f);
+}
+
+void
+KillProcesses ()
+{
+ // kill all processes that have been autolaunched,
+ // assuming that their id is their pid.
+ Serv* s;
+#ifndef CPLUS_BUG19
+ CcuListIterOf <Serv> iter (ServList);
+ while (s = *++iter) {
+#else
+ CcuListIter iter (ServList);
+ while (s = (Serv*) *++iter) {
+#endif
+ if (s->AutoLaunch && s->Ident) {
+// Log ("KillProcesses", "killing %s pid=%d", s->RunCmd [2], (void*) s->Ident);
+ kill (s->Ident, SIGTERM);
+ }
+ }
+}
+
+Serv*
+Register (const char* key, lword host, sword port, int id)
+{
+ Serv* s = 0;
+
+#ifndef CPLUS_BUG19
+ CcuListIterOf <Serv> iter (ServList);
+ while (s = *++iter) {
+#else
+ CcuListIter iter (ServList);
+ while (s = (Serv*) *++iter) {
+#endif
+ if (strcmp (key, s->Key) == 0)
+ break;
+ }
+ if (! s) {
+ s = new Serv;
+ s->Key = key;
+ s->AutoLaunch = FALSE;
+ s->LastRun = 0;
+ s->CmdLine = 0;
+ s->RunCmd = 0;
+ ServList.Append (s);
+ }
+ s->Host = host;
+ s->Port = port;
+ s->Ident = id;
+ CcuTimeStamp now;
+ s->LastRegistered = now;
+ s->Running = TRUE;
+ return s;
+}
+
+bool
+Remove (const char* key, lword host, sword port, int id)
+{
+ Serv*s;
+#ifndef CPLUS_BUG19
+ CcuListIterOf <Serv> iter (ServList);
+ CcuListIterOf <Serv> trailing_iter (ServList);
+#else
+ CcuListIter iter (ServList);
+ CcuListIter trailing_iter (ServList);
+#endif
+ while (++iter) {
+#ifndef CPLUS_BUG19
+ s = *iter;
+#else
+ s = (Serv*) *iter;
+#endif
+ if (host == s->Host && port == s->Port && id == s->Ident && strcmp (key, s->Key) == 0) {
+ if (s->AutoLaunch) {
+ s->Running = FALSE;
+ s->LastRun = 0;
+ s->LastRegistered = 0;
+ s->Host = 0;
+ s->Port = 0;
+ s->Ident = 0;
+ } else {
+ s->Key = 0;
+ delete s;
+ ServList.RemoveAfter (trailing_iter);
+ }
+ return TRUE;
+ }
+ ++trailing_iter;
+ }
+ Error (ErrWarn, key, "not found for remove");
+ return FALSE;
+}
+
+Serv*
+Inquire (const char* key)
+{
+ Serv*s;
+#ifndef CPLUS_BUG19
+ CcuListIterOf <Serv> iter (ServList);
+#else
+ CcuListIter iter (ServList);
+#endif
+ while (++iter) {
+#ifndef CPLUS_BUG19
+ s = *iter;
+#else
+ s = (Serv*) *iter;
+#endif
+
+ if (strcmp (key, s->Key) != 0)
+ continue;
+
+ if (!s->AutoLaunch)
+ return s;
+
+ if (s->Running) {
+ CcuTimeStamp now;
+ if (s->LastRegistered + REG_TIME < now) {
+// Log ("Inquire", "%s not registered for the last %s: autolaunching it", s->RunCmd [0], REG_STR);
+ if (s->Ident)
+ kill (s->Ident, SIGTERM);
+ s->Host = 0;
+ s->Port = 0;
+ s->Ident = 0;
+ AutoLaunch (s);
+ }
+ } else
+ AutoLaunch (s);
+ return s;
+ }
+ Error (ErrWarn, key, "not found");
+ return 0;
+}
+
+
+void
+Match (const char* key, UchDatagram& portserv)
+{
+ UchMsgBuffer buf (256);
+ UchPortServerReq ans (PortServMatch);
+ int nb = 0;
+
+ CcuRegExp re (key);
+ if (re.Compile ()) {
+ Serv*s;
+#ifndef CPLUS_BUG19
+ CcuListIterOf <Serv> iter (ServList);
+ while (++iter) {
+ s = *iter;
+#else
+ CcuListIter iter (ServList);
+ while (++iter) {
+ s = (Serv*) *iter;
+#endif
+ if (re.Match (s->Key)) {
+if (Trace) printf ("Match: %s\n", (const char*) s->Key);
+ ans.Host = s->Host;
+ ans.Port = s->Port;
+ ans.SetKey (s->Key);
+ buf.Append (ans);
+ portserv.Reply (buf);
+ nb++;
+ }
+ }
+ } else
+ nb = -1;
+ ans.Type = PortServEndMatch;
+ ans.Ident = nb;
+ ans.SetKey (0);
+ buf.Append (ans);
+ portserv.Reply (buf);
+}
+
+int
+Dump ()
+{
+ Serv*s;
+#ifndef CPLUS_BUG19
+ CcuListIterOf <Serv> iter (ServList);
+#else
+ CcuListIter iter (ServList);
+#endif
+ while (++iter) {
+#ifndef CPLUS_BUG19
+ s = *iter;
+#else
+ s = (Serv*) *iter;
+#endif
+ printf ("<%s> 0x%lx:%d (%d)\n", (const char*) s->Key, s->Host, s->Port, s->Ident);
+ }
+ return 0;
+}
+
+int
+DumpFile ()
+{
+if (Trace) printf ("dumping\n");
+ int fd = SaveFileIsFd;
+ if (fd < 0 && (fd = creat (SaveFile, 0600)) < 0) {
+ SysError (ErrWarn, SaveFile);
+ return 0;
+ }
+ UchFilDes Fd (fd);
+ char buf [256];
+ Serv*s;
+#ifndef CPLUS_BUG19
+ CcuListIterOf <Serv> iter (ServList);
+#else
+ CcuListIter iter (ServList);
+#endif
+ while (++iter) {
+#ifndef CPLUS_BUG19
+ s = *iter;
+#else
+ s = (Serv*) *iter;
+#endif
+ sprintf (buf, "0x%lx:%d %s\n", s->Host, s->Port, (const char*) s->Key);
+ Fd.Write ((byte*) buf, strlen (buf));
+ }
+ Mod = 0;
+ return 1;
+}
+
+// for handlers
+//
+void
+SigDump (int)
+{
+ Dump ();
+}
+
+void
+SigDumpFile (int)
+{
+ DumpFile ();
+}
+
+void
+Modified ()
+{
+ if (! Mod && SaveTime > 0) {
+ Mod++;
+ alarm (SaveTime);
+ return;
+ }
+ if (SaveTime < 0) {
+ Mod++;
+ DumpFile ();
+ }
+}
+
+void
+Usage ()
+{
+ Error (ErrUsage, "portserv", "[-t] [-s service] [-quit] [-save file [time]] [-load file]");
+}
+
+main (int argc, const char** argv)
+{
+ const char* servname = "portserv";
+ int running = 1;
+ bool qflag = FALSE;
+
+ ProgramName (argv [0]);
+ while (--argc) {
+ ++argv;
+ if (strcmp (*argv, "-quit") == 0) {
+ qflag = TRUE;
+ } else
+ if (strcmp (*argv, "-t") == 0) {
+ Trace = TRUE;
+ } else
+ if (strcmp (*argv, "-s") == 0) {
+ if (--argc < 1)
+ Usage ();
+ servname = *++argv;
+ } else
+ if (strcmp (*argv, "-save") == 0) {
+ if (--argc < 1)
+ Usage ();
+ SaveFile = *++argv;
+ if (strcmp (SaveFile, "-") == 0)
+ SaveFileIsFd = 1;
+ else if (strcmp (SaveFile, "--") == 0)
+ SaveFileIsFd = 2;
+
+ if (argc > 1 && argv [1][0] != '-') {
+ SaveTime = atoi (*++argv);
+ --argc;
+ } else
+ SaveTime = -1;
+ } else if (strcmp (*argv, "-load") == 0 || strcmp (*argv, "-l") == 0) {
+ if (--argc < 1)
+ Usage ();
+ LoadFile = *++argv;
+ } else
+ Usage ();
+ }
+
+ // special case: portserv -quit
+ // send a PortServQuit message to
+ // the portserver that is running
+ if (qflag) {
+ if (SaveFile || LoadFile)
+ Error (ErrFatal, "-quit", "incompatible with -save or -load");
+ UchPortServer ps (servname, 0);
+ ps.Quit ();
+ exit (0);
+ }
+
+ sword servport;
+
+ // get service name
+ //
+ struct servent* service = getservbyname (servname, 0);
+ if (service)
+ servport = ntohs (service->s_port);
+ else {
+ const char* portno = getenv (servname);
+ if (portno) {
+ servport = atoi (portno);
+ } else
+ Error (ErrFatal, servname, "service not available");
+ }
+
+ // load file
+ //
+ if (LoadFile)
+ LoadConfig ();
+
+ // open dgram socket
+ //
+if (Trace) printf ("opening\n");
+ UchDatagram portserv (new UchInetAddress (ANYADDR, servport), 0);
+ if (! portserv.Setup ())
+// Error (ErrFatal, "main", "Setup failed");
+ SysError (ErrFatal, "main");
+
+ signal (SIGHUP, SigDump);
+ if (SaveTime > 0)
+ signal (SIGALRM, SigDumpFile);
+
+ // handle requests
+ //
+ UchMsgBuffer buf (256);
+ UchPortServerReq req;
+
+if (Trace) printf ("looping\n");
+ while (running) {
+ buf.Flush ();
+ int n = portserv.Receive (buf);
+ if (n <= 0) {
+ SysError (ErrFatal, "Receive", EINTR);
+ continue;
+ }
+if (Trace) printf ("received\n");
+ if (! buf.Get (&req)) {
+ Error (ErrWarn, "Receive", "could not read message");
+ continue;
+ }
+
+ // respond
+ switch (req.Type) {
+ case PortServRegister :
+if (Trace) printf ("register\n");
+ Register (req.Key, req.Host, req.Port, int (req.Ident));
+ Modified ();
+ break;
+
+ case PortServRemove :
+if (Trace) printf ("remove\n");
+ Remove (req.Key, req.Host, req.Port, int (req.Ident));
+ Modified ();
+ break;
+
+ case PortServInquire :
+ {
+if (Trace) printf ("inquire\n");
+ UchPortServerReq ans;
+ Serv* inq = Inquire (req.Key);
+ if (inq) {
+ ans.Type = PortServAnswer;
+ ans.Host = inq->Host;
+ ans.Port = inq->Port;
+ } else {
+ ans.Type = PortServFail;
+ ans.Host = 0;
+ ans.Port = 0;
+ }
+ buf.Flush ();
+ buf.Append (ans);
+ portserv.Reply (buf);
+ }
+ break;
+
+ case PortServMatch :
+if (Trace) printf ("match\n");
+ Match (req.Key, portserv);
+ break;
+
+ case PortServDump :
+if (Trace) printf ("dump\n");
+ Dump ();
+ break;
+
+ case PortServQuit :
+if (Trace) printf ("quit\n");
+ if (SaveTime)
+ DumpFile ();
+ KillProcesses ();
+ running = 0; ////////dangerous
+ break;
+
+ default:
+if (Trace) printf ("unknown\n");
+ Error (ErrWarn, "Receive", "unknown request");
+ }
+ }
+}
+
diff --git a/comm/OLD/porttest.cc b/comm/OLD/porttest.cc
new file mode 100644
index 0000000..c73268d
--- /dev/null
+++ b/comm/OLD/porttest.cc
@@ -0,0 +1,72 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Port server: test program
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "PortServer.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+int
+fMatch (const char* match, lword host, sword port, const char*)
+{
+ printf ("0x%lx:%d -> %s\n", host, port, match);
+ return 1;
+}
+
+main (int argc, char **argv)
+{
+ char *host = 0;
+ char *service = "portserv";
+
+ if (argc > 1 && argv [1] [0] == ':') {
+ host = argv [1] + 1;
+ argc--, argv++;
+ }
+ if (argc > 1 && argv [1] [0] == '=') {
+ service = argv [1] + 1;
+ argc--, argv++;
+ }
+ UchPortServer ps (service, host);
+
+ switch (argc) {
+ case 1:
+ ps.Dump ();
+ break;
+ case 2:
+ if (strcmp (argv [1], "quit") == 0)
+ ps.Quit ();
+ else
+ if (argv [1] [0] == '?')
+ ps.Match (argv [1] + 1, fMatch);
+ else {
+ UchInetAddress* addr = ps.Inquire (argv [1]);
+ if (addr) {
+ printf ("0x%lx:%d\n", addr->Host (), addr->Port ());
+ delete addr;
+ } else
+ printf ("not found\n");
+ }
+ break;
+ case 3: {
+ UchInetAddress addr ("", atoi (argv [2]));
+ if (argv [1] [0] == '-')
+ ps.Remove (argv [1] + 1, addr, 987);
+ else
+ ps.Register (argv [1], addr, 987);
+ break;
+ }
+ }
+}
+
diff --git a/comm/OLD/reqgen.cc b/comm/OLD/reqgen.cc
new file mode 100644
index 0000000..648cc83
--- /dev/null
+++ b/comm/OLD/reqgen.cc
@@ -0,0 +1,34 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1993
+ * Centre d'Etudes de la Navigation Aerienne (CENA)
+ *
+ * Request management, by Stephane Chatty
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "ReqMgr.h"
+#include <stdio.h>
+
+static void
+usage ()
+{
+ fprintf (stderr, "usage: reqgen file\n");
+}
+
+main (int argc, const char** argv)
+{
+ if (argc !=2) {
+ usage ();
+ return 0;
+ }
+ UchReqMgr r;
+ r.Read (argv [1]);
+ r.DumpHeader ("reqgen.out.h");
+ r.DumpSource ("reqgen.out");
+} \ No newline at end of file
diff --git a/comm/Socket.cc b/comm/Socket.cc
new file mode 100644
index 0000000..1d5a542
--- /dev/null
+++ b/comm/Socket.cc
@@ -0,0 +1,274 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Sockets
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "Socket.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+
+extern int errno;
+
+/*?class UchSocket
+The class \typ{UchSocket} derives from \typ{UchChannel}.
+It is a virtual base class: no objects of class \typ{UchSocket} are ever created.
+It implements a Unix socket, that is a file descriptor and two addresses:
+the address the socket is bound to, and the address it is connected to.
+An address needs to be bound to a socket only if another process wants to connect to this socket;
+a socket needs to be connected to an address only for streams or connected datagrams.
+Thus, none of the addresses is mandatory.
+
+Addresses are always referenced by pointers.
+Smart pointers to addresses (\typ{^{pUchAddress}}) can be used anywhere a pointer is used.
+?*/
+
+/*?
+Construct a closed socket.
+?*/
+UchSocket :: UchSocket ()
+: UchChannel (),
+ BAddr (0),
+ CAddr (0),
+ AddrFamily (AF_UNSPEC)
+{
+}
+
+/*?
+Construct a closed socket bound to address \var{bound} and connected to address \var{connected}.
+Each or both arguments may be 0.
+?*/
+UchSocket :: UchSocket (UchAddress* bound, UchAddress* connected)
+: UchChannel (),
+ BAddr (bound),
+ CAddr (connected),
+ AddrFamily (AF_UNSPEC)
+{
+}
+
+/*?nodoc?*/
+UchSocket :: UchSocket (const UchSocket& s)
+: UchChannel (s),
+ BAddr (s.BAddr),
+ CAddr (s.CAddr)
+{
+}
+
+/*?nodoc?*/
+UchSocket :: ~UchSocket ()
+{
+ // will unreference addresses and destroy them if necessary
+ BAddr = 0;
+ CAddr = 0;
+}
+
+/*?nodoc?*/
+UchChannel*
+UchSocket :: Copy () const
+{
+ return new UchSocket (*this);
+}
+
+/*?
+This virtual function returns the type of the socket:
+one of \var{SOCK_UNSPEC}, \var{SOCK_STREAM}, \var{SOCK_DGRAM},
+depending on the class.
+?*/
+int
+UchSocket :: SockType ()
+{
+ return SOCK_UNSPEC;
+}
+
+/*?nextdoc?*/
+void
+UchSocket :: BindTo (UchAddress* a)
+{
+ BAddr = a;
+}
+
+/*?
+Set the address a socket is to be bound to or connected to.
+?*/
+void
+UchSocket :: ConnectTo (UchAddress* a)
+{
+ CAddr = a;
+}
+
+/*?
+Open the socket (with the \fun{socket} system call) if it is not already open.
+The socket must have an address bound or connected to get the protocol family,
+or its family must have been defined with \fun{SetFamily}.
+Return FALSE if the family is undefined or if a system error occurred.
+?*/
+bool
+UchSocket :: Open ()
+{
+ errno = 0;
+ if (FilDes () >= 0)
+ return TRUE;
+ if (AddrFamily == AF_UNSPEC) {
+ if (BAddr)
+ AddrFamily = BAddr->Family ();
+ else if (CAddr)
+ AddrFamily = CAddr->Family ();
+ else
+ return FALSE;
+ }
+ int fd = socket (AddrFamily, SockType (), 0);
+ if (fd < 0)
+ return FALSE;
+ UchChannel::Open (fd);
+ return TRUE;
+}
+
+/*?nextdoc?*/
+int
+UchSocket :: Bind ()
+{
+ if (! BAddr || ! BAddr->IsValid ())
+ return -1;
+ if (! Open ())
+ return -1;
+ int ret = bind (FilDes (), BAddr->GetSockAddr (), BAddr->Length ());
+ if (ret < 0)
+ return ret;
+
+ GEN_ADDR addr;
+ int alen = sizeof (addr);
+ if (getsockname (FilDes (), &addr.sa, &alen) < 0)
+ return -1;
+ BAddr = UchAddress::Decode (&addr, alen);
+ return ret;
+}
+
+/*?nextdoc?*/
+int
+UchSocket :: Bind (UchAddress* addr)
+{
+ if (addr)
+ BAddr = addr;
+ return Bind ();
+}
+
+/*?nextdoc?*/
+int
+UchSocket :: Connect ()
+{
+ if (! CAddr || ! CAddr->IsValid ())
+ return -1;
+ if (! Open ())
+ return -1;
+ int ret = connect (FilDes (), CAddr->GetSockAddr (), CAddr->Length ());
+ if (ret < 0)
+ return ret;
+
+ GEN_ADDR addr;
+ int alen = sizeof (addr);
+ if (getpeername (FilDes (), &addr.sa, &alen) < 0)
+ return -1;
+ CAddr = UchAddress::Decode (&addr, alen);
+ return ret;
+}
+
+/*?
+These two functions implement the Unix system calls \fun{bind} and \fun{connect}.
+If \var{addr} is given, it is first associated to the socket.
+Then the socket is opened, and finally the system call is performed.
+The returned value is that of the system call, unless opening failed in which case
+-1 is returned.
+?*/
+int
+UchSocket :: Connect (UchAddress* addr)
+{
+ if (addr)
+ CAddr = addr;
+ return Connect ();
+}
+
+/*?
+Open the socket if it is not already open.
+Bind and connect it depending on the addresses that are defined.
+You should use this function instead of calling \fun{Open}, \fun{Bind} and
+\fun{Connect}, for it is simpler.
+This function returns FALSE if a system error occurred. In this case, the caller can
+call \fun{SysError} to report the error.
+?*/
+bool
+UchSocket :: Setup ()
+{
+ if (! Open ())
+ return FALSE;
+ if (BAddr)
+ if (BAddr->IsValid ())
+ if (Bind () < 0)
+ return FALSE;
+ if (CAddr)
+ if (CAddr->IsValid ())
+ if (Connect () < 0)
+ return FALSE;
+ return TRUE;
+}
+
+/*?nodoc?*/
+char*
+UchSocket :: StrRepr (char* buf)
+{
+ UchChannel :: StrRepr (buf);
+ strcat (buf, " / ");
+ if (BAddr)
+ BAddr -> StrRepr (buf + strlen (buf));
+ else
+ strcat (buf, "null");
+ strcat (buf, " / ");
+ if (CAddr)
+ CAddr -> StrRepr (buf + strlen (buf));
+ else
+ strcat (buf, "null");
+ return buf;
+}
+
+#ifdef DOC
+
+/*?nextdoc?*/
+int
+UchSocket :: Family ()
+{ }
+
+/*?
+These function get and set the family.
+The possible values currently are \var{AF_UNSPEC}, \var{AF_UNIX} and \var{AF_INET}.
+Other values may be defined if supported by the system.
+The family must be defined for \fun{Open} (and thus \fun{Setup}) to succeed.
+If an address is bound or connected to the socket, its family is used.
+Thus, an application seldom needs to call \fun{SetFamily}.
+?*/
+void
+UchSocket :: SetFamily (int f)
+{ }
+
+/*?nextdoc?*/
+UchAddress*
+UchSocket :: BoundTo ()
+{ }
+
+/*?
+Return the address currently bound or connected to the socket.
+?*/
+UchAddress*
+UchSocket :: ConnectedTo ()
+{ }
+
+#endif /* DOC */
+
diff --git a/comm/Socket.h b/comm/Socket.h
new file mode 100644
index 0000000..db84209
--- /dev/null
+++ b/comm/Socket.h
@@ -0,0 +1,60 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Sockets
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#ifndef Socket_H_
+#define Socket_H_
+
+#include "cplus_bugs.h"
+#include "Channel.h"
+#include "Address.h"
+
+extern char* StrReprBuf;
+
+#define SOCK_UNSPEC 0
+
+// Virtual base class for sockets.
+// A socket is a channel and an address.
+// As a channel, it is immutable, and must be initialized once and for all at creation.
+//
+class UchSocket : public UchChannel {
+protected:
+ pUchAddress BAddr;
+ pUchAddress CAddr;
+ int AddrFamily;
+public:
+ UchSocket ();
+ UchSocket (UchAddress*, UchAddress*);
+ ~UchSocket ();
+ UchSocket (const UchSocket& s);
+
+ UchChannel* Copy () const;
+virtual int SockType (); // SOCK_UNSPEC, SOCK_STREAM, SOCK_DGRAM
+inline int Family () { return AddrFamily; }
+inline void SetFamily (int f) { AddrFamily = f; }
+
+ void BindTo (UchAddress*);
+ void ConnectTo (UchAddress*);
+inline UchAddress* BoundTo () { return BAddr; }
+inline UchAddress* ConnectedTo () { return CAddr; }
+ bool Open ();
+ int Bind ();
+ int Bind (UchAddress*);
+ int Connect ();
+ int Connect (UchAddress*);
+ bool Setup ();
+ char* StrRepr (char* = StrReprBuf);
+};
+
+#endif /* Socket_H_ */
+
diff --git a/comm/Stream.cc b/comm/Stream.cc
new file mode 100644
index 0000000..f03e298
--- /dev/null
+++ b/comm/Stream.cc
@@ -0,0 +1,124 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Streams
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "Stream.h"
+#include <stdlib.h>
+#include <sys/socket.h>
+
+/*?class UchStream
+Streams implement reliable point to point connections.
+UchStream connections do not keep the message boundaries:
+several messages can be read at a time, and a message can be read in several parts.
+
+Establishing a stream connection is not a symmetric operation:
+one process (the server) creates a bound socket and listens to it (with function \fun{Listen});
+other processes (the clients) create sockets and connect them to the address of the server.
+Each such connection is accepted (\fun{SockAccept}) by the server and results in the creation of a new socket.
+Communication with each client will then proceed on this channel,
+while the server still can accept connections.
+
+Because a server has to listen to pending connections and communicate
+with its connected clients, using a channel set is highly recommended:
+when a connection is pending, the channel is ready for reading,
+and you can accept the connection with \fun{SockAccept} (instead of reading data).
+Note that because \typ{UchStream} derives from \typ{UchSocket}
+and thus from \typ{UchChannel}, the virtual functions
+\fun{HandleRead} and \fun{HandeWrite} can be used.
+?*/
+
+/*?nextdoc?*/
+UchStream :: UchStream ()
+: UchSocket ()
+{
+}
+
+/*?
+These constructors are similar to those of the class \typ{UchSocket}.
+?*/
+UchStream :: UchStream (UchAddress* bound, UchAddress* connected)
+: UchSocket (bound, connected)
+{
+}
+
+/*?nodoc?*/
+UchStream :: UchStream (const UchStream& s)
+: UchSocket (s)
+{
+}
+
+/*?nodoc?*/
+UchStream :: ~UchStream ()
+{
+}
+
+/*?nodoc?*/
+UchChannel*
+UchStream :: Copy () const
+{
+ return new UchStream (*this);
+}
+
+/*?nodoc?*/
+int
+UchStream :: SockType ()
+{
+ return SOCK_STREAM;
+}
+
+/*?
+This function implements the Unix system call \fun{listen}.
+\var{n} is the maximum number of pending connections:
+if \var{n} clients are waiting for the server to accept their connection,
+the next client will have its connection refused.
+You can safely use the default value.
+The stream socket is first set up (with \fun{Setup}), and then the system call is performed.
+The returned value is that of the system call, unless the set up failed in which case
+-1 is returned.
+Note that it is useless to listen on a socket if it is not bound to an address.
+?*/
+int
+UchStream :: Listen (int n)
+{
+ if (! Setup ())
+ return -1;
+ return listen (FilDes (), n);
+}
+
+/*?
+This function implements the Unix system call \fun{accept}.
+The stream socket is first set up (with \fun{Setup}), and then the system call is performed.
+A new dynamically created socket is returned, connected to the address returned by \fun{accept}.
+If the socket could not be set up or a system error occurred, 0 is returned.
+Note that in order to accept connections, the socket must be listen to,
+and thus an address must be bound to it.
+?*/
+UchSocket*
+UchStream :: SockAccept ()
+{
+ GEN_ADDR addr;
+ int alen = sizeof (addr);
+ int fd;
+
+ if (! Setup ())
+ return (UchSocket*) 0;
+
+ if ((fd = accept (FilDes (), &addr.sa, &alen)) < 0)
+ return (UchSocket*) 0;
+
+ UchSocket* s = new UchSocket (0, UchAddress::Decode (&addr, alen));
+ s -> UchChannel :: Open (fd);
+ return s;
+}
+
+
diff --git a/comm/Stream.h b/comm/Stream.h
new file mode 100644
index 0000000..3a4282d
--- /dev/null
+++ b/comm/Stream.h
@@ -0,0 +1,33 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Streams
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#ifndef Stream_H_
+#define Stream_H_
+
+#include "cplus_bugs.h"
+#include "Socket.h"
+
+class UchStream : public UchSocket {
+public:
+ UchStream ();
+ UchStream (UchAddress*, UchAddress*);
+ ~UchStream ();
+ UchStream (const UchStream&);
+ UchChannel* Copy () const;
+ int SockType ();
+ int Listen (int = 5);
+ UchSocket* SockAccept ();
+};
+
+#endif /* Stream_H_ */
diff --git a/comm/TimeOut.cc b/comm/TimeOut.cc
new file mode 100644
index 0000000..18fb3ec
--- /dev/null
+++ b/comm/TimeOut.cc
@@ -0,0 +1,69 @@
+#include "TimeOut.h"
+#include "ccu/Signal.h"
+#include "Multiplexer.h"
+
+UchBaseTimeOut :: UchBaseTimeOut (UchMultiplexer& m, Millisecond period, int pulses)
+: CcuCoreTimer (period, pulses, m.GetTimerSet ()),
+ MyMpx (m)
+{
+ CcuSignalBlocker b (SigAlrm);
+
+ if (PulsesLeft != 0)
+ Activate ();
+}
+
+/*?hidden?*/
+UchBaseTimeOut :: ~UchBaseTimeOut ()
+{
+ /* stop it */
+ if (StatusFlag == Active)
+ Stop ();
+}
+
+/*?hidden?*/
+void
+UchBaseTimeOut :: StopAlarm ()
+{
+ MyMpx.SuppressTimeOut ();
+}
+
+/*?hidden?*/
+void
+UchBaseTimeOut :: SetAlarm (Millisecond when)
+{
+ MyMpx.SetTimeOut (when);
+}
+
+
+
+
+/*?class UchTimeOut
+The class \typ{UchTimeOut} is a derived class of \typ{UchBaseTimeOut} that
+can be used without deriving a new class.
+Each \typ{UchTimeOut} holds a pointer to a function which is called when the timer
+expires. This function, which is passed to the constructor, must
+take a \typ{Millisecond} argument and return \typ{void}.
+?*/
+
+/*?
+Create a timer associated to the multiplexer \var{m}, that will expire every \var{period} milliseconds and call
+the function \var{handler}.
+?*/
+UchTimeOut :: UchTimeOut (UchMultiplexer& m, Millisecond period, void (*handler) (Millisecond), int pulses)
+: UchBaseTimeOut (m, period, pulses),
+ Handler (handler)
+{
+}
+
+/*?nodoc?*/
+UchTimeOut :: ~UchTimeOut ()
+{
+}
+
+/*?nodoc?*/
+void
+UchTimeOut :: Handle (Millisecond ref)
+{
+ (*Handler) (ref);
+}
+
diff --git a/comm/TimeOut.h b/comm/TimeOut.h
new file mode 100644
index 0000000..9a05cb2
--- /dev/null
+++ b/comm/TimeOut.h
@@ -0,0 +1,32 @@
+#ifndef UchTimeOut_H_
+#define UchTimeOut_H_
+
+#include "ccu/Timer.h"
+
+class UchBaseTimeOut : public CcuCoreTimer {
+friend class UchMultiplexer;
+
+private:
+static void ClassInit ();
+
+protected:
+ UchMultiplexer& MyMpx;
+ void SetAlarm (Millisecond);
+ void StopAlarm ();
+
+public:
+ UchBaseTimeOut (UchMultiplexer&, Millisecond, int = -1);
+ ~UchBaseTimeOut ();
+};
+
+class UchTimeOut : public UchBaseTimeOut {
+protected:
+ void (*Handler) (Millisecond);
+ void Handle (Millisecond);
+
+public:
+ UchTimeOut (UchMultiplexer&, Millisecond, void (*) (Millisecond), int = -1);
+ ~UchTimeOut ();
+};
+
+#endif /* UchBaseOut_H_ */
diff --git a/comm/doc.main b/comm/doc.main
new file mode 100644
index 0000000..44abcd4
--- /dev/null
+++ b/comm/doc.main
@@ -0,0 +1,396 @@
+%
+% The Unix Channel
+%
+% by Michel Beaudouin-Lafon
+%
+% Copyright 1990-1993
+% Laboratoire de Recherche en Informatique (LRI)
+%
+% User's Manual main file - to be updated
+%
+% $Id$
+% $CurLog$
+%
+
+\namedoc{Unix Channel}
+\def\uch{{\sc Uch}}
+
+\chapter{Introduction}
+%--------------------------------
+
+The Unix Channel library provides a set of classes to build distributed applications
+under Unix using sockets.
+
+The first level of the library, described in chapter \ref{General io}, defines basic
+classes: messages to be exchanged, buffers to hold data (usually messages),
+file descriptors that represent communication channels, and channel sets for
+multiplexing input/output.
+
+The second level, described in chapters \ref{Addresses} and \ref{Basics}, defines
+classes to build networks of processes exchanging messages.
+The different classes implement different transport protocols: datagrams (fast but
+unreliable), byte streams (reliable but without message boundaries), and streams
+(reliable delivery of messages).
+
+The third level is a specialized set of classes for implementing distributed
+applications using the client/server architecture. Chapter \ref{Servers} describes
+the server side, while chapter \ref{Clients} describes the server side.
+An application can be both a server and the client of other applications.
+
+Finally chapter \ref{Portserv} describes a set of classes to communicate
+addresses between the components of a distributed application.
+Most of the time th eoperating system allocates the channel addresses;
+a cumbersome part of the protocol often is for a client to know the physical address
+of a server from a logical name.
+The port server described in chapter \ref{Portserv} implements a service that maps
+logical names to physical addresses with a context.
+
+The present documentation assumes some knowledge of the network facilities
+available under Unix, as described in the document
+``{\em A Socket-Based Interprocess Communication Tutorial}''.
+This document is part of the Unix documentation.
+
+Some knowledge of the {\em CCU} library is also needed.
+This library is described in the document
+``{\em CCU, the CENA C++ Utilities library}''.
+\uch\ uses that library mainly for {\em smart pointer} classes.
+Given a class \typ{A}, a smart pointer class to \typ{A}, usually named \typ{pA},
+implements a class that behaves like pointers to objects of class \typ{A} (\typ{A*}),
+with the following additional feature:
+the pointed to object maintains a count of the number of smart pointers
+pointing to it; when this reference count reaches zero and the object was
+allocated dynamically (with \fun{operator new}), it is automatically destroyed.
+
+
+\section{Using The Unix Channel}
+\uch\ is provided as 4 files~:
+
+\begin{enumerate}
+
+ \item
+ the header files \com{chan.h} and \com{uch.h}, usually installed in \com{/usr/include/CC},
+ contains the definitions for classes, functions and constants provided by \uch.
+ \com{chan.h} contains the classes described in chapter \ref{General io} and can be used
+ in simple applications.
+ \com{uch.h} contains all the classes and is to be used by advanced applications.
+ One of these files should be included in any file using the library.
+ As most of \uch\ functions are member functions, this file can
+ be used as a quick reference.
+
+ \item
+ \samepage
+ {the archive files \com{libUch.a} and \com{libChan.a}, which are usually installed in \com{/usr/lib}
+ or in \com{/usr/loc\-al/lib}, contain the library procedures. One of these libraries must be loaded
+ with the object files which use \uch. This is usually performed
+ by adding the flag \com{-lUch} or \com{-lChan} in the command line for your C++ compiler.
+ \uch\ uses the utilities library, so \com{libutils++.a} must be included when loading an application.
+ \com{libUch.a} contain the object files for the whole library, while \com{libChan.a}
+ contains only the object files for the classes described in chapter \ref{General io},
+ corresponding to the header file \com{chan.h}.
+ For instance, you can type~:
+ \begin{center}
+ \com{CC -o demo main.C -lUch -lCcu}
+ \end{center}}
+
+\end{enumerate}
+
+
+\chapter{Error management}
+
+#iclass UchError
+
+\chapter{General io}
+\label{General io}
+
+This chapter describes the very basic classes of \uch.
+
+Unix input/output is byte oriented: byte buffers can be read and written in file descriptors
+with the system calls \fun{read} and \fun{write}.
+\uch\ implements message oriented input/output,
+although byte io can be used if needed.
+The class \typ{Message} is the abstract base class for the messages
+to be exchanged between applications. A \typ{Message} only needs to
+know how to convert itself to and from a set of bytes in a \typ{MsgBuffer}.
+The class \typ{MsgBuffer} is used throughout \uch\ to buffer input and output.
+Buffering output can dramatically improve performances (especially if the
+messages are short), while buffering input is necessary to implement message
+delivery (a message can be received in several pieces that need to be gathered
+before actual delivery to the application).
+
+The following types are defined for machine independent handling of data:
+\begin{itemize}
+\item The type \typ{^{byte}} is defined as an 8-bit quantity. Buffers contain bytes.
+\item The type \typ{^{sword}} refers to a 16-bit quantity.
+\item The type \typ {^{lword}} refers to a 32-bit quantity.
+\end{itemize}.
+
+The class \typ{UchFilDes} encapsulates the Unix notion of file descriptor.
+The class \typ{UchChannel} derives from \typ{UchFilDes}, adding virtual functions
+for implementing multiplexing and communication protocols.
+The class \typ{UchMultiplexer} implements a set of channels for multiplexing
+input and output.
+
+These classes are not sufficient to write distributed applications because
+there is no way to establish a communication between two processes.
+However they can be used for traditional input/output to terminals and files.
+This is illustrated in section \ref{General io example}.
+
+#class UchMessage
+#class UchMsgBuffer
+#class UchFilDes
+#class UchChannel
+#class UchMultiplexer
+
+\section{Example}
+\label{General io example}.
+
+[to be done]
+
+\chapter{Addresses}
+\label{Addresses}
+
+Processes can communicate once a channel has been established
+between them. One of the process has to create the channel, and thus has to
+know the other process. More precisely, a process connects to an {\em address}
+on which the other process is listening.
+This chapter describes classes that represent such addresses.
+
+Addresses can be established in different {\em domains}.
+Think of e-mail addresses and surface mail addresses: they are
+completely different although both can be used to reach the same person.
+Two domains are implemented: Unix domain addresses, and internet domain addresses.
+
+A Unix domain address is represented by a file in the file system.
+A process only needs to open the file to connect to the process that created the address.
+An internet domain address is made of a host number and a port number.
+It makes it possible for processes on different machines to communicate.
+Unfortunately the port numbers of internet addresses are usually assigned by
+the system, so that a distant process cannot know this port number to
+establish the communication.
+The port server described in chapter \ref{Portserv} overcomes this problem.
+It is often used to create internet addresses from logical names,
+instead of the class \typ{UchInetAddress} described in this chapter.
+
+#class UchAddress
+#class UchUnixAddress
+#class UchInetAddress
+
+
+\chapter{Basic communications}
+%--------------------------------
+\label{Basics}
+
+This chapter describes the main classes for developing distributed applications.
+The communication between the processes is done by using Unix sockets.
+The class \typ{UchSocket} encapsulates such sockets, but it is an abstract base class.
+The classes \typ{UchDatagram} and \typ{UchStream} implement the two main
+protocols available under Unix, i.e. datagrams and streams.
+These protocols differ in the way the connection can be established and the way
+the data is transmitted between the processes.
+In particular, streams transfer bytes and do not know about messages.
+The class \typ{UchMsgStream} extends the stream protocol to transfer messages
+and not bytes.
+This is the class that will be used most of the time to build distributed applications,
+as illustrated in section \ref{Basics example}
+
+\fig{socketclasses}{Hierarchy of classes}
+
+Figure \ref{fig:socketclasses} shows the derivation tree for the classes described
+in this chapter. All derivations are public. This means that the member functions
+of a base class are available in its derived classes, although they appear in the
+documentation only for the base class.
+
+#class UchSocket
+#class UchDatagram
+#class UchStream
+#class UchMsgStream
+#class UchDGRAM
+
+\section{Example}
+\label{Basics example}
+
+[to be done]
+
+\chapter{Servers}
+%--------------------------------
+\label{Servers}
+
+This chapter describes the classes \typ{UchServer} and \typ{UchClient} to be used
+to implement a server.
+A server is a process that accepts connections from several clients,
+and processes their requests.
+
+When implementing a server, there is one object of class \typ{UchServer},
+and one object of class \typ{UchClient} for each connected client.
+Each such \typ{UchClient} object is connected to another process where
+a corresponding \typ{Service} object exists (see figure \ref{fig:clientserver}).
+We call these processes the clients of the server:
+a \typ{UchClient} represents a connected client in the server,
+while a \typ{Service} represents the server in a client.
+
+\fig{clientserver}{Objects in a client-server system}
+
+The class \typ{Service} is useful only if your application follows an
+event/request protocol.
+If this is not the case, you can create your own class, derived from
+class \typ{MsgStream}.
+The class \typ{Service} and the associated classes for managing
+events are described in chapter \ref{Clients}.
+
+#class UchServer
+#class UchClient
+
+\chapter{Clients}
+\label{Clients}
+
+An object of a class derived from \typ{UchMsgStream} must exist in a client process to communicate
+with its server (implemented by an object of class \typ{UchClient} in the server process).
+
+The class \typ{UchService} described here can be used when the protocol between the
+server and its clients is of the event/request type.
+
+\fig{evreq}{Event / request protocol model.}
+
+In the event/request model (see figure \ref{fig:evreq}),
+a client communicates with a server by sending requests.
+These requests are generally asynchronous, thus allowing buffered i/o.
+Some requests may need an answer from the server: those requests are synchronous by nature.
+The server sends events to its clients;
+events are asynchronous by nature: a server never waits for an answer from a client,
+for this would potentially block it.
+
+Incoming requests are stored in an event queue for later processing by the client:
+usually the client reads an event in its top-level loop, and responds to it;
+this may involve sending requests to the server, that in turn will generate events.
+
+A single client may want to be connected to several servers.
+The class \typ{UchService} makes this possible by allowing to share an event queue
+between several services.
+Thus incoming events are multiplexed, and the top level loop of the client is of the
+same form as above.
+Each event contains the server that sent it so that it can be easily dispatched by the client.
+
+Events are messages. They are implemented by the virtual base class \typ{EventMsg}.
+The event queue of a service is implemented by the class \typ{UchEvtMsgQueue}.
+
+#class UchService
+#class UchEventMsg
+#class UchGenEvtMsg
+#class UchEvtMsgQueue
+
+\chapter{Port registration server}
+\label{Portserv}
+
+A client and a server need to agree on an address to establish a communication.
+Internet addresses are generally allocated by the system by passing a null port number;
+thus, there is no simple way for the clients to know on which address the server is listening.
+
+The standard solution to this problem is to use the services database of Unix,
+to associate a given port number to a service name.
+This database is not designed to be updated easily (it is the job of the system administrator),
+so it is not possible for a server to register its address in this database each time the server is launched.
+Instead, for a given service, the database holds the service name and port number of a daemon,
+and this daemon maintains the address of the server currently implementing this service.
+Several servers may use the same daemon, for instance if a given service
+needs one server per different user.
+
+The port server is such a daemon.
+The port server is a separate process that stores associations between keys and addresses.
+Each such association represents a server that is available.
+The key can encode the service name, user, etc...
+Servers register themselves by entering their address with a key and an identification
+(usually their process number);
+they can (they should) remove their entry from the port server whenever they die.
+In order to connect to a server, a client inquires the address to the port server by sending it the key.
+It is also possible to ask the port server a set of keys based on a regular expression.
+
+The port server itself is run by the following command:
+\begin{ccode}
+portserv [ service [ file [ savetime ] ] ]
+\end{ccode}
+\var{service} is the name of the service as stored in the services database (default is ``\com{portserv}'').
+\var{file} is the name of a file to store the state of the port server.
+Reloading such a file is not currently implemented.
+If \var{file} is ``$-$'', standard output is used, if it is ``$--$'', standard error is used.
+If \var{file} is not specified, the saving feature is disabled.
+If \var{savetime} is specified, it determines how many seconds after a modification
+the new state is saved. If it is not specified, the state is saved immediately after each modification.
+The save time is intended for port servers that are heavily used in order to reduce file accesses.
+
+#class UchPortServer
+
+\section{Testing the port server}
+
+\com{porttest} is a simple program that can be used to interact with a port server.
+Each call to \com{porttest} connects to a port server and executes an operation on it,
+depending on the arguments of the command.
+The port server that \com{porttest} connects to has name ``\com{portserv}'',
+and is on the local host by default.
+If the first argument is of the form \com{=}\var{service},
+it connects to a service named \var{service} instead of \com{portserv}.
+If the first or second argument is of the form \com{:}\var{host},
+it connects to a portserv on the machine \var{host} instead of the local host.
+
+The different commands are the following:
+\begin{itemize}
+\item
+ \com{porttest [=service] [:host]}\\
+ Ask the port server to dump its state on its standard error.
+\item
+ \com{porttest [=service] [:host] quit}\\
+ Make the port server exit.
+\item
+ \com{porttest [=service] [:host] key}\\
+ Inquire \var{key}. If found, it is printed with its associated address,
+ else an error message is printed.
+\item
+ \com{porttest [=service] [:host] ?rekey}\\
+ Inquire all keys matching \var{rekey}.
+\item
+ \com{porttest [=service] [:host] key portnumber}\\
+ Register \var{key} with an address made of port \var{portnumber} on the local host.
+ \com{porttest} provides its own identification for registration.
+\item
+ \com{porttest [=service] [:host] key -portnumber}\\
+ Remove \var{key}, if bound to the address made of port \var{portnumber} on the local host.
+\end{itemize}
+
+\section{Sample usage}
+
+[to be done]
+
+\newpage
+\appendix
+
+\chapter{Class List}
+
+This chapter contains the list of the classes defined in \uch.
+The first section contains the inheritance tree of \uch\ classes.
+The second section contains for each class the ordered list of its base classes.
+The section number indicated after each class refers to the
+documentation of that class. The class names that appear in italic
+are defined in other libraries. Classes defined in \uch\ but
+which are not documented do not appear in the lists.
+
+\section{Inheritance Tree}
+This section contains the set of classes defined in \uch.
+Each base class is followed by the indented list of its subclasses.
+
+\input{inhtree.tex}
+
+\newpage
+\section{Inheritance List}
+This section contains the set of classes defined in \uch.
+Each class is followed by its base class, recursively.
+Thus, from a given class, one can follow the inheritance link
+and thus refer to the documentation for the inherited methods.
+
+\begin{inhlist}{XXXXXXXXXXXXXXXX}
+\input{inhlist.tex}
+\end{inhlist}
+
+\begin{theindex}
+\indexinc
+\end{theindex}
+
+\end{document}
diff --git a/comm/error.cc b/comm/error.cc
new file mode 100644
index 0000000..fc02eb9
--- /dev/null
+++ b/comm/error.cc
@@ -0,0 +1,261 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Error management
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "error.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+
+/*?class UchERROR
+The set of global functions described here are designed to handle errors.
+An error has a type, an origin, and a message.
+The type defines how the error is handled.
+The origin and the message are used to generate the error message.
+The origin is usually the function name where the error occurred.
+A log file may be defined for storing all error messages, in addition to the normal
+notification mechanism.
+
+Handling the error is a three steps process:
+first the error string is generated, then the error is emitted by a user-defined error handler,
+and finally the way the program continues is determined from the error type.
+These types are described by the following values (enumerated type \typ{^{errtype}}):
+\index{errtype :: ErrNone}\index{errtype :: ErrLog}\index{errtype :: ErrWarn}
+\index{errtype :: ErrAbort}\index{errtype :: ErrExit}\index{errtype :: ErrFatal}
+\index{errtype :: ErrUsage}
+\begin{itemize}
+\item \var{ErrNone} is not an error: the handler is not called and the program continues;
+\item \var{ErrLog} is an error that is only logged to the log file: the handler is not called and the program continues;
+\item \var{ErrWarn} is a warning: the handler is called and the program then continues;
+\item \var{ErrAbort} handles the error and calls \fun{abort};
+\item \var{ErrExit} handles the error and calls \fun{exit(0)};
+\item \var{ErrFatal} handles the error and calls \fun{exit(1)};
+\item \var{ErrUsage} handles the error and calls \fun{exit(1)};
+\var{ErrUsage} is different from \var{ErrFatal} in that the generated error message
+is not the same (see \fun{MakeErrorString}).
+\end{itemize}
+
+The type of an error handler is the following:
+\index{ErrorHandler}
+\begin{ccode}
+typedef errtype (*ErrorHandler) (errtype, const char* who, const char* what, const char* msg);
+\end{ccode}
+?*/
+
+// static data for error handling: message table and default handler
+//
+char* ErrorTable [] = {
+ "Subclass should implement virtual member", // ErrShouldImplement
+};
+
+static char ProgName [128], LogFile [128];
+static bool LogOn =FALSE;
+
+static errtype
+DefaultHandler (errtype how, const char* /*who*/, const char* /*what*/, const char* msg)
+{
+ write (2, msg, strlen (msg));
+ return how;
+}
+
+static ErrorHandler Handler = DefaultHandler;
+
+// main function for handling errors
+//
+static bool
+HandleError (errtype how, const char* who, const char* what)
+{
+ if (how == ErrNone)
+ return TRUE;
+
+ char *msg = MakeErrorString (how, who, what);
+
+ if (how >= ErrLog && LogOn) {
+ LogMessage (msg);
+ if (how == ErrLog)
+ return TRUE;
+ }
+
+ how = (*Handler) (how, who, what, msg);
+
+ switch (how) {
+ case ErrNone:
+ return TRUE;
+ case ErrLog:
+ case ErrWarn:
+ break;
+ case ErrAbort:
+ write (2, "aborting\n", 9);
+ abort ();
+ break;
+ case ErrExit:
+ exit (0);
+ break;
+ case ErrUsage:
+ case ErrFatal:
+ exit (1);
+ break;
+ }
+ return FALSE;
+}
+
+// public functions:
+// register program name, cleanup function, log file
+// change error handler
+// error functions
+//
+
+/*?
+Set the program name, that is used to label each output message.
+This usually called from \fun{main} with \com{argv[0]} as argument.
+?*/
+void
+ProgramName (const char* name)
+{
+ if (name)
+ strncpy (ProgName, name, sizeof (ProgName) -1);
+ else
+ ProgName [0] = 0;
+}
+
+/*?
+Set the log file name.
+All messages are appended to the logfile.
+If \var{reset} is TRUE, the file is cleared.
+NOTE : logging is not currently implemented.
+?*/
+void
+LogfileName (const char* file, bool reset)
+{
+ if (file) {
+ strncpy (LogFile, file, sizeof (LogFile) -1);
+ LogOn = TRUE;
+ if (reset) {
+ // to do: clear logfile
+ }
+ } else
+ LogOn = FALSE;
+}
+
+/*?nodoc?*/
+void
+CleanUp (CleanUpProc)
+{
+ // to do: register function
+}
+
+/*?
+Append a message to the logfile, if one has been specified.
+NOTE : logging is not currently implemented.
+?*/
+void
+LogMessage (const char*)
+{
+ // to be done
+}
+
+/*?
+Build an error message from the type of error, the place where the error occurred,
+and the error message itself, with the following format:\\
+``\com{ [program\_name :] [fatal error in]}\var{who}\com{: }\var{what}''\\
+When \var{how} is \var{ErrUsage}, the format is\\
+``\com{ usage: program\_name}\var{what}''.\\
+The string returned is the address of a static buffer that is overwritten
+each time this function is called.
+?*/
+char*
+MakeErrorString (errtype how, const char* who, const char* what)
+{
+ static char errmsg [1024];
+
+ if (how == ErrUsage)
+ sprintf (errmsg, "usage: %s %s\n", ProgName, what);
+ else
+ sprintf (errmsg, "%s%s%s%s: %s\n",
+ ProgName,
+ ProgName [0] ? ": " : "",
+ how >= ErrAbort ? "fatal error in " : "",
+ who,
+ what);
+ return errmsg;
+}
+
+/*?
+Change the error handler.
+The error handler is called each time an error is emitted.
+It is called with four arguments:
+the error type, the error source, the error message,
+and the error string as returned by \fun{MakeErrorString}.
+It should return an error type (usually the first argument) that determines
+what happens next: nothing, logging to a file, aborting, or exiting.
+The default error handler can be reset by passing the argument 0.
+The default error handler writes the error string to the standard error,
+and returns its first argument.
+?*/
+ErrorHandler
+SetErrorHandler (ErrorHandler h)
+{
+ ErrorHandler old = Handler;
+ if (! h)
+ h = DefaultHandler;
+ Handler = h;
+ return old;
+}
+
+/*?
+Emit an error of type \var{how}, from function \var{who}, with message \var{what}.
+?*/
+void
+Error (errtype how, const char* who, const char* what)
+{
+ HandleError (how, who, what);
+}
+
+/*?
+Emit a system error of type \var{how}, from function \var{who}.
+The message is retrieved from the value of the global variable \var{errno}.
+Thus, this function should be used after system calls that set \var{errno}.
+\var{exc1} and \var{exc2}, if non negative, are error codes (values of \var{errno}) to be ignored:
+if the current error code is one of these, the function returns FALSE.
+This is useful for instance to ignore interrupted system calls:
+\begin{ccode}
+ #include <errno.h>
+ n = read (...);
+ if (n < 0)
+ SysError (ErrWarn, "read", EINTR);
+\end{ccode}
+?*/
+bool
+SysError (errtype how, const char* who, int exc1, int exc2)
+{
+ extern int errno;
+ extern int sys_nerr;
+ extern char* sys_errlist [];
+ char* msg;
+ char defmsg [80];
+
+ if (! errno || errno == exc1 || errno == exc2)
+ return FALSE;
+
+ if (errno >= sys_nerr)
+ sprintf (msg = defmsg, "system error code %d", errno);
+ else
+ if (errno == 0)
+ msg = "internal error";
+ else
+ msg = sys_errlist [errno];
+ return HandleError (how, who, msg);
+}
+
diff --git a/comm/error.h b/comm/error.h
new file mode 100644
index 0000000..a0d11ef
--- /dev/null
+++ b/comm/error.h
@@ -0,0 +1,58 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Error management
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#ifndef UchError_H_
+#define UchError_H_
+
+#include "global.h"
+
+#ifdef DOC
+// fake class for global functions
+class UchError {
+};
+#endif
+
+enum errtype {
+ ErrNone = -4, // do nothing
+ ErrLog = -3, // only log to file
+ ErrWarn = -2, // warn and continue
+ ErrAbort = -1, // call abort
+ ErrExit = 0, // exit with code 0
+ ErrUsage = 1, // bad usage
+ ErrFatal = 2 // exit with code != 0
+};
+
+enum errcode {
+ ErrShouldImplement
+};
+
+extern char *ErrorTable [];
+
+typedef errtype (*ErrorHandler) (errtype, const char* who, const char* what, const char* msg);
+typedef void (*CleanUpProc) ();
+
+extern void ProgramName (const char*);
+extern void LogfileName (const char*, bool = FALSE);
+extern void CleanUp (CleanUpProc);
+extern ErrorHandler SetErrorHandler (ErrorHandler);
+
+extern void Error (errtype, const char* who, const char* what);
+extern bool SysError (errtype, const char* who, int exc1 = -1, int exc2 = -1);
+extern char* MakeErrorString (errtype, const char* who, const char* what);
+extern void LogMessage (const char*);
+
+extern int errno;
+
+#endif /* UchError_H_ */
+
diff --git a/comm/global.h b/comm/global.h
new file mode 100644
index 0000000..71244dc
--- /dev/null
+++ b/comm/global.h
@@ -0,0 +1,28 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * General definitions
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#ifndef UchGlobal_H_
+#define UchGlobal_H_
+
+#ifndef NIL
+#define NIL 0
+#define Nil(type) ((type) NIL)
+#endif /* NIL */
+
+typedef void *pointer;
+
+#include "ccu/bool.h"
+#include "ccu/word.h"
+
+#endif /* UchGlobal_H_ */
diff --git a/comm/version.h b/comm/version.h
new file mode 100644
index 0000000..8870ab1
--- /dev/null
+++ b/comm/version.h
@@ -0,0 +1,75 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Version and copyright header
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+/**
+ * Copyright 1990, 1991, 1992 Laboratoire de Recherche en Informatique (LRI)
+ * Copyright 1992, 1993 Centre d'Etudes de la Navigation Aerienne (CENA)
+ *
+ * Permission to use, copy, and modify this software and its documentation
+ * for your own purposes is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and use acknowledge that the software was developed
+ * by Laboratoire de Recherche en Informatique, Universite de Paris-Sud,
+ * Orsay, France, and Centre d'Etudes de la Navigation Aerienne, Toulouse,
+ * France. LRI and CENA make no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ * This software or modified versions of this software cannot be
+ * distributed in source or binary form, nor included into products
+ * without prior written permission of the author.
+ *
+ * LRI AND CENA DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL LRI OR
+ * CENA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
+ * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Maintainer:
+ *
+ * Stephane Chatty e-mail: chatty@dgac.fr
+ * CENA
+ * 7 avenue Edouard Belin phone: +33 62 25 95 42
+ * 31055 TOULOUSE - FRANCE fax: +33 62 25 95 99
+ *
+**/
+
+/**
+title The Unix Channel
+subtitle C++ Communication library
+version 3.1
+date January 1993
+copyright (c) 1989-1992 LRI, 1992-1993 CENA
+authors Michel Beaudouin-Lafon
+| St\'ephane Chatty
+address Laboratoire de Recherche en Informatique
+| B\^at. 490, Facult\'e d'Orsay
+| 91405 Orsay Cedex, France
+| and
+| Centre d'Etudes de la Navigation A\'erienne
+| 7 avenue Edouard Belin
+| 31055 TOULOUSE CEDEX, France
+e-mail mbl@lri.fr and chatty@dgac.fr
+phone +33 1 69 41 69 10 and +33 62 25 95 42
+**/
+
+#ifndef UchVersion
+
+#define UchVersion 3
+#define UchRelease 2
+#define UchPatchLevel 0
+
+#endif