/*                   ARexx serial manager                              *\
**            $RCSfile: rsm.c,v $
**            $Revision: 1.42 $
**            $Date: 92/11/18 23:01:29 $
**                                                                     **
**            Copyright 1991 Ron M. Battle  7-25-91                    **
**            Manx Aztec C V5.2                                        **
**            Can be freely distributed                                **
**            Use this as an example for your own serial manager!      **
**            Special Thanks to Inovatronics for their CanDo! SM       **
**            That is where I got my inspiration! (perspiration?)      **
**            RSM doesn't like binary nulls coming in the serial       **
**            port UNLESS used as a terminator character!              **
**                                                                     **
**            rbattle@hydra.unm.edu  (Internet)                        **
**                                                                     **
**            Revision History                                         **
**            ----------------------------------------------------     **
**            V1.1  7-25-91  Designed for special app using Dir2       **
**            V1.2  8-24-91  Added cmd line input: -u -t -c            **
**            V1.3  11-3-91  Added -l -b -d  & corrected logic         **
**            V1.4 04-19-92  Changed read and write ports to reflect   **
**                           Unit# so multiple RSMs can be run         **
**                           Used protos and pragmas, 32 bit ints      **
**                           Added prefix string for incoming serial   **
**            V1.41 9-28-92  Added space between prefix and incoming   **
**                           serial string, VERY important for CanDo!  **
**            V1.42 11-18-92 Previous code changes screwed up the      **
**                           adding of any terminator to an output     **
**                           string (CR,etc.), FIXED!! Also did        **
**                           general code clean up                     **
**                                                                     **
**  run rsm -bBuffSize -cCase -dDevice -lListenPort -pPrefix -tTalkPort -uUnit **
\*  eg:     -b32 -cN -dserial.device -lRSM0 -u0 -tdirector0  (default)   */

static char   *Version = "$VER: Renaldo's Serial Manager! 18Nov92 V1.42 (" ;

#include <functions.h>
#include <devices/serial.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <clib/rexxsyslib_protos.h>
#include <pragmas/rexxsyslib_lib.h>

/* #define DEBUG  */


/* defaults for serial device */
#define READ_BITS   8
#define WRITE_BITS  8
#define STOP_BITS   1
#define DEFAULT_BAUD 1200L

#define NO_ERR          0
#define RPORT_ERR       1
#define READ_EXTIO_ERR  2
#define DEVICE_ERR      3
#define WPORT_ERR       4
#define WRITE_EXTIO_ERR 5
#define REXXLIB_ERR     6
#define REXXPORT_ERR    7
#define REXXMSG_ERR     8
#define ARGSTR_ERR      9


#define MAXIN      276            /* size of in-buffer  */
#define MAXOUT     276           /* size of out-buffer */
#define MAXNAME    20
#define MAXPREFIX  8
#define SMALLREAD  32L  /* default # of chars collected in a serial read */

#define HOST_PORT_NAME     "RSM"                  /* Renaldo's Serial Manager! */
#define TALK_PORT_NAME "director0"

struct  RxsLib          *RexxSysBase = NULL ;
struct  MsgPort         *rexx_port   = NULL ;  /* for incoming commands       */
struct  MsgPort         *TalkPort= NULL ;     /* where serial input is sent  */
ULONG                   RexxSigMask ;        /* signal mask used for WAIT() */


char   *ErrString[] = {
                       "NONE!",
                       "Creating read port!",
                       "Allocating read i/o block!",
                       "Opening serial device!",
                       "Creating write port!",
                       "Allocating write i/o block!",
                       "Opening  rexx support library!",
                       "Creating rexx port!",
                       "Allocating rexx message!",
                       "Allocating rexx arg string!"};

char   HelpStr[] = {"\n\
\n    Renaldo's Serial Manager!       HyperBorea Studio B\n\
----------------------------------------------------------------------\n\
  USAGE: run rsm -bBuffer -cCase -lListen -tTalk -uUnit# -dDevice -pPrefix\n\
example: run rsm -b32 -cN -lRSM0 -tdirector0 -u0 -dserial.device (DEFAULT)\n\
 -b  buffer size 1-255 bytes, actually reads 1-255 bytes out of the\n\
     total serial device buffer which is usually 512 bytes, 1 byte\n\
     serial reads are useful when using Sony laser disc players\n\
 -c  case translation for incoming data from the serial port,\n\
     Upper Lower None\n\
 -l  listen port name, Input, where to send ARexx commands\n\
 -t  talk port name, Output, where RSM will SEND incoming serial data\n\
 -u  unit number for serial device, 0-31, if no listen port specified,\n\
     will be appended to RSM, eg: RSM0 RSM31 RSM1\n\
 -d  device name\n\
 -p  prefix ThisString to incoming serial string, 8 chars max\n\
ARexx commands:  (separate arguments with SPACES!)\n\
---------------\n\
quit      shuts down the serial manager\n\
flush     flushes out the serial device buffer\n\
prefix    prefix ThisString  to the incoming serial string (useful for CanDo!)\n\
          8 chars max\n\
send      send ThisString    out the serial port\n\
seteof    seteof OFF 10      if ON, will set terminator char (ASCII value)\n\
          for serial reads, will also be added as suffix to send strings\n\
          DEFAULT is OFF, for Pioneer LDPs try: setEOF ON 13\n\
          RSM chokes on incoming binary zeros (nulls) UNLESS used as a terminator\n\
setparams setparams BaudRate 8N1   will set serial port parameters,\n\
          baud=300-19200, 7-8 data bits, parity E O N, 1-2 stop bits\n\
          DEFAULT is 1200 baud, 8 data bits, No parity, 1 stop bit\n\
status    returns 16bit status comma then number of unread serial chars (4,0)\n\
test      test ThisString    will send Ready! out the serial port and\n\
          translate ThisString according to how Case was set\n\
ARexx CLI examples:\n\
  rx 'address RSM0 quit'  rx 'address RSM0 setEOF ON 10'\n\
  rx 'address RSM0 setparams 9600 8N1'\n\
----------------------------------------------------------------------\n\
                     RSM is NOT running!\n" };

#define NUMCMDS 9
char   *CmdStr[] = { "quit","flush","prefix","send","seteof","setparams","status",
                     "test","illegal"};
enum   cmds        {  quit,  flush,  prefix,  send,  seteof,  setparams,  status,
                      test,  illegal };

struct MsgPort  *myReadPort=NULL,*myWritePort=NULL ;
struct IOExtSer *myReadReq=NULL,*myWriteReq=NULL   ;
char   CmdTerm[2] ;         /* command terminator */
char   SerialParams[4] ;
char   ResultString[MAXNAME+1] ;  /* for ARexx returns */
char   Prefix[MAXPREFIX+1] ;   /* appended to start of incoming serial msg */
char   ReadPortName[MAXNAME+1], WritePortName[MAXNAME+1] ;
char   TalkPortName[MAXNAME+1], ListenPortName[MAXNAME+1], DeviceName[MAXNAME+1] ;
char   TranslateCase = 'n' ;     /* Upper, Lower, or None for input translation */
UBYTE  MyFlags = (SERF_7WIRE | SERF_SHARED); /* default SHARED mode  */
UBYTE  inbuff[MAXIN], outbuff[MAXOUT] ;
ULONG  wakeupmask, ReadSigMask, WriteSigMask ;
ULONG  BaudRate=1200L, SerialUnit=0L, MaxRead=SMALLREAD ;
USHORT LastErr            = NO_ERR;
BOOL   SerDeviceACTIVE    = FALSE ;
BOOL   ReadRequestPENDING = FALSE ;
BOOL   WriteRequestPENDING= FALSE ;
BOOL   TimeToExit         = FALSE ;        /* used to exit ProcessEvents()  */
BOOL   EofON              = FALSE ;       /* End of File mode              */

void StartSerialRead()
{
   long len=MaxRead ;  /* default */

   /* will read until MaxRead chars OR terminator char received                  */

   myReadReq->IOSer.io_Data    = (APTR)inbuff;  /* where to put incoming chars */
   myReadReq->IOSer.io_Length  = len ;         /* max chars to receive        */
   myReadReq->IOSer.io_Command = CMD_READ   ; /* this is a read request      */

   SendIO((struct IORequest *)myReadReq);   /* let task gather chars, move on */
}


void SetEOF(char termchar)   /* set terminator for serial reads */
{
  char   *termbuff  ;
  USHORT x          ;

  termbuff = (char *)&myReadReq->io_TermArray   ;
  for (x=0 ; x<8 ; x++) *(termbuff+x) = termchar;
  #ifdef DEBUG
    printf("TermArray[0]= %d\n",*termbuff) ;
  #endif

}


long SetParams(ULONG baud, char *params, char eof)  /* set baud rate and other */
{                                        /* can't be done if READ in progress */
  LONG   error=0L ;                     /* bit negation is...~               */
  USHORT rl,sb,pb ;

  if (ReadRequestPENDING) {         /* terminate any pending read request    */
    AbortIO((struct IORequest *)myReadReq) ;
    WaitIO((struct IORequest *)myReadReq)  ; /* recycle message            */
    ReadRequestPENDING = FALSE ;
  };

  pb = tolower(*(params+1)) ;   /* convert parity char to lowercase n,e,o   */

  switch (pb) {
    case 'n' : MyFlags &= ~SERF_PARTY_ON  ;  /* if none, turn off parity bit */
               MyFlags &= ~SERF_PARTY_ODD ; /* also clear odd parity        */
               break ;

    case 'e' : MyFlags &= ~SERF_PARTY_ODD ;   /* if even, turn off odd parity */
               MyFlags |=  SERF_PARTY_ON  ;  /* also turn on parity bit      */
               break ;

    case 'o' : MyFlags |=  SERF_PARTY_ODD ;  /* if odd, turn on odd parity */
               MyFlags |=  SERF_PARTY_ON  ; /* also turn on parity bit    */
               break ;

    default  : MyFlags &= ~SERF_PARTY_ON  ;  /* default to no parity checking */
               MyFlags &= ~SERF_PARTY_ODD ; /* also clear odd parity         */
               pb = 'n' ;
               break    ;
  };
  if (EofON) MyFlags |= SERF_EOFMODE ;   /* turn ON  (TRUE)   */
  else MyFlags &=  ~SERF_EOFMODE     ;  /*  turn OFF (FALSE) */

  if (baud>19200 || baud<300) baud = DEFAULT_BAUD ;  /* simple error checking   */
  rl = (USHORT)(*params-48) ;                       /* convert char to number  */
  if (rl<7 || rl>9) rl = READ_BITS ;               /* if error, set default   */
  sb = (USHORT)( *(params+2)-48)   ;
  if (sb<1 || sb>2) sb = STOP_BITS ;

  BaudRate = baud     ;                       /* now save globals */
  *CmdTerm     = eof  ;
  *(CmdTerm+1) = '\0' ;
  strcpy(SerialParams,params) ;

  myReadReq->io_Baud      = baud    ;
  myReadReq->io_ReadLen   = rl      ;
  myReadReq->io_WriteLen  = rl      ;
  myReadReq->io_StopBits  = sb      ;
  myReadReq->io_SerFlags  = MyFlags ;
  SetEOF(eof) ;                                    /* fill array with eof char */
  myReadReq->IOSer.io_Command = SDCMD_SETPARAMS ;
  #ifdef DEBUG
  printf("baud=%ld rl=%d par=%c sb=%d flags=%u EofON=%d EOF=%d MaxRead=%ld\n",
         BaudRate,rl,pb,sb,MyFlags,EofON,*CmdTerm,MaxRead) ;
  #endif
  if ((error = DoIO((struct IORequest *)myReadReq)) != 0L) {
    printf("SetParams error = %ld\n",error) ;
  };
  return(error) ;
}



void SendSerialString(char *buff)
{
   long   len  ;

   strcpy(outbuff,buff) ;    /* copy string to out text buffer */

   if (EofON) {                    /* add terminator if End Of File mode on */
     if (*CmdTerm != '\0') {
       strcat(outbuff,CmdTerm) ;
       len = strlen(outbuff)   ;
       #ifdef DEBUG
         printf("OutString= %s\n len= %d\n",outbuff,len) ;
       #endif
     }
     else len = (-1) ;       /* if len= -1, will send until nul, also send nul */

   }
   else len = strlen(outbuff) ;

   if (WriteRequestPENDING) {
     WaitIO((struct IORequest *)myWriteReq) ;
     WriteRequestPENDING = FALSE ;
     #ifdef DEBUG
       puts("Write finished within SendSerialString.") ;
     #endif
   };

   myWriteReq->IOSer.io_Data    = (APTR)outbuff ; /* I had buff here! WRONG! */
   myWriteReq->IOSer.io_Length  = len ;
   myWriteReq->IOSer.io_Command = CMD_WRITE  ;

   SendIO((struct IORequest *)myWriteReq)    ;   /* asynchronous */
   WriteRequestPENDING = TRUE ;
}


void ClearSerialBuffer(void)
{
   ULONG   error ;

   myWriteReq->IOSer.io_Command = CMD_CLEAR; /* clear out serial device buffer  */

   if ((error = DoIO((struct IORequest *)myWriteReq)) != 0L) { /* synchronous  */
     printf("Clear serial buffer error= %ld\n",error);
   }
}


long GetSerialStatus(void)   /* fills ResultString with 16bit int, unread chars */
{                           /*  (numbers separated by a comma)                 */
   long  error=0L ;

   if (WriteRequestPENDING) {
     WaitIO((struct IORequest *)myWriteReq) ;
     WriteRequestPENDING = FALSE ;
   };

   myWriteReq->IOSer.io_Command = SDCMD_QUERY ;                 /* query command */
   if ((error = DoIO((struct IORequest *)myWriteReq)) != 0L) { /* synchronous   */
     printf("serial query error= %ld\n",error);
   };
sprintf(ResultString,"%u,%lu\0",myWriteReq->io_Status,myWriteReq->IOSer.io_Actual);
   return(error) ;
}


void MyCleanup(USHORT err)
{
 if (err) printf("RSM stopped because: ERROR %s\n",ErrString[err]);

 if (ReadRequestPENDING) {                    /* clean up any pending requests */
   AbortIO((struct IORequest *)myReadReq) ;
   WaitIO((struct IORequest *)myReadReq)  ;
 };
 if (WriteRequestPENDING) {
   AbortIO((struct IORequest *)myWriteReq) ;
   WaitIO((struct IORequest *)myWriteReq)  ;
 };

 if (SerDeviceACTIVE)     CloseDevice((struct IORequest *)myReadReq) ;
 if (myReadReq   != NULL) DeleteExtIO((struct IORequest *)myReadReq) ;
 if (myReadPort  != NULL) DeletePort(myReadPort)  ;
 if (myWriteReq  != NULL) DeleteExtIO((struct IORequest *)myWriteReq);
 if (myWritePort != NULL) DeletePort(myWritePort) ;
 if (rexx_port   != NULL) DeletePort(rexx_port)   ;
}

void MyExit(USHORT err)
{
 MyCleanup(err) ;
 exit(FALSE) ;
}


/* ARexx routines */

struct MsgPort *SetupRexxPort(void)
{
   struct MsgPort *the_port ;
   int            err       ;

   Forbid() ;
   if (FindPort(ListenPortName)) {
     Permit() ;
     err = printf("A public port called '%s' already exists!\n",ListenPortName);
     return(NULL) ;
   }
   the_port = CreatePort(ListenPortName,0L) ;
   Permit() ;
   return(the_port) ;
}


USHORT InitARexx(void)
{
    RexxSysBase = (struct RxsLib *)OpenLibrary(RXSNAME, 0L) ;
    if (RexxSysBase == NULL)  return(REXXLIB_ERR) ;
    if ((rexx_port = SetupRexxPort()) == NULL) return(REXXPORT_ERR) ;
    RexxSigMask = (1L<<rexx_port->mp_SigBit) ;  /* setup signal for Wait()   */
    Forbid() ;       /* now look and see if there is something to talk to!  */
    if ((TalkPort=FindPort(TalkPortName))==NULL) {
      Permit() ;
      printf("%s port NOT found!\n",TalkPortName) ;

    };
    Permit() ;
    return(NO_ERR) ;
}


USHORT ExportSerialInput(UBYTE *info)
{
    struct RexxMsg *rexxmsg    ;
    char           temp[MAXIN] ;

    if ( (rexxmsg = CreateRexxMsg(rexx_port,NULL,TalkPortName)) == NULL) {
      return(REXXMSG_ERR) ;
    } ;
    temp[0] = '\0' ;
    if (*Prefix != '\0') { /* concat prefix and info */
      sprintf(temp,"%s %s",Prefix,info) ; /* SPACE between CRUCIAL for CanDo */
      rexxmsg->rm_Args[0] = CreateArgstring(temp,strlen(temp)) ;
    }
    else rexxmsg->rm_Args[0] = CreateArgstring(info,strlen(info)); /* no prefix */
    if (rexxmsg->rm_Args[0]==NULL) {
      DeleteRexxMsg(rexxmsg) ;
      return(ARGSTR_ERR) ;
    } ;
    #ifdef DEBUG
      printf("1st char from serial port is: %d\n",*info) ;
    #endif
    rexxmsg->rm_Action = RXCOMM ;        /* command mode, NOT function */
    PutMsg(TalkPort,(struct Message *)rexxmsg)  ;
    return(0) ;
}

void RecycleRexxMessage(struct RexxMsg *rexxmessage)
{
    DeleteArgstring(rexxmessage->rm_Args[0]) ;
    DeleteRexxMsg(rexxmessage) ;
}

/* ******** */


void TranslateString(char *str)
{
  switch (TranslateCase) {

    case    'u' : strupr(str) ;
                  break       ;

    case    'l' : strlwr(str) ;
                  break       ;

    default     : break ;
  }
}


long DoCommand(char *cmd, char *arg1, char *arg2, char *rawstr)
{
  USHORT    CurrentCmd=0,eof ;
  long      result=0L        ;
  ULONG     baud             ;

  ResultString[0] = '\0' ;    /* start with null for return string */
  strlwr(cmd) ;  /* convert command to lower case */
  /*  now check first 5 letters of command and get an index into CmdStr[]  */
while((CurrentCmd< NUMCMDS)&&(strncmp(cmd,CmdStr[CurrentCmd],5L)!= 0))CurrentCmd++;

  switch (CurrentCmd) {

    case quit       : TimeToExit = TRUE  ;
                      puts("   RSM DONE!!") ;
                      break ;

    case flush      : ClearSerialBuffer() ;
                      #ifdef DEBUG
                        puts("flush received!") ;
                      #endif
                      break ;

    case prefix     : strncpy(Prefix,arg1,MAXPREFIX) ;   /* save prefix   */
                      #ifdef DEBUG
                        printf("prefix is: %s\n",Prefix) ;
                      #endif
                      break ;

    case send       : SendSerialString(rawstr) ;    /* send out what came in! */
                      #ifdef DEBUG                 /* minus the send cmd!    */
                        puts("send received!") ;
                      #endif
                      break ;

    case seteof     : strlwr(arg1) ;  /* convert to lower case */
                      if (strncmp(arg1,"on",2L)==0) EofON = TRUE ;
                      else EofON = FALSE ;
                      eof = (USHORT)atoi(arg2) ;
                      #ifdef DEBUG         /* ifdef won't work right after colon */
                        puts("SetEOF received!") ;
                      #endif
                      result = SetParams(BaudRate,SerialParams,eof) ;
                      break ;

    case setparams  : baud = atol(arg1) ;
                      result = SetParams(baud,arg2,*CmdTerm) ;
                      #ifdef DEBUG
                        puts("setparams received!") ;
                      #endif
                      break ;

    case status     : /* 16bits of status comma unread character count  */
                      result = GetSerialStatus() ; /* put in ResultString */
                      #ifdef DEBUG
                        puts("status received!") ;
                      #endif
                      break ;

    case test       : SendSerialString("Ready!") ;
                      #ifdef DEBUG
                        puts("test received!");
                      #endif
                      TranslateString(arg1) ;
                      printf("translated= %s\n",arg1) ;
                      break ;

    default         : result = 2L ;  /* return error if not valid command */
                      #ifdef DEBUG
                        puts("illegal command received!") ;
                      #endif
                      break ;


  };  /* switch */
  return(result) ;
}

void ReplyRexxMessage(struct RexxMsg *rexxmessage, long result)
{
  rexxmessage->rm_Result1 = result ;
  rexxmessage->rm_Result2 = 0L  ;
  if ((result==0)&&(*ResultString != '\0')) {  /* send back result string       */
    rexxmessage->rm_Result2 = ResultString ;  /* point to global result string */
  };
  ReplyMsg((struct Message *)rexxmessage)  ;
}

void ProcessRexxCommand(struct RexxMsg *rexxmessage)
{
  char    *temp      ;
  char    rawstr[MAXOUT] ;  /* storage for whole string, no cmd         */
  char    *token[3]  ;     /* token[0]=Command, other=arguments        */
  USHORT  x          ;    /* token buffer full of pointers to strings */
  long    result=0L  ;

  temp = strchr((char *)rexxmessage->rm_Args[0],' ') ;   /* look for first space */
  temp++ ;                                         /* increment past the space! */
  strcpy(rawstr,temp) ;                           /* save raw                  */
  for (x=0; x<3; token[x]=NULL,x++) ;            /* clear out token buffer    */
  token[0] = (char *)rexxmessage->rm_Args[0] ;  /* parse ARexx string,       */
  token[0] = strtok(token[0],(char *)" ,") ;   /* look for space or ,       */
  token[1] = strtok(NULL," ,") ;
  token[2] = strtok(NULL," ,") ;
  result = DoCommand(token[0],token[1],token[2],rawstr) ;
  ReplyRexxMessage(rexxmessage,result) ;
}



USHORT SetupPorts(void)     /* setup separate read and write ports/requests */
{
  LONG      i, error    ;
  BYTE      *b, *c      ;
  char      scratch[4]  ;

  strcpy(ReadPortName,"RSMrp") ;                /* name of read port    */
  sprintf(scratch,"%ld",SerialUnit) ;          /* anomaly of Aztec C!  */
  strcat(ReadPortName,scratch) ;              /* append the unit #    */
  myReadPort = CreatePort(ReadPortName,0L) ;
  if (myReadPort == NULL) return(RPORT_ERR) ;

  i = (LONG)sizeof(struct IOExtSer) ;
  myReadReq = (struct IOExtSer *)CreateExtIO(myReadPort,i) ;
  if (myReadReq == NULL) return(READ_EXTIO_ERR) ;
  myReadReq->io_SerFlags= MyFlags ;
  error = OpenDevice(DeviceName,SerialUnit,(struct IORequest *)myReadReq,0L) ;
  if (error != 0) return(DEVICE_ERR) ;
  else SerDeviceACTIVE = TRUE ;
  ReadSigMask = (1L<<myReadPort->mp_SigBit) ;  /* signal used for Wait() */

  strcpy(WritePortName,"RSMwp") ;                    /* name of write port   */
  sprintf(scratch,"%ld",SerialUnit) ;               /* anomaly of Aztec C!  */
  strcat(WritePortName,scratch) ;                  /* append the unit #    */
  myWritePort = CreatePort(WritePortName,0L) ;
  if (myWritePort == NULL) return(WPORT_ERR) ;

  myWriteReq = (struct IOExtSer *)CreateExtIO(myWritePort,
               sizeof(struct IOExtSer)) ;
  if (myWriteReq == NULL) return(WRITE_EXTIO_ERR) ;

  b = (BYTE *) myReadReq  ;    /* start of Read request block  */
  c = (BYTE *) myWriteReq ;   /* start of Write request block */

  for(i=0; i<sizeof(struct IOExtSer); i++) *c++ = *b++ ; /* copy read->write */
  myWriteReq->IOSer.io_Message.mn_ReplyPort= myWritePort ;
  WriteSigMask = (1L<<myWritePort->mp_SigBit) ;  /* signal used for Wait() */
  return(NO_ERR) ;
}

void ParseCmdLine(char *arg[])   /* global variables will be modified */
{
  ULONG     offset ;
  USHORT    i ;
  char      *mycase ;
  char      *myunit, *talkto, *listento, *mybuff, *device, *prefix ;
  char      buff[22]         ;

  if (*arg[1]=='?') {                         /* give help */
    printf("%s",HelpStr) ;
    exit(0) ;
  };
  for (i=0; arg[i] != NULL; i++) { /* look at all cmd line arguments  */
    strcpy(buff,arg[i]) ;                /* save original string                 */
    strlwr(arg[i]) ;                    /* convert to lower case first          */
    if ((myunit = strstr(arg[i],"-u"))!= NULL) { /* look for unit number       */
      myunit += 2L ;                          /*   increment pointer past 'u' */
      SerialUnit = atol(myunit) ;            /* convert char to long int     */
      if (SerialUnit<0L)  SerialUnit =  0L ;
      if (SerialUnit>31L) SerialUnit = 31L ;
    };
    if ((mybuff = strstr(arg[i],"-b"))!= NULL) { /* look for buffer size */
      mybuff += 2L ;
      MaxRead = atol(mybuff) ;
      if (MaxRead < 1L)  MaxRead = SMALLREAD ;
      if (MaxRead > (MAXIN-MAXNAME-1)) MaxRead = MAXIN-MAXNAME-1;
    };
    if ((listento = strstr(arg[i],"-l"))!= NULL) { /* look for listen port name */
      offset = (listento - arg[i]) + 2L ;
      strncpy(ListenPortName,buff+offset,MAXNAME) ;
    };
    if ((talkto  = strstr(arg[i],"-t"))!= NULL) { /* look for talkto port name   */
      offset = (talkto - arg[i]) + 2L ; /* calculate offset into original str   */
      strncpy(TalkPortName,buff+offset,MAXNAME);
    };
    if ((device = strstr(arg[i],"-d"))!= NULL) {  /* look for device name       */
      offset = (device - arg[i]) + 2L ;
      strncpy(TalkPortName,buff+offset,MAXNAME);
    };
    if ((prefix = strstr(arg[i],"-p")) != NULL) { /* look for prefix string */
      offset = (prefix - arg[i]) + 2L ;
      strncpy(Prefix,buff+offset,MAXPREFIX) ;
    };
    if ((mycase = strstr(arg[i],"-c"))!= NULL) { /* look for case translation  */
      mycase += 2L ;
      TranslateCase = *mycase ;

      switch (TranslateCase) {
        case 'u': case 'l':
          break ;

        default :
          TranslateCase = 'n'   ;
          break ;

      }  /* switch   */
    }   /* if case  */
  }    /* for loop */
}     /* proc     */



void ProcessEvents(void)
{
  struct RexxMsg      *rexxmessage ;
  long                end          ;

  while (!TimeToExit)  {

    wakeupmask=Wait(ReadSigMask|WriteSigMask|RexxSigMask); /* wait for events */

    if (wakeupmask & WriteSigMask) {            /* serial write has completed   */
      WaitIO((struct IORequest *)myWriteReq) ; /* remove message and clean-up  */
      WriteRequestPENDING = FALSE ;
      #ifdef DEBUG
        puts("Write Request finished!!") ;
      #endif
    };

    if (wakeupmask & ReadSigMask) {             /* string ready from serial port */
      WaitIO((struct IORequest *)myReadReq);   /* remove message and clean-up   */
      ReadRequestPENDING = FALSE ;            /* reset flag                    */
      end = (myReadReq->IOSer.io_Actual)  ;  /* get actual length of string   */
      inbuff[end] = '\0' ;          /* null terminate incoming string        */
      if (TranslateCase!= 'n') TranslateString((char *)inbuff); /* u/l case */
      if (TalkPort != NULL) {
        LastErr = ExportSerialInput(inbuff);      /* send it on to TalkToPort  */
        if (LastErr != NO_ERR) TimeToExit=TRUE ; /* allows nice cleanup       */
      };
      #ifdef DEBUG
        puts("Read Request finished!!") ;
      #endif
      StartSerialRead() ;     /* setup next asynchronous read from serial port */
      ReadRequestPENDING = TRUE ;
    };

    if (wakeupmask & RexxSigMask) {               /* process Rexx commands */
      while ( (rexxmessage = (struct RexxMsg *)GetMsg(rexx_port)) ) {
        if (rexxmessage->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) {
          RecycleRexxMessage(rexxmessage) ; /* recycle if only a reply message */
        }
        else ProcessRexxCommand(rexxmessage)   ;
      }
    }

  }; /* while !TimeToExit */
  while ( (rexxmessage = (struct RexxMsg *)GetMsg(rexx_port)) ) { /* CLEANUP! */
   if (rexxmessage->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) {
     RecycleRexxMessage(rexxmessage) ;
   }
   else ReplyMsg( (struct Message *) rexxmessage );
  }    /* cleanup while */
}     /* proc          */


/*  *******  MAIN  PROGRAM  ******  */

main(int argc, char *argv[])
{
  USHORT  err   ;
  char    scratch[4] ;

  ListenPortName[0]='\0' ;     /* initialize */
  TalkPortName[0]  ='\0' ;
  DeviceName[0]    ='\0' ;
  Prefix[0]        ='\0' ;

  if (argc>1) ParseCmdLine(argv) ;         /* look for cmd line args     */

  if (ListenPortName[0]=='\0') {
    strcpy(ListenPortName,HOST_PORT_NAME) ;   /* default              */
    sprintf(scratch,"%ld",SerialUnit) ;      /* anomaly of Aztec C!  */
    strcat(ListenPortName,scratch) ;        /* append the unit #    */
  } ;
  if (TalkPortName[0]=='\0') strcpy(TalkPortName,TALK_PORT_NAME) ;
  if (DeviceName[0]  =='\0') strcpy(DeviceName,SERIALNAME)       ;

  #ifdef DEBUG
    printf("SerialUnit=%ld ListenPort=%s TalkPort=%s DeviceName=%s Prefix=%s\n",
            SerialUnit,ListenPortName,TalkPortName,DeviceName,Prefix) ;
  #endif

  if ((err = SetupPorts())!= NO_ERR) MyExit(err) ;
  if ((err = InitARexx()) != NO_ERR) MyExit(err) ;

  ClearSerialBuffer() ;
  SetParams(1200L,"8n1",'\r')    ;    /* default serial parameters */
  StartSerialRead()  ;
  ReadRequestPENDING = TRUE ;

  ProcessEvents()    ;

  MyCleanup(LastErr) ;
  exit(0)  ;

}



