summaryrefslogtreecommitdiff
path: root/comm/OLD
diff options
context:
space:
mode:
authorchatty1993-04-07 11:50:31 +0000
committerchatty1993-04-07 11:50:31 +0000
commitba066c34dde204aa192d03a23a81356374d93731 (patch)
tree39391f6235d2cf8a59a0634ac5ea430cdd21f5d4 /comm/OLD
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/OLD')
-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
25 files changed, 5624 insertions, 0 deletions
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