/* EHLLAPI interface for XANT 

   This module implements a subset of the Emulator High-Level Language
   Application Program Interface (EHLLAPI) as described in the EHLLAPI
   Programming Reference (S01F-0266).  The code here is called directly
   by an application and uses the apicomc module to communicate with the
   emulator.
*/

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <memory.h>
#include "hapi_c.h"
#include "api.h"
#include "keys.h"

#define MAX_XMIT 8192
#define MAX_RECV 32768
#define True 1
#define False 0
#define HWORD(a, n) ((a[n] << 8) + a[(n) + 1])
#define storew(a, i, val) { a[i] = (val) >> 8; a[(i)+1] = (val) & 0xff; }
typedef char Boolean;


/* Global variables */
static int inited = 0;		/* Non-zero if initialization was done */
static char *xmitbuff;		/* Buffer for sending data to server */
static char *recvbuff;		/* Buffer for receiving data from server */
static int sockndx;		/* Socket index for currently connected PS */


/* Session options */
static Boolean opt_eot;		/* True if in STREOT mode */
static char opt_eotchar;	/* The EOT character if in STREOT mode */
static Boolean opt_srchall;	/* Search should scan the whole PS */
static Boolean opt_srchfrwd;	/* Searches should be forwards */
static Boolean opt_attrb;	/* Leave attribute bytes alone */
static Boolean opt_fpause;	/* Pause should be full duration */
static char opt_esc;		/* Escape character */
static Boolean opt_autoreset;	/* Do automatic reset if inhibited */
static enum { timed, untimed, nowait} opt_wait; /* Wait option */
static Boolean opt_eab;		/* Pass EABs */
static Boolean opt_conlog;	/* Do not jump to PS on connections */


/* Forward routine declarations */
static int dohllc();
static void resetopt();
static char tr_esc(), tr_esc_a(), *nextopt();


/* Macro to send request to server and check response */
#define sendreq(cmd, s, xmitlen, checklen)				\
  xmitbuff[0] = (cmd);							\
  recvlen = MAX_RECV;							\
  putrc = xant_comc_put((s), xmitbuff, (xmitlen), recvbuff, &recvlen);	\
  if (putrc == -1) return HARC_INVALID_PS;				\
  else if (putrc < 0 || recvlen < (checklen) || recvbuff[0] == 1)	\
    return HARC_SYSTEM_ERROR;



/* Main entry point */
hllc(function, data_string, data_length, rc)
     int *function, *data_length, *rc;
     char *data_string;
{
  if (!inited)
    {
      xant_comc_init();
      if (!(xmitbuff = (char *) malloc(MAX_XMIT)) ||
	  !(recvbuff = (char *) malloc(MAX_RECV)))
	return HARC_SYSTEM_ERROR;
      sockndx = -1;
      resetopt();
      inited = 1;
    }
  *rc = dohllc(*function, data_string, data_length, *rc);
  return 0;
}


/* More reasonable version of hllc() routine */
static int dohllc(function, data_string, data_length, pos)
     int function, *data_length;
     char *data_string;
     int pos;
{
  char sessid, *ptr;
  int j, putrc, len, ndx, recvlen, state, count, rows, cols, startpos, scrsize;

  switch (function)
    {
    case HA_CONNECT_PS:
      /* +----------------------------+ */
      /* | Connect Presentation Space | */
      /* +----------------------------+ */

      /* Convert session ID to an index */
      j = data_string[0];
      sessid = isupper(j) ? tolower(j) : j;
      if (sessid < 'a' || sessid > 'z') return HARC_INVALID_PS;
      ndx = sessid - 'a';

      /* Connect to the server */
      if (xant_comc_connect(ndx) < 0) return HARC_UNAVAILABLE;

      /* Verify that the server's version is recent enough */
      sendreq(API_VERSION, ndx, 1, 2);
      if (recvbuff[1] < API_VERSION_NUM)
	{
	  (void) xant_comc_disc(ndx);
	  return HARC_UNAVAILABLE;
	}

      /* Set new value for currently connected presentation space */
      sockndx = ndx;
      break;
      
    case HA_DISCONNECT_PS:
      /* +-------------------------------+ */
      /* | Disconnect Presentation Space | */
      /* +-------------------------------+ */
      if (sockndx < 0 || xant_comc_disc(sockndx) < 0) return HARC_INVALID_PS;
      sockndx = -1;
      break;
      
    case HA_SENDKEY:
      /* +----------+ */
      /* | Send Key | */
      /* +----------+ */
      if (sockndx < 0) return HARC_INVALID_PS;
      len = *data_length;
      if (opt_eot) len = 255;
      else if (len > 255) return HARC_BAD_PARM;

      ptr = &xmitbuff[1];
      if (opt_autoreset)
	{
	  *ptr++ = k_reset;
	  ptr++;
	}
      state = 0;
      for (j = 0; j < len; j++)
	{
	  if (opt_eot && data_string[j] == opt_eotchar) break;
	  if (state == 0)
	    {
	      /* Initial state */
	      if (data_string[j] == opt_esc)
		{
		  state = 1;
		  continue;
		}
	      *ptr = k_string;	      
	    }
	  else if (state == 1)
	    {
	      /* Processing escaped character */
	      if (data_string[j] == 'A')
		{
		  state = 2;
		  continue;
		}
	      *ptr = tr_esc(data_string[j]);
	      state = 0;
	      if (*ptr == k_undefined) continue;
	    }
	  else if (state == 2)
	    {
	      /* Escape A received */
	      if (data_string[j] == opt_esc)
		{
		  state = 3;
		  continue;
		}
	      *ptr = k_string;	      
	      state = 0;
	    }
	  else if (state == 3)
	    {
	      /* Processing character after escape A */
	      *ptr = tr_esc_a(data_string[j]);
	      state = 0;
	      if (*ptr == k_undefined) continue;
	    }
	  ptr++;
	  *ptr++ = data_string[j];
	}
      if (ptr - xmitbuff < 2) break;
      sendreq(API_SENDKEY, sockndx, ptr - xmitbuff, 1);
      if (recvbuff[0] != 0) return HARC_BUSY;
      break;

    case HA_WAIT:
      /* +------+ */
      /* | Wait | */
      /* +------+ */
      if (sockndx < 0) return HARC_INVALID_PS;
      if (opt_wait == nowait)
	{
	  /* No waiting desired, so just find out if keyboard is locked */
	  sendreq(API_STATUS, sockndx, 1, 9);
	  if (recvbuff[8] != 0) return HARC_LOCKED;
	  break;
	}

      /* Wait for keyboard to unlock */
      xmitbuff[1] = (opt_wait == untimed);
      sendreq(API_WAIT, sockndx, 2, 2);
      if (recvbuff[1] != 0) return HARC_BUSY;
      break;

    case HA_COPY_PS:
      /* +-------------------------+ */
      /* | Copy Presentation Space | */
      /* +-------------------------+ */
      if (sockndx < 0) return HARC_INVALID_PS;

      /* Get screen size */
      sendreq(API_STATUS, sockndx, 1, 9);
      scrsize = HWORD(recvbuff, 1) * HWORD(recvbuff, 3);
      if (scrsize > MAX_RECV - 2) return HARC_SYSTEM_ERROR;
      
      /* Fetch the whole screen */
      storew(xmitbuff, 1, 0);
      storew(xmitbuff, 3, scrsize);
      sendreq(API_COPYPS, sockndx, 5, scrsize + 2);
      if (recvbuff[0] != 0) return HARC_SYSTEM_ERROR;
      memcpy(data_string, &recvbuff[2], scrsize);
      if (recvbuff[1] != 0) return HARC_LOCKED;
      break;

    case HA_SEARCH_PS:
      /* +---------------------------+ */
      /* | Search Presentation Space | */
      /* +---------------------------+ */
      if (sockndx < 0) return HARC_INVALID_PS;

      /* Get screen size */
      sendreq(API_STATUS, sockndx, 1, 9);
      scrsize = HWORD(recvbuff, 1) * HWORD(recvbuff, 3);
      if (scrsize > MAX_RECV - 2) return HARC_SYSTEM_ERROR;
      
      /* Fetch the whole screen */
      storew(xmitbuff, 1, 0);
      storew(xmitbuff, 3, scrsize);
      sendreq(API_COPYPS, sockndx, 5, scrsize + 2);
      if (recvbuff[0] != 0) return HARC_SYSTEM_ERROR;

      /* If target string is delimited by EOT, search for it */
      if (opt_eot)
	{
	  *data_length = scrsize;
	  for (j = 0; j < scrsize; j++)
	    if (data_string[j] == opt_eotchar)
	      {
		*data_length = j;
		break;
	      }
	}
            
      /* Check starting position */
      if (opt_srchall) pos = 1;
      if (pos < 1 || pos > scrsize || *data_length == 0) return HARC_BAD_PARM;
      pos--;

      /* Search forwards or backwards for data string */
      if (opt_srchfrwd)
	{
	  for (j = pos; j <= scrsize - *data_length; j++)
	    if (!strncmp(&recvbuff[j + 2], data_string, *data_length))
	      {
		*data_length = j + 1;
		return HARC_SUCCESS;
	      }
	}
      else
	{
	  for (j = scrsize - *data_length; j >= pos; j--)
	    if (!strncmp(&recvbuff[j + 2], data_string, *data_length))
	      {
		*data_length = j + 1;
		return HARC_SUCCESS;
	      }
	}
      *data_length = 0;
      return HARC_STR_NOT_FOUND_UNFM_PS;

    case HA_QUERY_CURSOR_LOC:
      /* +-----------------------+ */
      /* | Query Cursor Location | */
      /* +-----------------------+ */
      if (sockndx < 0) return HARC_INVALID_PS;
      sendreq(API_STATUS, sockndx, 1, 9);
      *data_length = HWORD(recvbuff, 5) + 1;
      break;

    case HA_COPY_PS_TO_STR:
      /* +-------------------+ */
      /* | Copy PS to String | */
      /* +-------------------+ */
      if (sockndx < 0) return HARC_INVALID_PS;
      if (*data_length == 0) return HARC_BAD_PARM;
      if (pos < 1) return HARC_INVALID_PS_POS;
      storew(xmitbuff, 1, pos - 1);
      storew(xmitbuff, 3, *data_length);
      sendreq(API_COPYPS, sockndx, 5, *data_length + 2);
      if (recvbuff[0] != 0) return HARC_SYSTEM_ERROR;
      memcpy(data_string, &recvbuff[2], *data_length);
      if (recvbuff[1] != 0) return HARC_LOCKED;
      break;

    case HA_SET_SESSION_PARMS:
      /* +------------------------+ */
      /* | Set Session Parameters | */
      /* +------------------------+ */
      if (*data_length == 0) break;
      ptr = data_string;
      count = 0;
      while (ptr < data_string + *data_length)
	{
	  char opt[40];
	  len = sizeof opt - 1;
	  ptr = nextopt(ptr, data_string + *data_length - ptr, opt, &len);
	  if (!ptr)
	    {
	      *data_length = count;
	      return HARC_BAD_PARM;
	    }
	  opt[len] = 0;
	  if (!strcmp(opt, "STRLEN"))		opt_eot = False;
	  else if (!strcmp(opt, "STREOT"))	opt_eot = True;
	  else if (len == 5 && !strncmp(opt, "EOT=", 4)) opt_eotchar = opt[4];
	  else if (!strcmp(opt, "SRCHALL"))	opt_srchall = True;
	  else if (!strcmp(opt, "SRCHFROM"))	opt_srchall = False;
	  else if (!strcmp(opt, "SRCHFRWD"))	opt_srchfrwd = True;
	  else if (!strcmp(opt, "SRCHBKWD"))	opt_srchfrwd = False;
	  else if (!strcmp(opt, "NOATTRB"))	opt_attrb = False;
	  else if (!strcmp(opt, "ATTRB"))	opt_attrb = True;
	  else if (!strcmp(opt, "FPAUSE"))	opt_fpause = True;
	  else if (!strcmp(opt, "IPAUSE"))	opt_fpause = False;
	  else if (!strcmp(opt, "NOQUIET"))	/* opt_quiet = False */;
	  else if (!strcmp(opt, "QUIET"))	/* opt_quiet = True */;
	  else if (len == 9 && !strncmp(opt, "TIMEOUT=", 8));
	  else if (len == 5 && !strncmp(opt, "ESC=", 4)) opt_esc = opt[4];
	  else if (!strcmp(opt, "AUTORESET"))	opt_autoreset = True;
	  else if (!strcmp(opt, "NORESET"))	opt_autoreset = False;
	  else if (!strcmp(opt, "TWAIT"))	opt_wait = timed;
	  else if (!strcmp(opt, "LWAIT"))	opt_wait = untimed;
	  else if (!strcmp(opt, "NWAIT"))	opt_wait = nowait;
	  else if (!strcmp(opt, "TROFF"))	/* Not implemented */;
	  else if (!strcmp(opt, "TRON"))	/* Not implemented */;
	  else if (!strcmp(opt, "EAB"))		opt_eab = True;
	  else if (!strcmp(opt, "NOEAB"))	opt_eab = False;
	  else if (!strcmp(opt, "XLATE"))	/* Not implemented */;
	  else if (!strcmp(opt, "NOXLATE"))	/* Not implemented */;
	  else if (!strcmp(opt, "CONLOG"))	opt_conlog = True;
	  else if (!strcmp(opt, "CONPHYS"))	opt_conlog = False;
	  else if (!strcmp(opt, "NEWOIA"))	/* Not implemented */;
	  else if (!strcmp(opt, "OLDOIA"))	/* Not implemented */;
	  else if (!strcmp(opt, "CFGSIZE"))	/* Not implemented */;
	  else if (!strcmp(opt, "NOCFGSIZE"))	/* Not implemented */;
	  else
	    {
	      *data_length = count;
	      return HARC_BAD_PARM;
	    }
	  count++;
	}
      break;

    case HA_QUERY_SESSIONS:
      /* +----------------+ */
      /* | Query Sessions | */
      /* +----------------+ */
      if (*data_length < 12) return HARC_BAD_PARM;
      count = 0;
      ptr = data_string;
      for (ndx = 0; ndx < 26; ndx++)
	{
	  xmitbuff[0] = API_STATUS;
	  recvlen = MAX_RECV;
	  putrc = xant_comc_put(ndx, xmitbuff, 1, recvbuff, &recvlen);
	  if (putrc == -1) continue;
	  else if (putrc < 0 || recvlen < 9 || recvbuff[0] == 1)
	    return HARC_SYSTEM_ERROR;
	  if (ptr >= data_string + *data_length) return HARC_BAD_PARM;

	  memset(ptr, 0, 12);
	  ptr[0] = ndx + 'a';	/* Short session ID */
	  ptr[1] = ndx + 'a';	/* Long name of session */
	  ptr[9] = 'H';		/* Session type */
	  storew(ptr, 10, HWORD(recvbuff, 1) * HWORD(recvbuff, 3)); /* Size */

	  count++;
	  ptr += 12;
	}
      *data_length = count;
      break;

    case HA_RESERVE:
      /* +---------+ */
      /* | Reserve | */
      /* +---------+ */
      return HARC_UNSUPPORTED;

    case HA_RELEASE:
      /* +---------+ */
      /* | Release | */
      /* +---------+ */
      return HARC_UNSUPPORTED;

    case HA_COPY_OIA:
      /* +----------+ */
      /* | Copy OIA | */
      /* +----------+ */
      return HARC_UNSUPPORTED;

    case HA_QUERY_FIELD_ATTR:
      /* +-----------------------+ */
      /* | Query Field Attribute | */
      /* +-----------------------+ */
      if (sockndx < 0) return HARC_INVALID_PS;
      if (pos < 1) return HARC_INVALID_PS_POS;
      storew(xmitbuff, 1, pos - 1);
      xmitbuff[3] = 0;		/* Ask for this field */
      xmitbuff[4] = 0;		/* Don't care about field type */
      sendreq(API_GETFIELD, sockndx, 5, 6);
      if (recvbuff[0] != 0) return HARC_INVALID_PS_POS;
      if (!recvbuff[5]) return HARC_STR_NOT_FOUND_UNFM_PS;
      *data_length = recvbuff[5] | 0xC0;
      break;

    case HA_COPY_STR_TO_PS:
      /* +-------------------+ */
      /* | Copy String to PS | */
      /* +-------------------+ */
      if (sockndx < 0) return HARC_INVALID_PS;
      len = opt_eot ? MAX_XMIT - 2 : *data_length;
      if (len == 0 || len > MAX_XMIT - 2) return HARC_BAD_PARM;
      storew(xmitbuff, 1, pos - 1);
      ptr = &xmitbuff[3];
      for (j = 0; j < len; j++)
	{
	  if (opt_eot && data_string[j] == opt_eotchar) break;
	  *ptr++ = data_string[j];
	}      
      sendreq(API_COPYSTR, sockndx, ptr - xmitbuff, 2);
      if (recvbuff[1] == 1) return HARC_LOCKED;
      else if (recvbuff[1] == 2) return HARC_TRUNCATION;
      else if (recvbuff[1] == 3) return HARC_INVALID_PS_POS;
      else if (recvbuff[0] != 0) return HARC_SYSTEM_ERROR;
      break;

    case HA_STORAGE_MGR:
      /* +-----------------+ */
      /* | Storage Manager | */
      /* +-----------------+ */
      return HARC_UNSUPPORTED;

    case HA_PAUSE:
      /* +-------+ */
      /* | Pause | */
      /* +-------+ */
      return HARC_UNSUPPORTED;

    case HA_QUERY_SYSTEM:
      /* +--------------+ */
      /* | Query System | */
      /* +--------------+ */
      memset(data_string, 0, 35);
      data_string[0] = API_VERSION_NUM + '0';	/* EHLLAPI version number */
      memcpy(&data_string[1], "00", 2);		/* EHLLAPI level number */
      memcpy(&data_string[3], "000000", 6);	/* EHLLAPI date */
      data_string[12] = 'U';			/* Hardware base */
      data_string[13] = 'A';			/* Control program type */
      memcpy(&data_string[14], "0000", 4);	/* Sequence and version */
      data_string[18] = '1';			/* Session ID */
      memcpy(&data_string[19], "0000", 4);	/* Return code */
      memcpy(&data_string[23], "0000", 4);	/* Return code */
      data_string[31] = 'U';			/* Type of monitor */
      break;

    case HA_RESET_SYSTEM:
      /* +--------------+ */
      /* | Reset System | */
      /* +--------------+ */
      resetopt();
      for (sockndx = 0; sockndx < 26; sockndx++)
	xant_comc_disc(sockndx);
      sockndx = -1;
      break;

    case HA_QUERY_SESSION_STATUS:
      /* +----------------------+ */
      /* | Query Session Status | */
      /* +----------------------+ */
      if (*data_length < 18) return HARC_BAD_PARM;
      j = data_string[0];
      sessid = isupper(j) ? tolower(j) : j;
      if (sessid == ' ' || sessid == 0)
	ndx = sockndx;
      else
	{
	  if (sessid < 'a' || sessid > 'z') return HARC_INVALID_PS;
	  ndx = sessid - 'a';
	}
      
      sendreq(API_STATUS, sockndx, 1, 9);

      memset(data_string, 0, 18);
      data_string[0] = ndx + 'a'; 		  /* Short session ID */
      data_string[1] = ndx + 'a'; 		  /* Long name of session */
      data_string[9] = 'D';			  /* Session type */
      if (recvbuff[7] != 0) data_string[10] |= 1; /* Session characteristics */
      memcpy(&data_string[11], &recvbuff[1], 4);  /* Rows and columns */
      storew(data_string, 16, 910); 		  /* Host code page number */

      *data_length = 18;
      break;

    case HA_START_HOST_NOTIFY:
      /* +-------------------------+ */
      /* | Start Host Notification | */
      /* +-------------------------+ */
      return HARC_UNSUPPORTED;

    case HA_QUERY_HOST_UPDATE:
      /* +-------------------+ */
      /* | Query Host Update | */
      /* +-------------------+ */
      return HARC_UNSUPPORTED;

    case HA_STOP_HOST_NOTIFY:
      /* +------------------------+ */
      /* | Stop Host Notification | */
      /* +------------------------+ */
      return HARC_UNSUPPORTED;

    case HA_SEARCH_FIELD:
      /* +--------------+ */
      /* | Search Field | */
      /* +--------------+ */
      if (sockndx < 0) return HARC_INVALID_PS;

      /* Get screen size */
      sendreq(API_STATUS, sockndx, 1, 9);
      scrsize = HWORD(recvbuff, 1) * HWORD(recvbuff, 3);
      if (scrsize > MAX_RECV - 2) return HARC_SYSTEM_ERROR;
      
      /* Get starting position and size of the field */
      if (pos < 1 || pos > scrsize) return HARC_INVALID_PS_POS;
      storew(xmitbuff, 1, pos - 1);
      xmitbuff[3] = 0;		/* Ask for this field */
      xmitbuff[4] = 0;		/* Don't care about field type */
      sendreq(API_GETFIELD, sockndx, 5, 6);
      if (recvbuff[0] != 0) return HARC_INVALID_PS_POS;
      startpos = HWORD(recvbuff, 1);
      len = HWORD(recvbuff, 3);
      if (!recvbuff[5] || !len) return HARC_STR_NOT_FOUND_UNFM_PS;

      /* Fetch the whole field */
      storew(xmitbuff, 1, startpos);
      storew(xmitbuff, 3, len);
      sendreq(API_COPYPS, sockndx, 5, len + 2);
      if (recvbuff[0] != 0) return HARC_SYSTEM_ERROR;

      /* If target string is delimited by EOT, search for it */
      if (opt_eot)
	{
	  *data_length = scrsize;
	  for (j = 0; j < scrsize; j++)
	    if (data_string[j] == opt_eotchar)
	      {
		*data_length = j;
		break;
	      }
	}
      if (*data_length == 0) return HARC_BAD_PARM;
            
      /* Change starting position to be position with field */
      pos -= startpos + 1;
      if (pos < 0) pos += scrsize;

      /* Search forwards or backwards for data string */
      if (opt_srchfrwd)
	{
	  for (j = pos; j <= len - *data_length; j++)
	    if (!strncmp(&recvbuff[j + 2], data_string, *data_length))
	      {
		*data_length = startpos + j;
		if (*data_length >= scrsize) *data_length -= scrsize;
		(*data_length)++;
		return HARC_SUCCESS;
	      }
	}
      else
	{
	  for (j = len - *data_length; j >= pos; j--)
	    if (!strncmp(&recvbuff[j + 2], data_string, *data_length))
	      {
		*data_length = startpos + j;
		if (*data_length >= scrsize) *data_length -= scrsize;
		(*data_length)++;
		return HARC_SUCCESS;
	      }
	}
      *data_length = 0;
      return HARC_STR_NOT_FOUND_UNFM_PS;

    case HA_FIND_FIELD_POS:
      /* +---------------------+ */
      /* | Find Field Position | */
      /* +---------------------+ */
      if (sockndx < 0) return HARC_INVALID_PS;
      if (pos < 1) return HARC_INVALID_PS_POS;
      storew(xmitbuff, 1, pos - 1);
      xmitbuff[3] = data_string[0] == 'P' ? 2 : data_string[0] == 'N' ? 1 : 0;
      xmitbuff[4] = data_string[1] == 'U' ? 2 : data_string[1] == 'P' ? 1 : 0;
      sendreq(API_GETFIELD, sockndx, 5, 6);
      if (recvbuff[0] != 0) return HARC_INVALID_PS_POS;
      *data_length = 0;
      if (!recvbuff[5]) return HARC_STR_NOT_FOUND_UNFM_PS;
      *data_length = HWORD(recvbuff, 3);
      if (!*data_length) return HARC_FIELD_LEN_ZERO;
      *data_length = HWORD(recvbuff, 1);
      (*data_length)++;
      break;

    case HA_FIND_FIELD_LEN:
      /* +-------------------+ */
      /* | Find Field Length | */
      /* +-------------------+ */
      if (sockndx < 0) return HARC_INVALID_PS;
      if (pos < 1) return HARC_INVALID_PS_POS;
      storew(xmitbuff, 1, pos - 1);
      xmitbuff[3] = data_string[0] == 'P' ? 2 : data_string[0] == 'N' ? 1 : 0;
      xmitbuff[4] = data_string[1] == 'U' ? 2 : data_string[1] == 'P' ? 1 : 0;
      sendreq(API_GETFIELD, sockndx, 5, 6);
      if (recvbuff[0] != 0) return HARC_INVALID_PS_POS;
      *data_length = 0;
      if (!recvbuff[5]) return HARC_STR_NOT_FOUND_UNFM_PS;
      *data_length = HWORD(recvbuff, 3);
      if (!*data_length) return HARC_FIELD_LEN_ZERO;
      break;

    case HA_COPY_STR_TO_FIELD:
      /* +----------------------+ */
      /* | Copy String to Field | */
      /* +----------------------+ */
      if (sockndx < 0) return HARC_INVALID_PS;

      /* Get starting position and size of the field */
      if (pos < 1) return HARC_INVALID_PS_POS;
      storew(xmitbuff, 1, pos - 1);
      xmitbuff[3] = 0;		/* Ask for this field */
      xmitbuff[4] = 0;		/* Don't care about field type */
      sendreq(API_GETFIELD, sockndx, 5, 6);
      if (recvbuff[0] != 0) return HARC_INVALID_PS_POS;
      startpos = HWORD(recvbuff, 1);
      count = HWORD(recvbuff, 3);
      if (!recvbuff[5]) return HARC_STR_NOT_FOUND_UNFM_PS;
      if (!count) return HARC_TRUNCATION;
      storew(xmitbuff, 1, startpos);

      /* Send the string */
      len = opt_eot ? MAX_XMIT - 2 : *data_length;
      if (len == 0 || len > MAX_XMIT - 2) return HARC_BAD_PARM;
      ptr = &xmitbuff[3];
      for (j = 0; j < len && j < count; j++)
	{
	  if (opt_eot && data_string[j] == opt_eotchar) break;
	  *ptr++ = data_string[j];
	}      
      sendreq(API_COPYSTR, sockndx, ptr - xmitbuff, 2);
      if (recvbuff[1] == 1) return HARC_LOCKED;
      else if (recvbuff[1] == 2) return HARC_TRUNCATION;
      else if (recvbuff[1] == 3) return HARC_INVALID_PS_POS;
      else if (recvbuff[0] != 0) return HARC_SYSTEM_ERROR;
      else if (j == count && count < len) return HARC_TRUNCATION;
      break;

    case HA_COPY_FIELD_TO_STR:
      /* +----------------------+ */
      /* | Copy Field to String | */
      /* +----------------------+ */
      if (sockndx < 0) return HARC_INVALID_PS;

      /* Get starting position and size of the field */
      if (pos < 1) return HARC_INVALID_PS_POS;
      storew(xmitbuff, 1, pos - 1);
      xmitbuff[3] = 0;		/* Ask for this field */
      xmitbuff[4] = 0;		/* Don't care about field type */
      sendreq(API_GETFIELD, sockndx, 5, 6);
      if (recvbuff[0] != 0) return HARC_INVALID_PS_POS;
      startpos = HWORD(recvbuff, 1);
      len = HWORD(recvbuff, 3);
      if (!recvbuff[5] || !len) return HARC_STR_NOT_FOUND_UNFM_PS;

      /* Fetch the whole field */
      storew(xmitbuff, 1, startpos);
      storew(xmitbuff, 3, len);
      sendreq(API_COPYPS, sockndx, 5, len + 2);
      if (recvbuff[0] != 0) return HARC_SYSTEM_ERROR;

      /* Copy field to target data string */
      j = len;
      if (j > *data_length) j = *data_length;
      memcpy(data_string, &recvbuff[2], j);
      if (j != len) return HARC_TRUNCATION;
      break;

    case HA_SET_CURSOR:
      /* +------------+ */
      /* | Set Cursor | */
      /* +------------+ */
      if (sockndx < 0) return HARC_INVALID_PS;
      if (pos < 1) return HARC_INVALID_PS_POS;
      storew(xmitbuff, 1, pos - 1);
      sendreq(API_SETCURS, sockndx, 3, 1);
      if (recvbuff[0] != 0) return HARC_BUSY;
      break;

    case HA_START_CLOSE_INTERCEPT:
      /* +-----------------------+ */
      /* | Start Close Intercept | */
      /* +-----------------------+ */
      return HARC_UNSUPPORTED;

    case HA_QUERY_CLOSE_INTERCEPT:
      /* +-----------------------+ */
      /* | Query Close Intercept | */
      /* +-----------------------+ */
      return HARC_UNSUPPORTED;

    case HA_STOP_CLOSE_INTERCEPT:
      /* +----------------------+ */
      /* | Stop Close Intercept | */
      /* +----------------------+ */
      return HARC_UNSUPPORTED;

    case HA_START_KEY_INTERCEPT:
      /* +---------------------------+ */
      /* | Start Keystroke Intercept | */
      /* +---------------------------+ */
      return HARC_UNSUPPORTED;

    case HA_GET_KEY:
      /* +---------+ */
      /* | Get Key | */
      /* +---------+ */
      return HARC_UNSUPPORTED;

    case HA_POST_INTERCEPT_STATUS:
      /* +-----------------------+ */
      /* | Post Intercept Status | */
      /* +-----------------------+ */
      return HARC_UNSUPPORTED;

    case HA_STOP_KEY_INTERCEPT:
      /* +--------------------------+ */
      /* | Stop Keystroke Intercept | */
      /* +--------------------------+ */
      return HARC_UNSUPPORTED;

    case HA_SEND_FILE:
      /* +-----------+ */
      /* | Send File | */
      /* +-----------+ */
      return HARC_UNSUPPORTED;

    case HA_RECEIVE_FILE:
      /* +--------------+ */
      /* | Receive file | */
      /* +--------------+ */
      return HARC_UNSUPPORTED;

    case HA_CONVERT_POS_ROW_COL:
      /* +--------------------------------+ */
      /* | Convert Position or Row Column | */
      /* +--------------------------------+ */
      j = data_string[0];
      sessid = isupper(j) ? tolower(j) : j;
      if (sessid < 'a' || sessid > 'z') return HARC99_INVALID_PS;
      ndx = sessid - 'a';
      if (data_string[1] != 'R' && data_string[1] != 'P')
	return HARC99_INVALID_CONV_OPT;

      xmitbuff[0] = API_STATUS;
      recvlen = MAX_RECV;
      putrc = xant_comc_put(ndx, xmitbuff, 9, recvbuff, &recvlen);
      if (putrc < 0 || recvlen < 1 || recvbuff[0] == 1)
	return HARC99_INVALID_PS;

      rows = HWORD(recvbuff, 1);
      cols = HWORD(recvbuff, 3);
      pos--;

      if (data_string[1] == 'P')
	{
	  /* Convert position to row and column */
	  if (pos < 0 || pos >= rows * cols) return HARC99_INVALID_INP;
	  *data_length = pos / cols + 1;	/* Row */
	  return pos % cols + 1; 		/* Column */
	}
      else
	{
	  /* Convert row and column to position */
	  (*data_length)--;			/* Row */
	  if (*data_length < 0 || *data_length >= rows)
	    {
	      *data_length = 0;
	      return HARC99_INVALID_INP;
	    }
	  if (pos < 0 || pos >= cols) return HARC99_INVALID_INP;
	  return pos + *data_length * cols + 1;
	}
      /*NOTREACHED*/
      break;

    case HA_CONNECT_PM_SRVCS:
      /* +----------------------------+ */
      /* | Connect PM Window Services | */
      /* +----------------------------+ */
      return HARC_UNSUPPORTED;

    case HA_DISCONNECT_PM_SRVCS:
      /* +-------------------------------+ */
      /* | Disconnect PM Window Services | */
      /* +-------------------------------+ */
      return HARC_UNSUPPORTED;

    case HA_QUERY_WINDOW_COORDS:
      /* +-----------------------------+ */
      /* | Query PM Window Coordinates | */
      /* +-----------------------------+ */
      return HARC_UNSUPPORTED;

    case HA_PM_WINDOW_STATUS:
      /* +------------------+ */
      /* | PM Window Status | */
      /* +------------------+ */
      return HARC_UNSUPPORTED;

    case HA_CHANGE_SWITCH_NAME:
      /* +----------------------------+ */
      /* | Change Switch List LT Name | */
      /* +----------------------------+ */
      return HARC_UNSUPPORTED;

    case HA_CHANGE_WINDOW_NAME:
      /* +-----------------------+ */
      /* | Change PS Window Name | */
      /* +-----------------------+ */
      return HARC_UNSUPPORTED;

    default:
      return HARC_UNSUPPORTED;
    }
  return HARC_SUCCESS;
}


/* Reset session options to their default values */
static void resetopt()
{
  opt_eot = False;
  opt_eotchar = 0;
  opt_srchall = True;
  opt_srchfrwd = True;
  opt_attrb = False;
  opt_fpause = True;
  opt_esc = '@';
  opt_autoreset = True;
  opt_wait = timed;
  opt_eab = False;
  opt_conlog = True;
}


/* Convert EHLLAPI escape code to internal 3270 key code */
static char tr_esc(ch)
     char ch;
{
  switch (ch)
    {
    case 'B': return k_backtab;
    case 'C': return k_clear;
    case 'D': return k_delete;
    case 'E': return k_enter;
    case 'F': return k_erase_eof;
    case 'I': return k_insert;
    case 'L': return k_left;
    case 'N': return k_newline;
    case 'R': return k_reset;
    case 'T': return k_tab;
    case 'U': return k_up;
    case 'V': return k_down;
    case 'Z': return k_right;
    case '0': return k_home;
    case '1': return k_pf1;
    case '2': return k_pf2;
    case '3': return k_pf3;
    case '4': return k_pf4;
    case '5': return k_pf5;
    case '6': return k_pf6;
    case '7': return k_pf7;
    case '8': return k_pf8;
    case '9': return k_pf9;
    case 'a': return k_pf10;
    case 'b': return k_pf11;
    case 'c': return k_pf12;
    case 'd': return k_pf13;
    case 'e': return k_pf14;
    case 'f': return k_pf15;
    case 'g': return k_pf16;
    case 'h': return k_pf17;
    case 'i': return k_pf18;
    case 'j': return k_pf19;
    case 'k': return k_pf20;
    case 'l': return k_pf21;
    case 'm': return k_pf22;
    case 'n': return k_pf23;
    case 'o': return k_pf24;
    case 'q': return k_end;
    case 'x': return k_pa1;
    case 'y': return k_pa2;
    case 'z': return k_pa3;
    case '<': return k_backspace;
    case '@': return k_string;
    }
  return k_undefined;
}


/* Convert EHLLAPI escape code after "@A" to internal 3270 key code */
static char tr_esc_a(ch)
     char ch;
{
  switch (ch)
    {
    case 'D': return k_kill_word;
    case 'F': return k_erase_input;
    case 'H': return k_sysreq;
    case 'L': return k_left;
    case 'U': return k_up;
    case 'V': return k_down;
    case 'Z': return k_right;
    case 'y': return k_forward_word;
    case 'z': return k_backward_word;
    case '@': return k_string;
    }
  return k_undefined;
}


/* Extract next option from session parameters and return updated pointer */
static char *nextopt(ptr, len, opt, optlen)
     char *ptr;
     int len;
     char *opt;
     int *optlen;
{
  register int j, c;

  if (len <= 0) return NULL;

  /* Copy characters until first comma or blank */
  for (j = 0; j < len && j < *optlen; j++)
    {
      if (ptr[j] == ',' || ptr[j] == ' ') break;
      c = ptr[j];
      opt[j] = islower(c) ? toupper(c) : c;
    }
  if (j == *optlen) return NULL;
  *optlen = j;

  /* Skip delimiter */
  if (j < len) j++;
  
  /* Skip over blanks */
  for (; j < len && ptr[j] == ' '; j++);
  
  return &ptr[j];
}
