/*
    $Header: Welmat:src/Welmat/xprfts/RCS/support.c,v 1.2 92/11/24 23:40:35 rwm Exp Locker: rwm $

    General XPR support routines.

    Copyright (C) 1992 Russell McOrmond

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

 */

#include <proto/all.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <exec/exec.h>
#include <devices/timer.h>
#include "xproto.h"
#include "xmodem.h"
#include "xprfts.h"

/* Transfer options to use if XProtocolSetup not called */
struct SetupVars Default_Config = {
  NULL, NULL, 0, 0,
  { "Y" }, { "Y" }, { "B" }, { "B" }, { "Y" }, { "Y" }, 
  { "N" }, { "Y" }, { "N" }, { "N" }, { "Y" }, { "Y" }
};


/* Called by comm program to set transfer options */
long __saveds XProtocolSetup(struct XPR_IO *io) {
  struct SetupVars *sv;
  UBYTE buf[512], *p;

  /* Allocate memory for transfer options string */
  if (!(sv = (void *)io->xpr_data)) {
    io->xpr_data = AllocMem((long)sizeof(struct SetupVars),MEMF_CLEAR);
    if (!(sv = (void *)io->xpr_data)) {
      ioerr(io,"Not enough memory");
      return XPRS_FAILURE;
    }
    /* Start out with default options; merge user changes into defaults */
    *sv = Default_Config;
  }

  /* If options string passed by comm prog, use it; else use defaults */
  if (io->xpr_filename)
    strcpy(buf,io->xpr_filename);
  else
    return XPRS_FAILURE;

  /* Upshift options string for easier parsing */
  strupr(buf);

  if (p = find_option(buf,'7')) {
    if (*p == 'Y' || *p == 'N') *sv->option_7 = *p;
    else ioerr(io,"Invalid 7 Flag Ignored; Should Be Y or N");
  }

  if (p = find_option(buf,'C')) {
    if (*p == 'Y' || *p == 'N') *sv->option_c = *p;
    else ioerr(io,"Invalid C Flag Ignored; Should Be Y or N");
  }

  if (p = find_option(buf,'O')) {
    if (*p == 'B' || *p == 'Y' || *p == 'N') *sv->option_o = *p;
    else ioerr(io,"Invalid O Flag Ignored; Should Be Y, N, Or B");
  }

  if (p = find_option(buf,'I')) {
    if (*p == 'B' || *p == 'Y' || *p == 'N') *sv->option_i = *p;
    else ioerr(io,"Invalid I Flag Ignored; Should Be Y, N, Or B");
  }

  if (p = find_option(buf,'P')) {
    if (*p == 'Y' || *p == 'N') *sv->option_p = *p;
    else ioerr(io,"Invalid P Flag Ignored; Should Be Y or N");
  }

  if (p = find_option(buf,'S')) {
    if (*p == 'F' || *p == 'Y' || *p == 'N') *sv->option_s = *p;
    else ioerr(io,"Invalid S Flag Ignored; Should Be Y, N, Or F");
  }

  if (p = find_option(buf,'B')) {
    if (*p == 'Y' || *p == 'N') *sv->option_b = *p;
    else ioerr(io,"Invalid B Flag Ignored; Should Be Y or N");
  }

  if (p = find_option(buf,'A')) {
    if (*p == 'Y' || *p == 'N') *sv->option_a = *p;
    else ioerr(io,"Invalid A Flag Ignored; Should Be Y Or N");
  }

  if (p = find_option(buf,'N')) {
    if (*p == 'Y' || *p == 'N') *sv->option_n = *p;
    else ioerr(io,"Invalid N Flag Ignored; Should Be Y Or N");
  }

  if (p = find_option(buf,'W')) {
    if (*p == 'Y' || *p == 'N') *sv->option_w = *p;
    else ioerr(io,"Invalid W Flag Ignored; Should Be Y Or N");
  }

  if (p = find_option(buf,'F')) {
    if (*p == 'Y' || *p == 'N') *sv->option_f = *p;
    else ioerr(io,"Invalid F Flag Ignored; Should Be Y Or N");
  }

  if (p = find_option(buf,'M')) {
    if (*p == 'Y' || *p == 'N') *sv->option_m = *p;
    else ioerr(io,"Invalid M Flag Ignored; Should Be Y Or N");
  }

  if (p = find_option(buf,'D')) {
    sv->debug=atoi(p);
  }
  return ( XPRS_SUCCESS );
}


/* Called by comm program to give us a chance to clean up before program ends */
long __saveds XProtocolCleanup(struct XPR_IO *io) {
  /* Release option memory, if any */
  if (io->xpr_data) {
    FreeMem(io->xpr_data,(long)sizeof(struct SetupVars));
    io->xpr_data = NULL;
  }
  return XPRS_SUCCESS;
}


/* Called by comm program to let us monitor user's inputs; we never ask for
   this to be called, but it's better to recover gracefully than guru the machine */
long __saveds XProtocolHostMon(struct XPR_IO *io,UBYTE *serbuff,long actual,long maxsize) {
  return actual;
}


/* Called by comm program to let us monitor user's inputs; we never ask for
   this to be called, but it's better to recover gracefully than guru the machine */
long __saveds XProtocolUserMon(struct XPR_IO *io,UBYTE *serbuff,long actual,long maxsize) {
  return actual;
}



/* Perform setup and initializations common to both Send and Receive routines */
struct Vars *setup(struct XPR_IO *io) {
  struct SetupVars *sv;
  struct Vars *v;

  /* Make sure comm program supports the required call-back functions */
  if (!io->xpr_update) return NULL;
  if (!io->xpr_fopen || !io->xpr_fclose || !io->xpr_fread || !io->xpr_fwrite ||
      !io->xpr_fseek || !io->xpr_sread || !io->xpr_swrite ||
      !io->xpr_sflush) {
    ioerr(io,"Comm Prog Missing Required Function(s); See Docs");
    return NULL;
  }

  /* Hook in default transfer options if XProtocolSetup wasn't called */
  if (!(sv = (void *)io->xpr_data)) {
    io->xpr_data = AllocMem((long)sizeof(struct SetupVars),MEMF_CLEAR);
    if (!(sv = (void *)io->xpr_data)) {
      ioerr(io,"Not Enough Memory");
      return NULL;
    }
    *sv = Default_Config;
  }

  /* Allocate memory for our unshared variables, to provide reentrancy */
  if (!(v = AllocMem((long)sizeof(struct Vars),MEMF_CLEAR))) {
    ioerr(io,"Not Enough Memory");
    return NULL;
  }

  /* Copy caller's io struct into our Vars for easier passing */
  v->io = *io;

  /* clear the file count */
  v->infiles=v->outfiles=0;

  v->option_7=sv->option_7[0];
  v->option_c=sv->option_c[0];
  v->option_o=sv->option_o[0];
  v->option_i=sv->option_i[0];
  v->option_p=sv->option_p[0];
  v->option_s=sv->option_s[0];
  v->option_b=sv->option_b[0];
  v->option_a=sv->option_a[0];
  v->option_n=sv->option_n[0];
  v->option_w=sv->option_w[0];
  v->option_f=sv->option_f[0];
  v->option_m=sv->option_m[0];
  v->debug=sv->debug;
  return v;
}

/* Search for specified option setting in string */
UBYTE *find_option(UBYTE *buf,UBYTE option) {
  while (*buf) {
    buf += strspn(buf," ,\t\r\n");
    if (*buf == option) return ++buf;
    buf += strcspn(buf," ,\t\r\n");
  }
  return NULL;
}

/* Have the comm program display an error message for us, using a
   temporary XPR_UPDATE structure; used to display errors before Vars
   gets allocated */
void ioerr(struct XPR_IO *io,char *msg) {
  struct XPR_UPDATE xpru;

  if (io->xpr_update) {
    xpru.xpru_updatemask = XPRU_ERRORMSG;
    xpru.xpru_errormsg = msg;
    xpr_update(io,&xpru);
  }
}

/* Have the comm program display an error message for us, using the
   normal XPR_IO structure allocated in Vars */
void upderr(struct Vars *v,char *msg) {
  v->xpru.xpru_updatemask = XPRU_ERRORMSG;
  v->xpru.xpru_errormsg = msg;
  xpr_update(&v->io,&v->xpru);
}

/* Have the comm program display a normal message for us */
void updmsg(struct Vars *v,char *msg) {
  v->xpru.xpru_updatemask = XPRU_MSG;
  v->xpru.xpru_msg = msg;
  xpr_update(&v->io,&v->xpru);
}

/* Set the pass/fail status of this file */
void updstatus(struct Vars *v,char *filename,long status) {
  if(strlen(filename)) {  /* don't update when no filename given */
    v->xpru.xpru_updatemask = XPRU_FILENAME|XPRU_STATUS;
    v->xpru.xpru_filename = filename;
    v->xpru.xpru_status = status;
    xpr_update(&v->io,&v->xpru);
  }
}



/* Amiga-type replacement for standard Unix time() function.  Can't use
   Lattice's time() function from within a library because it's not
   reentrant (has some hidden static vars it sets) and because it wants
   you to have opened dos.library for some reason, which is supposedly
   a bad idea inside a library.  Oh, well... this is quite a bit
   smaller & faster anyway.  B-) */


/* # seconds between 1-1-70 (Unix time base) and 1-1-78 (Amiga time base).
   Add this value to the returned seconds count to convert Amiga system time
   to normal Unix system time. */
ULONG UnixTimeOffset = 252482400;



/* Returns current system time in standard Amiga-style timeval structure, if
   you pass a pointer to one.  Also returns the seconds part as the return
   value.  This lets you get just the seconds by calling GetSysTime(NULL)
   if that's all you want, or the full timeval struct if you need that.
   This is very similar to how the standard time() function is used. */

ULONG GetSysTimeXPR(struct timeval *tv) {
  struct timerequest tr;

  /* timer.device must be working or the system would've died, so let's
     not bother with error checking. */
  memset(&tr,0,sizeof(tr));
  OpenDevice(TIMERNAME,UNIT_VBLANK,(struct IORequest *)&tr,0L);

  tr.tr_node.io_Message.mn_Node.ln_Type = NT_MESSAGE;
  tr.tr_node.io_Command = TR_GETSYSTIME;

  DoIO((struct IORequest *)&tr);

  if (tv)
    *tv = tr.tr_time;

  CloseDevice((struct IORequest *)&tr);

  return tr.tr_time.tv_secs;
}
