/** xprfuncs.c
*
*   Call-back functions for eXternal PRotocol support
*
**/
#include "vt100.h"
#include <stat.h>
/*
*   xproto.h is given in Appendix B
*/
#include "xproto.h"
/*
*   xfer.h is a VLT private header file containing some information for
*   file transfer protocols
*/
#include "xfer.h"

/*
*   These are the C versions of the interface
*/
long        vlt_update(),  vlt_swrite(),   vlt_fread(),     vlt_fopen(),
            vlt_fclose(),  vlt_gets(),     vlt_sread(),     vlt_chkabort(),
            vlt_fwrite(),  vlt_fseek(),    vlt_ffirst(),    vlt_fnext(),
            vlt_sflush(),  vlt_chkmisc(),  vlt_setserial(), vlt_finfo(),
            vlt_options();
/*
*   These are the assembly level glue functions, see vltface.asm
*/
extern long avlt_update(), avlt_swrite(),  avlt_fread(),     avlt_fopen(),
            avlt_fclose(), avlt_gets(),    avlt_sread(),     avlt_chkabort(),
            avlt_fwrite(), avlt_fseek(),   avlt_ffirst(),    avlt_fnext(),
            avlt_sflush(), avlt_chkmisc(), avlt_setserial(), avlt_finfo(),
            avlt_options();

/**
*
*   This function initializes an XPR_IO structure.
*
**/
xpr_setup(IO)
struct XPR_IO *IO;
{
/*
*   NULL out all the functions we don't do yet.
*   Fill the other ones with the addresses to the assembler glue version
*   of the interface routines. See vltface.asm
*/
   IO->xpr_filename  = NULL;
   IO->xpr_fopen     = avlt_fopen;
   IO->xpr_fclose    = avlt_fclose;
   IO->xpr_fread     = avlt_fread;
   IO->xpr_fwrite    = avlt_fwrite;
   IO->xpr_sread     = avlt_sread;
   IO->xpr_swrite    = avlt_swrite;
   IO->xpr_sflush    = avlt_sflush;
   IO->xpr_update    = avlt_update;
   IO->xpr_chkabort  = avlt_chkabort;
   IO->xpr_chkmisc   = avlt_chkmisc;
   IO->xpr_gets      = avlt_gets;
   IO->xpr_setserial = avlt_setserial;
   IO->xpr_ffirst    = avlt_ffirst;
   IO->xpr_fnext     = avlt_fnext;
   IO->xpr_finfo     = avlt_finfo;
   IO->xpr_fseek     = avlt_fseek;
/*
*   We support 1 extension field
*/
   IO->xpr_extension = 1L;
   IO->xpr_options   = avlt_options;
/*
*   NULL out the XPR private data field.
*/
   IO->xpr_data      = NULL;

   return;
}

/**
*
*   Interface to VLT's MsgDisplay() function.
*
**/
/*
*   These are formats for VLT's requester
*/
static char *xprnamfmt = "%s\n%s\n\n\n\n";
static char *filnamfmt = "\n\n%s\n\n\n";
static char *blksizfmt = "\n\n\n\nBlock:  %6ld  --  Block Size:  %6ld\n";
static char *errtimfmt = "\n\n\n\n\nErrors: %6ld  --  Timeouts:    %6ld";
static char *delayfmt  = "\n\n\n\n\nPacket delay %ld";
/*
*   Below are some VLT globals to orchestrate the display
*/
long xpr_blocks = 0L, xpr_blocksize = 0L, xpr_errors = 0L, xpr_timeouts = 0L;
/*
*   The function
*/
long vlt_update(x)
struct XPR_UPDATE *x;
{
   extern struct Window *mywindow;
   extern char *XPR_Name;
/*
*   First time, determine the window size (50 chars wide, 5 lines tall).
*/
   SetMsgWindow(mywindow, 50, 6);
/*
*   Use VLT's PostMsg function to display all the information.
*/
   if (x->xpru_updatemask & XPRU_PROTOCOL) {
      PostMsg(mywindow, xprnamfmt, XPR_Name, x->xpru_protocol);
   }
   if (x->xpru_updatemask & XPRU_MSG) {
      PostMsg(mywindow, xprnamfmt, XPR_Name, x->xpru_msg);
   }
   if (x->xpru_updatemask & XPRU_ERRORMSG) {
      PostMsg(mywindow, xprnamfmt, XPR_Name, x->xpru_errormsg);
   }
   if (x->xpru_updatemask & XPRU_FILENAME) {
      PostMsg(mywindow, filnamfmt, x->xpru_filename);
   }
   if (x->xpru_updatemask & XPRU_PACKETDELAY) {
      PostMsg(mywindow, delayfmt, x->xpru_packetdelay);
   }
   if (x->xpru_updatemask & (XPRU_BLOCKS | XPRU_BLOCKSIZE)) {
      if (x->xpru_updatemask & XPRU_BLOCKS)    xpr_blocks    = x->xpru_blocks;
      if (x->xpru_updatemask & XPRU_BLOCKSIZE) xpr_blocksize = x->xpru_blocksize;
      PostMsg(mywindow, blksizfmt, xpr_blocks, xpr_blocksize);
   }
   if (x->xpru_updatemask & (XPRU_ERRORS | XPRU_TIMEOUTS)) {
      if (x->xpru_updatemask & XPRU_ERRORS)   xpr_errors   = x->xpru_errors;
      if (x->xpru_updatemask & XPRU_TIMEOUTS) xpr_timeouts = x->xpru_timeouts;
      PostMsg(mywindow, errtimfmt, xpr_errors, xpr_timeouts);
   }
   return(0L);
}

/**
*
*   Prompt the user for input
*
**/
long vlt_gets(s, t)
char *s, *t;
{
/*
*   Use VLT's DoRequest() function
*/
   return((long) DoRequest(mywindow, t, s, NULL, " Cancel "));
}

/**
*
*   Write a string to the serial port
*
**/
long vlt_swrite(s, n)
char *s;
long n;
{
/*
*   Use VLT's SendString() function
*/
   SendString(s, (int) n);
   return(0L);
}

/**
*
*   Read characters from the serial port
*
**/
long vlt_sread(buff, length, micros)
unsigned char *buff;
long length, micros;
{
   extern int timeout;
   long secs = 0L;

   if (buff == NULL) return(-1L);
/*
*   Convert timeout to seconds and micros if necessary
*/
   if (micros) {
      if (micros > 1000000L) {
         secs   = micros / 1000000L;
         micros = micros % 1000000L;
      }
   }
/*
*   What follows is pseudo code. VLT's implementation is at this point
*   different.
*/
   StartTimer(secs, micros);
/*
*   VLT has a global called timeout. This comes in xfer.h.
*   If the read was successful, add character to buffer.
*/
   for (i = 0; (timeout == GOODREAD) && (i < size); i++)
      buff[i] = (unsigned char) readchar();
/*
*   Either timed out or buffer is full.
*/
   if (timeout != TIMEOUT) AbortTimer();
/*
*   If carrier dropped, return error condition
*/
   if (timeout == CARRIER_DROPPED) return(-1L);
/*
*   Else return the number of characters read.
*/
   return(i);
}

/**
*
*   Flush the serial buffer.
*
**/
long vlt_sflush()
{
   ClearBuffer();
   return(0L);
}

/**
*
*   Interfaces to stdio
*
**/
long vlt_fopen(s, t)
char *s, *t;
{
   return((long) fopen(s, t));
}

long vlt_fclose(fp)
FILE *fp;
{
   return((long) fclose(fp));
}

long vlt_fread(buff, size, count, fp)
char *buff;
long size, count;
FILE *fp;
{
   int res;
   res = fread(buff, (int) size, (int) count, fp);
   return((long) res);
}

long vlt_fwrite(buff, size, count, fp)
char *buff;
long size, count;
FILE *fp;
{
   int res;
   res = fwrite(buff, (int) size, (int) count, fp);
   return((long) res);
}

long vlt_fseek(fp, offset, origin)
FILE *fp;
long offset;
long origin;
{
   int res;
   res = fseek(fp, offset, (int) origin);
   return((long) res);
}

/*
*   File name match (Marco's version).
*/
extern char *scdir();		/* MANX pattern matching function */

long vlt_ffirst(buff, pattern)
char *buff;
char *pattern;
{
   char *name;

   name = scdir(pattern);
   if (name) {
      strcpy(buff, name);
      return(1L);
   }
   else return(0L);
}

long vlt_fnext(oldstate, buff, pattern)
long oldstate;
char *buff;
char *pattern;
{
   return(vlt_ffirst(buff, pattern));
}

/**
*
*   Check for Abort
*
**/
long vlt_chkabort()
{
/*
*   VLT aborts its protocols when the escape key is pressed.
*   CheckForKey loops over the UserPort messages looking for an escape.
*/
   return((long) CheckForKey(69));
}

/**
*
*   Check for miscellaneous items
*
**/
long vlt_chkmisc()
{
/*
*   VLT does nothing
*/
   return(0L);
}

/**
*
*   File information
*
**/
long vlt_finfo(filename, infotype)
char *filename;
long infotype;
{
   struct stat st;

   if      (infotype == 1L) {
      if (stat(filename, &st) != -1) return(st.st_size);
      else return(0L);
   }
   else if (infotype == 2L) {
      return((long) (p_xlatemode + 1));
   }
}

/**
*
*   This function set the serial port and returns the current status.
*
**/
long vlt_setserial(newstatus)
long newstatus;
{
   long oldstatus, getserial();

/*
*   If only want to know current status return it
*/
   if (newstatus == -1L) return(getserial());
/*
*   Fields we don't support
*/
   if (   newstatus & 0xFF00E070L)               return(-1L);
/*
*   Baud rates we don't support
*/
   if ( ((newstatus & 0x00FF0000L) >> 16L) > 6L) return(-1L);
/*
*   Otherwise get old status
*/
   oldstatus = getserial();
/*
*   Set new status
*/
   setserial(newstatus);
/*
*   And return old status
*/
   return(oldstatus);
}

/**
*
*   Get current serial status
*
**/
static long getserial()
{
   static long pariarr[] = { 0x0000L, 0x0301L, 0x0101L, 0x0001L, 0x0003L,
                             0x0400L, 0x1B01L, 0x1901L, 0x1801L, 0x1803L,
                             0x1C00L, 0x1F01L, 0x1D01L, 0x1C01L, 0x1C03L  };
   long oldstatus;

   oldstatus = pariarr[p_parity];
/*
*   No Xon/Xoff
*/
   if ((p_handshake & 1) == 0) oldstatus |= 0x0080L;
/*
*   7-wire
*/
   if (p_handshake & 2) oldstatus |= 0x0004L;
/*
*   Baud rate
*/
   oldstatus |= ( ((long) p_baud) << 16L );

   return(oldstatus);
}

/**
*
*   Set new serial status
*
**/
static int setserial(newstatus)
long newstatus;
{
/*
*   Parity
*/
   switch (newstatus & 0xFFFFL) {
      case 0x0000L :
         p_parity = 0;
         break;
      case 0x0301L :
         p_parity = 1;
         break;
      case 0x0101L :
         p_parity = 2;
         break;
      case 0x0001L :
         p_parity = 3;
         break;
      case 0x0003L :
         p_parity = 4;
         break;
      case 0x0400L :
         p_parity = 5;
         break;
      case 0x1B01L :
         p_parity = 6;
         break;
      case 0x1901L :
         p_parity = 7;
         break;
      case 0x1801L :
         p_parity = 8;
         break;
      case 0x1803L :
         p_parity = 9;
         break;
      case 0x1C00L :
         p_parity = 10;
         break;
      case 0x1F01L :
         p_parity = 11;
         break;
      case 0x1D01L :
         p_parity = 12;
         break;
      case 0x1C01L :
         p_parity = 13;
         break;
      case 0x1C03L :
         p_parity = 14;
         break;
   }
   BaudService(4, p_parity);
/*
*   Protocol
*/
   p_handshake = 0;

   if ((newstatus & 0x0080L) == 0) p_handshake  = 1;
   if ( newstatus & 0x0004L      ) p_handshake |= 2;

   BaudService(3, p_handshake);
/*
*   Baud rate
*/
   p_baud = (newstatus & 0x00FF0000) >> 16L;
   BaudService(2, p_baud);

   return;
}

/**
*
*   Options function
*
**/
long vlt_options(n, opt)
long n;
struct xpr_option *opt[];
{
   char buff[256];
   long changed = 0L, i;
/*
*   Just loop over the options until we have time to implement a single
*   requester.
*/
   for (i = 0L; i < n; i++) {
      strncpy(buff, opt[i]->xpro_value, (int) opt[i]->xpro_length);
      if (DoRequest(mywindow, buff, opt[i]->xpro_description, NULL, " Cancel ")) {
         strncpy(opt[i]->xpro_value, buff, (int) opt[i]->xpro_length);
         changed |= (1L << i);
      }
   }
   return(changed);
}
