#define	    ident	"1C06"
#ifdef VAXC
#module	    AsynSrv_Utility	ident
#endif
#ifdef __DECC
#pragma	    module	    AsynSrv_Utility	ident
#endif
/*
** +--------------------------------------------------------------+
** |                  Paul Scherrer Institute                     |
** |                     Department ASQ				  |
** |                                                              |
** | 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.|
** +--------------------------------------------------------------+
**
** Module Name  . . . . . . . . : [...LIB.SINQ]AsynSrv_Utility.C
**
** Author   . . . . . . . . . . : D. Maden
** Date of creation . . . . . . : Mar 1996
**
**  To compile this module, use:

	$ define/group sinq_c_tlb mad_lib:sinq_c.tlb
	$ cc /debug /noopt /obj=[]AsynSrv_Utility -
			lnsa01::tasmad_disk:[mad.psi.lib.sinq]AsynSrv_Utility +
			sinq_c_tlb/lib

**  To include this module in SINQ.OLB, use:

  $ define/group sinq_c_tlb mad_lib:sinq_c.tlb
  $
  $ define/group sinq_olb mad_lib:sinq_dbg.olb
  $ @lnsa01::tasmad_disk:[mad.psi.lib.sinq]sinq_olb AsynSrv_Utility debug
  $
  $ define/group sinq_olb mad_lib:sinq.olb
  $ @lnsa01::tasmad_disk:[mad.psi.lib.sinq]sinq_olb AsynSrv_Utility
**
** Updates:
**  1A01 21-Mar-1996 DM.	Initial version.
**  1B01 12-Sep-1996 DM.	Allow host name to be in dot format too.
**  1B02  5-May-1997 DM.	Set 5 sec time-out on "connect" on VMS systems.
**  1B07 11-Mar-1998 DM.	Allow range of msecTmo to be 0 - 999999 (it was
**				    100 - 999999).
**  1C01 21-Mar-2000 MZ.        Introduced idleHandler
**  1C02 30-Mar-2000 DM.        Add trace and flush facilities.
**  1C06 30-Aug-2000 DM.        Add AsynSrv_GetLenTerm.
**============================================================================
** The entry points included in this module are described below. Prototypes
** can be defined via:
**
**    #include <sinq_prototypes.h>
**
**	AsynSrv_ChanClose   - Send a "CLOSE CHAN" request to RS-232-C Server.
**	AsynSrv_Close	    - Close a connection to an RS-232-C Server.
**	AsynSrv_Config	    - Configure an open AsynSrv_Utility connection.
**	AsynSrv_ConfigDflt  - Set defaults for AsynSrv_Open.
**	AsynSrv_ErrInfo	    - Return detailed status from last operation.
**	AsynSrv_Flush       - Send a "FLUSH" request to an RS-232-C Server.
**	AsynSrv_GetLenTerm  - Get length and terminator of a reply.
**	AsynSrv_GetReply    - Get next reply from a reply buffer.
**	AsynSrv_Open	    - Open a connection  to an RS-232-C Server.
**	AsynSrv_OpenNew	    - Same as AsynSrv_Open but forces the opening
**				of a new socket.
**	AsynSrv_SendCmnds   - Send commands to a channel of an RS-232-C Server.
**	AsynSrv_SendCmndsBig - Similar to AsynSrv_SendCmnds but with user
**				defined buffer sizes.
**	AsynSrv_Trace       - Send a "TRACE ON" or "TRACE OFF" request to
**				an RS-232-C Server.
**	AsynSrv_Trace_Write - Send a "TRACE WRITE" request to RS-232-C Server.
**  Other entry points which are private (i.e. not in <sinq_prototypes.h>):
**	AsynSrv_SendSpecCmnd - Send a "special" command to an RS-232-C Server.
**---------------------------------------------------------------------
**    int AsynSrv_ChanClose (&asyn_info)
**	  -----------------
**  Input Args:
**	struct AsynSrv__info  *asyn_info  - the structure used in the call to
**				AsynSrv_Open. It contains settings required
**				for setting up and sending send_buff.
**  Output Args:
**	none
**  Modified Args:
**	none
**  Return status:
**	True if no problems detected, otherwise False and errcode (see
**	AsynSrv_ErrInfo) is set to indicate the nature of the problem.
**	See AsynSrv_SendSpecCmnd for possible error codes.
**  Routines called:
**	AsynSrv_SendSpecCmnd
**  Description:
**	AsynSrv_SendSpecCmnd is called to send the 4-byte "special" 
**	command "-006" to the server to cause it to close its serial ports.
**---------------------------------------------------------------------
**    int AsynSrv_Close (&asyn_info, force_flag)
**	  -------------
**  Input Args:
**	int   force_flag - if non-zero, all connections using the same socket
**			   will also be marked as force-closed (socket number
**			   set to -1) and the connection will really be
**			   closed. This is needed for error recovery operations.
**  Output Args:
**	none
**  Modified Args:
**	struct AsynSrv__info *asyn_info  - a structure holding skt, host and
**					   port of the connection. On return
*					   skt = 0.
**  Return status:
**	True if no problems detected, otherwise False and AsynSrv_errcode
**	is set to indicate the nature of the problem as follows:
**	  ASYNSRV__BAD_PAR  --> skt does not match with host/port.
**  Routines called:
**	Socket library, "close".
**  Description:
**	The routine decrements the usage count on the connection to host/port.
**	If the counter is still >0, the routine simply returns.
**	If the counter is now 0, the routine sends a "-001" message to the
**	server to inform it that we are about to close the link, waits for a
**	possible 4 bytes of response and then closes the TCP/IP connection.
**---------------------------------------------------------------------
**    int AsynSrv_Config (&asyn_info, &par_id, par_val, ...)
**	  --------------
**  Input Args:
**	char*   par_id	- Text string identifying the next argument (see below).
**			    NULL indicates the end of the argument list.
**	<??>	par_val	- The value to set for the argument. The type of the
**			    argument can depend on par_id.
**  Output Args:
**	none
**  Modified Args:
**	struct AsynSrv__info  *asyn_info  - the structure used in the call to
**				AsynSrv_Open. It is used to hold the config
**				info for the connection.
**  Return status:
**	True if no problems detected, otherwise False and AsynSrv_errcode
**	is set to indicate the nature of the problem as follows:
**	  ASYNSRV__BAD_PAR  --> Unrecognised par_id or msecTmo < 0 or
**					msecTmo > 999'999 or bad eot or ..
**  Routines called:
**	None
**  Description:
**	AsynSrv_Config may be used for setting values of parameters for
**	use in subsequent calls to AsynSrv_SendCmnds. Defaults for these
**	parameters are set via a call to AsynSrv_ConfigDflt, prior to
**	calling AsynSrv_Open. Values which may be taken by par_id (warning --
**	par_id is case-sensitive) and the corresponding variable type of
**	par_val are:
**
**	  "msecTmo"  int   	 The time-out response for commands sent
**				   to a serial channel on the server. The
**				   valid range is 0 to 999'999.
**	  "eot"     char*  	 The expected terminators in responses to
**				    commands sent to a serial channel on the
**				    server. The first character specifies the
**				    number of terminators (max=3).
**        "idleHdl" void (*hdl) (int msecTmo, int socket)            MZ.
**                               A handler which is called in AsynSrv_SendCmds
**                               before receiving the response. The handler
**                               should contain a call to "select ()" and return
**                               on a read event on the socket passed as
**                               argument, or after the timeout specified
**                               has expired.
**---------------------------------------------------------------------
**    int AsynSrv_ConfigDflt (&par_id, par_val, ...)
**	  ------------------
**  Input Args:
**	char*   par_id	- Text string identifying the next argument (see below).
**			    NULL indicates the end of the argument list.
**	<??>	par_val	- The value to set for the argument. The type of the
**			    argument can depend on par_id.
**  Output Args:
**	none
**  Modified Args:
**	none
**  Return status:
**	True if no problems detected, otherwise False and AsynSrv_errcode
**	is set to indicate the nature of the problem as follows:
**	  ASYNSRV__BAD_PAR  --> Unrecognised par_id or msecTmo < 0 or
**					msecTmo > 999'999 or bad eot or ..
**  Routines called:
**	None
**  Description:
**	AsynSrv_ConfigDflt may be used for setting default values of parameters
**	for use in subsequent calls to AsynSrv_Open. Values which may be taken
**	by par_id (warning -- par_id is case-sensitive) and the corresponding
**	variable type of par_val are:
**
**	  "TmoC"     int   	 The time-out in seconds to be used when
**				   opening a connection to a server. This
**				   value is only effective on VMS systems. For
**				   UNIX systems, the systemwide default (usually
**				   75 secs) cannot be changed. The initial
**				   setting for "TmoC" is 5 secs.
**	  "msecTmo"  int   	 The time-out response for commands sent
**				   to a serial channel on the server. The
**				   valid range is 0 to 999'999. The initial
**				   setting for "msecTmo" is 10'000 msec.
**	  "eot"     char*  	 The expected terminators in responses to
**				    commands sent to a serial channel on the
**				    server. The first character specifies the
**				    number of terminators (max=3). The initial
**				    setting for "eot" is "1\r".
**-------------------------------------------------------------------------
**    void AsynSrv_ErrInfo (&entry_txt_ptr, &errcode, &my_errno, &vaxc_errno)
**	   ---------------
**  Input Args:
**	None
**  Output Args:
**	char **entry_txt_ptr - Pointer to a text string giving the call stack
**				at the time that the error was detected.
**	int  *errcode	 - An internal error code indicating the detected error.
**	int  *my_errno	 - Saved value of errno.
**	int  *vaxc_errno - Saved value of vaxc$errno (OpenVMS only).
**  Modified Args:
**	none
**  Return status:
**	none
**  Routines called:
**	none
**  Description:
**	Returns detailed status of the last operation. Once an error has been
**	detected, the error status is frozen until this routine has been called.
**-------------------------------------------------------------------------
**    int AsynSrv_Flush (&asyn_info)
**	  -------------
**  Input Args:
**	struct AsynSrv__info  *asyn_info  - the structure used in the call to
**				AsynSrv_Open. It contains settings required
**				for setting up and sending send_buff.
**  Output Args:
**	none
**  Modified Args:
**	none
**  Return status:
**	Same as AsynSrv_ChanClose
**  Routines called:
**	Same as AsynSrv_ChanClose
**  Description:
**	AsynSrv_SendSpecCmnd is called to send the 4-byte "special" 
**	command "-004" to the server to cause it to close its serial ports.
**-------------------------------------------------------------------------
**    int   AsynSrv_GetLenTerm (&asyn_info, &rcve_buff, &rply, *len, &term)
**	    ------------------
**  Input Args:
**	struct AsynSrv__info  *asyn_info  - the structure used in the call to
**				AsynSrv_Open. It is used to hold status info
**				between calls to this routine.
**	struct RS__RespStruct *rcve_buff  - address of receive buffer used
**					      in last call to AsynSrv_SendCmnds.
**	char		      *rply	  - address of a reply in rcve_buff as
**					      returned by AsynSrv_GetReply.
**  Output Args:
**	int		      *len	  - address of location to receive the
**					      length of the reply.
**	char		      *term	  - address of location to receive the
**					      terminator of the reply.
**  Modified Args:
**	none
**  Return status:
**	True if everything seems to be OK. Otherwise, False.
**  Routines called:
**	none
**  Description:
**	AsynSrv_GetLenTerm simply converts the length of the reply as saved
**	in rply[-hdr_size-1] from ASCII to binary, subtracts 2 from it (to
**	allow for the terminator byte and the null termination character) and
**	returns it to the caller. *term is set to the value of the character
**	at location rply[-1].
**-------------------------------------------------------------------------
**    char *AsynSrv_GetReply (&asyn_info, &rcve_buff, &last_rply)
**	    ----------------
**  Input Args:
**	struct RS__RespStruct *rcve_buff    - address of receive buffer used
**					      in last call to AsynSrv_SendCmnds.
**	char		      *last_rply    - Address of last reply processed
**					      or NULL.
**  Output Args:
**	none
**  Modified Args:
**	struct AsynSrv__info  *asyn_info  - the structure used in the call to
**				AsynSrv_Open. It is used to hold status info
**				between calls to this routine.
**  Return status:
**	Address of next reply in the buffer or NULL if no more. Note that this
**	is a pointer to the reply and not to the head of the reply structure.
**	The terminator byte found is therefore at index [-1] from this address.
**  Routines called:
**	none
**  Description:
**	AsynSrv_GetReply unpacks the replies in the response packet from the
**	RS232C server which is an argument in the call to AsynSrv_SendCmnds.
**	If the routine is called with last_rply = NULL, a pointer to the
**	first reply is returned. On calling AsynSrv_GetReply again with
**	last_rply set to this address, one receives the address of the second
**	reply and so on, until NULL is returned, indicating that all responses
**	have been exhausted.
**  Warning:
**	AsynSrv_GetReply keeps count of the number of responses it returns.
**	Responses must therefore be processed in order.
**-------------------------------------------------------------------------
**    int AsynSrv_Open (&asyn_info)
**	  ------------
**  Input Args:
**	struct AsynSrv__info *asyn_info
**	  asyn_info->host - Name of host offering the RS-232-C service. The name
**			    can be either symbolic or numeric, e.g.
**			    "lnsw02.psi.ch" or "129.129.90.18".
**	  asyn_info->port - Number of TCP/IP port of TCP/IP server.
**	  asyn_info->chan - Number of RS-232-C channel to be used.
**  Output Args:
**	none
**  Modified Args:
**	struct AsynSrv__info *asyn_info  - a structure holding skt, host and
**					   port of the connection. On return
**					   skt = socket number of connection.
**					   Set to 0 if error.
**  Return status:
**	If non-zero, no problems detected and asyn_info->skt is the socket to
**		use for communicating with the server. Otherwise, a problem
**		was detected and AsynSrv_errcode may be set as follows
**		to indicate the nature of the problem:
**	  ASYNSRV__BAD_HOST    --> Call to "gethostbyname" failed to get
**					network addr of host.
**	  ASYNSRV__BAD_SOCKET  --> Call to "socket" failed.
**	  ASYNSRV__BAD_BIND    --> Call to "bind" failed.
**	  ASYNSRV__BAD_CONNECT --> Call to "connect" failed.
**	  ASYNSRV__BAD_PAR     --> Bad parameter found. Probably
**					    asyn_info->port or asyn_info->chan
**					    are out of range.
**	  BAD_PROT_LVL	       --> Server protocol level is not valid.
**	  ASYNSRV__NO_ROOM     --> Host/port table full or Active-link
**					    table full.
**  Routines called:
**	Socket library routine "open".
**  Description:
**	The routine maintains a list of hosts/ports to which it has open
**	sockets. If an entry is found in the list, the socket is returned
**	and the usage count of this connection is incremented. If no entry
**	is found in the list, a connection to the host is established and
**	entered into the list.
**	The routine also maintains a table of active links so that the
**	"force-close" function can be performed. The link is added to this
**	table too.
**-------------------------------------------------------------------------
**    int AsynSrv_OpenNew (&asyn_info)
**	  ---------------
**  Input Args:
**	struct AsynSrv__info *asyn_info
**	  asyn_info->host - Name of host offering the RS-232-C service. The name
**			    can be either symbolic or numeric, e.g.
**			    "lnsw02.psi.ch" or "129.129.90.18".
**	  asyn_info->port - Number of TCP/IP port of TCP/IP server.
**	  asyn_info->chan - Number of RS-232-C channel to be used.
**  Output Args:
**	none
**  Modified Args:
**	struct AsynSrv__info *asyn_info  - a structure holding skt, host and
**					   port of the connection. On return
**					   skt = socket number of connection.
**					   Set to 0 if error.
**  Return status:
**	See AsynSrv_Open
**  Routines called:
**	See AsynSrv_Open
**  Description:
**	This routine is the same as AsynSrv_Open but forces the opening
**	of a new socket. The socket will be marked to ensure that no other
**	connections share this connection.
**-------------------------------------------------------------------------
**    int AsynSrv_SendCmnds (&asyn_info, &send_buff, &rcve_buff, ...)
**	  -----------------
**  Input Args:
**	struct AsynSrv__info  *asyn_info  - the structure used in the call to
**				AsynSrv_Open. It contains settings required
**				for setting up and sending send_buff.
**	char	* ...	- A list of commands, terminated by NULL, for
**			    sending to the channel on the server. The commands
**			    must have any necessary \r characters included.
**  Output Args:
**	struct RS__RespStruct *rcve_buff  - a buffer to receive the response
**				from the server.
**  Modified Args:
**	struct RS__MsgStruct  *send_buff  - a buffer for holding the commands
**				for sending to the server.
**  Return status:
**	True if no problems detected, otherwise False and errcode (see
**	AsynSrv_ErrInfo) is set to indicate the nature of the problem.
**	AsynSrv_errcode may be set as follows:
**	    ASYNSRV__BAD_SEND_LEN --> Too much to send; either too many
**					    commands or too long. The buffer
**					    is 232 bytes long and each command
**					    has a 2-byte header.
**	    ASYNSRV__BAD_CMND_LEN --> A command is too long - it's length cannot
**					be encoded into the command header field
**	  The next 4 errors are related to network errors whilst sending the
**	  message buffer to the server:
**	    ASYNSRV__BAD_SEND	   --> Network problem - server has
**					    probably abended.
**	    ASYNSRV__BAD_SEND_PIPE --> Network pipe broken - probably same
**					    cause as ASYNSRV__BAD_SEND.
**	    ASYNSRV__BAD_SEND_NET  --> Some other network problem. "errno"
**					    may be helpful.
**	    ASYNSRV__BAD_SEND_UNKN --> Some other network problem happened
**					    resulting in the message not
**					    getting sent completely. "errno" is
**					    probably not helpful in this case.
**	    ASYNSRV__BAD_RECV	    \	These are network errors whilst
**	    ASYNSRV__BAD_RECV_PIPE   >	receiving the 4-byte response header.
**	    ASYNSRV__BAD_RECV_NET   /	They are analogous to ASYNSRV__BAD_SEND
**	    ASYNSRV__BAD_RECV_UNKN /	... ASYNSRV__BAD_SEND_UNKN.
**	    ASYNSRV__BAD_NOT_BCD   --> The 4-byte response header is not an
**					  ASCII coded decimal integer.
**	    ASYNSRV__BAD_RECV_LEN  --> The body of the response would be too
**					  big to fit in the input buffer. The
**					  buffer is ??? bytes long and each
**					  response has a 5-byte header and a
**					  trailing zero-byte. The response
**					  is flushed.
**	    ASYNSRV__BAD_FLUSH     --> Some network error was detected
**					  during flushing. This is an "or"
**					  of errors ASYNSRV__BAD_RECV to
**					  ASYNSRV__BAD_RECV_UNKN.
**	    ASYNSRV__FORCED_CLOSED --> The connection to the channel has been
**					   forcefully closed. See below.
**	    ASYNSRV__BAD_REPLY     --> The n_rply field of the response was
**					either non-numeric or <0, indicating
**					that the Terminal Server detected an
**					error. The reply is added to the
**					routine call stack for debug purposes.
**				
**	    ASYNSRV__BAD_RECV1	   \   These are network errors whilst receiving
**	    ASYNSRV__BAD_RECV1_PIPE >	the body of the response. They are
**	    ASYNSRV__BAD_RECV1_NET /	equivalent to ASYNSRV__BAD_RECV,
**					ASYNSRV__BAD_RECV_PIPE and
**					ASYNSRV__BAD_RECV_NET.
**	    ASYNSRV__FORCED_CLOSED occurs if AsynSrv_Close has been called
**		for another device on the same server and the 'force_flag'
**		was set (see AsynSrv_Close). The caller should call
**		AsynSrv_Close and then AsynSrv_Open to re-establish a
**		connection to the server.
**  Routines called:
**	Socket library routines send and recv.
**  Description:
**	The list of commands is assembled into a message buffer with appropriate
**	header information and sent off to the server. The response is then
**	awaited and read in when it arrives.
**
**    For any of the following errors:
**	ASYNSRV__BAD_SEND		(Note: ASYNSRV__BAD_SEND_LEN and 
**	ASYNSRV__BAD_SEND_PIPE		       ASYNSRV__BAD_RECV_LEN and
**	ASYNSRV__BAD_SEND_NET		       ASYNSRV__BAD_REPLY
**	ASYNSRV__BAD_SEND_UNKN		   do not cause a close)
**	ASYNSRV__BAD_RECV
**	ASYNSRV__BAD_RECV_PIPE
**	ASYNSRV__BAD_RECV_NET
**	ASYNSRV__BAD_RECV_UNKN
**	ASYNSRV__BAD_NOT_BCD
**	ASYNSRV__BAD_FLUSH
**	ASYNSRV__BAD_RECV1
**	ASYNSRV__BAD_RECV1_PIPE
**	ASYNSRV__BAD_RECV1_NET
**    the network link to the server is force-closed via a call to
**    AsynSrv_Close. Once the error has been corrected, the link can be
**    re-opened via a call to AsynSrv_Open. As a result of the force-close,
**    other active handles will need to be released via a call to
**    AsynSrv_Close before AsynSrv_Open is called.
**-------------------------------------------------------------------------
**    int AsynSrv_SendCmndsBig (&asyn_info, &send_buff, send_buff_size,
**	  --------------------		    &rcve_buff, rcve_buff_size, ...)
**  Input Args:
**	struct AsynSrv__info  *asyn_info  - the structure used in the call to
**				AsynSrv_Open. It contains settings required
**				for setting up and sending send_buff.
**	int	send_buff_size	- The size of *send_buff in bytes.
**	int	rcve_buff_size	- The size of *rcve_buff in bytes.
**	int	*c_len \    - a list of argument pairs specifying the commands
**	char	*cmd    >     to be sent. The list is terminated by
**	...	       /      c_len == NULL. If *c_len > 0, it specifies the
**			      number of bytes in the command. Otherwise, *cmd
**			      is assumed to be a zero-terminated string.
**			      The *cmd string must include any terminator
**			      byte(s) but, if *c_len > 0, it does not need to
**			      be zero-terminated.
**  Output Args:
**	struct RS__RespStruct *rcve_buff  - a buffer to receive the response
**				from the server. Note that this structure must
**				be extended to size rcve_buff_size by the use
**				of suitable unions.
**  Modified Args:
**	struct RS__MsgStruct  *send_buff  - a buffer for holding the commands
**				for sending to the server. Note that this
**				structure must be extended to size
**				send_buff_size by the use of suitable unions.
**  Return status:
**	Same as AsynSrv_SendCmnds with the addition of error code:
**	    ASYNSRV__BAD_SEND_PAR --> Either send_buff_size or rcve_buff_size
**					is less than 64.
**  Routines called:
**	Socket library routines send and recv.
**  Description:
**	The procedure is similar to AsynSrv_SendCmnds except that the commands
**	are specified by a pair of arguments (to allow for the binary
**	transmission of zeros) and the send and receive structures are assumed
**	to have been extended to the sizes specified by suitable declarations
**	in the calling module.
**-------------------------------------------------------------------------
**    int AsynSrv_SendSpecCmnd (&asyn_info, &cmnd)
**	  -------------------
**  Input Args:
**	struct AsynSrv__info  *asyn_info  - the structure used in the call to
**				AsynSrv_Open. It contains settings required
**				for setting up and sending send_buff.
**	char	*cmnd    - the 4-byte special command to be sent.
**  Output Args:
**	none
**  Modified Args:
**	none
**  Return status:
**	True if no problems detected, otherwise False and errcode (see
**	AsynSrv_ErrInfo) is set to indicate the nature of the problem.
**	AsynSrv_errcode may be set as follows:
**	  The next 4 errors are related to network errors whilst sending the
**	  4-byte message buffer to the server:
**	    ASYNSRV__BAD_SEND	   --> Network problem - server has
**					    probably abended.
**	    ASYNSRV__BAD_SEND_PIPE --> Network pipe broken - probably same
**					    cause as ASYNSRV__BAD_SEND.
**	    ASYNSRV__BAD_SEND_NET  --> Some other network problem. "errno"
**					    may be helpful.
**	    ASYNSRV__BAD_SEND_UNKN --> Some other network problem happened
**					    resulting in the message not
**					    getting sent completely. "errno" is
**					    probably not helpful in this case.
**	    ASYNSRV__BAD_RECV	    \	These are network errors whilst
**	    ASYNSRV__BAD_RECV_PIPE   >	receiving the 4-byte response.
**	    ASYNSRV__BAD_RECV_NET   /	They are analogous to ASYNSRV__BAD_SEND
**	    ASYNSRV__BAD_RECV_UNKN /	... ASYNSRV__BAD_SEND_UNKN.
**	    ASYNSRV__BAD_NOT_BCD   --> The 4-byte response header is not an
**					  echo of the 4 bytes which were sent.
**	    ASYNSRV__FORCED_CLOSED --> The connection to the channel has been
**					   forcefully closed. See below.
**	    ASYNSRV__FORCED_CLOSED occurs if AsynSrv_Close has been called
**		for another device on the same server and the 'force_flag'
**		was set (see AsynSrv_Close). The caller should call
**		AsynSrv_Close and then AsynSrv_Open to re-establish a
**		connection to the server.
**  Routines called:
**	Socket library routines send and recv.
**  Description:
**    AsynSrv_SendSpecCmnd sends the 4-byte "special" command, cmnd, to the
**    server and reads the response. The response should be an echo of the
**    command which was sent.
**   Note:
**    For any of the following errors:
**	ASYNSRV__BAD_SEND	
**	ASYNSRV__BAD_SEND_PIPE	
**	ASYNSRV__BAD_SEND_NET	
**	ASYNSRV__BAD_SEND_UNKN	
**	ASYNSRV__BAD_RECV
**	ASYNSRV__BAD_RECV_PIPE
**	ASYNSRV__BAD_RECV_NET
**	ASYNSRV__BAD_RECV_UNKN
**	ASYNSRV__BAD_NOT_BCD
**    the network link to the server is force-closed via a call to
**    AsynSrv_Close. Once the error has been corrected, the link can be
**    re-opened via a call to AsynSrv_Open. As a result of the force-close,
**    other active handles will need to be released via a call to
**    AsynSrv_Close before AsynSrv_Open is called.
**-------------------------------------------------------------------------
**    int AsynSrv_Trace (&asyn_info, state)
**	  -------------
**  Input Args:
**	struct AsynSrv__info  *asyn_info  - the structure used in the call to
**				AsynSrv_Open. It contains settings required
**				for setting up and sending send_buff.
**	int    state	- True/False to turn tracing on/off.
**  Output Args:
**	none
**  Modified Args:
**	none
**  Return status:
**	Same as AsynSrv_ChanClose
**  Routines called:
**	Same as AsynSrv_ChanClose
**  Description:
**	AsynSrv_SendSpecCmnd is called to send a 4-byte "special" command
**	to the server. The command is "-002" to turn on tracing and "-003"
**	to turn off tracing.

**  Description:
**	To turn on  tracing, the 4-byte message "-002" is sent to the server.
**	To turn off tracing, the 4-byte message "-003" is sent to the server.
**	The server is expected to respond by echoing the message.
**
**-------------------------------------------------------------------------
**    int AsynSrv_Trace_Write (&asyn_info)
**	  -------------------
**  Input Args:
**	struct AsynSrv__info  *asyn_info  - the structure used in the call to
**				AsynSrv_Open. It contains settings required
**				for setting up and sending send_buff.
**  Output Args:
**	none
**  Modified Args:
**	none
**  Return status:
**	Same as AsynSrv_ChanClose
**  Routines called:
**	Same as AsynSrv_ChanClose
**  Description:
**	AsynSrv_SendSpecCmnd is called to send the 4-byte "special" 
**	command "-005" to the server to cause it to write its trace
**	buffer to disk.
**============================================================================*/
/*
**---------------------------------------------------------------------------
**					Global Definitions
*/
#include    <stdlib.h>
#include    <stdio.h>
#include    <stdarg.h>
#include    <errno.h>
#include    <signal.h>
#include    <netdb.h>
#include    <sys/socket.h>
#include    <netinet/in.h>
#include    <arpa/inet.h>

#include    <string.h>

#ifdef	__VMS
#pragma	    nostandard          /* The "$" characters in ucx$inetdef.h give trouble! */
#include    <ucx$inetdef.h>
#include    <unixio.h>
#else
#include    <unistd.h>
#endif
/*-----------------------------------------------------------------*/
#include    <asynsrv_def.h>
#include    <rs232c_def.h>
#include    <sinq_prototypes.h>

#define True	1
#define False	0

#define MAX_OPEN    64

int AsynSrv_SendSpecCmnd(       /* A prototype for a local routine */
                          struct AsynSrv__info *asyn_info, char *cmnd);
/*--------------------------------------------------------------------------
**					Global Variables
*/
static int AsynSrv_call_depth = 0;
static char AsynSrv_routine[5][64];
static int AsynSrv_errcode = 0;
static int AsynSrv_errno, AsynSrv_vaxc_errno;
static int AsynSrv_connect_tmo = 5;     /* Time-out on "connect" */
static int AsynSrv_msec_tmo = 10000;    /* Time-out for responses */
static char AsynSrv_eot[] = { '1', '\r', '\0', '\0' };  /* Terminators */

/*
**	The following is the list of open connections (= number of
**	active sockets).
*/
static int AsynSrv_n_cnct = 0;
static struct AsynSrv_HostPortSkt AsynSrv_HPS_list[AsynSrv_MAX_LINK];
/*
**	The following is the list of active calls to AsynSrv_Open.
*/
static int AsynSrv_n_active = 0;
static struct AsynSrv__info *AsynSrv_active[MAX_OPEN];
/*
**---------------------------------------------------------------------------
**		AsynSrv_ChanClose:  Send a "CLOSE CHAN" request to
**					RS232C server.
*/
int AsynSrv_ChanClose(
/*	=================
*/ struct AsynSrv__info *asyn_info)
{

  int status;
  char cmnd[8], rply[8];
                    /*----------------------------------------------
		    ** Pre-set the routine name (in case of error)
		    */
  if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) {
    strcpy(AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_ChanClose");
    AsynSrv_call_depth++;
  }
                    /*----------------------------------------------
		    ** Do nothing if no connection - the connection gets
		    ** closed if an error is detected. The connection may
		    ** also be marked to have been forcefully closed.
		    */
  if (asyn_info->skt <= 0) {
    if ((AsynSrv_errcode == 0) && (asyn_info->skt < 0)) {
      AsynSrv_errcode = ASYNSRV__FORCED_CLOSED;
    }
    return False;
  }
                    /*----------------------------------------------
		    ** Send message and get reply.
		    */
  status = AsynSrv_SendSpecCmnd(asyn_info, "-006");

  if (AsynSrv_errcode == 0)
    AsynSrv_call_depth--;
  return status;
}

/*
**---------------------------------------------------------------------------
**		AsynSrv_Close:  Close a connection to an RS-232-C server.
*/
int AsynSrv_Close(
/*	=============
*/ struct AsynSrv__info *asyn_info,
                   int force_flag)
{

  int i, j, k, my_skt;
  char buff[4];
                /*-----------------------------------------------
		*/
  if (asyn_info == NULL)
    return True;                /* Just return if nothing to do! */
  my_skt = asyn_info->skt;
  if (my_skt <= 0)
    return True;                /* Just return if nothing to do! */
                /*-----------------------------------------------
		** Pre-set the routinename (in case of error)
		*/
  if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) {
    strcpy(AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_Close");
    AsynSrv_call_depth++;
  }
                /*------------------------------------------------------
		** Start by finding the table entry for this connection
		*/
  for (i = 0; i < AsynSrv_n_cnct; i++) {
    if (AsynSrv_HPS_list[i].skt != my_skt)
      continue;
    if (AsynSrv_HPS_list[i].port != asyn_info->port)
      continue;
    if (strcmp(AsynSrv_HPS_list[i].host, asyn_info->host) == 0)
      break;
  }
  if (i >= AsynSrv_n_cnct) {    /* Did we find the entry? */
    AsynSrv_errcode = ASYNSRV__BAD_PAR; /* No! */
    return False;
  }
                /*------------------------------------------------------
		** Now find the table entry for the AsynSrvOpen call.
		*/
  for (j = 0; j < AsynSrv_n_active; j++) {
    if ((AsynSrv_active[j] == asyn_info) &&
        (AsynSrv_active[j]->skt == my_skt)) {
      break;
    }
  }
  if (j >= AsynSrv_n_active) {  /* Did we find the entry? */
    AsynSrv_errcode = ASYNSRV__BAD_PAR; /* No! */
    return False;
  }
                /*------------------------------------------------------
		**  i is the index for the connection table entry.
		**  j is the index for the caller's AsynSrvOpen call entry.
		*/
  if (AsynSrv_HPS_list[i].usage_cnt <= 0) {     /* Is the connection active? */
    AsynSrv_errcode = ASYNSRV__BAD_PAR; /* No               */
    return False;
  }
                /*------------------------------------------------------
		** For the caller, simply set his socket number to zero,
		** mark the AsynSrvOpen entry as free and decrease the
		** usage count (the entries will be compressed later).
		*/
  AsynSrv_active[j]->skt = 0;   /* Mark the close .. */
  AsynSrv_active[j] = NULL;     /* .. and flag entry to be removed. */
  AsynSrv_HPS_list[i].usage_cnt--;      /* Decrease usage count */
                /*------------------------------------------------------
		** If this is a force-close, go through all AsynSrv_Open
		** entries looking for a socket match, mark them as
		** free and decrease usage count.
		*/
  if (force_flag != 0) {
    for (k = 0; k < AsynSrv_n_active; k++) {
      if (AsynSrv_active[k] != NULL) {
        if (AsynSrv_active[k]->skt == my_skt) {
          AsynSrv_active[k]->skt = -1;  /* Mark the force-close */
          AsynSrv_active[k] = NULL;     /* Mark entry to be removed */
          AsynSrv_HPS_list[i].usage_cnt--;      /* Decrease usage count */
        }
      }
    }
    if (AsynSrv_HPS_list[i].usage_cnt != 0) {   /* Use count should now be .. */
      AsynSrv_errcode = ASYNSRV__BAD_PAR;       /* .. zero or there's a bug.  */
      return False;
    }
  }
                /*------------------------------------------------------
		** Compress the list of AsynSrv_Open entries
		*/
  j = 0;
  for (k = 0; k < AsynSrv_n_active; k++) {
    if (AsynSrv_active[k] != NULL) {
      AsynSrv_active[j] = AsynSrv_active[k];
      j++;
    }
  }
  for (k = j; k < AsynSrv_n_active; k++)
    AsynSrv_active[k] = NULL;
  AsynSrv_n_active = j;
                /*------------------------------------------------------
		** If the link is now idle, really close it and compress
		** the connection table entry out of the list.
		*/
  if (AsynSrv_HPS_list[i].usage_cnt == 0) {
    send(my_skt, "-001", 4, 0); /* Tell the TCP/IP server that ..
                                 ** .. we are about to quit.
                                 */
    recv(my_skt, buff, sizeof(buff), 0);        /* And wait for his ack */
    close(my_skt);
    for (j = i; j < AsynSrv_n_cnct; j++) {
      memcpy((char *) &AsynSrv_HPS_list[j],
             (char *) &AsynSrv_HPS_list[j + 1],
             sizeof(AsynSrv_HPS_list[0]));
    }
    AsynSrv_HPS_list[AsynSrv_n_cnct].skt = 0;   /* Invalidate the free entry */
    AsynSrv_n_cnct--;
  }
  if (AsynSrv_errcode == 0)
    AsynSrv_call_depth--;
  return True;
}

/*
**---------------------------------------------------------------------------
**		AsynSrv_Config:  Configure an open connection.
*/
int AsynSrv_Config(
/*	==============
*/ struct AsynSrv__info *asyn_info,
                    ...)
{

  char buff[16], my_eot[4];
  va_list ap;                   /* Pointer to variable args */
  char *txt_ptr;
  int intval;
  typedef void (*IdleHandler) (int, int);
  /*
   ** Pre-set the routinename (in case of error)
   */
  if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) {
    strcpy(AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_Config");
    AsynSrv_call_depth++;
  }

  va_start(ap, asyn_info);      /* Set up var arg machinery */
  txt_ptr = va_arg(ap, char *); /* Get pntr to first parameter ident */
  while (txt_ptr != NULL) {
    if (strcmp(txt_ptr, "msecTmo") == 0) {
      intval = va_arg(ap, int);
      if ((intval < 0) || (intval > 999999)) {
        AsynSrv_errcode = ASYNSRV__BAD_PAR;
        return False;
      }
      sprintf(buff, "%04d", intval / 100);      /* Convert to ASCII as ..
                                                 ** .. deci-secs */
      memcpy(asyn_info->tmo, buff, 4);
    } else if (strcmp(txt_ptr, "eot") == 0) {
      txt_ptr = va_arg(ap, char *);
      if (txt_ptr == NULL) {
        AsynSrv_errcode = ASYNSRV__BAD_PAR;
        return False;
      }
      memcpy(my_eot, "\0\0\0\0", 4);
      switch (txt_ptr[0]) {
      case '3':
        my_eot[3] = txt_ptr[3];
      case '2':
        my_eot[2] = txt_ptr[2];
      case '1':
        my_eot[1] = txt_ptr[1];
      case '0':
        my_eot[0] = txt_ptr[0];
        break;
      default:
        AsynSrv_errcode = ASYNSRV__BAD_PAR;
        return False;
      }
      memcpy(asyn_info->eot, my_eot, 4);
    } else if (strcmp(txt_ptr, "idleHdl") == 0) {       /* MZ. */
      asyn_info->idleHandler = va_arg(ap, IdleHandler);
    } else {
      AsynSrv_errcode = ASYNSRV__BAD_PAR;
      return False;
    }
    txt_ptr = va_arg(ap, char *);       /* Get pntr to next parameter ident */
  }

  if (AsynSrv_errcode == 0)
    AsynSrv_call_depth--;
  return True;
}

/*
**---------------------------------------------------------------------------
**		AsynSrv_ConfigDflt:  Set default values in AsynSrv_Utility
**				     which will be used to initialise
**				     structures in AsynSrv_Open.
*/
int AsynSrv_ConfigDflt(
/*	==================
*/ char *par_id,
                        ...)
{
  int i;
  char buff[4];
  va_list ap;                   /* Pointer to variable args */
  char *txt_ptr;
  int intval;
  /*
   ** Pre-set the routinename (in case of error)
   */
  if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) {
    strcpy(AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_ConfigDflt");
    AsynSrv_call_depth++;
  }

  va_start(ap, par_id);         /* Set up var arg machinery */
  txt_ptr = par_id;             /* Point to first arg */
  while (txt_ptr != NULL) {
    if (strcmp(txt_ptr, "tmoC") == 0) {
      intval = va_arg(ap, int);
      if ((intval < 1) || (intval > 3600)) {
        AsynSrv_errcode = ASYNSRV__BAD_PAR;
        return False;
      }
      AsynSrv_connect_tmo = intval;
    } else if (strcmp(txt_ptr, "msecTmo") == 0) {
      intval = va_arg(ap, int);
      if ((intval < 0) || (intval > 999999)) {
        AsynSrv_errcode = ASYNSRV__BAD_PAR;
        return False;
      }
      AsynSrv_msec_tmo = intval;
    } else if (strcmp(txt_ptr, "eot") == 0) {
      txt_ptr = va_arg(ap, char *);
      if (txt_ptr == NULL) {
        AsynSrv_errcode = ASYNSRV__BAD_PAR;
        return False;
      }
      switch (txt_ptr[0]) {
      case '3':
        AsynSrv_eot[3] = txt_ptr[3];
      case '2':
        AsynSrv_eot[2] = txt_ptr[2];
      case '1':
        AsynSrv_eot[1] = txt_ptr[1];
      case '0':
        AsynSrv_eot[0] = txt_ptr[0];
        break;
      default:
        AsynSrv_errcode = ASYNSRV__BAD_PAR;
        return False;
      }
      switch (txt_ptr[0]) {
      case '0':
        AsynSrv_eot[1] = '\0';
      case '1':
        AsynSrv_eot[2] = '\0';
      case '2':
        AsynSrv_eot[3] = '\0';
      }
    } else {
      AsynSrv_errcode = ASYNSRV__BAD_PAR;
      return False;
    }
    txt_ptr = va_arg(ap, char *);       /* Get pntr to next parameter ident */
  }

  if (AsynSrv_errcode == 0)
    AsynSrv_call_depth--;
  return True;
}

/*
**---------------------------------------------------------------------------
**		AsynSrv_ErrInfo:  Return detailed status from last operation.
*/
void AsynSrv_ErrInfo(
/*	===============
*/ char **entry_txt,
                      int *errcode, int *my_errno, int *vaxc_errno)
{

  int i, j, k;
  char buff[80];

  if (AsynSrv_call_depth <= 0) {
    strcpy(AsynSrv_routine[0], "AsynSrv_no_error_detected");
    *errcode = 0;
    *my_errno = 0;
    *vaxc_errno = 0;
  } else {
    if (AsynSrv_call_depth > 1) {       /* Concatenate the names */
      for (i = 1; i < AsynSrv_call_depth; i++) {
        strcat(AsynSrv_routine[0], "/");
        StrJoin(AsynSrv_routine[0], sizeof(AsynSrv_routine),
                AsynSrv_routine[0], AsynSrv_routine[i]);
      }
    }
    *errcode = AsynSrv_errcode;
    *my_errno = AsynSrv_errno;
    *vaxc_errno = AsynSrv_vaxc_errno;
    switch (AsynSrv_errcode) {
    case ASYNSRV__BAD_BIND:
      strcpy(buff, "/ASYNSRV__BAD_BIND");
      break;
    case ASYNSRV__BAD_CMND_LEN:
      strcpy(buff, "/ASYNSRV__BAD_CMND_LEN");
      break;
    case ASYNSRV__BAD_CONNECT:
      strcpy(buff, "/ASYNSRV__BAD_CONNECT");
      break;
    case ASYNSRV__BAD_FLUSH:
      strcpy(buff, "/ASYNSRV__BAD_FLUSH");
      break;
    case ASYNSRV__BAD_HOST:
      strcpy(buff, "/ASYNSRV__BAD_HOST");
      break;
    case ASYNSRV__BAD_NOT_BCD:
      strcpy(buff, "/ASYNSRV__BAD_NOT_BCD");
      break;
    case ASYNSRV__BAD_PAR:
      strcpy(buff, "/ASYNSRV__BAD_PAR");
      break;
    case ASYNSRV__BAD_PROT_LVL:
      strcpy(buff, "/ASYNSRV__BAD_PROT_LVL");
      break;
    case ASYNSRV__BAD_RECV:
      strcpy(buff, "/ASYNSRV__BAD_RECV");
      break;
    case ASYNSRV__BAD_RECV_LEN:
      strcpy(buff, "/ASYNSRV__BAD_RECV_LEN");
      break;
    case ASYNSRV__BAD_RECV_NET:
      strcpy(buff, "/ASYNSRV__BAD_RECV_NET");
      break;
    case ASYNSRV__BAD_RECV_PIPE:
      strcpy(buff, "/ASYNSRV__BAD_RECV_PIPE");
      break;
    case ASYNSRV__BAD_RECV_UNKN:
      strcpy(buff, "/ASYNSRV__BAD_RECV_UNKN");
      break;
    case ASYNSRV__BAD_RECV1:
      strcpy(buff, "/ASYNSRV__BAD_RECV1");
      break;
    case ASYNSRV__BAD_RECV1_NET:
      strcpy(buff, "/ASYNSRV__BAD_RECV1_NET");
      break;
    case ASYNSRV__BAD_RECV1_PIPE:
      strcpy(buff, "/ASYNSRV__BAD_RECV1_PIPE");
      break;
    case ASYNSRV__BAD_REPLY:
      strcpy(buff, "/ASYNSRV__BAD_REPLY");
      break;
    case ASYNSRV__BAD_SEND:
      strcpy(buff, "/ASYNSRV__BAD_SEND");
      break;
    case ASYNSRV__BAD_SEND_LEN:
      strcpy(buff, "/ASYNSRV__BAD_SEND_LEN");
      break;
    case ASYNSRV__BAD_SEND_NET:
      strcpy(buff, "/ASYNSRV__BAD_SEND_NET");
      break;
    case ASYNSRV__BAD_SEND_PIPE:
      strcpy(buff, "/ASYNSRV__BAD_SEND_PIPE");
      break;
    case ASYNSRV__BAD_SEND_UNKN:
      strcpy(buff, "/ASYNSRV__BAD_SEND_UNKN");
      break;
    case ASYNSRV__BAD_SOCKET:
      strcpy(buff, "/ASYNSRV__BAD_SOCKET");
      break;
    case ASYNSRV__FORCED_CLOSED:
      strcpy(buff, "/ASYNSRV__FORCED_CLOSED");
      break;
    case ASYNSRV__NO_ROOM:
      strcpy(buff, "/ASYNSRV__NO_ROOM");
      break;
    default:
      sprintf(buff, "/ASYNSRV__unkn_err_code: %d", AsynSrv_errcode);
    }
    StrJoin(AsynSrv_routine[0], sizeof(AsynSrv_routine),
            AsynSrv_routine[0], buff);
  }
  *entry_txt = AsynSrv_routine[0];
  AsynSrv_call_depth = 0;
  AsynSrv_errcode = 0;
}

/*
**---------------------------------------------------------------------------
**		AsynSrv_Flush:  Send a Flush command to RS232C server.
*/
int AsynSrv_Flush(
/*	=============
*/ struct AsynSrv__info *asyn_info)
{

  int status;
  char cmnd[8], rply[8];
                    /*----------------------------------------------
		    ** Pre-set the routine name (in case of error)
		    */
  if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) {
    strcpy(AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_Flush");
    AsynSrv_call_depth++;
  }
                    /*----------------------------------------------
		    ** Do nothing if no connection - the connection gets
		    ** closed if an error is detected. The connection may
		    ** also be marked to have been forcefully closed.
		    */
  if (asyn_info->skt <= 0) {
    if ((AsynSrv_errcode == 0) && (asyn_info->skt < 0)) {
      AsynSrv_errcode = ASYNSRV__FORCED_CLOSED;
    }
    return False;
  }
                    /*----------------------------------------------
		    ** Send message and get reply.
		    */
  status = AsynSrv_SendSpecCmnd(asyn_info, "-004");

  if (AsynSrv_errcode == 0)
    AsynSrv_call_depth--;
  return status;
}

/*
**---------------------------------------------------------------------------
**		AsynSrv_GetLenTerm:  Get length and terminator of given
**				     reply from reply buffer.
*/
int AsynSrv_GetLenTerm(
/*	==================
*/ struct AsynSrv__info *asyn_info,
                        struct RS__RespStruct *rcve_buff, char *rply,   /* In: Addr of a reply as got ..
                                                                         **     .. got from _GetReply */
                        int *len,       /* Out: The returned length */
                        char *term)
{                               /* Out: The returned t'nator */

  int i;

  i = sscanf((rply - asyn_info->rply_hdr_len - 1), asyn_info->rply_fmt,
             len);
  *len = (i == 1) ? (*len - 2) : 0;
  *term = *(rply - 1);

  if (i != 1)
    return False;
  return True;
}

/*
**---------------------------------------------------------------------------
**		AsynSrv_GetReply:  Get next reply from a reply buffer.
*/
char *AsynSrv_GetReply(
/*	 ================
*/ struct AsynSrv__info *asyn_info,
                        struct RS__RespStruct *rcve_buff, char *last_rply)
{

  char *pntr = NULL;
  int i, rply_len;

  if (last_rply == NULL) {      /* Start with first reply? */
    /* Yes */
    asyn_info->n_replies = 1;
    if (asyn_info->max_replies > 0) {
      pntr = rcve_buff->u.rplys;
      pntr = pntr + 1 + asyn_info->rply_hdr_len;
    }
  } else {                      /* No - get next reply */
    if (asyn_info->n_replies < asyn_info->max_replies) {        /* If there is one */
      i = sscanf((last_rply - asyn_info->rply_hdr_len - 1),
                 asyn_info->rply_fmt, &rply_len);
      if ((i == 1) && (rply_len >= 0)) {
        pntr = last_rply + rply_len + asyn_info->rply_hdr_len;
      }
    }
  }
  return pntr;
}

/*
**---------------------------------------------------------------------------
**		AsynSrv_Open:  Open a connection to an RS-232-C Server.
*/
int AsynSrv_Open(
/*	============
*/ struct AsynSrv__info *asyn_info)
{

  int i, status;
  int my_skt;
  char old_time_out[4];
  union {
    char chars[4];
    int val;
  } time_out;
  char buff[128];
  struct RS__MsgStruct s_buff;
  struct RS__RespStruct r_buff;
  unsigned int oto_len, oto_status;
  struct hostent *rmt_hostent;
  struct in_addr *rmt_inet_addr_pntr;
  struct in_addr rmt_inet_addr;
  int rmt_sockname_len;
  struct sockaddr_in lcl_sockname;
  struct sockaddr_in rmt_sockname;

  char *errtxt_ptr;
  int errcode, my_errno, my_vaxc_errno;
/*--------------------------------------------------------
*/
  asyn_info->skt = 0;
  asyn_info->msg_id = 0;
/*--------------------------------------------------------
**			Initialise the error info stack and pre-set the
**			routine name (in case of error).
*/
  AsynSrv_errcode = AsynSrv_errno = AsynSrv_vaxc_errno = 0;
  strcpy(AsynSrv_routine[0], "AsynSrv_Open");
  AsynSrv_call_depth = 1;
/*--------------------------------------------------------
**		Is there room for a new AsynSrv_Open table entry?
*/
  if (AsynSrv_n_active >= MAX_OPEN) {
    AsynSrv_errcode = ASYNSRV__NO_ROOM; /* There isn't! */
    return False;
  }
/*--------------------------------------------------------
**		See if a table entry for this connection already exists.
*/
  for (i = 0; i < AsynSrv_n_cnct; i++) {
    if (AsynSrv_HPS_list[i].status != 0)
      continue;
    if (AsynSrv_HPS_list[i].port != asyn_info->port)
      continue;
    if (strcmp(AsynSrv_HPS_list[i].host, asyn_info->host) == 0)
      break;
  }
  if (i < AsynSrv_n_cnct) {     /* Did we find an entry? */
    /* Yes */
    AsynSrv_HPS_list[i].usage_cnt++;    /* Up the usage count and .. */
    AsynSrv_active[AsynSrv_n_active] =  /* .. remember the open and .. */
        asyn_info;
    AsynSrv_n_active++;
    asyn_info->skt =            /* .. return the socket. */
        AsynSrv_HPS_list[i].skt;
    if (asyn_info->chan < 0)
      asyn_info->chan = 0;
    if (asyn_info->chan > 255)
      asyn_info->chan = 0;
    sprintf(buff, "%04d", asyn_info->chan);     /* Convert channel # to ASCII */
    memcpy(asyn_info->chan_char, buff, sizeof(asyn_info->chan_char));

    asyn_info->protocol_code = AsynSrv_HPS_list[i].protocol_code;
    memcpy(asyn_info->protocol_id,
           AsynSrv_HPS_list[i].protocol_id,
           sizeof(asyn_info->protocol_id));
    asyn_info->cmnd_hdr_len = AsynSrv_HPS_list[i].cmnd_hdr_len;
    sprintf(asyn_info->cmnd_fmt, "%%0%dd", asyn_info->cmnd_hdr_len);
    asyn_info->rply_hdr_len = AsynSrv_HPS_list[i].rply_hdr_len;
    sprintf(asyn_info->rply_fmt, "%%%dd", asyn_info->rply_hdr_len);

    sprintf(buff, "%04d", AsynSrv_msec_tmo / 100);      /* Set dflt time-out ..
                                                         ** ..(deci-secs) */
    memcpy(asyn_info->tmo, buff, sizeof(asyn_info->tmo));

    memcpy(asyn_info->eot,      /* Set dflt terminator(s) */
           AsynSrv_eot, sizeof(asyn_info->eot));

    asyn_info->max_replies = asyn_info->n_replies = 0;
    asyn_info->idleHandler = NULL;
    AsynSrv_call_depth--;
    return True;
  }
/*--------------------------------------------------------
**		There is no existing connection. Open a new one.
*/
  status = AsynSrv_OpenNew(asyn_info);
  if (!status)
    return False;
/*--------------------------------------------------------
**		Allow the entry to be shared (i.e. status = 0)
*/
  AsynSrv_HPS_list[AsynSrv_n_cnct - 1].status = 0;
/*--------------------------------------------------------
*/
  AsynSrv_errcode = AsynSrv_errno = AsynSrv_vaxc_errno = 0;
  AsynSrv_call_depth = 0;
  return True;
}

/*
**---------------------------------------------------------------------------
**		AsynSrv_OpenNew:  Open a new connection to an RS-232-C Server.
*/
int AsynSrv_OpenNew(
/*	===============
*/ struct AsynSrv__info *asyn_info)
{

  int i, status;
  int my_skt;
  char old_time_out[4];
  union {
    char chars[4];
    int val;
  } time_out;
  char buff[128];
  struct RS__MsgStruct s_buff;
  struct RS__RespStruct r_buff;
  unsigned int oto_len, oto_status;
  struct hostent *rmt_hostent;
  struct in_addr *rmt_inet_addr_pntr;
  struct in_addr rmt_inet_addr;
  int rmt_sockname_len;
  struct sockaddr_in lcl_sockname;
  struct sockaddr_in rmt_sockname;

  char *errtxt_ptr;
  int errcode, my_errno, my_vaxc_errno;
/*--------------------------------------------------------
*/
  asyn_info->skt = 0;
  asyn_info->msg_id = 0;
/*--------------------------------------------------------
**			Initialise the error info stack and pre-set the
**			routine name (in case of error).
*/
  AsynSrv_errcode = AsynSrv_errno = AsynSrv_vaxc_errno = 0;
  if ((AsynSrv_call_depth == 1) &&
      (strcmp(AsynSrv_routine[0], "AsynSrv_Open") == 0)) {
    strcpy(AsynSrv_routine[1], "AsynSrv_OpenNew");
    AsynSrv_call_depth = 2;
  } else {
    strcpy(AsynSrv_routine[0], "AsynSrv_OpenNew");
    AsynSrv_call_depth = 1;
  }
/*--------------------------------------------------------
**		Is there room for a new AsynSrv_Open table entry?
*/
  if (AsynSrv_n_active >= MAX_OPEN) {
    AsynSrv_errcode = ASYNSRV__NO_ROOM; /* There isn't! */
    return False;
  }
/*--------------------------------------------------------
**		Is there room for a new connection entry?
*/
  if (AsynSrv_n_cnct >= AsynSrv_MAX_LINK) {
    AsynSrv_errcode = ASYNSRV__NO_ROOM; /* There isn't! */
    return False;
  }
/*--------------------------------------------------------
**		There's room for a new connection but, before going any
**		further, do some quick checks on values in asyn_info.
*/
  if ((asyn_info->port <= 0) ||
      (asyn_info->port > 65535) ||
      (asyn_info->chan < 0) || (asyn_info->chan > 255)) {
    AsynSrv_errcode = ASYNSRV__BAD_PAR; /* Something is bad! */
    return False;
  }
/*--------------------------------------------------------
**		Set up a new connection.
*/
  StrJoin(AsynSrv_HPS_list[AsynSrv_n_cnct].host,
          sizeof(AsynSrv_HPS_list[AsynSrv_n_cnct].host),
          asyn_info->host, "");
  AsynSrv_HPS_list[AsynSrv_n_cnct].port = asyn_info->port;
                /*---------------------------
		** Get the Internet address of the server.
		*/
  rmt_inet_addr.s_addr = inet_addr(asyn_info->host);
  if (rmt_inet_addr.s_addr != -1) {
    rmt_inet_addr_pntr = &rmt_inet_addr;
  } else {
    rmt_hostent = gethostbyname(asyn_info->host);
    if (rmt_hostent == NULL) {
      AsynSrv_errcode = ASYNSRV__BAD_HOST;
      GetErrno(&AsynSrv_errno, &AsynSrv_vaxc_errno);    /* Save errno info */
      fprintf(stderr,
              "\nAsynSrv_OpenNew/gethostbyname: Failed to get Internet "
              "address of \"%s\".\n", asyn_info->host);
      return False;
    }
    rmt_inet_addr_pntr = (struct in_addr *) rmt_hostent->h_addr_list[0];
  }
                /*---------------------------
		** Create a TCP/IP socket for connecting to server and bind it.
		*/
  my_skt = socket(AF_INET, SOCK_STREAM, 0);
  if (my_skt <= 0) {
    AsynSrv_errcode = ASYNSRV__BAD_SOCKET;
    GetErrno(&AsynSrv_errno, &AsynSrv_vaxc_errno);      /* Save the errno info */
    fprintf(stderr,
            "\nAsynSrv_OpenNew/socket: Failed to create a socket.\n");
    return False;
  }
  lcl_sockname.sin_family = AF_INET;
  lcl_sockname.sin_port = htons(0);
  lcl_sockname.sin_addr.s_addr = 0;
  status = bind(my_skt, (struct sockaddr *) &lcl_sockname,
                sizeof(lcl_sockname));
  if (status == -1) {
    close(my_skt);
    AsynSrv_errcode = ASYNSRV__BAD_BIND;
    GetErrno(&AsynSrv_errno, &AsynSrv_vaxc_errno);      /* Save the errno info */
    fprintf(stderr, "\nAsynSrv_OpenNew/bind: Failed to bind socket.\n");
    return False;
  }
                /*---------------------------
		** Set short time-out (VMS systems only)
		*/
#ifdef	__VMS
  oto_len = sizeof(old_time_out);       /* Save current time-out first */
  oto_status = getsockopt(my_skt, IPPROTO_TCP, UCX$C_TCP_PROBE_IDLE,
                          old_time_out, &oto_len);

  if (oto_status == 0) {
    time_out.val = AsynSrv_connect_tmo; /* Set new time-out */
    status = setsockopt(my_skt, IPPROTO_TCP, UCX$C_TCP_PROBE_IDLE,
                        time_out.chars, sizeof(time_out));
  }
#endif
                /*---------------------------
		** Connect to RS-232-C Server.
		*/
  rmt_sockname_len = sizeof(rmt_sockname);
  rmt_sockname.sin_family = AF_INET;
  rmt_sockname.sin_port = htons(asyn_info->port);
  rmt_sockname.sin_addr.s_addr = rmt_inet_addr_pntr->s_addr;
  status = connect(my_skt, (struct sockaddr *) &rmt_sockname,
                   sizeof(rmt_sockname));
  if (status != 0) {
    close(my_skt);
    AsynSrv_errcode = ASYNSRV__BAD_CONNECT;
    GetErrno(&AsynSrv_errno, &AsynSrv_vaxc_errno);      /* Save the errno info */
    fprintf(stderr,
            "\nAsynSrv_OpenNew/connect: Failed to connect to server.\n");
    perror("AsynSrv_OpenNew");
    return False;
  }
                /*---------------------------
		** Restore time-out (VMS only)
		*/
#ifdef	__VMS
  if (oto_status == 0) {
    setsockopt(my_skt, IPPROTO_TCP, UCX$C_TCP_PROBE_IDLE,
               old_time_out, oto_len);
  }
#endif
                /*---------------------------------------------------
		** Setup the defaults in the AsynSrv__info data structure.
		*/
  asyn_info->skt = my_skt;      /* Return socket number to caller */

  asyn_info->protocol_code = 0; /* Ensure protocol_code set to "unknown" */
  memcpy(asyn_info->protocol_id, "\0\0\0\0",
         sizeof(asyn_info->protocol_id));
  asyn_info->cmnd_hdr_len = 4;
  strcpy(asyn_info->cmnd_fmt, "%04d");
  asyn_info->rply_hdr_len = 4;
  strcpy(asyn_info->rply_fmt, "%4d");

  sprintf(buff, "%04d", asyn_info->chan);       /* Convert channel # to ASCII */
  memcpy(asyn_info->chan_char, buff, sizeof(asyn_info->chan_char));

  sprintf(buff, "%04d", AsynSrv_msec_tmo / 100);        /* Set dflt time-out ..
                                                         ** .. (deci-secs) */
  memcpy(asyn_info->tmo, buff, sizeof(asyn_info->tmo));

  memcpy(asyn_info->eot, AsynSrv_eot, sizeof(asyn_info->eot));  /* Set ..
                                                                 ** .. dflt terminator(s) */
  asyn_info->max_replies = 0;
  asyn_info->n_replies = 0;
  asyn_info->idleHandler = NULL;
  /*
   ** Send a null command buffer to the server. This should give
   ** a "protocol mismatch" error response and from this we can get
   ** the actual protocol level supported by the server.
   */
  status = AsynSrv_SendCmnds(asyn_info, &s_buff, &r_buff, NULL);
  if (!status && (AsynSrv_errcode == ASYNSRV__BAD_PROT_LVL)) {
    /*
     ** As expected, we got a "protocol mismatch" error.
     ** Save the server's protocol level for future use.
     */
    memcpy(asyn_info->protocol_id, r_buff.s_pcol_lvl,
           sizeof(r_buff.s_pcol_lvl));
    if (strncmp(r_buff.s_pcol_lvl, RS__PROTOCOL_ID_V01B,
                strlen(RS__PROTOCOL_ID_V01B)) == 0) {
      asyn_info->protocol_code = RS__PROTOCOL_CODE_V01B;
      asyn_info->cmnd_hdr_len = 4;
      strcpy(asyn_info->cmnd_fmt, "%04d");
      asyn_info->rply_hdr_len = 4;
      strcpy(asyn_info->rply_fmt, "%4d");
    } else if (strncmp(r_buff.s_pcol_lvl, RS__PROTOCOL_ID,
                       strlen(RS__PROTOCOL_ID)) == 0) {
      asyn_info->protocol_code = RS__PROTOCOL_CODE;
      asyn_info->cmnd_hdr_len = 2;
      strcpy(asyn_info->cmnd_fmt, "%02d");
      asyn_info->rply_hdr_len = 2;
      strcpy(asyn_info->rply_fmt, "%2d");
    } else {
      close(my_skt);
      asyn_info->skt = 0;
      fprintf(stderr,
              "\nAsynSrv_OpenNew: Server protocol level is unrecognised.\n"
              "       Server level is \"%4s\"\n", r_buff.s_pcol_lvl);
      return False;
    }
  } else {
    close(my_skt);
    asyn_info->skt = 0;
    AsynSrv_errcode = ASYNSRV__BAD_PROT_LVL;
    fprintf(stderr,
            "\nAsynSrv_OpenNew: Problem getting protocol level of Server!\n");
    return False;
  }
                /*---------------------------------------------------
		** Complete the setup of the connection table entry
		*/
  AsynSrv_HPS_list[AsynSrv_n_cnct].skt = my_skt;
  AsynSrv_HPS_list[AsynSrv_n_cnct].protocol_code =
      asyn_info->protocol_code;
  memcpy(AsynSrv_HPS_list[AsynSrv_n_cnct].protocol_id,
         asyn_info->protocol_id, sizeof(asyn_info->protocol_id));
  AsynSrv_HPS_list[AsynSrv_n_cnct].cmnd_hdr_len = asyn_info->cmnd_hdr_len;
  AsynSrv_HPS_list[AsynSrv_n_cnct].rply_hdr_len = asyn_info->rply_hdr_len;
  AsynSrv_HPS_list[AsynSrv_n_cnct].usage_cnt = 1;
  AsynSrv_HPS_list[AsynSrv_n_cnct].status = 1;
  AsynSrv_n_cnct++;

  AsynSrv_active[AsynSrv_n_active] =    /* Remember the open in case .. */
      asyn_info;                /* .. there's a force-exit */
  AsynSrv_n_active++;

  AsynSrv_errcode = AsynSrv_errno = AsynSrv_vaxc_errno = 0;
  AsynSrv_call_depth--;
  return True;
}

/*
**---------------------------------------------------------------------------
**		AsynSrv_SendCmnds:  Send commands to RS232C server.
*/
int AsynSrv_SendCmnds(
/*	=================
*/ struct AsynSrv__info *asyn_info,
                       struct RS__MsgStruct *send_buff,
                       struct RS__RespStruct *rcve_buff, ...)
{                               /* Now we have list of commands -
                                 **   char *txt   = pntr to cmnd strng
                                 ** Terminate list with *txt = NULL.
                                 */
  int i, status, c_len, size, max_size, ncmnds;
  int bytes_to_come = 1, bytes_left;
  char *nxt_byte_ptr;
  char err_text[80];
  char text[20];
  va_list ap;                   /* Pointer to variable args */
  char *txt_ptr;
  char *cmnd_lst_ptr;
                    /*----------------------------------------------
		    ** Pre-set the routine name (in case of error)
		    */
  if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) {
    strcpy(AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_SendCmnds");
    AsynSrv_call_depth++;
  }
                    /*----------------------------------------------
		    ** Do nothing if no connection - the connection gets
		    ** closed if an error is detected. The connection may
		    ** also be marked to have been forcefully closed.
		    */
  if (asyn_info->skt <= 0) {
    memset(rcve_buff, '0', sizeof(*rcve_buff));
    if ((AsynSrv_errcode == 0) && (asyn_info->skt < 0)) {
      AsynSrv_errcode = ASYNSRV__FORCED_CLOSED;
    }
    return False;
  }
                    /*----------------------------------------------
		    ** Build message for server from the list of commands.
		    */
  asyn_info->max_replies = asyn_info->n_replies = 0;

  asyn_info->msg_id++;          /* Set up an incrementing message id */
  if (asyn_info->msg_id > 9999)
    asyn_info->msg_id = 1;
  sprintf(send_buff->msg_id, "%04d", asyn_info->msg_id);

  memcpy(send_buff->c_pcol_lvl, asyn_info->protocol_id,
         sizeof(send_buff->c_pcol_lvl));

  memcpy(send_buff->serial_port, asyn_info->chan_char,
         sizeof(send_buff->serial_port));

  memcpy(send_buff->tmo, asyn_info->tmo, sizeof(send_buff->tmo));

  memcpy(send_buff->terms, asyn_info->eot, sizeof(send_buff->terms));

  memcpy(send_buff->n_cmnds, "0000", sizeof(send_buff->n_cmnds));

  va_start(ap, rcve_buff);      /* Set up var arg machinery */

  txt_ptr = va_arg(ap, char *); /* Get pntr to next cmnd string */
  ncmnds = 0;
  cmnd_lst_ptr = &send_buff->cmnds[0];
  bytes_left = sizeof(*send_buff) -
      OffsetOf(struct RS__MsgStruct, cmnds[0]);

  while (txt_ptr != NULL) {
    c_len = strlen(txt_ptr);
    size = asyn_info->cmnd_hdr_len + c_len;
    if (size > bytes_left) {
      AsynSrv_errcode = ASYNSRV__BAD_SEND_LEN;  /* Too much to send */
      fprintf(stderr, "\nAsynSrv_SendCmnds/send: too much to send"
              " - request ignored.\n");
      memset(rcve_buff->msg_size, '0', sizeof(rcve_buff->msg_size));
      return False;
    } else {
      sprintf(cmnd_lst_ptr, asyn_info->cmnd_fmt, c_len);
      if (cmnd_lst_ptr[asyn_info->cmnd_hdr_len] != '\0') {
        AsynSrv_errcode = ASYNSRV__BAD_CMND_LEN;
        fprintf(stderr, "\nAsynSrv_SendCmnds/send: command too long -"
                " - request ignored.\n");
        memset(rcve_buff->msg_size, '0', sizeof(rcve_buff->msg_size));
        return False;
      }
      cmnd_lst_ptr += asyn_info->cmnd_hdr_len;
      strcpy(cmnd_lst_ptr, txt_ptr);
      cmnd_lst_ptr += c_len;
      ncmnds++;
      bytes_left = bytes_left - size;
      txt_ptr = va_arg(ap, char *);
    }
  }
  sprintf(text, "%04d", ncmnds);
  memcpy(send_buff->n_cmnds, text, sizeof(send_buff->n_cmnds));

  size = cmnd_lst_ptr - send_buff->msg_id;
  size = (size + 3) & (~3);     /* Round up to multiple of 4 */
  sprintf(text, "%04d", size);
  memcpy(send_buff->msg_size, text, sizeof(send_buff->msg_size));

  size += sizeof(send_buff->msg_size);
  status = send(asyn_info->skt, (char *) send_buff, size, 0);
  if (status != size) {
    GetErrno(&AsynSrv_errno, &AsynSrv_vaxc_errno);
    if (status == 0) {
      AsynSrv_errcode = ASYNSRV__BAD_SEND;      /* Server exited (probably) */
      fprintf(stderr,
              "\nAsynSrv_SendCmnds/send: probable network problem");
    } else if (status == -1) {
      if (AsynSrv_errno == EPIPE) {
        AsynSrv_errcode = ASYNSRV__BAD_SEND_PIPE;       /* Server exited (probably) */
        fprintf(stderr, "\nAsynSrv_SendCmnds/send: broken network pipe");
      } else {
        AsynSrv_errcode = ASYNSRV__BAD_SEND_NET;        /* It's some other net problem */
        perror("AsynSrv_SendCmnds/send");
      }
    } else {
      AsynSrv_errcode = ASYNSRV__BAD_SEND_UNKN; /* TCP/IP problems */
      fprintf(stderr, "\nAsynSrv_SendCmnds/send: probable TCP/IP problem");
    }
    AsynSrv_Close(asyn_info, True);     /* Force close TCP/IP connection */
    fprintf(stderr, " - link to server force-closed.\n");
    return False;
  }

  if (asyn_info->idleHandler != NULL) { /* MZ. */
    sscanf(asyn_info->tmo, "%4d", &i);  /* Decode timeout from ASCII ..
                                         ** .. encoded deci-sec */
    asyn_info->idleHandler(i * 150, asyn_info->skt);    /* Wait for an event ..
                                                         ** .. on asyn_info->skt or a ..
                                                         ** .. timeout of 1.5*tmo */
  }

  memset(rcve_buff, 0, sizeof(*rcve_buff));
  size = sizeof(rcve_buff->msg_size);
  memset(rcve_buff->msg_size, 0, size);
  status = recv(asyn_info->skt, rcve_buff->msg_size, size, 0);
  if (status != size) {
    GetErrno(&AsynSrv_errno, &AsynSrv_vaxc_errno);
    if (status == 0) {
      AsynSrv_errcode = ASYNSRV__BAD_RECV;      /* Server exited (probably) */
      fprintf(stderr,
              "\nAsynSrv_SendCmnds/recv: probable network problem");
    } else if (status == -1) {
      if (AsynSrv_errno == EPIPE) {
        AsynSrv_errcode = ASYNSRV__BAD_RECV_PIPE;       /* Server exited (probably) */
        fprintf(stderr, "\nAsynSrv_SendCmnds/recv: broken network pipe");
      } else {
        AsynSrv_errcode = ASYNSRV__BAD_RECV_NET;        /* It's some other net problem */
        perror("AsynSrv_SendCmnds/recv");
      }
    } else {
      AsynSrv_errcode = ASYNSRV__BAD_RECV_UNKN; /* TCP/IP problems */
      fprintf(stderr, "\nAsynSrv_SendCmnds/recv: probable TCP/IP problem");
    }
    AsynSrv_Close(asyn_info, True);     /* Force close TCP/IP connection */
    fprintf(stderr, " - link to server force-closed.\n");
    return False;
  }
  if (sscanf(rcve_buff->msg_size, "%4d", &bytes_to_come) != 1) {
    AsynSrv_errcode = ASYNSRV__BAD_NOT_BCD;     /* Header not an ASCII BCD integer */
    AsynSrv_Close(asyn_info, True);     /* Force close TCP/IP connection */
    fprintf(stderr, "\nAsynSrv_SendCmnds/recv: non-BCD byte count"
            " - link to server force-closed.\n");
    return False;
  }
  max_size = sizeof(*rcve_buff) - size;
  if (bytes_to_come > max_size) {
    AsynSrv_errcode = ASYNSRV__BAD_RECV_LEN;
    fprintf(stderr,
            "\nAsynSrv_SendCmnds/recv: pending message length too big"
            " - flushing ...\n");
    nxt_byte_ptr = &rcve_buff->msg_size[size];
    while (bytes_to_come > 0) { /* Flush out the incoming message */
      bytes_left = bytes_to_come;
      if (bytes_left > max_size)
        bytes_left = max_size;
      status = recv(asyn_info->skt, nxt_byte_ptr, bytes_left, 0);
      if (status <= 0) {
        AsynSrv_errcode = ASYNSRV__BAD_FLUSH;   /* TCP/IP problem during flush */
        GetErrno(&AsynSrv_errno, &AsynSrv_vaxc_errno);
        AsynSrv_Close(asyn_info, True); /* Force close TCP/IP connection */
        fprintf(stderr, "\nAsynSrv_SendCmnds/recv: network problem during"
                " flush.\nLink to server force-closed.\n");
        return False;
      }
      bytes_to_come = bytes_to_come - status;
    }
    fprintf(stderr, "\n flushed OK.\n");
    memset(rcve_buff->msg_size, '0', sizeof(rcve_buff->msg_size));
    return False;
  } else {
    nxt_byte_ptr = &rcve_buff->msg_size[size];
    bytes_left = bytes_to_come;
    while (bytes_left > 0) {    /* Read the rest of the response */
      status = recv(asyn_info->skt, nxt_byte_ptr, bytes_left, 0);
      if (status <= 0) {
        GetErrno(&AsynSrv_errno, &AsynSrv_vaxc_errno);
        if (status == 0) {
          AsynSrv_errcode = ASYNSRV__BAD_RECV1; /* Server exited (probably) */
          fprintf(stderr, "\nAsynSrv_SendCmnds/recv/1: probable network "
                  "problem");
        } else {
          if (AsynSrv_errno == EPIPE) {
            AsynSrv_errcode = ASYNSRV__BAD_RECV1_PIPE;  /* Server exited (probably) */
            fprintf(stderr,
                    "\nAsynSrv_SendCmnds/recv/1: broken network pipe");
          } else {
            AsynSrv_errcode = ASYNSRV__BAD_RECV1_NET;   /* It's some other net fault */
            perror("AsynSrv_SendCmnds/recv/1");
          }
        }
        AsynSrv_Close(asyn_info, True); /* Force close TCP/IP connection */
        fprintf(stderr, " - link to server force-closed.\n");
        return False;
      }
      bytes_left = bytes_left - status;
      nxt_byte_ptr = nxt_byte_ptr + status;
    }
    if (strncmp(asyn_info->protocol_id, rcve_buff->s_pcol_lvl,
                sizeof(rcve_buff->s_pcol_lvl)) != 0) {
      AsynSrv_errcode = ASYNSRV__BAD_PROT_LVL;  /* Protocol level is bad */
      return False;
    }
    if ((sscanf(rcve_buff->n_rply, "%4d", &asyn_info->max_replies) != 1) ||
        (asyn_info->max_replies < 0)) {
      AsynSrv_errcode = ASYNSRV__BAD_REPLY;     /* Reply is bad */
      if (AsynSrv_call_depth < 5) {     /* Add reply to routine stack */
        bytes_to_come = bytes_to_come + 4;
        if (bytes_to_come >= sizeof(AsynSrv_routine[0]))
          bytes_to_come = sizeof(AsynSrv_routine[0]) - 1;
        for (i = 0; i < bytes_to_come; i++) {
          if (rcve_buff->msg_size[i] == '\0')
            rcve_buff->msg_size[i] = '.';
        }
        rcve_buff->msg_size[bytes_to_come] = '\0';
        strcpy(AsynSrv_routine[AsynSrv_call_depth], rcve_buff->msg_size);
        AsynSrv_call_depth++;
      }
      return False;
    }
  }
  if (AsynSrv_errcode == 0)
    AsynSrv_call_depth--;
  return True;
}

/*
**---------------------------------------------------------------------------
**		AsynSrv_SendCmndsBig:  Same as AsynSrv_SendCmnds but with
**					user defined buffer sizes.
*/
int AsynSrv_SendCmndsBig(
/*	====================
*/ struct AsynSrv__info *asyn_info,
                          struct RS__MsgStruct *send_buff,
                          int send_buff_size,
                          struct RS__RespStruct *rcve_buff,
                          int rcve_buff_size, ...)
{                               /* Now we have list of commands -
                                 **   char *txt   = pntr to cmnd strng
                                 ** Terminate list with *txt = NULL.
                                 */
  int i, status, size, max_size, ncmnds;
  int bytes_to_come, bytes_left;
  char *nxt_byte_ptr;
  char err_text[80];
  char text[20];
  va_list ap;                   /* Pointer to variable args */
  int *c_len, s_len;
  char *txt_ptr;
  char *cmnd_lst_ptr;
                    /*----------------------------------------------
		    ** Pre-set the routine name (in case of error)
		    */
  if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) {
    strcpy(AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_SendCmndsBig");
    AsynSrv_call_depth++;
  }
                    /*----------------------------------------------
		    ** Do nothing if no connection - the connection gets
		    ** closed if an error is detected. The connection may
		    ** also be marked to have been forcefully closed.
		    */
  if (asyn_info->skt <= 0) {
    memset(rcve_buff->msg_size, '0', sizeof(rcve_buff->msg_size));
    if ((AsynSrv_errcode == 0) && (asyn_info->skt < 0)) {
      AsynSrv_errcode = ASYNSRV__FORCED_CLOSED;
    }
    return False;
  }
                    /*----------------------------------------------
		    ** Build message for server from the list of commands.
		    */
  if (send_buff_size < 64 || rcve_buff_size < 64) {
    AsynSrv_errcode = ASYNSRV__BAD_PAR;
    return False;
  }

  asyn_info->max_replies = asyn_info->n_replies = 0;

  asyn_info->msg_id++;          /* Set up an incrementing message id */
  if (asyn_info->msg_id > 9999)
    asyn_info->msg_id = 1;
  sprintf(send_buff->msg_id, "%04d", asyn_info->msg_id);

  memcpy(send_buff->c_pcol_lvl, asyn_info->protocol_id,
         sizeof(send_buff->c_pcol_lvl));

  memcpy(send_buff->serial_port, asyn_info->chan_char,
         sizeof(send_buff->serial_port));

  memcpy(send_buff->tmo, asyn_info->tmo, sizeof(send_buff->tmo));

  memcpy(send_buff->terms, asyn_info->eot, sizeof(send_buff->terms));

  memcpy(send_buff->n_cmnds, "0000", sizeof(send_buff->n_cmnds));

  va_start(ap, rcve_buff_size); /* Set up var arg machinery */

  c_len = va_arg(ap, int *);    /* Get pntr to length of next cmnd string */
  ncmnds = 0;
  cmnd_lst_ptr = &send_buff->cmnds[0];
  bytes_left = send_buff_size - OffsetOf(struct RS__MsgStruct, cmnds[0]);

  while (c_len != NULL) {
    txt_ptr = va_arg(ap, char *);
    s_len = *c_len;
    if (s_len <= 0)
      s_len = strlen(txt_ptr);
    size = asyn_info->cmnd_hdr_len + s_len;
    if (size > bytes_left) {
      AsynSrv_errcode = ASYNSRV__BAD_SEND_LEN;  /* Too much to send */
      fprintf(stderr, "\nAsynSrv_SendCmndsBig/send: too much to send"
              " - request ignored.\n");
      memset(rcve_buff->msg_size, '0', sizeof(rcve_buff->msg_size));
      return False;
    }
    sprintf(cmnd_lst_ptr, asyn_info->cmnd_fmt, s_len);
    if (cmnd_lst_ptr[asyn_info->cmnd_hdr_len] != '\0') {
      AsynSrv_errcode = ASYNSRV__BAD_CMND_LEN;
      fprintf(stderr, "\nAsynSrv_SendCmndsBig/send: command too long -"
              " - request ignored.\n");
      memset(rcve_buff->msg_size, '0', sizeof(rcve_buff->msg_size));
      return False;
    }
    cmnd_lst_ptr += asyn_info->cmnd_hdr_len;
    memcpy(cmnd_lst_ptr, txt_ptr, s_len);
    cmnd_lst_ptr += s_len;
    ncmnds++;
    bytes_left = bytes_left - size;
    c_len = va_arg(ap, int *);
  }
  sprintf(text, "%04d", ncmnds);
  memcpy(send_buff->n_cmnds, text, sizeof(send_buff->n_cmnds));

  size = cmnd_lst_ptr - send_buff->msg_id;
  size = (size + 3) & (~3);     /* Round up to multiple of 4 */
  sprintf(text, "%04d", size);
  memcpy(send_buff->msg_size, text, sizeof(send_buff->msg_size));

  size += sizeof(send_buff->msg_size);
  status = send(asyn_info->skt, (char *) send_buff, size, 0);
  if (status != size) {
    GetErrno(&AsynSrv_errno, &AsynSrv_vaxc_errno);
    if (status == 0) {
      AsynSrv_errcode = ASYNSRV__BAD_SEND;      /* Server exited (probably) */
      fprintf(stderr,
              "\nAsynSrv_SendCmndsBig/send: probable network problem");
    } else if (status == -1) {
      if (AsynSrv_errno == EPIPE) {
        AsynSrv_errcode = ASYNSRV__BAD_SEND_PIPE;       /* Server exited (probably) */
        fprintf(stderr,
                "\nAsynSrv_SendCmndsBig/send: broken network pipe");
      } else {
        AsynSrv_errcode = ASYNSRV__BAD_SEND_NET;        /* It's some other net problem */
        perror("AsynSrv_SendCmndsBig/send");
      }
    } else {
      AsynSrv_errcode = ASYNSRV__BAD_SEND_UNKN; /* TCP/IP problems */
      fprintf(stderr,
              "\nAsynSrv_SendCmndsBig/send: probable TCP/IP problem");
    }
    AsynSrv_Close(asyn_info, True);     /* Force close TCP/IP connection */
    fprintf(stderr, " - link to server force-closed.\n");
    return False;
  }

  size = sizeof(rcve_buff->msg_size);
  status = recv(asyn_info->skt, rcve_buff->msg_size, size, 0);
  if (status != size) {
    GetErrno(&AsynSrv_errno, &AsynSrv_vaxc_errno);
    if (status == 0) {
      AsynSrv_errcode = ASYNSRV__BAD_RECV;      /* Server exited (probably) */
      fprintf(stderr,
              "\nAsynSrv_SendCmndsBig/recv: probable network problem");
    } else if (status == -1) {
      if (AsynSrv_errno == EPIPE) {
        AsynSrv_errcode = ASYNSRV__BAD_RECV_PIPE;       /* Server exited (probably) */
        fprintf(stderr,
                "\nAsynSrv_SendCmndsBig/recv: broken network pipe");
      } else {
        AsynSrv_errcode = ASYNSRV__BAD_RECV_NET;        /* It's some other net problem */
        perror("AsynSrv_SendCmndsBig/recv");
      }
    } else {
      AsynSrv_errcode = ASYNSRV__BAD_RECV_UNKN; /* TCP/IP problems */
      fprintf(stderr,
              "\nAsynSrv_SendCmndsBig/recv: probable TCP/IP problem");
    }
    AsynSrv_Close(asyn_info, True);     /* Force close TCP/IP connection */
    fprintf(stderr, " - link to server force-closed.\n");
    return False;
  }
  if (sscanf(rcve_buff->msg_size, "%4d", &bytes_to_come) != 1) {
    AsynSrv_errcode = ASYNSRV__BAD_NOT_BCD;     /* Header not an ASCII BCD integer */
    AsynSrv_Close(asyn_info, True);     /* Force close TCP/IP connection */
    fprintf(stderr, "\nAsynSrv_SendCmndsBig/recv: non-BCD byte count"
            " - link to server force-closed.\n");
    return False;
  }
  max_size = rcve_buff_size - size;
  if (bytes_to_come > max_size) {
    AsynSrv_errcode = ASYNSRV__BAD_RECV_LEN;
    fprintf(stderr, "\nAsynSrv_SendCmndsBig/recv: pending message length "
            "too big - flushing ...\n");
    nxt_byte_ptr = &rcve_buff->msg_size[size];
    while (bytes_to_come > 0) { /* Flush out the incoming message */
      bytes_left = bytes_to_come;
      if (bytes_left > max_size)
        bytes_left = max_size;
      status = recv(asyn_info->skt, nxt_byte_ptr, bytes_left, 0);
      if (status <= 0) {
        AsynSrv_errcode = ASYNSRV__BAD_FLUSH;   /* TCP/IP problem during flush */
        GetErrno(&AsynSrv_errno, &AsynSrv_vaxc_errno);
        AsynSrv_Close(asyn_info, True); /* Force close TCP/IP connection */
        fprintf(stderr,
                "\nAsynSrv_SendCmndsBig/recv: network problem during"
                " flush.\nLink to server force-closed.\n");
        return False;
      }
      bytes_to_come = bytes_to_come - status;
    }
    fprintf(stderr, "\n flushed OK.\n");
    memset(rcve_buff->msg_size, '0', sizeof(rcve_buff->msg_size));
    return False;
  } else {
    nxt_byte_ptr = &rcve_buff->msg_size[size];
    bytes_left = bytes_to_come;
    while (bytes_left > 0) {    /* Read the rest of the response */
      status = recv(asyn_info->skt, nxt_byte_ptr, bytes_left, 0);
      if (status <= 0) {
        GetErrno(&AsynSrv_errno, &AsynSrv_vaxc_errno);
        if (status == 0) {
          AsynSrv_errcode = ASYNSRV__BAD_RECV1; /* Server exited (probably) */
          fprintf(stderr,
                  "\nAsynSrv_SendCmndsBig/recv/1: probable network "
                  "problem");
        } else {
          if (AsynSrv_errno == EPIPE) {
            AsynSrv_errcode = ASYNSRV__BAD_RECV1_PIPE;  /* Server exited (probably) */
            fprintf(stderr,
                    "\nAsynSrv_SendCmndsBig/recv/1: broken network pipe");
          } else {
            AsynSrv_errcode = ASYNSRV__BAD_RECV1_NET;   /* It's some other net fault */
            perror("AsynSrv_SendCmndsBig/recv/1");
          }
        }
        AsynSrv_Close(asyn_info, True); /* Force close TCP/IP connection */
        fprintf(stderr, " - link to server force-closed.\n");
        return False;
      }
      bytes_left = bytes_left - status;
      nxt_byte_ptr = nxt_byte_ptr + status;
    }
    if (strncmp(asyn_info->protocol_id, rcve_buff->s_pcol_lvl,
                sizeof(rcve_buff->s_pcol_lvl)) != 0) {
      AsynSrv_errcode = ASYNSRV__BAD_PROT_LVL;  /* Protocol level is bad */
      return False;
    }
    if ((sscanf(rcve_buff->n_rply, "%4d", &asyn_info->max_replies) != 1) ||
        (asyn_info->max_replies < 0)) {
      AsynSrv_errcode = ASYNSRV__BAD_REPLY;     /* Reply is bad */
      if (AsynSrv_call_depth < 5) {     /* Add reply to routine stack */
        bytes_to_come = bytes_to_come + 4;
        if (bytes_to_come >= sizeof(AsynSrv_routine[0]))
          bytes_to_come = sizeof(AsynSrv_routine[0]) - 1;
        for (i = 0; i < bytes_to_come; i++) {
          if (rcve_buff->msg_size[i] == '\0')
            rcve_buff->msg_size[i] = '.';
        }
        rcve_buff->msg_size[bytes_to_come] = '\0';
        strcpy(AsynSrv_routine[AsynSrv_call_depth], rcve_buff->msg_size);
        AsynSrv_call_depth++;
      }
      return False;
    }
  }
  if (AsynSrv_errcode == 0)
    AsynSrv_call_depth--;
  return True;
}

/*
**---------------------------------------------------------------------------
**		AsynSrv_SendSpecCmnd:  Send a "special" command to an
**					RS232C server.
*/
int AsynSrv_SendSpecCmnd(
/*	====================
*/ struct AsynSrv__info *asyn_info,
                          char *cmnd)
{

  int status;
  char rply[8];
                    /*----------------------------------------------
		    ** Pre-set the routine name (in case of error)
		    */
  if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) {
    strcpy(AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_SendSpecCmnd");
    AsynSrv_call_depth++;
  }
                    /*----------------------------------------------
		    ** Do nothing if no connection - the connection gets
		    ** closed if an error is detected. The connection may
		    ** also be marked to have been forcefully closed.
		    */
  if (asyn_info->skt <= 0) {
    if ((AsynSrv_errcode == 0) && (asyn_info->skt < 0)) {
      AsynSrv_errcode = ASYNSRV__FORCED_CLOSED;
    }
    return False;
  }
                    /*----------------------------------------------
		    ** Send the message to the server.
		    */
  status = send(asyn_info->skt, cmnd, 4, 0);
  if (status != 4) {
    GetErrno(&AsynSrv_errno, &AsynSrv_vaxc_errno);
    if (status == 0) {
      AsynSrv_errcode = ASYNSRV__BAD_SEND;      /* Server exited (probably) */
      fprintf(stderr,
              "\nAsynSrv_SendSpecCmnd/send: probable network problem");
    } else if (status == -1) {
      if (AsynSrv_errno == EPIPE) {
        AsynSrv_errcode = ASYNSRV__BAD_SEND_PIPE;       /* Server exited (probably) */
        fprintf(stderr,
                "\nAsynSrv_SendSpecCmnd/send: broken network pipe");
      } else {
        AsynSrv_errcode = ASYNSRV__BAD_SEND_NET;        /* It's some other net problem */
        perror("AsynSrv_SendSpecCmnd/send");
      }
    } else {
      AsynSrv_errcode = ASYNSRV__BAD_SEND_UNKN; /* TCP/IP problems */
      fprintf(stderr,
              "\nAsynSrv_SendSpecCmnd/send: probable TCP/IP problem");
    }
    AsynSrv_Close(asyn_info, True);     /* Force close TCP/IP connection */
    fprintf(stderr, " - link to server force-closed.\n");
    return False;
  }

  status = recv(asyn_info->skt, rply, 4, 0);
  if (status != 4) {
    GetErrno(&AsynSrv_errno, &AsynSrv_vaxc_errno);
    if (status == 0) {
      AsynSrv_errcode = ASYNSRV__BAD_RECV;      /* Server exited (probably) */
      fprintf(stderr,
              "\nAsynSrv_SendSpecCmnd/recv: probable network problem");
    } else if (status == -1) {
      if (AsynSrv_errno == EPIPE) {
        AsynSrv_errcode = ASYNSRV__BAD_RECV_PIPE;       /* Server exited (probably) */
        fprintf(stderr,
                "\nAsynSrv_SendSpecCmnd/recv: broken network pipe");
      } else {
        AsynSrv_errcode = ASYNSRV__BAD_RECV_NET;        /* It's some other net problem */
        perror("AsynSrv_SendSpecCmnd/recv");
      }
    } else {
      AsynSrv_errcode = ASYNSRV__BAD_RECV_UNKN; /* TCP/IP problems */
      fprintf(stderr,
              "\nAsynSrv_SendSpecCmnd/recv: probable TCP/IP problem");
    }
    AsynSrv_Close(asyn_info, True);     /* Force close TCP/IP connection */
    fprintf(stderr, " - link to server force-closed.\n");
    return False;
  }
  if (memcmp(cmnd, rply, 4) != 0) {
    AsynSrv_errcode = ASYNSRV__BAD_NOT_BCD;     /* Message not echoed OK */
    AsynSrv_Close(asyn_info, True);     /* Force close TCP/IP connection */
    fprintf(stderr,
            "\nAsynSrv_SendSpecCmnd/recv: command not echoed correctly"
            " - link to server force-closed.\n");
    return False;
  }
  if (AsynSrv_errcode == 0)
    AsynSrv_call_depth--;
  return True;
}

/*
**---------------------------------------------------------------------------
**		AsynSrv_Trace:  Send a "TRACE" request to RS232C server.
*/
int AsynSrv_Trace(
/*	=============
*/ struct AsynSrv__info *asyn_info,
                   int state)
{

  int status;
  char cmnd[8], rply[8];
                    /*----------------------------------------------
		    ** Pre-set the routine name (in case of error)
		    */
  if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) {
    strcpy(AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_Trace");
    AsynSrv_call_depth++;
  }
                    /*----------------------------------------------
		    ** Do nothing if no connection - the connection gets
		    ** closed if an error is detected. The connection may
		    ** also be marked to have been forcefully closed.
		    */
  if (asyn_info->skt <= 0) {
    if ((AsynSrv_errcode == 0) && (asyn_info->skt < 0)) {
      AsynSrv_errcode = ASYNSRV__FORCED_CLOSED;
    }
    return False;
  }
                    /*----------------------------------------------
		    ** Select message for server according to value of state.
		    */
  if (state) {
    strcpy(cmnd, "-002");
  } else {
    strcpy(cmnd, "-003");
  }
                    /*----------------------------------------------
		    ** Send message and get reply.
		    */
  status = AsynSrv_SendSpecCmnd(asyn_info, cmnd);

  if (AsynSrv_errcode == 0)
    AsynSrv_call_depth--;
  return status;
}

/*
**---------------------------------------------------------------------------
**		AsynSrv_Trace_Write:  Send a Trace_Write command to
**					RS232C server.
*/
int AsynSrv_Trace_Write(
/*	===================
*/ struct AsynSrv__info *asyn_info)
{

  int status;
  char cmnd[8], rply[8];
                    /*----------------------------------------------
		    ** Pre-set the routine name (in case of error)
		    */
  if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) {
    strcpy(AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_Trace_Write");
    AsynSrv_call_depth++;
  }
                    /*----------------------------------------------
		    ** Do nothing if no connection - the connection gets
		    ** closed if an error is detected. The connection may
		    ** also be marked to have been forcefully closed.
		    */
  if (asyn_info->skt <= 0) {
    if ((AsynSrv_errcode == 0) && (asyn_info->skt < 0)) {
      AsynSrv_errcode = ASYNSRV__FORCED_CLOSED;
    }
    return False;
  }
                    /*----------------------------------------------
		    ** Send message and get reply.
		    */
  status = AsynSrv_SendSpecCmnd(asyn_info, "-005");

  if (AsynSrv_errcode == 0)
    AsynSrv_call_depth--;
  return status;
}

/*-------------------------------------------- End of AsynSrv_Utility.C -----*/
