#define	    ident	    "1B04"

#ifdef __DECC
#pragma	    module	    SerPortServer	ident
#endif
/*
** Updates:
**  1A01 26-Apr-1999 DM.	Initial version.
**  1A04 14-Jan-2000 DM.	Increase # RS-232-C ports to 20.
**  1A08  3-Apr-2000 DM.	Add fortify.h
**  1A10  7-Apr-2000 DM.	Change time-out units to 0.2 secs.
**  1B01 21-Aug-2000 DM.	Handle client requests in parallel and put
**				    time-out units back to 0.1 secs.
**
** +--------------------------------------------------------------+
** |                  Paul Scherrer Institute                     |
** |                     SINQ Project				  |
** |                                                              |
** | This software may be used freely by non-profit organizations.|
** | It may be copied provided that the name of P.S.I. and of the |
** | author is included. Neither P.S.I. nor the author assume any |
** | responsibility for the use of this software outside of P.S.I.|
** +--------------------------------------------------------------+
**
** Link_options - Here is the Linker Option File
**!$	if p1 .eqs. "DEBUG" then dbg1 := /debug
**!$	if p1 .eqs. "DEBUG" then dbg2 := _dbg
**!$	link 'dbg1'/exe=new_SerPortServer'dbg2'.exe sys$input/options
**!	    new_SerPortServer
**!	    mad_lib:sinq_dbg/lib
**!	    sys$share:decw$xmlibshr12/share
**!	    sys$share:decw$xtlibshrr5/share
**!	    sys$share:decw$xlibshr/share
**!$	purge/nolog new_SerPortServer'dbg2'.exe
**!$	set prot=w:re new_SerPortServer'dbg2'.exe
**!$	write sys$output "Exec file is ''f$environment ("default")'new_SerPortServer''DBG2'.EXE
**!$	exit
**!$!
**!$! To build on LNSA09 ...
**!$!	$ import tasmad dev
**!$!	$ build_cc_select :== decc
**!$!	$ define/job deltat_c_tlb sinq_c_tlb
**!$!	$ bui tas_src:[utils]new_SerPortServer debug
**!$!
** Link_options_end
**
**	Building on Alpha Digital Unix:
**
**	    setenv TAS_BASE ~maden/tasmad
**	    source $TAS_BASE/tasmad.setup
**	    rcp -p "lnsa09:tas_src:[utils]new_SerPortServer.c" \
**			    $TAS_SRC/utils/new_SerPortServer.c
**	    cc -std -g -o $TAS_BIN/new_SerPortServer	\
**		    -I$TAS_INC				\
**		    $TAS_SRC/utils/new_SerPortServer.c	\
**		    -L$TAS_LIB -lsinq -lXm -lXt -lX11
**
** Resources and Options	    File:	decw$user_defaults:SinQ_rc.dat
** ---------------------	    or		$HOME/SinQ_rc
**
**  Resource		Option    Default	    Description
**  --------		------    -------	    -----------
**  -			-name	  SerPortServer	Name to use when looking up the
**						   resources. The default is the
**						   image file name.
**  *serPortTsName	-ts       none		 Name or IP address of Terminal
**						   Server.
**  *serPortServPort	-port     4000		 TCP/IP port service number.
**  *serPortTrace	-trace	  		 Turn on tracing.
**  *serPortTraceSize	-tsize  0x40000	(= 256k) Trace buffer size.
**  *serPortPeriod	-period      60	    Period for writing trace time-stamps
**  *serPortMax         -max      20             Maximum number of serial ports
**                                               to use
**
**  A value given via -name will be converted to lowercase before being used.
**---------------------------------------------------------------------------
** Module Name  . . . . . . . . : [...MOTIF]SerPortServer.C
**
** Author   . . . . . . . . . . : D. Maden
** Date of creation . . . . . . : Apr 1999
**
** Purpose
** =======
**	SerPortServer is a TCP/IP server program which should accept TCP/IP
**	connections from EL734, EL737 or other clients obeying the AsynSrv
**	protocol and then transmit requests to an RS232C device connected to
**	a Lantronix ETSnnP terminal server. It should provide the same
**	functions as the SerPortServer program on the Mac.
** Use:
** ===
**  1) Set up the name of the Terminal Server and the TCP/IP port number on
**	which this TCP/IP server will listen for connections in the resources
**	file (see above). These can also be supplied as an option on the
**	command line.
**
**  2) Run the program
**
**	    $ spawn/nowait/out=nl: run <directory>SerPortServer
**
**	or, to specify the options on the command line, use a
**	DCL foreign command:
**
**	    $ SerPortServer :== $<directory>SerPortServer
**	    $ SerPortServer -ts <TS-name> -port <port-number>
**
**====================================================================
** Clients send packets of commands to SerPortServer. These packets consist
** of zero or more commands to a serial port. The commands are sent and
** the responses are read. The responses are packed up ito a response packet
** which is sent to the client when complete. See rs232c_def.h for a description
** of the packet structure.
**
** If an error is detected, the field n_rply in the response packet is set
** to "-001" and the field u.sub_status may take one of the values:
**  "-00000000001" if field n_cmnds is a non-decimal ASCII string.
**  "-00000000002" if field n_cmnds is illegal.
**  "-00000000003" if runaway command list.
**  "-00000000004" if non-decimal ASCII command length.
**  "-00000000005" if illegal command length.
**  "-00000000006" if bad status from send.
**  "-00000000007" if request aborted while in pending list.
**  "-00000000008" if field serial_port is illegal.
**  "-00000000009" if command list execution terminated because another request
**			has been received from this client before this one
**			was completed.
**  "-00000000010" if the request timed out whilst still on the pending queue
**			for the specified channel.
**  "-00000000011" if command list execution terminated via <Ctrl-C>.
**  "-00000000012" if the terminal server IP address is not known.
**  "-00000000014" if TS socket create failed.
**  "-00000000015" if TS socket bind failed.
**  "-00000000016" if TS socket connect failed.
**  "-00000000017" if no more TS indices available.
**  "-00000000018" if runaway pending list.
**  "000000005001" if protocol level mismatch.
**====================================================================
*/
#include    <stdlib.h>
#include    <stdio.h>
#include    <netdb.h>
#include    <signal.h>
#include    <ctype.h>
#include    <errno.h>
#include    <time.h>

#include    <sys/socket.h>
#include    <sys/time.h>
#include    <sys/types.h>

#ifdef  __VMS
  #include    <ucx$inetdef.h>
  #include    <unixio.h>
  #include    <lib$routines.h>
  #include    <libdtdef.h>
#else
  #include    <unistd.h>
  #include    <netinet/tcp.h>
  #ifdef FORTIFY
    #include    <fortify/fortify.h>
  #endif
#endif

#include    <netinet/in.h>

#include    <arpa/inet.h>

#include    <X11/Intrinsic.h>
/*

**--------------------------------------------------------------------------
**			Define global structures and constants.
*/
#include    <sinq_prototypes.h>
#ifdef	__VMS
    int	    sys$gettim (int *time, ...);
#endif

#define	RS__MAX_CLIENTS	 8	/* Up to 8 clients will be supported */
static int  RS__MAX_ASYNCH =	20;	/* Asynch "ports" 0 - 19 will be allowed */
#define MAX_OPEN_CHANS	20

#define MAX_PKT_SIZE	10000	/* The SerPortServer packet protocol has a
				** 4 char ASCII header giving the packet length.
				** The max packet size is therefore 9999.
				*/
#define	NIL	'\0'

  enum ACTION {
    IN,
    OUT,
    ERROR,
    OPEN,
    CLOSE,
    TMO,
    FLUSH};

  enum CL_SKT_STATUS {
    CL_SS_IDLE,
    CL_SS_BUSY,
    CL_SS_PENDING};

  enum TS_SKT_STATUS {
    TS_SS_IDLE = -1};
/*-------------------------------------------------------------
**			Prototypes of Routines in this File
*/
  void	    abortPending (
		int	 indx);
  void	    addToBuffer (
		char	 byte,
		char	*buff,
		int	 b_size,
		int	*nxt);
  void	    addToPendList (
		int	 ts_indx,
		int	 cl_indx);
  void	    chanFlush (
		int	 indx);
  void	    closeClient (
		int	 indx);
  void	    closeTs (
		int	 indx);
  void	    ctrlC_Handler (
		int	 sigint);
  void	    freeAll ();
  void	    getCurrentTime (
		struct timeval	*time_now);
  void	    handleClientRecv (
		int	 indx);
  void	    handlePendTmo (
		int	 indx);
  void	    handleTmo (
		int	 indx);
  void	    handleTsRecv (
		int	 indx);
  int	    lookForTerm (
		int	    *len,
		char	    *term,
		int	     nterm,
		char	    *terms,
		char	    *buff,
		int	     nch);
  int	    open_RS232C_Chan (
		char		*server,
		int		 chan,
		struct RS__RespStruct   *rply);
  void	    sendCmndToTs (
		int		 ts_indx,
		int		 cl_indx);
  int	    setupErrReply (
		struct RS__RespStruct *rply,
		char		*msg_id,
		char		*sub_status);
  void	    setupNewClient (
		int	    skt);
  int	    setupSocket (
		int	    port);
  char	   *setupTime (
		char	*buff,
		int	 buff_size);
  int	    setupXrmDatabase (
		XrmDatabase *db,
		char	    *name[],
		int	    *argc,
		char	    *argv[]);
  void	    startUserRequest (
		int	 indx);
  float	    subtractTimes (
		struct timeval	time0,
		struct timeval	time1);
  void	    traceAdd (
		int	 n_txt,
		char	*txt);
  void	    traceDo (
		char	    prefix,
		int	    indx,
		enum ACTION in_or_out,
		int	    n_txt,
		char	   *txt);
  void	    traceWrite ();
  void	    USR1_Handler (
		int	 sigint);
/*-------------------------------------------------------------
**	Global Variables
*/
  static int	Inet_port;		/* TCP/IP Port number for listening */
  static int	Cnct_skt;		/* Base socket for "accept" */
  static fd_set	Skt_mask;		/* Mask for "select"	*/
  static int	Max_skt  = 0;		/* Max socket to check by "select" */
  static float	Next_tmo_secs;		/* Number of secs till next time-out */

  static char	Ts_name[32];		/* Name of Terminal Server */

  static int	N_clients;	/* The number of connected clients */
  static int	N_open_chans;	/* The number of open RS-232-C chans */

  static struct cl_recd {	      /* Data structures for the open clients */
    struct RS__MsgStruct  *msge;       /* Pntr to bufr for msge from client */
    struct RS__RespStruct *rply;       /* Pntr to bufr for reply to client */
    enum CL_SKT_STATUS	  status;      /* Status of the connection */
    char		  host[64];    /* Name of client host */
    int			  port;	       /* Client's TCP/IP port number */
    int			  skt;	       /* The socket number */
    int			  pending;     /* Index of a pending request */
    struct timeval	  pend_time;   /* Time-stamp if rqst pending */
    int			  msg_len;     /* The length of the data in *msge */
    int			  pcol_code;   /* The Protocol level of *msge */
    char		  fmt_in[8];   /* The format for decoding */
    char		  fmt_out[8];  /* The format for encoding */
    int			  hdr_size;    /* The size of header fields (2 or 4) */
    int			  chan;	       /* The channel # from *msge */
    int			  chnx;	       /* The channel index */
    float		  tmo;	       /* Time-out in secs from *msge */
    int			  nterm;       /* # terminators from *msge */
    char		  terms[4];    /* The terminators from *msge */
    int			  n_cmnds;     /* # cmnds from *msge */
    int			  n_rplys;     /* # replies in from *rply */
    int			  c_len;       /* Len of current cmnd from *msge */
    int			  msge_size;   /* The size of the *msge buffer */
    int			  rply_size;   /* The size of the *rply buffer */
    int			  remaining;   /* # bytes available in *rply */
    char		 *nxt_cmnd_ptr;	/* Pntr to next cmnd in *msge */
    char		 *nxt_rply_ptr0;/* Pntr to hdr of nxt rply in *rply */
    char		 *nxt_rply_ptr1;/* Pntr to next byte to fill in *rply */
  } *Cl_info;

  static struct ts_recd {		/* Data structures for the open chans */
    enum TS_SKT_STATUS	status;		/* Status of the connection
					**  >= 0 ==> in use by a client. The
					**	value is the client's index.
					*/
    char		server[64];	/* Name of terminal server */
    char		dot_addr[20];	/* Internet addr of TS in dot format */
    int			chan;		/* The channel number */
    int			port;		/* Remote TCP/IP port number */
    int			skt;		/* The socket number */
    fd_set		mask;		/* A select mask for the socket */
    char		tp_ahd[2048];	/* Type-ahead buffer */
    int			tp_nxt;		/* Index of next free byte in tp_ahd */
    float		tmo;		/* Time-out for current cmnd in secs */
    struct timeval	time_stamp;	/* Time-stamp for current cmnd */
    int			pending;	/* Index of a pending request */
  } *Ts_info;

  static int		Trace;			/* Trace transactions if True */
  static int		Tr_buf_size = 0x40000;	/* Size of trace buffer */
  static unsigned char *Tr_buf = NULL;		/* Buffer to hold trace info. */
  static int		Tr_buf_len = 0;		/* Length of allo trace buff */
  static unsigned char *Tr_buf_nxt;		/* Next free byte in trace bf */
  static int		Tr_buf_free;		/* # free bytes in trace buff */
  static int		Tr_buf_shuff;		/* Trace buffer shuffle value */
  static struct timeval	Tr_timer;		/* Trace time-stamp */
  static int		Tr_period = 60;		/* Trace time-stamp period */

  static int	Debug;			/* Debug mode if True */
  static int	Usr1_has_happened;   /* Set to True when USR1 signal detected */

  static int		Ctrl_C_has_happened; /* Set to True when <Ctrl-C> hit */
  static int		Ctrl_C_pending;	     /* True if <Ctrl-C> exit is
					     **	    pending */
  static struct timeval	Ctrl_C_time;	     /* Time-stamp of detection
					     **	    of <Ctrl-C> */
/*
**---------------------------------------------------------------------------
**		abortPending:  Abort any pending requests on a channel.
*/
  void	abortPending (
/*	============
*/	    int		 indx) {	/* The index of the channel */

    int	    i, j, status;

    if (Ts_info[indx].pending >= 0) {
      i = Ts_info[indx].pending;
      Ts_info[indx].pending = -1;
      while (i != -1) {
	j = setupErrReply (Cl_info[i].rply,
				Cl_info[i].msge->msg_id, "-00000000007");
	status = send (Cl_info[i].skt, (char *) Cl_info[i].rply, j, 0);
	if (Trace) traceDo ('S', Cl_info[i].skt, ERROR, 16, " sub_status = -7");
	j = Cl_info[i].pending;
	Cl_info[i].status = CL_SS_IDLE;
	Cl_info[i].pending = -1;
	j = i;
      }
    }
    return;
  }
/*
**---------------------------------------------------------------------------
**		addToBuffer:  Add a byte to the end of a type-ahead buffer.
*/
  void	addToBuffer (
/*	===========
*/	    char	 byte,	    /* The byte to be added */
	    char	*buff,	    /* The buffer start address */
	    int		 b_size,    /* The size of the buffer */
	    int		*nxt) {	    /* Index of first free byte in buffer */

    int		i;

    if (*nxt >= b_size) {		/* Must the buffer be shuffled down? */
      for (i = 1; i < b_size; i++) buff[i-1] = buff[i];	    /* Yes */
      *nxt = b_size - 1;
    }
    buff[*nxt] = byte;
    *nxt += 1;
  }
/*
**---------------------------------------------------------------------------
**			addToPendList	- add a request to pending list
*/
  void	addToPendList (
/*	=============
*/		int	    ts_indx,	/* Term srvr index */
		int	    cl_indx) {	/* Client index of pending request */

    int		i, j, run_away = 0;

    Cl_info[cl_indx].status  = CL_SS_PENDING;
    Cl_info[cl_indx].pending = -1;
    getCurrentTime (&Cl_info[cl_indx].pend_time);

    if (Ts_info[ts_indx].pending == -1) {
      Ts_info[ts_indx].pending = cl_indx;
    }else {
      i = Ts_info[ts_indx].pending;
      while ((Cl_info[i].pending != -1) && (run_away < 10)) {
	i = Cl_info[i].pending; run_away++;
      }
      if (Cl_info[i].pending != -1) {
	if (Trace) traceDo ('C', Ts_info[ts_indx].chan, ERROR,
                                    23, " <runaway pending list>");
	if (Trace) traceDo ('S', Cl_info[cl_indx].skt, ERROR,
                                    23, " <runaway pending list>");
	printf ("   <runaway pending list> C:%d S:%d\n",
			Ts_info[ts_indx].chan, Cl_info[cl_indx].skt);
	j = setupErrReply (Cl_info[cl_indx].rply,
				Cl_info[cl_indx].msge->msg_id, "-00000000018");
	send (Cl_info[cl_indx].skt,
                            (char *) Cl_info[cl_indx].rply, j, 0);
	Cl_info[cl_indx].status = CL_SS_IDLE;
	return;
      }
      Cl_info[i].pending = cl_indx;
    }
  }
/*
**---------------------------------------------------------------------------*/
  void	chanFlush (
/*	=========
**				Flush out any pending input
*/
	    int		indx) {	    /* The Ts_info index of channel to flush */

    int			i, status;
    fd_set		my_rd_msk;
    char		buff[80];
    struct timeval	tmo, zero_tmo = {0, 0};
		    /*
		    ** First flush the type-ahead buffer
		    */
    if ((Trace) && (Ts_info[indx].tp_nxt > 0)) {
      traceDo ('C', Ts_info[indx].chan, FLUSH,
			Ts_info[indx].tp_nxt, Ts_info[indx].tp_ahd);
    }
    Ts_info[indx].tp_nxt = 0;
		    /*
		    ** Then flush the socket
		    */
    my_rd_msk = Ts_info[indx].mask;
    tmo = zero_tmo;
    status = select (Ts_info[indx].skt+1,
			    &my_rd_msk, NULL, NULL, &tmo);
    while (status > 0) {
      status = recv (Ts_info[indx].skt, buff, sizeof (buff), 0);
      if ((Trace) && (status > 0)) traceDo ('C', Ts_info[indx].chan, FLUSH,
						    status, buff);
      my_rd_msk = Ts_info[indx].mask;
      tmo = zero_tmo;
      status = select (Ts_info[indx].skt+1,
			    (fd_set *) &my_rd_msk, NULL, NULL, &tmo);
    }
  }
/*
**--------------------------------------------------------------------------*/
  void	closeClient (
/*	===========
**		    Close a client socket and remove the item from the
**		    Cl_info array and compress the rest.
*/
	    int		indx) {	    /* The index of the client to be closed */

    int	    i, j;

    if ((indx < 0) || (indx >= N_clients)) return;
		    /*
		    ** Ensure that the client is not in any active or pending
		    ** list and remove it, if so.
		    */
    for (i = 0; i < N_open_chans; i++) {
      if (Ts_info[i].status != TS_SS_IDLE) {
	if (Ts_info[i].status == indx) {
	  Ts_info[i].status = TS_SS_IDLE;
	  if (Ts_info[i].pending >= 0) {
	    j = Ts_info[i].pending;
	    startUserRequest (j);
	    Ts_info[i].pending = Cl_info[j].pending;  /* Move up pending chain */
	    while (Cl_info[j].pending >= 0) {
	      Cl_info[j].pending = Cl_info[Cl_info[j].pending].pending;
	      j = Cl_info[j].pending;
	    }
	  }
	}
	if (Ts_info[i].pending == indx) {
	  j = Ts_info[i].pending;
	  Ts_info[i].pending = Cl_info[j].pending;
	}
      }
    }
    for (i = 0; i < N_clients; i++) {
      if ((i != indx) && (Cl_info[i].status != CL_SS_IDLE)) {
	if (Cl_info[i].pending == indx) {
	  Cl_info[i].pending = Cl_info[indx].pending;
	}
      }
    }
		    /*---------------------------------------------*/
    close (Cl_info[indx].skt);
    for (i = (indx + 1); i < N_clients; i++) {
      memcpy (Cl_info[i-1].msge, Cl_info[i].msge, MAX_PKT_SIZE);
      memcpy (Cl_info[i-1].rply, Cl_info[i].rply, MAX_PKT_SIZE);
      Cl_info[i-1].status	= Cl_info[i].status;
      memcpy (Cl_info[i-1].host, Cl_info[i].host, sizeof (Cl_info[i].host));
      Cl_info[i-1].port		= Cl_info[i].port;
      Cl_info[i-1].skt		= Cl_info[i].skt;
      Cl_info[i-1].pending	= Cl_info[i].pending;
      if (Cl_info[i-1].pending > indx) Cl_info[i-1].pending -= 1;
      Cl_info[i-1].pend_time	= Cl_info[i].pend_time;
      Cl_info[i-1].msg_len	= Cl_info[i].msg_len;
      Cl_info[i-1].pcol_code	= Cl_info[i].pcol_code;
      strcpy (Cl_info[i-1].fmt_in,  Cl_info[i].fmt_in);
      strcpy (Cl_info[i-1].fmt_out, Cl_info[i].fmt_out);
      Cl_info[i-1].hdr_size	= Cl_info[i].hdr_size;
      Cl_info[i-1].chan		= Cl_info[i].chan;
      Cl_info[i-1].chnx		= Cl_info[i].chnx;
      Cl_info[i-1].tmo		= Cl_info[i].tmo;
      Cl_info[i-1].nterm	= Cl_info[i].nterm;
      memcpy (Cl_info[i-1].terms, Cl_info[i].terms, sizeof (Cl_info[i].terms));
      Cl_info[i-1].n_cmnds	= Cl_info[i].n_cmnds;
      Cl_info[i-1].n_rplys	= Cl_info[i].n_rplys;
      Cl_info[i-1].c_len	= Cl_info[i].c_len;
      Cl_info[i-1].msge_size	= Cl_info[i].msge_size;
      Cl_info[i-1].rply_size	= Cl_info[i].rply_size;
      Cl_info[i-1].remaining	= Cl_info[i].remaining;
      Cl_info[i-1].nxt_cmnd_ptr	= Cl_info[i].nxt_cmnd_ptr -
	    ((char *) Cl_info[i].msge - (char *) Cl_info[i-1].msge);
      Cl_info[i-1].nxt_rply_ptr0 = Cl_info[i].nxt_rply_ptr0 -
	    ((char *) Cl_info[i].rply - (char *) Cl_info[i-1].rply);
      Cl_info[i-1].nxt_rply_ptr1 = Cl_info[i].nxt_rply_ptr1 -
	    ((char *) Cl_info[i].rply - (char *) Cl_info[i-1].rply);
    }
    N_clients--;

    for (i = 0; i < N_open_chans; i++) {
      if (Ts_info[i].pending > indx) Ts_info[i].pending -= 1;
    }
			/*
			** Ensure Max_skt and Skt_mask are up to date
			*/
    FD_ZERO (&Skt_mask); FD_SET (Cnct_skt, &Skt_mask);
    Max_skt = Cnct_skt + 1;
    for (i = 0; i < N_open_chans; i++) {
      FD_SET (Ts_info[i].skt, &Skt_mask);
      if (Ts_info[i].skt >= Max_skt) Max_skt = Ts_info[i].skt + 1;
    }
    for (i = 0; i < N_clients; i++) {
      FD_SET (Cl_info[i].skt, &Skt_mask);
      if (Cl_info[i].skt >= Max_skt) Max_skt = Cl_info[i].skt + 1;
    }
  }
/*
**--------------------------------------------------------------------------*/
  void	closeTs (
/*	=======
**		    Close a socket and remove the item from the
**		    Ts_info array and compress the rest.
*/
	    int		indx) {	    /* The index of the channel to be closed */

    int	    i;

    if ((indx < 0) || (indx >= N_open_chans)) return;

    abortPending (indx);
    close (Ts_info[indx].skt);

    for (i = (indx + 1); i < N_open_chans; i++) {
      Ts_info[i-1].status   = Ts_info[i].status;
      memmove (&Ts_info[i-1].server, &Ts_info[i].server,
			    sizeof (Ts_info[i-1].server));
      memmove (&Ts_info[i-1].dot_addr, &Ts_info[i].dot_addr,
			    sizeof (Ts_info[i-1].dot_addr));
      Ts_info[i-1].chan	      = Ts_info[i].chan;
      Ts_info[i-1].port	      = Ts_info[i].port;
      Ts_info[i-1].skt	      = Ts_info[i].skt;
      memmove(&Ts_info[i-1].mask,&Ts_info[i].mask,sizeof(fd_set));
      memmove (&Ts_info[i-1].tp_ahd, &Ts_info[i].tp_ahd, 
	       sizeof(Ts_info[i].tp_ahd));
      Ts_info[i-1].tp_nxt     = Ts_info[i].tp_nxt;
      Ts_info[i-1].tmo	      = Ts_info[i].tmo;
      Ts_info[i-1].time_stamp = Ts_info[i].time_stamp;
      Ts_info[i-1].pending    = Ts_info[i].pending;
    }
    N_open_chans--;

    for (i = 0; i < N_clients; i++) {
      if (Cl_info[i].chnx > indx) Cl_info[i].chnx -= 1;
    }
			/*
			** Ensure Max_skt and Skt_mask are up to date
			*/
    FD_ZERO (&Skt_mask); FD_SET (Cnct_skt, &Skt_mask);
    Max_skt = Cnct_skt + 1;
    for (i = 0; i < N_open_chans; i++) {
      FD_SET (Ts_info[i].skt, &Skt_mask);
      if (Ts_info[i].skt >= Max_skt) Max_skt = Ts_info[i].skt + 1;
    }
    for (i = 0; i < N_clients; i++) {
      FD_SET (Cl_info[i].skt, &Skt_mask);
      if (Cl_info[i].skt >= Max_skt) Max_skt = Cl_info[i].skt + 1;
    }
  }
/*
**--------------------------------------------------------------------------
**		ctrlC_Handler:  Signal handler to detect <Ctrl-C> on keyboard.
*/
  void	ctrlC_Handler (int sigint) {
/*      =============
*/
    Ctrl_C_has_happened = True;
  }
/*
**--------------------------------------------------------------------------
**		freeAll:  Free all allocated space.
*/
  void	freeAll () {
/*      =======
*/
    int	    i;

    if (Cl_info != NULL) {
      for (i = 0; i < RS__MAX_CLIENTS; i++) {
	if (Cl_info[i].msge != NULL) free (Cl_info[i].msge);
	if (Cl_info[i].rply != NULL) free (Cl_info[i].rply);
	Cl_info[i].msge = NULL; Cl_info[i].rply = NULL;
      }
      free (Cl_info);
      Cl_info = NULL;
    }
    if (Ts_info != NULL) {
      free (Ts_info);
      Ts_info = NULL;
    }
  }
/*
**---------------------------------------------------------------------------
**		    getCurrentTime - get current time
*/
  void	getCurrentTime (
/*	==============
*/		struct timeval	*time_now) {	/* Return value */

#ifdef __VMS
    int	    my_time_now[2];

    sys$gettim (my_time_now);
    time_now->tv_sec  = my_time_now[0];
    time_now->tv_usec = my_time_now[1];
#else
    gettimeofday (time_now, NULL);
#endif
  }
/*
**---------------------------------------------------------------------------*/
  void	handleClientRecv (
/*	================
**			A recv from a client is pending. Handle it.
*/
	    int	    indx) {	 /* In  -- Client index of pending recv */

    int		    i, j, status, max_msg_size;
    int		    r_size, bytes_to_come;
    int		    my_errno, my_vaxc_errno;
    char	    buff[120], *p_nxt_byte;
		/*
		** If there is currently outstanding work for this client,
		** abort it with an error.
		*/
    if (Cl_info[indx].status != CL_SS_IDLE) {
      i = setupErrReply (Cl_info[indx].rply, Cl_info[indx].msge->msg_id,
					"-00000000009");
      status = send (Cl_info[indx].skt, (char *) Cl_info[indx].rply, i, 0);
      Cl_info[indx].status = CL_SS_IDLE;
      if (Trace) traceDo ('S', Cl_info[indx].skt, ERROR,
				16, " sub_status = -9");
    }
		/*
		** Call recv to get the client's message. First get the
		** first 4 bytes to see how much more must be read.
		*/
    max_msg_size = Cl_info[indx].msge_size -
			    sizeof (Cl_info[indx].msge->msg_size);
    status = recv (Cl_info[indx].skt,
                                    Cl_info[indx].msge->msg_size,
                                    sizeof (Cl_info[indx].msge->msg_size), 0);
    if (status != sizeof (Cl_info[indx].msge->msg_size)) {
      GetErrno (&my_errno, &my_vaxc_errno);
      setupTime (buff, sizeof (buff));
      printf ("%s: ", buff);
      if (((status == -1) && (errno == EPIPE)) ||
           (status == 0)) {
        printf ("Socket %d --> %s:%d closed.\n",
			    Cl_info[indx].skt, Cl_info[indx].host, Cl_info[indx].port);
        if (Trace) traceDo ('S', Cl_info[indx].skt, CLOSE, 9, " <Closed>");
      }else if ((status == -1) && (errno == ECONNRESET)) {
        printf ("Socket %d --> %s:%d. Connection reset by peer.\n",
			    Cl_info[indx].skt, Cl_info[indx].host, Cl_info[indx].port);
        if (Trace) traceDo ('S', Cl_info[indx].skt, CLOSE,
				    26, " <ECONNRESET - skt closed>");
      }else if (status == -1) {
        printf ("Socket %d --> %s:%d. recv error.\n",
			    Cl_info[indx].skt, Cl_info[indx].host, Cl_info[indx].port);
	perror ("handleClientRecv: ");
        if (Trace) traceDo ('S', Cl_info[indx].skt, CLOSE,
				    24, " <recv err - skt closed>");
      }else {
        printf ("recv: Did not get full message header on ");
        printf ("socket %d.\n", Cl_info[indx].skt);
        printf ("      Should be: %d. Was: %d. Connection closed.\n",
                                    sizeof (Cl_info[indx].msge->msg_size), status);
        if (Trace) traceDo ('S', Cl_info[indx].skt, ERROR,
					 30, " <Only partial message header>");
      }
      closeClient (indx);
    }else {
      Cl_info[indx].msge->msg_size[status] = NIL;	/* Terminate msg_size */
      if (sscanf (Cl_info[indx].msge->msg_size, "%d",
		    &Cl_info[indx].msg_len) != 1) {
        printf ("recv: Non-decimal ASCII message size on ");
        printf ("socket %d.\n", Cl_info[indx].skt);
        printf ("      Value received = \"%s\". Connection closed.\n",
                                    Cl_info[indx].msge->msg_size);
	closeClient (indx);
      }else if (Cl_info[indx].msg_len == -1) {     /* Close connection? */
        if (Trace) traceDo ('S', Cl_info[indx].skt, IN,
                                          status, Cl_info[indx].msge->msg_size);
        send (Cl_info[indx].skt, "-001", 4, 0);    /* Yes */
        if (Trace) {
	  strcpy (&Cl_info[indx].msge->msg_size[status], " # close connection");
	  traceDo ('S', Cl_info[indx].skt, IN,
			strlen (Cl_info[indx].msge->msg_size),
			Cl_info[indx].msge->msg_size);
	}
        setupTime (buff, sizeof (buff));	/* Yes */
	printf ("%.15s - Closing socket %d to client %s:%d ...", &buff[4],
		    Cl_info[indx].skt, Cl_info[indx].host, Cl_info[indx].port);
	fflush (NULL);
        closeClient (indx);
	printf (" done.\n");
      }else if (Cl_info[indx].msg_len == -2) {     /* Turn trace on? */
        setupTime (buff, sizeof (buff));	/* Yes */
        printf ("%s: ", buff);
        printf ("\"Trace on\" rqst from Socket %d.\n", Cl_info[indx].skt);
        if (Tr_buf == NULL) {
          Tr_buf = malloc (Tr_buf_size);  /* Allocate buf for trace */
          if (Tr_buf != NULL) {
            Tr_buf_len = Tr_buf_size;
          }else {
            printf ("    No buffer space available for tracing!\n");
          }
        }
        if (Tr_buf != NULL) {
          Tr_buf_free = Tr_buf_len;
          Tr_buf_nxt = Tr_buf;
          Tr_buf_shuff = (Tr_buf_size * 25)/100; /* Shuffle buff by 25% */
          Trace = True;
	  StrJoin (buff, sizeof (buff), "Trace on at ", buff);
	  traceAdd (strlen (buff), buff);
          getCurrentTime (&Tr_timer);  /* Initialise trace time stamp */
        }
        if (Trace) {
	  strcpy (&Cl_info[indx].msge->msg_size[status], " # trace on");
	  traceDo ('S', Cl_info[indx].skt, IN,
			strlen (Cl_info[indx].msge->msg_size),
			Cl_info[indx].msge->msg_size);
	}
        send (Cl_info[indx].skt, "-002", 4, 0);
        if (Trace) traceDo ('S', Cl_info[indx].skt, OUT, 4, "-002");
      }else if (Cl_info[indx].msg_len == -3) {     /* Turn trace off? */
        if (Trace) {				/* Yes */
		  strcpy (&Cl_info[indx].msge->msg_size[status], " # trace off");
		  traceDo ('S', Cl_info[indx].skt, IN,
			strlen (Cl_info[indx].msge->msg_size),
			Cl_info[indx].msge->msg_size);
	}
        setupTime (buff, sizeof (buff));
        printf ("%s: ", buff);
        printf ("\"Trace off\" rqst from Socket %d.\n",
                                Cl_info[indx].skt);
        if (Trace) {
          StrJoin (buff, sizeof (buff), "Trace off at ", buff);
          traceAdd (strlen (buff), buff);
          Trace = False;
        }
        fflush (NULL);
        send (Cl_info[indx].skt, "-003", 4, 0);
      }else if (Cl_info[indx].msg_len == -4) {     /* Flush output? */
        if (Trace) {				/* Yes */
	  strcpy (&Cl_info[indx].msge->msg_size[status], " # flush output");
	  traceDo ('S', Cl_info[indx].skt, IN,
			strlen (Cl_info[indx].msge->msg_size),
			Cl_info[indx].msge->msg_size);
	}
        setupTime (buff, sizeof (buff));
        printf ("%s: ", buff);
        printf ("\"Flush rqst\" from Socket %d.\n", Cl_info[indx].skt);
        fflush (NULL);
        if (Trace) traceAdd (7, "Flushed");
        send (Cl_info[indx].skt, "-004", 4, 0);
        if (Trace) traceDo ('S', Cl_info[indx].skt, OUT, 4, "-004");
      }else if (Cl_info[indx].msg_len == -5) {     /* Write trace? */
        if (Trace) {				/* Yes */
	  strcpy (&Cl_info[indx].msge->msg_size[status], " # write trace");
	  traceDo ('S', Cl_info[indx].skt, IN,
			strlen (Cl_info[indx].msge->msg_size),
			Cl_info[indx].msge->msg_size);
	}
        setupTime (buff, sizeof (buff));
        printf ("%.15s: ", &buff[4]);
        printf ("\"Write trace\" rqst from Socket %d ...", Cl_info[indx].skt);
	fflush (NULL);
        traceWrite ();
        printf (" ... done.\n");
        fflush (NULL);
        send (Cl_info[indx].skt, "-005", 4, 0);
        if (Trace) traceDo ('S', Cl_info[indx].skt, OUT, 4, "-005");
      }else if (Cl_info[indx].msg_len == -6) {     /* Close channels? */
        if (Trace) {				/* Yes */
	  strcpy (&Cl_info[indx].msge->msg_size[status], " # close channels");
	  traceDo ('S', Cl_info[indx].skt, IN,
			strlen (Cl_info[indx].msge->msg_size),
	  Cl_info[indx].msge->msg_size);
	}
        setupTime (buff, sizeof (buff));
        printf ("%s: ", buff);
        printf ("\"Close channels\" rqst from Socket %d ...\n",
                                    Cl_info[indx].skt);
	if (N_open_chans <= 0) {
	  printf ("    No RS-232-C channels are open.\n");
	}else {
	  for (i = N_open_chans; i > 0; i--) {
	    if (Trace) traceDo ('C', Ts_info[0].chan, CLOSE,
					    18, " <Close-chan rqst>");
	    printf ("    Closing connection to "
				    "channel %d ...\n", Ts_info[0].chan);
	    if (Ts_info[0].status >= 0) {   /* Is the channel busy? */
					    /* Yes, terminate the work in
					    **   progress */
	      i = Ts_info[0].status;
	      if (Trace) traceDo ('S', Cl_info[i].skt, ERROR,
					31, " <Close-chan> - Work terminated");
	      j = setupErrReply (Cl_info[i].rply,
				Cl_info[i].msge->msg_id, "-00000000011");
	      status = send (Cl_info[i].skt,
                            (char *) Cl_info[i].rply, j, 0);
	      Cl_info[i].status = CL_SS_IDLE;
	      Ts_info[0].status = TS_SS_IDLE;
	    }
	    closeTs (0);
	  }
	  N_open_chans = 0;
	}
        printf ("    ... done.\n");
        send (Cl_info[indx].skt, "-006", 4, 0);
        if (Trace) traceDo ('S', Cl_info[indx].skt, OUT, 4, "-006");
      }else if ((Cl_info[indx].msg_len <= 0) ||
                (Cl_info[indx].msg_len > max_msg_size)) {
        if (Trace) traceDo ('S', Cl_info[indx].skt, IN,
                                          status, Cl_info[indx].msge->msg_size);
        printf ("recv: Illegal pending message size on ");
        printf ("socket %d.\n", Cl_info[indx].skt);
        printf ("    Max is: %d. Received: %d. Connection closed.\n",
                                  max_msg_size, Cl_info[indx].msg_len);
        close (Cl_info[indx].skt);
        if (Trace) traceDo ('S', Cl_info[indx].skt, ERROR,
                                                  14, " <Bad msg hdr>");
        if (Trace) traceDo ('S', Cl_info[indx].skt, CLOSE, 9, " <Closed>");
	closeClient (indx);
      }else {
        bytes_to_come = Cl_info[indx].msg_len;
        p_nxt_byte = Cl_info[indx].msge->msg_id;
        while (bytes_to_come > 0) {
          status = recv (Cl_info[indx].skt, p_nxt_byte, bytes_to_come, 0);
          if (status <= 0) break;
          bytes_to_come -= status;
          p_nxt_byte += status;
        }

        if (bytes_to_come != 0) {
          printf ("recv: Did not get all of expected data on "
                                "socket %d.\n", Cl_info[indx].skt);
          printf ("     Should be: %d. Was: %d. Connection closed.\n",
                Cl_info[indx].msg_len, (Cl_info[indx].msg_len - bytes_to_come));
          if (Trace) traceDo ('S', Cl_info[indx].skt, ERROR,
                                            18, " <bad recv length>");
          status = send (Cl_info[indx].skt, "-001", 4, 0);
          if (Trace) traceDo ('S', Cl_info[indx].skt, OUT, 4, "-001");
	  closeClient (indx);
        }else {
          if (Trace) traceDo ('S', Cl_info[indx].skt, IN,
		       Cl_info[indx].msg_len + 4, Cl_info[indx].msge->msg_size);
          startUserRequest (indx);
        }
      }
    }
  }
/*
**---------------------------------------------------------------------------
**		    handlePendTmo - a pending client request has timed-out.
**				    Handle it.
*/
  void	handlePendTmo (
/*	=============
*/
	    int	    indx) {	 /* Index of timed-out client */

    int	    j, n, status, chnx;

    if (Trace) traceDo ('S', Cl_info[indx].skt, ERROR,
				19, " <pending time-out>");
			/*
			** Send the client an error response.
			*/
    j = setupErrReply (Cl_info[indx].rply, Cl_info[indx].msge->msg_id,
					"-00000000010");
    status = send (Cl_info[indx].skt, (char *) Cl_info[indx].rply, j, 0);
    if (status != j) {
      printf ("send: Bad byte count sent to ");
      printf ("socket %d.\n", Cl_info[indx].skt);
      printf ("      Should have been: %d\n", j);
      printf ("      Was:              %d\n", status);
      if (Trace) traceDo ('S', Cl_info[indx].skt, ERROR,
                                            16, " <bad send size>");
      if (Trace) traceDo ('S', Cl_info[indx].skt, CLOSE,
                                            9, " <Closed>");
      closeClient (indx);
      return;
    }
	    /*
	    ** The channel is now idle.
	    */
    Cl_info[indx].status = CL_SS_IDLE;
	    /*
	    ** Remove it from the channel's pending linked list.
	    */
    chnx = Cl_info[indx].chnx;
    if (Ts_info[chnx].pending == indx) {
      Ts_info[chnx].pending = Cl_info[indx].pending;
    }else {
      j = Ts_info[chnx].pending;
      for (n = 0; ((n < 10) && (Cl_info[j].pending != -1)); n++) {
	if (Cl_info[j].pending == indx) {
	  Cl_info[j].pending = Cl_info[indx].pending;
	  break;
	}
	j = Cl_info[j].pending;
      }
    }
    return;
  }
/*
**---------------------------------------------------------------------------
**		    handleTmo	- a channel has timed-out. Handle it.
*/
  void	handleTmo (
/*	=========
*/
	    int	    indx) {	 /* TS channel index of timed-out channel */

    int	    i, j, cl_x, hdr_size, remaining, status;
    char    *nxt, *nxt_rply_ptr0, *nxt_rply_ptr1, buff[16];

    if (Trace) traceDo ('C', Ts_info[indx].chan, ERROR,
				19, " <channel time-out>");

    cl_x = Ts_info[indx].status;	/* Get index of associated client */

    nxt_rply_ptr0 = Cl_info[cl_x].nxt_rply_ptr0;
    nxt_rply_ptr1 = Cl_info[cl_x].nxt_rply_ptr1;
    hdr_size	  = Cl_info[cl_x].hdr_size;
    remaining	  = Cl_info[cl_x].remaining;
				/*
				** Close off the current cmnd by prefixing
				** it with "?TMO".
				*/
    if (remaining >= 4) {		    /* Room for "?TMO"? */
					    /* Yes. */
      for (nxt = nxt_rply_ptr1; nxt > nxt_rply_ptr0; nxt--) nxt[4] = nxt[0];
      memcpy (&nxt_rply_ptr0[hdr_size+1], "?TMO", 4);
      nxt_rply_ptr1 += 4;
      remaining -= 4;
    }
    *nxt_rply_ptr1++ = NIL; remaining--;          /* Terminate the response */
    j = nxt_rply_ptr1 - nxt_rply_ptr0 - hdr_size;   /* Fill in rply length
						** Note that this also sets
						** terminator to NIL */
    sprintf (nxt_rply_ptr0, Cl_info[cl_x].fmt_out, j);
    nxt_rply_ptr0 = nxt_rply_ptr1;
    nxt_rply_ptr1 = &nxt_rply_ptr0[hdr_size+1];
    remaining -= (hdr_size + 1);
    Cl_info[cl_x].n_cmnds--;
    Cl_info[cl_x].n_rplys++;
				/*
				** Close off all remaining cmnds with "?TMO".
				*/
    for (i = 0; i < Cl_info[cl_x].n_cmnds; i++) {
      if (remaining > 5) {
	strcpy (nxt_rply_ptr1, "?TMO");
	sprintf (nxt_rply_ptr0, Cl_info[cl_x].fmt_out, 6); /* Fill in rply len
						  ** Note that this also sets
						  ** terminator to NIL */
	remaining -= (hdr_size + 6);
	nxt_rply_ptr0 += (hdr_size + 6);
	nxt_rply_ptr1 = &nxt_rply_ptr0[hdr_size+1];
	Cl_info[cl_x].n_rplys++;
      }
    }
		    /*
		    **	    Set up # of replies
		    */
    sprintf (buff, "%04.4d", Cl_info[cl_x].n_rplys); /* Set up # of replies */
    memcpy (Cl_info[cl_x].rply->n_rply, buff, 4);
		    /*
		    **	    Set up reply length
		    */
    j = nxt_rply_ptr0 - Cl_info[cl_x].rply->msg_id;
    j = (j + 3) & (~3);
    sprintf (buff, "%04.4d", j);
    memcpy (Cl_info[cl_x].rply->msg_size, buff, 4);
		    /*
		    **	    Send the reply
		    */
    j += 4;
    status = send (Cl_info[cl_x].skt, (char *) Cl_info[cl_x].rply, j, 0);
    if (status != j) {
      printf ("send: Bad byte count sent to ");
      printf ("socket %d.\n", Cl_info[cl_x].skt);
      printf ("      Should have been: %d\n", j);
      printf ("      Was:              %d\n", status);
      if (Trace) traceDo ('S', Cl_info[cl_x].skt, ERROR,
                                            16, " <bad send size>");
      if (Trace) traceDo ('S', Cl_info[cl_x].skt, CLOSE,
                                            9, " <Closed>");
      closeClient (cl_x);
    }else {
      Cl_info[cl_x].status = CL_SS_IDLE;    /* The client is now idle */
    }
	    /*
	    ** The channel is now idle, see if there are any pending requests
	    */
    Ts_info[indx].status = TS_SS_IDLE;
    if (Ts_info[indx].pending >= 0) {
		/*
		** There is something pending ...
		*/
      cl_x = i = Ts_info[indx].pending;
		/*
		** ... move up the chain of pending requests ...
		*/
      Ts_info[indx].pending = Cl_info[i].pending;
      while (Cl_info[i].pending >= 0) {
	j = Cl_info[i].pending;
	Cl_info[i].pending = Cl_info[j].pending;
	i = j;
      }
		/*
		** ... and start up the pending request.
		*/
      startUserRequest (cl_x);
    }
    return;
  }
/*
**---------------------------------------------------------------------------*/
  void	handleTsRecv (
/*	============
**			A recv from a Terminal Server channel is pending.
**			Handle it.
*/
	    int	    indx) {	 /* In  -- TS channel index of pending recv */

    int		    i, j, cl_x, status, term_fnd, bytes_got, r_size;
    char	    my_term, rcve[512], buff[32], *p_rcve;
			/*
			** If there's anything in type-ahd buff, put this
			** in front of what is about to be received.
			*/
    r_size = sizeof (rcve);
    p_rcve = rcve;
    if (Ts_info[indx].tp_nxt > 0) {
      i = (Ts_info[indx].tp_nxt > r_size) ? r_size : Ts_info[indx].tp_nxt;
      memcpy (p_rcve, Ts_info[indx].tp_ahd, i);
      Ts_info[indx].tp_nxt -= i;
      if (Ts_info[indx].tp_nxt > 0) {
	for (j = 0; j < Ts_info[indx].tp_nxt; j++)
	  Ts_info[indx].tp_ahd[j] = Ts_info[indx].tp_ahd[j+i];
      }
      r_size -= i;
      p_rcve += i;
    }
    if (r_size > 0) {
      j = recv (Ts_info[indx].skt, p_rcve, r_size, 0);
      if (j <= 0) {
		/*
		** An error status was returned by recv. Report the error,
		** shut down any pending work on the channel and close the
		** socket to the Terminal Server.
		*/
	printf ("handleTsRecv: bad recv length: %d\n", j);
	if (Trace) traceDo ('C', Ts_info[indx].chan, ERROR,
				    18, " <Bad recv length>");
	closeTs (indx);		    /* Close the socket to the channel */
	return;
      }
      j += (p_rcve - rcve);
    }
		/*--------------------------------------------------
		** We got some input. See if it's expected and, if so, put it
		** in the buffer for sending to the client. If not, move it
		** to the type-ahead buffer for the channel.
		*/
    i = Ts_info[indx].status;	    /* See if channel is "busy" */
    if (i < 0) {		    /* If < 0, it's not. */
      for (i = 0; i < j; i++) {  /* Channel is not busy, so add to Tp-ahead */
	  addToBuffer (rcve[i], Ts_info[indx].tp_ahd,
		sizeof (Ts_info[0].tp_ahd), &Ts_info[indx].tp_nxt);
      }
      return;
    }else {
      term_fnd = lookForTerm (&bytes_got, &my_term,
                          Cl_info[i].nterm, Cl_info[i].terms, rcve, j);
      if (bytes_got > Cl_info[i].remaining) {  /* If string too long, .. */
	bytes_got = Cl_info[i].remaining; /* .. force "terminator-not-fnd" status */
	term_fnd = False;
	my_term = NIL;
      }
      if (bytes_got > 0) {
	memcpy (Cl_info[i].nxt_rply_ptr1, rcve, bytes_got);
	Cl_info[i].remaining -= bytes_got;
	Cl_info[i].nxt_rply_ptr1 += bytes_got;

	if( (j - bytes_got) > 0){
	  memcpy (Ts_info[indx].tp_ahd, &rcve[bytes_got], (j - bytes_got));
	}
	Ts_info[indx].tp_nxt = j - bytes_got;
      }
      if (term_fnd) {		    /* Response complete? */
	sprintf (Cl_info[i].nxt_rply_ptr0, Cl_info[i].fmt_out,	    /* Yes */
	    (Cl_info[i].nxt_rply_ptr1 -
		Cl_info[i].nxt_rply_ptr0 - Cl_info[i].hdr_size));
	Cl_info[i].nxt_rply_ptr0[Cl_info[i].hdr_size] = my_term;
	Cl_info[i].n_rplys++;
	Cl_info[i].nxt_rply_ptr0 = Cl_info[i].nxt_rply_ptr1;
	Cl_info[i].nxt_rply_ptr1 = Cl_info[i].nxt_rply_ptr0 +
					Cl_info[i].hdr_size + 1;
	Cl_info[i].remaining -= (Cl_info[i].hdr_size + 1);
		    /*
		    ** Move to next cmnd
		    */
	Cl_info[i].nxt_cmnd_ptr += (Cl_info[i].hdr_size + Cl_info[i].c_len);
	Cl_info[i].n_cmnds--;
	if (Cl_info[i].n_cmnds <= 0) {
		    /*
		    ** No more commands, so send reply to client
		    */
	  Cl_info[i].status = CL_SS_IDLE;	/* The client is now idle */
	  Ts_info[indx].status = TS_SS_IDLE;	/* The channel is now idle */
		    /*
		    **	    Set up # of replies
		    */
	  sprintf (buff, "%04.4d", Cl_info[i].n_rplys); /* Set up # of replies */
	  memcpy (Cl_info[i].rply->n_rply, buff, 4);
		    /*
		    **	    Set up reply length
		    */
	  r_size = Cl_info[i].nxt_rply_ptr0 - Cl_info[i].rply->msg_id;
	  r_size = (r_size + 3) & (~3);
	  sprintf (buff, "%04.4d", r_size);
	  memcpy (Cl_info[i].rply->msg_size, buff, 4);
		    /*
		    **	    Send the reply
		    */
	  r_size += 4;
	  status = send (Cl_info[i].skt, (char *) Cl_info[i].rply, r_size, 0);
	  if (status != r_size) {
            printf ("send: Bad byte count sent to ");
            printf ("socket %d.\n", Cl_info[i].skt);
            printf ("      Should have been: %d\n", r_size);
            printf ("      Was:              %d\n", status);
            if (Trace) traceDo ('S', Cl_info[i].skt, ERROR,
                                            16, " <bad send size>");
            if (Trace) traceDo ('S', Cl_info[i].skt, CLOSE,
                                            9, " <Closed>");
            closeClient (i);
	  }
	}else {
	  sendCmndToTs (indx, i);
	}
      }
    }
	    /*
	    ** If the channel is now idle, see if there are any pending requests
	    */
    if ((Ts_info[indx].status == TS_SS_IDLE) &&
	(Ts_info[indx].pending >= 0)) {
		/*
		** There is something pending ...
		*/
      cl_x = i = Ts_info[indx].pending;
		/*
		** ... move up the chain of pending requests ...
		*/
      Ts_info[indx].pending = Cl_info[i].pending;
      while (Cl_info[i].pending >= 0) {
	j = Cl_info[i].pending;
	Cl_info[i].pending = Cl_info[j].pending;
	if(i != j){
		i = j;
	}else {
		break;
	}
      }
		/*
		** ... and start up the pending request.
		*/
      startUserRequest (cl_x);
    }
  }
/*
**---------------------------------------------------------------------------*/
  int	lookForTerm (
/*	===========
**			Look for a terminator. Note that NIL is an allowed
**			terminator so the normal C library routines are not
**			very useful.
**
**			Return value is False if no terminator found.
*/
	    int	    *len,	 /* Out -- The length of string, including
				 **		the terminator (if any) */
	    char    *term,	 /* Out -- The found terminator (if any) */
	    int	     nterm,	 /* In  -- The number of terminators */
	    char    *terms,	 /* In  -- Pntr to string of valid term's */
	    char    *buff,	 /* In  -- Pntr to buffer to search */
	    int	     nch) {	 /* In  -- The number of chars in buff */

    int		 i, j, noTerm = -1, nLen = -1;

    if (nterm <= 0) {
      *len = nch;	    /* No terminator. So return complete string */
      *term = NIL;
      return False;
    }else {			/* Search string for a terminator */
      for (i = 0; i < nch; i++) {
	for (j = 0; j < nterm; j++) {
	   if (buff[i] == terms[j]) {
	      /* replace terminator with NIL */
	      buff[i] = NIL; /* replace terminator with NIL */
              noTerm = j;
              break;
           }
        }
      }
      *len = i;
      if (noTerm >= 0 ) {    /* Was a terminator found? */
	*term = terms[noTerm];	    /* Yes. Return it */
	*len += 1;		    /* And include the term in the length */
	return True;
      }else {
	*term = NIL;		    /* No */
	return False;
      }
    }
  }
/*
**--------------------------------------------------------------------------*/
  int	open_RS232C_Chan (
/*	================
**			See if a socket is already open to an RS-232-C
**			channel on a Terminal Server. If so simply return its
**			data structure index.
**			Otherwise, open a connection to the Terminal Server
**			channel, set up its data structure and return the
**			index to it.
**
**			If the connection fails, return -1.
*/
	    char		   *server,	/* The name of the TS */
	    int			    chan,	/* The channel to open */
	    struct RS__RespStruct  *rply) {	/* Buffer for forming reply --
						**  only an error status is put
						**  in so we don't need to know
						**  its length.
						*/
/*
**    If an error is detected, this routine will set rply->u.sub_status
**    as follows:
**	    "-00000000012" - the terminal server IP address is not known.
**	    "-00000000014" - socket create failed.
**	    "-00000000015" - socket bind failed.
**	    "-00000000016" - socket connect failed.
**	    "-00000000017" - no more indices available.
**------------------------------------------------------------------
*/
    int			 i, status;
    char		 old_time_out[4], buff[80];
    union {
      char  chars[4];
      int   val;
    } time_out;
    unsigned int	 oto_status;
    struct sockaddr_in	 lcl_sockname;
    struct sockaddr_in	 rmt_sockname;
    struct in_addr	 rmt_inet_addr;
    struct in_addr	*rmt_inet_addr_pntr;
    struct hostent	*rmt_hostent;
    int			 rmt_sockname_len;
    char		*ts_dot_addr;
#ifdef  __VMS
    unsigned int    oto_len;
    int		    tcp_option = UCX$C_TCP_PROBE_IDLE;
#else
    int		    oto_len;
  #ifdef LINUX
    int		    tcp_option = 0;
  #else
    int		    tcp_option = TCP_KEEPINIT;
  #endif
#endif
/*------------------------------------------------------------------
**		See if the channel is already open
*/
    for (i = 0; i < N_open_chans; i++) {
      if ((strcmp (server, Ts_info[i].server) == 0) &&
	  (chan == Ts_info[i].chan)) {
	return i;		    /* It is, simply return the index */
      }
    }
/*------------------------------------------------------------------
**		Channel is not open. We must open it.
*/
    setupTime (buff, sizeof (buff));
    printf ("%.15s - Opening channel %d on %s ",
		&buff[4], chan, server); fflush (NULL);

    i = N_open_chans;
    if (i >= MAX_OPEN_CHANS) {
      printf ("\nFailed! --"
	      " exceeded maximum # of open channels (%d).\n", MAX_OPEN_CHANS);
      memcpy (rply->u.sub_status, "-00000000017", 12);
      if (Trace) traceDo ('C', chan, ERROR, 16, " <No more slots>");
      return -1;
    }
		/*
		** Get the Internet address of the Terminal Server
		*/
    rmt_inet_addr.s_addr = inet_addr (server);
    if (rmt_inet_addr.s_addr != -1) {
      rmt_inet_addr_pntr = &rmt_inet_addr;
    }else {
      rmt_hostent = gethostbyname (server);
      if (rmt_hostent == NULL) {
	printf ("\nFailed! --"
	      " Terminal server, \"%s\", is not a known "
			"Internet address.\n", server);
	memcpy (rply->u.sub_status, "-00000000012", 12);
	if (Trace) traceDo ('C', chan, ERROR, 16, " <TS not known>");
	return -1;
      }
      rmt_inet_addr_pntr = (struct in_addr *) rmt_hostent->h_addr_list[0];
    }
    ts_dot_addr = inet_ntoa (*rmt_inet_addr_pntr);
    printf ("(%s) ... ", ts_dot_addr); fflush (NULL);

    Ts_info[i].status  = TS_SS_IDLE;
    Ts_info[i].pending = -1;
    StrJoin (Ts_info[i].server, sizeof (Ts_info[i].server), server, "");
    StrJoin (Ts_info[i].dot_addr, sizeof (Ts_info[i].dot_addr), ts_dot_addr,"");
    Ts_info[i].chan = chan;
    Ts_info[i].tp_nxt = 0;	    /* Empty type-ah'd buffer */
	    /*---------------------------
	    ** Get the Internet port number of the device on the Terminal Server
	    */
    Ts_info[i].port = 3000 + chan;
	    /*---------------------------
	    ** Create a TCP/IP socket for connecting to server and bind it.
	    */
    Ts_info[i].skt = socket (AF_INET, SOCK_STREAM, 0);
    if (Ts_info[i].skt <= 0) {
      printf ("failed!\n\n"
	      "   Could not create a socket.\n\n");
      perror ("open_RS232C_Chan");
      memcpy (rply->u.sub_status, "-00000000014", 12);
      if (Trace) traceDo ('C', chan, ERROR, 16, " <socket error>");
      return -1;
    }
    lcl_sockname.sin_family = AF_INET;
    lcl_sockname.sin_port   = htons (0);
    lcl_sockname.sin_addr.s_addr = 0;
    status = bind (Ts_info[i].skt, (struct sockaddr *) &lcl_sockname,
					sizeof (lcl_sockname));
    if (status == -1) {
      close (Ts_info[i].skt);
      printf ("failed!\n\n"
	      "   Could not bind the socket.\n\n");
      perror ("open_RS232C_Chan");
      memcpy (rply->u.sub_status, "-00000000015", 12);
      if (Trace) traceDo ('C', chan, ERROR, 16, " <bind error>");
      return -1;
    }
		/*---------------------------
		** Set short time-out
		*/
    oto_len = sizeof (old_time_out);	/* Save current time-out first */
    oto_status = getsockopt (Ts_info[i].skt, IPPROTO_TCP, tcp_option,
				old_time_out, &oto_len);
    if (oto_status == 0) {
      time_out.val = 5;			/* Set new time-out */
      status = setsockopt (Ts_info[i].skt, IPPROTO_TCP, tcp_option,
			    time_out.chars, sizeof (time_out));
    }
		/*---------------------------
		** Connect to Terminal Server
		*/
    rmt_sockname_len = sizeof (rmt_sockname);
    rmt_sockname.sin_family	 = AF_INET;
    rmt_sockname.sin_port	 = htons (Ts_info[i].port);
    rmt_sockname.sin_addr.s_addr = rmt_inet_addr_pntr->s_addr;
    status = connect (Ts_info[i].skt, (struct sockaddr *) &rmt_sockname,
				    sizeof (rmt_sockname));
    if (status != 0) {
      close (Ts_info[i].skt);
      printf ("failed!\n\n"
	      "   Could not connect to Terminal Server %s, Port %d.\n\n",
		    server, Ts_info[i].port);
      perror ("open_RS232C_Chan");
      memcpy (rply->u.sub_status, "-00000000016", 12);
      if (Trace) traceDo ('C', chan, ERROR, 16, " <connect error>");
      return -1;
    }
		/*---------------------------
		** Restore time-out
		*/
    if (oto_status == 0) {
      setsockopt (Ts_info[i].skt, IPPROTO_TCP, tcp_option,
			    old_time_out, oto_len);
    }
    printf (" done.\n");

    FD_ZERO (&Ts_info[i].mask); FD_SET (Ts_info[i].skt, &Ts_info[i].mask);
    FD_SET (Ts_info[i].skt, &Skt_mask);
    if (Ts_info[i].skt >= Max_skt) Max_skt = Ts_info[i].skt + 1;
    N_open_chans++;

    if (Trace) {
      sprintf (buff, " <Connected to chan %d via socket %d:%d>",
			chan, Ts_info[i].skt, Ts_info[i].port);
      traceDo ('C', chan, OPEN, strlen (buff), buff);
    }
    return i;
  }
/*
**---------------------------------------------------------------------------
**			sendCmndToTs	- send a command to the Terminal Server
*/
  void	sendCmndToTs (
/*	============
*/		int	    ts_indx,	/* Term srvr index */
		int	    cl_indx) {	/* Client index */

    int		i, c_len, status, term_fnd, bytes_got, r_size, hdr_size;
    char	my_term, buff[32], *fmt_in, *fmt_out;
    char	*nxt_cmnd_ptr, *nxt_rply_ptr0, *nxt_rply_ptr1;

    hdr_size	= Cl_info[cl_indx].hdr_size;
    fmt_in	= Cl_info[cl_indx].fmt_in;
    fmt_out	= Cl_info[cl_indx].fmt_out;
    nxt_cmnd_ptr    = Cl_info[cl_indx].nxt_cmnd_ptr;
    nxt_rply_ptr0   = Cl_info[cl_indx].nxt_rply_ptr0;
    nxt_rply_ptr1   = Cl_info[cl_indx].nxt_rply_ptr1;

    getCurrentTime (&Ts_info[ts_indx].time_stamp);
    sscanf (nxt_cmnd_ptr, fmt_in, &c_len);
    Cl_info[cl_indx].c_len = c_len;
    if (c_len > 0) {		/* If there is a command to send, .. */
      chanFlush (ts_indx);	/* .. flush the input first. */
      status = send (Ts_info[ts_indx].skt, &nxt_cmnd_ptr[hdr_size], c_len, 0);
      if (status != c_len) {
	if (status == -1) perror ("sendCmndToTs/send");
	printf ("sendCmndToTs: bad send length on channel %d: %d %d\n",
                          Cl_info[cl_indx].chan, c_len, status);
        memcpy (Cl_info[cl_indx].rply->u.sub_status, "-00000000006", 12);
	send (Cl_info[cl_indx].skt, (char *) Cl_info[cl_indx].rply, 28, 0);
        if (Trace) traceDo ('C', Cl_info[cl_indx].chan, ERROR,
				34, " <bad send length. Channel closed>");
	closeTs (ts_indx);
        if (Trace) traceDo ('C', Cl_info[cl_indx].chan, CLOSE, 9, " <Closed>");
	Cl_info[cl_indx].status = CL_SS_IDLE;
        return;
      }
    }
		/*
		** If there is anything in the typ-ahead buf, transfer it
		** to the client's buffer.
		*/
    if (Ts_info[ts_indx].tp_nxt > 0) {
      term_fnd = lookForTerm (&bytes_got, &my_term,
			Cl_info[cl_indx].nterm, Cl_info[cl_indx].terms,
			Ts_info[ts_indx].tp_ahd, Ts_info[ts_indx].tp_nxt);
      if (bytes_got > Cl_info[cl_indx].remaining) {  /* If string too long, .. */
	bytes_got = Cl_info[cl_indx].remaining; /* .. force "terminator-not-fnd" status */
	term_fnd = False;
	my_term = NIL;
      }
      memcpy (nxt_rply_ptr1, Ts_info[ts_indx].tp_ahd, bytes_got);
      Cl_info[cl_indx].remaining -= bytes_got;
      nxt_rply_ptr1 += bytes_got;

      Ts_info[ts_indx].tp_nxt -= bytes_got;
      if (Ts_info[ts_indx].tp_nxt > 0) {
	for (i = 0; i < Ts_info[ts_indx].tp_nxt; i++)
	  Ts_info[ts_indx].tp_ahd[i] = Ts_info[ts_indx].tp_ahd[i+bytes_got];
      }
      if (term_fnd) {		    /* Response complete? */
	sprintf (nxt_rply_ptr0, fmt_out,   /* Yes */
	    (nxt_rply_ptr1 -
		nxt_rply_ptr0 - hdr_size));
	nxt_rply_ptr0[hdr_size] = my_term;
	Cl_info[cl_indx].n_rplys++;
	nxt_rply_ptr0 = nxt_rply_ptr1;
	nxt_rply_ptr1 = &nxt_rply_ptr0[hdr_size+1];
	Cl_info[cl_indx].remaining -= (hdr_size + 1);
		    /*
		    ** Move to next cmnd
		    */
	nxt_cmnd_ptr += (hdr_size + Cl_info[cl_indx].c_len);
	Cl_info[cl_indx].n_cmnds--;
	if (Cl_info[cl_indx].n_cmnds <= 0) {
		    /*
		    ** No more commands, so send reply to client
		    */
	  Ts_info[ts_indx].status = TS_SS_IDLE;	/* The channel is now idle */
	  Cl_info[cl_indx].status = CL_SS_IDLE;	/* The client is now idle */
		    /*
		    **	    Set up # of replies
		    */
	  sprintf (buff, "%04.4d", Cl_info[cl_indx].n_rplys); /* Set up # of replies */
	  memcpy (Cl_info[cl_indx].rply->n_rply, buff, 4);
		    /*
		    **	    Set up reply length
		    */
	  r_size = nxt_rply_ptr0 - Cl_info[cl_indx].rply->msg_id;
	  r_size = (r_size + 3) & (~3);
	  sprintf (buff, "%04.4d", r_size);
	  memcpy (Cl_info[cl_indx].rply->msg_size, buff, 4);
		    /*
		    **	    Send the reply
		    */
	  r_size += 4;
	  status = send (Cl_info[cl_indx].skt, (char *) Cl_info[cl_indx].rply,
							r_size, 0);
	  if (status != r_size) {
            printf ("send: Bad byte count sent to ");
            printf ("socket %d.\n", Cl_info[cl_indx].skt);
            printf ("      Should have been: %d\n", r_size);
            printf ("      Was:              %d\n", status);
            if (Trace) traceDo ('S', Cl_info[cl_indx].skt, ERROR,
                                            16, " <bad send size>");
            if (Trace) traceDo ('S', Cl_info[cl_indx].skt, CLOSE,
                                            9, " <Closed>");
            closeClient (cl_indx);
	  }
	  Ts_info[ts_indx].status = TS_SS_IDLE;
	  Cl_info[cl_indx].status = CL_SS_IDLE;
	  return;
	}else {
	  sendCmndToTs (ts_indx, cl_indx);
	}
      }
    }
    Cl_info[cl_indx].nxt_cmnd_ptr  = nxt_cmnd_ptr;
    Cl_info[cl_indx].nxt_rply_ptr0 = nxt_rply_ptr0;
    Cl_info[cl_indx].nxt_rply_ptr1 =  nxt_rply_ptr1;

    if (Ts_info[ts_indx].tmo < Next_tmo_secs)
      Next_tmo_secs = Ts_info[ts_indx].tmo;

    Ts_info[ts_indx].status = cl_indx;
    Cl_info[cl_indx].status = CL_SS_BUSY;
    return;
  }
/*
**---------------------------------------------------------------------------
**			setupErrReply	- set up a reply with an error status
*/
  int	setupErrReply (
/*	=============
*/		struct RS__RespStruct *rply,
		char		      *msg_id,
		char		      *sub_status) {

    strcpy (rply->msg_size,     "0024");
    memcpy (rply->msg_id,	msg_id, sizeof (rply->msg_id));
    strcpy (rply->s_pcol_lvl,   RS__PROTOCOL_ID_V01B);
    strcpy (rply->n_rply,	"-001");
    memcpy (rply->u.sub_status, sub_status, sizeof (rply->u.sub_status));
    return 28;
  }
/*
**---------------------------------------------------------------------------
**			setupNewClient	- accept a new client connection
*/
  void	setupNewClient (int skt) {
/*	==============
*/
    int			rw_skt;
    char		*rmt_host;
    struct hostent	*rmt_host_name;
    int			 rmt_port;
    struct sockaddr_in	rmt_sockname;
    char		buff[120];
/*------------------------------------------------
**		Note:
**		    There seems to be an inconsistency between the prototype
**		    definitions of the accept function for VMS and Digital Unix.
**		    This conditional code is a hack to avoid error messages from
**		    the compiler.
*/
#ifdef	__VMS
    unsigned int	rmt_sockname_len;
#else
    int			rmt_sockname_len;
#endif
/*------------------------------------------------*/
    rmt_sockname_len = sizeof (rmt_sockname);
    rw_skt = accept (Cnct_skt, (struct sockaddr *) &rmt_sockname,
					    &rmt_sockname_len);
    if (rw_skt <= 0) {
	    FailInet ("SerPortServer: accept error\n");
    }
    rmt_host = inet_ntoa (rmt_sockname.sin_addr);
    rmt_port = ntohs (rmt_sockname.sin_port);
    rmt_host_name = gethostbyaddr ((char *) &rmt_sockname.sin_addr,
				sizeof (rmt_sockname.sin_addr), AF_INET);
    if (N_clients < RS__MAX_CLIENTS) {
      Cl_info[N_clients].status = CL_SS_IDLE;
      StrJoin (Cl_info[N_clients].host, sizeof Cl_info[N_clients].host,
		"", (rmt_host_name != NULL) ? rmt_host_name->h_name : rmt_host);
      Cl_info[N_clients].port    = rmt_port;
      Cl_info[N_clients].skt     = rw_skt;
      Cl_info[N_clients].pending = -1;
      FD_SET (rw_skt, &Skt_mask);
      if (rw_skt >= Max_skt) Max_skt = rw_skt + 1;
      setupTime (buff, sizeof (buff));
      printf ("%.15s - Connected to client %s:%d via socket %d.\n",
			  &buff[4], Cl_info[N_clients].host, rmt_port, rw_skt);
      if (Trace) {
	sprintf (buff, " <Connected to %s:%d>",
			    Cl_info[N_clients].host, rmt_port);
	traceDo ('S', rw_skt, OPEN, strlen (buff), buff);
      }
      N_clients++;
    }else {
      printf ("accept: Connection to %s:%d rejected.\n",
			(rmt_host_name != NULL) ? rmt_host_name->h_name :
						  rmt_host,
			rmt_port);
      printf ("        Client slots exhausted.\n");
      close (rw_skt);
          if (Trace) traceDo ('S', Cnct_skt, ERROR, 16, " <No more slots>");
    }
  }
/*
**---------------------------------------------------------------------------
**			setupSocket	- declare ourselves as TCP/IP server
*/
  int	setupSocket (int port) {
/*	===========
*/
    int	    status;
    int	    my_skt;			/* listener Socket */
    int	    one = 1;
    struct sockaddr_in	lcl_sockname;

	/*===============================================================
	**	Create a TCP/IP socket for receiving connection requests and
	**			bind it and set it to listen.
	*/
    my_skt = socket (AF_INET, SOCK_STREAM, 0);
    if (my_skt == -1) FailInet ("SerPortServer -- socket error\n");
	/*
	**	Set the socket option to ease rapid re-starting of SerPortServer
	*/
    status = setsockopt (my_skt, SOL_SOCKET, SO_REUSEADDR,
			    (char *) &one, sizeof (one));

    lcl_sockname.sin_family = AF_INET;
    lcl_sockname.sin_port   = htons (port);
    lcl_sockname.sin_addr.s_addr = 0;
    status = bind (my_skt, (struct sockaddr *) &lcl_sockname,
				    sizeof (lcl_sockname));
    if (status != 0) FailInet ("SerPortServer -- bind error\n");

    status = listen (my_skt, 5);
    if (status != 0) FailInet ("SerPortServer -- listen error\n");

    return my_skt;
  }
/*
**---------------------------------------------------------------------------
**			setupTime	- get the current time in ASCII
*/
  char	*setupTime (char *buff, int buff_size) {
/*	==========
*/
    time_t	    time_now;

    time_now = time (NULL);	/* Get the current time */
    StrJoin (buff, buff_size,	/* Convert to ASCII and copy to buffer */
		asctime (localtime (&time_now)), "");
    strtok (buff, "\n");	/* Remove the trailing "\n" character. */
    return buff;
  }
/*
**---------------------------------------------------------------------------
**			setupXrmDatabase    - setup Resource Manager Database
*/
  int	setupXrmDatabase (
/*	================
*/			XrmDatabase	*db,
			char		*name[],
			int		*argc,
			char		*argv[]) {

    static XrmOptionDescRec opTable[] = {
      {"-name",	   ".name",	        XrmoptionSepArg, (XPointer) NULL},
      {"-ts",	   ".serPortTsName",    XrmoptionSepArg, (XPointer) NULL},
      {"-port",	   ".serPortServPort",  XrmoptionSepArg, (XPointer) NULL},
      {"-trace",   ".serPortTrace",     XrmoptionNoArg,  (XPointer) "1"},
      {"-tsize",   ".serPortTraceSize", XrmoptionSepArg, (XPointer) NULL},
      {"-period",  ".serPortPeriod",    XrmoptionSepArg, (XPointer) NULL},
      {"-debug",   ".serPortDebug",	XrmoptionNoArg,  (XPointer) "1"},
      {"-max",     ".serPortMax",	XrmoptionSepArg, (XPointer) NULL},
    };

    static char	our_name[80] = "Unknown";   /* This holds the program name */

    int		status, i;
    char	text[80];
    char	full_nm0[80];
    char	full_nm1[80];
    char	*p_fil[10];
    char	*my_name;
    char	*my_name_last;

    char	*name_type;
    XrmValue	name_value;

    XrmDatabase cmnd_line_db = NULL;
    XrmDatabase	lcl_db	     = NULL;
  /*-----------------------------------------------------*/
    status = True;		/* Assume success	*/

    our_name[0] = NIL;
	/*
	** Make a list of the databases to be merged, highest priority first.
	*/
#ifdef  __VMS
    p_fil[0] = "decw$user_defaults:SinQ_rc.dat";
    p_fil[1] = "decw$group_defaults:SinQ_rc.dat";
    p_fil[2] = "decw$system_defaults:SinQ_rc.dat";
    p_fil[3] = "mad_dflt:SinQ_rc.dat";
    p_fil[4] = "decw$user_defaults:decw$xdefaults.dat";
    p_fil[5] = "decw$group_defaults:decw$xdefaults.dat";
    p_fil[6] = "decw$system_defaults:decw$xdefaults.dat";
    p_fil[7] = "mad_dflt:decw$xdefaults.dat";
    p_fil[8] = NULL;
    if (*argc > 0) {	     /* Find out what we are called - parse file nm */
      my_name = strstr (argv[0], "]");	/* Find end of directory, i.e. "]" */
      my_name++;			/* Skip over "]" */
      if (my_name[0] == '[') {		/* Handle possible concealed device */
        my_name = strstr (my_name, "]");
	my_name++;
      }
      StrJoin (our_name, sizeof (our_name), my_name, "");   /* Copy the rest */
      strtok (our_name, ".");		/* Close it off at "." */
    }
    for (i = 0; our_name[i] != 0; i++) our_name[i] = tolower (our_name[i]);
#else
    p_fil[0] = StrJoin (full_nm0, sizeof (full_nm0),
			    getenv ("HOME"), "/SinQ_rc");
    p_fil[1] = "/usr/lib/X11/app-defaults/SinQ_rc";
    p_fil[2] = StrJoin (full_nm1, sizeof (full_nm1),
			    getenv ("HOME"), "/.Xdefaults");
    p_fil[3] = "/usr/lib/X11/app-defaults/Xdefaults";
    p_fil[4] = NULL;
    if (*argc > 0) {	      /* Find out what we are called - parse file nm  */
			      /* Find end of directories */
      my_name = argv[0] - 1;
      while (my_name != NULL) {
        my_name_last = my_name;
	my_name_last++;
	my_name = strstr (my_name_last, "/");
      }
      StrJoin (our_name, sizeof (our_name), my_name_last, "");
    }
#endif
	/*
	**  Initialise and combine all databases
	*/
    XrmInitialize ();
    i = 0;
    while (p_fil[i] != NULL) {
      status = XrmCombineFileDatabase (p_fil[i], &lcl_db, False);
      i++;
    }
	/*
	**  See if there's anything specified on cmnd line, incl "name".
	*/
    XrmParseCommand (&cmnd_line_db,
			opTable, XtNumber(opTable), our_name, argc, argv);
    if (cmnd_line_db != NULL) {
	    /*
	    ** There was at least 1 item on cmnd line. Process the line.
	    ** If -name was specified, adopt it.
	    */
      status = XrmGetResource (cmnd_line_db,  /* See if a name was specified  */
			    StrJoin (text, sizeof (text), our_name, ".name"),
			    "ProgramName.Values",
			    &name_type, &name_value);
      if (status) {
        StrJoin (our_name, sizeof (our_name), name_value.addr, "");
        for (i = 0; our_name[i] != 0; i++) our_name[i] = tolower (our_name[i]);
        printf ("Option list name is \"%s\"\n", our_name);
      }
	    /*
	    ** Loop over all items in opTable and merge them into database,
	    ** taking care of any possible name changes.
	    */
      for (i = 0; i < XtNumber (opTable); i++) {
	if (strcmp (opTable[i].option, "-name") == 0) continue;
        status = XrmGetResource (cmnd_line_db,
				StrJoin (text, sizeof (text),
				    our_name, opTable[i].specifier),
				"ProgramName.Values",
				&name_type, &name_value);
	if (status) {
	  StrJoin (text, sizeof (text), our_name, opTable[i].specifier);
	  XrmPutResource (&lcl_db, text, "String", &name_value);
	}
      }
    }
    *name = our_name;
    *db = lcl_db;
    if (lcl_db == NULL) printf ("Warning -- no resource database found.\n");

    XrmDestroyDatabase (cmnd_line_db);

    return status;
  }
/*
**---------------------------------------------------------------------------
**			startUserRequest	- respond to user
*/
  void	startUserRequest (
/*	================
**				Start the handling of a user request.
*/
	    int		indx) {	    /* The index of the client */
	/*
	** Return value is CL_SS_IDLE if the request has been handled
	** completely. Otherwise, the first command of the user's request
	** will have been sent to the specified channel and the various
	** data structures set-up so that the command will continue to be
	** processed in the "select" loop.
	*/
    int			status, i, rply_size, c_len, chnx;
    char		*nxt_cmnd_ptr;
    char		zero[] = {0, 0, 0, 0};

    setupErrReply (Cl_info[indx].rply,	    /* Set up a null error reply */
			Cl_info[indx].msge->msg_id,
			"000000000000");
		/*----------------------------------------------
		** Check protocol level of request. If all zero, give error
		** response silently (AsynSrv_Open is probably trying to
		** establish our level). Otherwise, it must be recognised.
		*/
    if (memcmp (Cl_info[indx].msge->c_pcol_lvl, &zero, 4) == 0) {
      memcpy (Cl_info[indx].rply->n_rply, "0000", 4); /* Protocol level is null.
					    ** Return "bad protocol level" err.
					    */
      memcpy (Cl_info[indx].rply->u.sub_status, "000000005001", 12);
      send (Cl_info[indx].skt, (char *) Cl_info[indx].rply, 28, 0);
      Cl_info[indx].status = CL_SS_IDLE; return;
    }else if (memcmp (Cl_info[indx].msge->c_pcol_lvl,
			RS__PROTOCOL_ID_V01B, 4) == 0) {
      Cl_info[indx].pcol_code = RS__PROTOCOL_CODE_V01B;
      strcpy (Cl_info[indx].fmt_in, "%4d");
      strcpy (Cl_info[indx].fmt_out, "%4.4d");
      Cl_info[indx].hdr_size = 4;
    }else if (memcmp (Cl_info[indx].msge->c_pcol_lvl, RS__PROTOCOL_ID, 4) == 0) {
      Cl_info[indx].pcol_code = RS__PROTOCOL_CODE;
      strcpy (Cl_info[indx].fmt_in, "%2d");
      strcpy (Cl_info[indx].fmt_out, "%2.2d");
      Cl_info[indx].hdr_size = 2;
    }else {
      printf ("startUserRequest -- bad protocol level: \"%.4s\"\n",
		    Cl_info[indx].msge->c_pcol_lvl);
      memcpy (Cl_info[indx].rply->u.sub_status, "000000005001", 12);
      send (Cl_info[indx].skt, (char *) Cl_info[indx].rply, 28, 0);
      Cl_info[indx].status = CL_SS_IDLE; return;
    }
    nxt_cmnd_ptr = Cl_info[indx].nxt_cmnd_ptr =
			(char *) &Cl_info[indx].msge->cmnds;
    Cl_info[indx].nxt_rply_ptr0 = (char *) Cl_info[indx].rply->u.rplys;
    Cl_info[indx].nxt_rply_ptr1 = Cl_info[indx].nxt_rply_ptr0 +
				    Cl_info[indx].hdr_size + 1;
    Cl_info[indx].remaining = Cl_info[indx].rply_size -
	    (Cl_info[indx].nxt_rply_ptr1 - &Cl_info[indx].rply->msg_size[0]);
    Cl_info[indx].n_rplys = 0;
		/*----------------------------------------------
		** Protocol level seems OK. Continue decyphering the message
		*/
    status = sscanf (Cl_info[indx].msge->serial_port, "%4d",
			&Cl_info[indx].chan);
    if ((status != 1) ||
	(Cl_info[indx].chan < 0) ||
	(Cl_info[indx].chan >= RS__MAX_ASYNCH)) {
      printf ("startUserRequest: Bad Cl_info[indx].msge->serial_port.\n");
      if (Trace) traceDo ('S', Cl_info[indx].skt, ERROR,
			    16, " <bad channel #>");
      memcpy (Cl_info[indx].rply->u.sub_status, "-00000000008", 12);
      send (Cl_info[indx].skt, (char *) Cl_info[indx].rply, 28, 0);
      Cl_info[indx].status = CL_SS_IDLE; return;
    }

    if (sscanf (Cl_info[indx].msge->tmo, "%4d", &i) != 1) i = 50;
    if (i < 0) i = 0;
    Cl_info[indx].tmo = ((float) i)/10.0;

    switch (Cl_info[indx].msge->terms[0]) {
     case '0': Cl_info[indx].nterm = 0; break;
     case '1': Cl_info[indx].nterm = 1; break;
     case '2': Cl_info[indx].nterm = 2; break;
     case '3': Cl_info[indx].nterm = 3; break;
     default:  Cl_info[indx].nterm = 0;
    }
    if (Cl_info[indx].nterm > 0) memcpy (Cl_info[indx].terms,
			&Cl_info[indx].msge->terms[1], Cl_info[indx].nterm);
    Cl_info[indx].terms[Cl_info[indx].nterm] = NIL;

    if (sscanf (Cl_info[indx].msge->n_cmnds, "%4d",
			    &Cl_info[indx].n_cmnds) != 1) {
      printf ("startUserRequest: Non-decimal ASCII "
		    "Cl_info[indx].msge->n_cmnds.\n");
      memcpy (Cl_info[indx].rply->u.sub_status, "-00000000001", 12);
      send (Cl_info[indx].skt, (char *) Cl_info[indx].rply, 28, 0);
      if (Trace) traceDo ('S', Cl_info[indx].skt, ERROR,
			    14, " <bad n_cmnds>");
      Cl_info[indx].status = CL_SS_IDLE; return;
    }else if (Cl_info[indx].n_cmnds == 0) {
      memcpy (Cl_info[indx].rply->n_rply, "0000", 4);	    /* Nothing to do!! */
      send (Cl_info[indx].skt, (char *) Cl_info[indx].rply, 28, 0);
      Cl_info[indx].status = CL_SS_IDLE; return;
    }else if (Cl_info[indx].n_cmnds < 0) {
      printf ("startUserRequest: Illegal Cl_info[indx].msge->n_cmnds: %d\n",
		    Cl_info[indx].n_cmnds);
      memcpy (Cl_info[indx].rply->u.sub_status, "-00000000002", 12);
      send (Cl_info[indx].skt, (char *) Cl_info[indx].rply, 28, 0);
      if (Trace) traceDo ('S', Cl_info[indx].skt, ERROR,
			    14, " <bad n_cmnds>");
      Cl_info[indx].status = CL_SS_IDLE; return;
    }
		/*
		** Check the structure of the command list
		*/
    for (i = 0; i < Cl_info[indx].n_cmnds; i++) {
      if ((nxt_cmnd_ptr - Cl_info[indx].msge->msg_id) >=
		Cl_info[indx].msg_len) {
        printf ("startUserRequest: runaway command list.\n");
        memcpy (Cl_info[indx].rply->u.sub_status, "-00000000003", 12);
	send (Cl_info[indx].skt, (char *) Cl_info[indx].rply, 28, 0);
	if (Trace) traceDo ('S', Cl_info[indx].skt, ERROR,
			    20, " <runaway cmnd list>");
	Cl_info[indx].status = CL_SS_IDLE; return;
      }
      if (sscanf (nxt_cmnd_ptr, Cl_info[indx].fmt_in, &c_len) != 1) {
        printf ("startUserRequest: Non-decimal ASCII command length.\n");
        memcpy (Cl_info[indx].rply->u.sub_status, "-00000000004", 12);
	send (Cl_info[indx].skt, (char *) Cl_info[indx].rply, 28, 0);
	if (Trace) traceDo ('S', Cl_info[indx].skt, ERROR,
			    15, " <bad cmnd len>");
	Cl_info[indx].status = CL_SS_IDLE; return;
      }
      if ((c_len <  0) || (c_len >= Cl_info[indx].msg_len)) { 
        printf ("startUserRequest: illegal command length: %d\n", c_len);
        memcpy (Cl_info[indx].rply->u.sub_status, "-00000000005", 12);
	send (Cl_info[indx].skt, (char *) Cl_info[indx].rply, 28, 0);
	if (Trace) traceDo ('S', Cl_info[indx].skt, ERROR,
			    16, " <illg cmnd len>");
	Cl_info[indx].status = CL_SS_IDLE; return;
      }
      nxt_cmnd_ptr += (Cl_info[indx].hdr_size + c_len); /* Move to next cmnd */
    }
		/*
		** The list of commands seems to be OK. Start processing them.
		** Find the index of the data structure for the channel. If
		** necessary, a socket will be opened.
		*/
    chnx = open_RS232C_Chan (Ts_name, Cl_info[indx].chan, Cl_info[indx].rply);
    if (chnx < 0) {
      send (Cl_info[indx].skt, (char *) Cl_info[indx].rply, 28, 0);
      if (Trace) traceDo ('S', Cl_info[indx].skt, ERROR,
			    23, " <bad open_RS232C_Chan>");
      Cl_info[indx].status = CL_SS_IDLE; return;
    }
    Cl_info[indx].chnx = chnx;
    if (Ts_info[chnx].status == TS_SS_IDLE) {	/* If channel is idle ... */
      Ts_info[chnx].tmo  = Cl_info[indx].tmo;	/* .. start processing the .. */
      sendCmndToTs (chnx, indx);		/* .. commands. */
    }else {
      addToPendList (chnx, indx);	    /* Otherwise, pend the request */
    }
  }
/*
**---------------------------------------------------------------------------
**			subtractTimes	- subtract two times. Return value
**					    is time difference in seconds.
*/
  float	subtractTimes (
/*	=============
*/		struct timeval	time0,	    /* The first time */
		struct timeval	time1) {    /* The time to be subtracted */

	/*
	** Under UNIX, time0 & time1 contain the time of day in secs/microsecs
	** since 1970.
	*/
    int	    diff_secs, diff_usec;
    float   val;

#ifdef __VMS
    int	    my_time0[2], my_time1[2], deltaTime[2];

    my_time0[0] = time0.tv_sec;
    my_time0[1] = time0.tv_usec;

    my_time1[0] = time1.tv_sec;
    my_time1[1] = time1.tv_usec;

    lib$sub_times (my_time0, my_time1, deltaTime);
    lib$cvtf_from_internal_time (&LIB$K_DELTA_SECONDS_F, &val, deltaTime);
#else
    if (time0.tv_sec > time1.tv_sec) {		/* Is delta-Time positive? */
      diff_secs = time0.tv_sec  - time1.tv_sec;	/* Yes */
      diff_usec = time0.tv_usec - time1.tv_usec;
      if (diff_usec < 0) {diff_secs -= 1; diff_usec += 1000000;}
    }else if (time0.tv_sec == time1.tv_sec) { /* Maybe - anyway, it's < 1sec */
      diff_secs = 0; diff_usec = time0.tv_usec - time1.tv_usec;
      if (diff_usec < 0) {diff_secs = -1; diff_usec += 1000000;}
    }else {					/* delta-Time is negative */
      diff_secs = time0.tv_sec  - time1.tv_sec;
      diff_usec = time0.tv_usec - time1.tv_usec;
      if (diff_usec < 0) {diff_secs -= 1; diff_usec += 1000000;}
    }
    val = (float) diff_usec; val /= 1000000;
    val += (float) diff_secs;
#endif
    return val;
  }
/*
**---------------------------------------------------------------------------
**			    Add a trace message to the trace buffer
*/
  void	traceAdd (
/*	========
*/		int	n_chars,    /* # chars in buffer */
		char	*buf) {	    /* The buffer of chars */

    int		    i = n_chars, j;
    unsigned char  *nxt;

    if (Tr_buf == NULL) return;

    if (i > 252) i = 252;	/* Ensure length fits in 1 byte */

    if (i >= (Tr_buf_free - 2)) {	/* Must we shuffle the buffer? */
      nxt = Tr_buf;		/* Yes */
      j = 0;
      while ((*nxt != 0)&&(j < Tr_buf_shuff)) {j += *nxt + 1; nxt += *nxt + 1;}
      memmove (Tr_buf, nxt, (Tr_buf_len - j));
      Tr_buf_free += j;
      Tr_buf_nxt -= j;
      if (i >= Tr_buf_free) {	/* Double check, in case trace buf is small! */
        printf ("Trace buffer shuffle problems, trace turned off!\n");
        Trace = False; fflush (NULL); return;
      }
    }
    memcpy (&Tr_buf_nxt[1], buf, i);
    *Tr_buf_nxt = i;
    if (i != n_chars) {
      Tr_buf_nxt[i+1] = '.';
      Tr_buf_nxt[i+2] = '.';
      Tr_buf_nxt[i+3] = '.';
      *Tr_buf_nxt += 3;
    }
    Tr_buf_free -= *Tr_buf_nxt; Tr_buf_free--;
    Tr_buf_nxt += *Tr_buf_nxt;  Tr_buf_nxt++;
    *Tr_buf_nxt = 0;
  }
/*
**---------------------------------------------------------------------------
**			    Add a message to the trace
*/
  void	traceDo (
/*	=======
*/
	    char	prefix,		/* 'C' or 'S' for Chan or Socket */
	    int		indx,		/* The channel or socket # */
	    enum ACTION in_or_out,	/* The 'action' to be traced */
	    int		n_txt,		/* The # chars in the text */
	    char       *txt) {		/* The text */

    int	    i;
    char   *my_buf, *nxt;

    my_buf = malloc (2 * n_txt + 10);
    if (my_buf == NULL) return;

    my_buf[0] = prefix;
    if ((indx < 0) || (indx > 99)) indx = 99;
    sprintf (&my_buf[1], "%02d", indx);
    switch (in_or_out) {
     case IN:	    my_buf[3] = '<'; break;
     case OUT:	    my_buf[3] = '>'; break;
     case ERROR:    my_buf[3] = 'E'; break;
     case OPEN:	    my_buf[3] = 'O'; break;
     case CLOSE:    my_buf[3] = 'C'; break;
     case TMO:	    my_buf[3] = 'T'; break;
     case FLUSH:    my_buf[3] = 'F'; break;
     default:	    my_buf[3] = '?';
    }
    nxt = &my_buf[4];
    for (i = 0; i < n_txt; i++) {
      *nxt = txt[i];
      if (*nxt == '^') {nxt++; *nxt = '^';}
      if (!isprint (*nxt)) {*nxt++ = '^'; *nxt = txt[i] + '@';}
      nxt++;
    }
    *nxt = NIL;
    traceAdd (strlen (my_buf), my_buf);
    free (my_buf);
  }
/*
**---------------------------------------------------------------------------*/
  void	traceWrite () {
/*	==========
**				Write trace to disk
*/
    FILE	    *fd;
    int		     i;
    unsigned char   *nxt;
    char	     buff[40];

    if (Tr_buf == NULL) return;

#ifdef  __VMS
    fd = fopen ("mad_log:SerPortServer.trace", "wb");
    printf ("    Writing trace to mad_log:SerPortServer.trace ...");
#else
    fd = fopen ("/tmp/SerPortServer.trace", "wb");
    printf ("    Writing trace to /tmp/SerPortServer.trace ...");
#endif
    fflush (NULL);
    if (fd != NULL) {
      nxt = Tr_buf;
      while (*nxt != 0) {
        fwrite (&nxt[1], 1, *nxt, fd);
        fwrite ("\n", 1, 1, fd);
        nxt += *nxt + 1;
      }

      setupTime (buff, sizeof (buff)); fprintf (fd, "%s\n", buff);
      fprintf (fd, "The listener socket is %d, Port %d\n", Cnct_skt, Inet_port);

      if (N_clients > 0) {
	fprintf (fd, "The following client connections are open:\n");
	for (i = 0; i < N_clients; i++)
	  fprintf (fd, "    Skt %2d --> %s:%d\n",
			Cl_info[i].skt, Cl_info[i].host, Cl_info[i].port);
      }else {
	fprintf (fd, "There are no open client connections.\n");
      }

      if (N_open_chans > 0) {
	fprintf (fd, "The following channels are open:\n");
	for (i = 0; i < N_open_chans; i++)
	  fprintf (fd,
	    "    Chan %2d via Skt %2d and Port %4d\n",
	    Ts_info[i].chan, Ts_info[i].skt, Ts_info[i].port);
      }else {
	fprintf (fd, "There are no open channels.\n");
      }
      fclose (fd);
      printf (" done.\n");
    }else {
      printf (" failed to open file!\n");
    }
  }
/*
**--------------------------------------------------------------------------
**		USR1_Handler:  Signal handler to detect USR1 (31) signals.
*/
  void	USR1_Handler (int sigint) {
/*      ============
*/
    Usr1_has_happened = True;
  }
/*
**==========================================================================
**			Main line program
**			------------------
*/
  int	main (int argc, char **argv) {
/*      ============================
*/
    Widget	    main_window;	    /* MainWindow */
    char	    buff[120], *p_nxt_byte;
    int		    buff_len;
    int		    status, i, j, indx, fds_bits_last = 0;
    fd_set	    my_rd_mask;
    int		    r_size, bytes_to_come;
    struct timeval  tmo, time_now;
    pid_t	    my_pid;

    int			my_errno, my_vaxc_errno;

    int				max_msg_size;
    char			*appName;

    XrmDatabase		    my_db;
    char		    *type;
    XrmValue		     value;
/*-----------------------------------------------------------------------*/
    setupTime (buff, sizeof (buff));
    my_pid = getpid ();
    printf ("%s - SerPortServer, Ident %s, started. PID = %d/0x%x\n",
			&buff[4], ident, my_pid, my_pid);
	/*--------------------------------------------------------------
	**	Setup the resource database and look up the resources.
	*/
    setupXrmDatabase (&my_db, &appName, &argc, argv);
	/* - - - - - - - - - - - - - - - - - - - - - - - -         -ts */
    status = XrmGetResource (my_db,
			     StrJoin (buff, sizeof (buff),
				      appName, ".serPortTsName"),
			     "ProgramName.Values",
			     &type, &value);
    if (!status) {
      printf ("    Terminal server name not defined via the\n");
      printf ("               \"serPortTsName\" resource or the\n");
      printf ("               \"-ts\" option.\n");
      printf ("    There is no default!\n");
      exit (EXIT_FAILURE);
    }
    StrJoin (Ts_name, sizeof (Ts_name), value.addr, "");
    printf ("    Terminal Server to use is %s\n", Ts_name);
	/* - - - - - - - - - - - - - - - - - - - - - - - -         -port */
    status = XrmGetResource (my_db,
			     StrJoin (buff, sizeof (buff),
				      appName, ".serPortServPort"),
			     "ProgramName.Values",
			     &type, &value);
    if (!status ||
        (sscanf (value.addr, "%d", &Inet_port) != 1)) {
      Inet_port = 4000;
      printf ("    TCP/IP server port number defaulting to %d\n", Inet_port);
    }else {
      printf ("    TCP/IP server port number = %d\n", Inet_port);
    }
	/* - - - - - - - - - - - - - - - - - - - - - - - -         -tsize */
    status = XrmGetResource (my_db,
			     StrJoin (buff, sizeof (buff),
				      appName, ".serPortTraceSize"),
			     "ProgramName.Values",
			     &type, &value);
    if (!status ||
        (sscanf (value.addr, "%d", &Tr_buf_size) != 1)) {
      Tr_buf_size = 0x40000;
    }
	/* - - - - - - - - - - - - - - - - - - - - - - - -         -trace */
    status = XrmGetResource (my_db,
			     StrJoin (buff, sizeof (buff),
				      appName, ".serPortTrace"),
			     "ProgramName.Values",
			     &type, &value);
    Trace = (status) ? True : False;
    if (Trace) {
      Tr_buf = malloc (Tr_buf_size);	    /* Allocate buffer for tracing */
      if (Tr_buf != NULL) {
        printf ("    Tracing is on.\n");
	Tr_buf_len = Tr_buf_free = Tr_buf_size;
	Tr_buf_nxt = Tr_buf;
	Tr_buf_shuff = (Tr_buf_size * 25)/100;	/* Shuffle buff by 25% */
	getCurrentTime (&Tr_timer);	    /* Initialise trace time stamp */
	setupTime (buff, sizeof (buff));
	traceAdd (strlen (buff), buff);
      }else {
	Trace = False;
        printf ("    No buffer space available for tracing!\n");
      }
    }
	/* - - - - - - - - - - - - - - - - - - - - - - - -         -period */
    status = XrmGetResource (my_db,
			     StrJoin (buff, sizeof (buff),
				      appName, ".serPortPeriod"),
			     "ProgramName.Values",
			     &type, &value);
    if (!status ||
        (sscanf (value.addr, "%d", &i) != 1)) {
      i = 60;
    }
    Tr_period = (float) i;
	/* - - - - - - - - - - - - - - - - - - - - - - - -         -debug */
    status = XrmGetResource (my_db,
			     StrJoin (buff, sizeof (buff),
				      appName, ".serPortDebug"),
			     "ProgramName.Values",
			     &type, &value);
    Debug = (status) ? True : False;
	/* - - - - - - - - - - - - - - - - - - - - - - - -         -max */
    status = XrmGetResource (my_db,
			     StrJoin (buff, sizeof (buff),
				      appName, ".serPortMax"),
			     "ProgramName.Values",
			     &type, &value);
    if (!status ||
        (sscanf (value.addr, "%d", &RS__MAX_ASYNCH) != 1)) {
      RS__MAX_ASYNCH =  20;
      printf ("    Max Serial Lines defaulting to %d\n", RS__MAX_ASYNCH);
    }else {
      printf ("    Max Serial Lines Taken = %d\n", RS__MAX_ASYNCH);
    }
/*-----------------------------------------------------------------------*/
    N_clients = N_open_chans = 0;

    Cl_info = calloc (RS__MAX_CLIENTS, sizeof (Cl_info[0]));
    if (Cl_info == NULL) {
      printf ("    Unable to allocate space for Cl_info\n");
      exit (EXIT_FAILURE);
    }
    for (i = 0; i < RS__MAX_CLIENTS; i++) {
      Cl_info[i].status = CL_SS_IDLE;
      Cl_info[i].msge_size = MAX_PKT_SIZE;
      Cl_info[i].rply_size = MAX_PKT_SIZE;
      Cl_info[i].msge = calloc (MAX_PKT_SIZE, sizeof (char));
      Cl_info[i].rply = calloc (MAX_PKT_SIZE, sizeof (char));
      if ((Cl_info[i].msge == NULL) || (Cl_info[i].rply == NULL)) {
	printf ("    Unable to allocate space for Cl_info[%d].msge or"
		    " Cl_info[%d].rply\n", i, i);
	freeAll (); exit (EXIT_FAILURE);
      }
    }

    Ts_info = calloc (RS__MAX_ASYNCH, sizeof (Ts_info[0]));
    if (Ts_info == NULL) {
      printf ("    Unable to allocate space for Ts_info\n");
      freeAll (); exit (EXIT_FAILURE);
    }
    for (i = 0; i < RS__MAX_ASYNCH; i++) Ts_info[i].status = TS_SS_IDLE;
/*-----------------------------------------------------------------------*/
    printf ("    Creating RS-232-C Server Socket ...\n");
    Cnct_skt = setupSocket (Inet_port);
/*-----------------------------------------------------------------------*/
    printf ("    RS-232-C Server ready. Waiting for work ...\n");

    FD_ZERO (&Skt_mask); FD_SET (Cnct_skt, &Skt_mask);
    Max_skt  = Cnct_skt + 1;
		    /*
		    ** Declare a signal handler to catch <Ctrl-C> so that we
		    ** can exit cleanly.
		    */
    Ctrl_C_has_happened = False;
    Ctrl_C_pending	= False;
    signal (SIGINT,  ctrlC_Handler);	/* SIGINT  is signal #2  */
    signal (SIGTERM, ctrlC_Handler);	/* SIGTERM is signal #15 */
		    /*
		    ** Declare a signal handler to catch USR1 signals. These
		    ** cause the trace to be written to disk.
		    */
    Usr1_has_happened = False;
    signal (SIGUSR1, USR1_Handler);	/* SIGUSR1 is signal #31 */
		    /*--------------------------------------------------------
		    **	Poll looking for work: accepts, reads or exceptions.
		    **	Each time, check for <Ctrl-C> signals (= SIGINT/SIGTERM)
		    **  and USR1 signals too and handle them.
		    */
    Next_tmo_secs = 1.0;	/* Time-out is 1 sec by default. It can be
				** shorter if client requests need it to be. */
    for (;;) {
      fflush (NULL);

#ifdef LINUX
  #define fds_bits __fds_bits
#endif	    /* Linux */

      if (Debug && (Skt_mask.fds_bits[0] != fds_bits_last)) {
	printf ("Skt_mask.fds_bits[0] is now 0x%04x\n", Skt_mask.fds_bits[0]);
	fds_bits_last = Skt_mask.fds_bits[0];
      }

      tmo.tv_sec  = (int) Next_tmo_secs;	    /* Set up the time-out */
      tmo.tv_usec = (int) ((Next_tmo_secs - (float) tmo.tv_sec) * 1000000.0);
      my_rd_mask = Skt_mask;
      status = select (Max_skt, &my_rd_mask, NULL, NULL, &tmo);

      Next_tmo_secs = 1.0;
      if (status < 0) {				    /* Select error? */
	if (errno != EINTR) {			    /* Yes. Ignore signal
						    **	interrupts of select */
	  FailInet ("SerPortServer: select error.\n");
	}
      }else if (status > 0) {
					/* Look for pending new client */
	if (FD_ISSET (Cnct_skt, &my_rd_mask)) {
	  status--;
	  setupNewClient (Cnct_skt);
        }
					/* Look for pending recv's from TS */
	for (i = 0; (i < N_open_chans) && (status > 0); i++) {
          if (FD_ISSET (Ts_info[i].skt, &my_rd_mask) != 0) {
	    handleTsRecv (i);
	    status--;
	  }
	}
				    /* Look for pending recv's from Clients */
	for (i = 0; (i < N_clients) && (status > 0); i++) {
          if (FD_ISSET (Cl_info[i].skt, &my_rd_mask) != 0) {
	    handleClientRecv (i);
	    status--;
          }
        }
      }
		    /*
		    ** Check all active channels for time-out
		    */
      getCurrentTime (&time_now);
      for (i = 0; i < N_open_chans; i++) {
	if (Ts_info[i].status != TS_SS_IDLE) {
	  if (subtractTimes (time_now, Ts_info[i].time_stamp) >=
					    Ts_info[i].tmo) {
 	    subtractTimes(time_now, Ts_info[i].time_stamp);
	    printf("TMO: Difference: %f, allowed %f\n",
		   subtractTimes(time_now, Ts_info[i].time_stamp),
		   Ts_info[i].tmo);
	    handleTmo (i);	/* This channel has timed-out! */
	  }
	}
      }
		    /*
		    ** Check all pending clients for time-out
		    */
      for (i = 0; i < N_clients; i++) {
	if (Cl_info[i].status == CL_SS_PENDING) {
	  if (subtractTimes (time_now, Cl_info[i].pend_time) >=
					    Cl_info[i].tmo) {
	    handlePendTmo (i);	/* This pending client request has timed-out! */
	  }
	}
      }

      if ((Trace) && (subtractTimes (time_now, Tr_timer) > Tr_period)) {
	setupTime (buff, sizeof (buff)); memcpy (&buff[5], "===== ", 6);
	traceAdd (14, &buff[5]);
	Tr_timer = time_now;
      }

      if (Ctrl_C_has_happened) {    /* Check for <Ctrl-C> (= SIGINT/SIGTERM) */
			/* <Ctrl-C> detected.
			** If any RS-232-C channels are open, close them and
			**	start a timer.
			** If no RS-232-C channels are open, see if a timer is
			**	active. Exit if it's less than 5 secs since
			**	previous <Ctrl-C>.
			*/
	Ctrl_C_has_happened = False;
	setupTime (buff, sizeof (buff));
        printf ("%.15s - <Ctrl-C> detected ...\n", &buff[4]);
	if (N_open_chans <= 0) {
	  printf ("    No RS-232-C channels were open.\n");
	  if (Ctrl_C_pending) {
	    if (subtractTimes (time_now, Ctrl_C_time) < 5.0) {
	      printf ("    SerPortServer closing down ...\n");
	      if (N_clients <= 0) {
		printf ("    No client connections were open.\n");
	      }else {
		for (i = N_clients; i > 0; i--) {
		  printf ("    Closing client socket %d --> %s:%d ...\n",
			Cl_info[0].skt, Cl_info[0].host, Cl_info[0].port);
		  closeClient (0);
		}
		printf ("    All client sockets closed.\n");
	      }
	      printf ("    Closing listener socket %d ...\n", Cnct_skt);
	      if (Trace) {
		setupTime (buff, sizeof (buff));
		StrJoin (buff, sizeof(buff), buff,
					": SerPortServer terminating");
		traceAdd (strlen (buff), buff);
		traceWrite ();
	      }
	      close (Cnct_skt); Cnct_skt = 0; freeAll ();
	      printf ("    SerPortServer exiting.\n");
	      exit (EXIT_SUCCESS);
	    }
	  }
	}else {
	  while (N_open_chans > 0) {
	    if (Trace) traceDo ('C', Ts_info[0].chan, CLOSE,
					18, " <Ctrl-C> detected");
	    printf ("    Closing connection to channel %d ...\n",
			    Ts_info[0].chan);
	    if (Ts_info[0].status >= 0) {   /* Is the channel busy? */
					    /* Yes, terminate the work in
					    **   progress */
	      indx = Ts_info[0].status;
	      if (Trace) traceDo ('S', Cl_info[indx].skt, ERROR,
					27, " <Ctrl-C> - Work terminated");
	      j = setupErrReply (Cl_info[indx].rply,
				Cl_info[indx].msge->msg_id, "-00000000011");
	      status = send (Cl_info[indx].skt,
                            (char *) Cl_info[indx].rply, j, 0);
	      Cl_info[indx].status = CL_SS_IDLE;
	      Ts_info[0].status = TS_SS_IDLE;
	    }
	    closeTs (0);
	  }
	  N_open_chans = 0;
	  printf ("    All RS-232-C channels closed.\n");
	}
	if (Trace) traceWrite ();
	Ctrl_C_pending = True;
	Ctrl_C_time = time_now;
	printf ("    Hit <Ctrl-C> again fairly soon to quit program.\n");

	signal (SIGINT,  ctrlC_Handler);    /* Re-enable <Ctrl-C> signal */
	signal (SIGTERM, ctrlC_Handler);    /* Re-enable <Ctrl-C> signal */
      }else {
	if (Ctrl_C_pending) {	     /* No <Ctrl-C> detected. If a <Ctrl-C> was
				     ** detected recently, see if the time-out
				     ** has elapsed. */
	  if (subtractTimes (time_now, Ctrl_C_time) >= 5.0) {
	    Ctrl_C_pending = False;	/* The time-out has elapsed. */
	    printf ("    <Ctrl-C> time has expired.\n");
	  }
	}
      }

      if (Usr1_has_happened) {
	Usr1_has_happened = False;
	setupTime (buff, sizeof (buff));
	if (Tr_buf != NULL) {
	  printf ("%.15s - USR1 signal detected. Write trace to disk.\n",
				    &buff[4]);
	  traceWrite ();
	}else {
	  printf ("%.15s - USR1 signal detected. There is no trace buffer.\n",
				    &buff[4]);
	}
        fflush (NULL);
	signal (SIGUSR1, USR1_Handler);	    /* Re-enable USR1 signal */
      }
    }
  }
/*------------------------------------------------- End of SerPortServer.C */
