/************************************************************************
*  a terminal program that has ascii and xmodem transfer capability
*
*  use esc to abort xmodem transfer
*
*  written by Michael Mounier
*
*  Modification History -  ( these mods by Steve Allen)
*   Feb 1986    - added Half-duplex and Echoplex
*               - added timing to and fixed bugs in XMODEM receive
*               - changed Ascii Send to Prompted Upload
*               - added Command-Key sequences to menu
*   March 86    - added function-keys
*               - changed keyboard and screen io to console.device,
*                 which allows ansi standard vt-100 emulation
************************************************************************/

/*  compiler directives to fetch the necessary header files */
#include <libraries/dos.h>
#include <devices/serial.h>
#include <fcntl.h>

#include "con.h"         /* which will manage console i/o  */

#define INTUITION_REV NULL
#define GRAPHICS_REV NULL

/* things for xmodem send and receive */
#define SECSIZ   128             /* This many bytes in an xmodem block */
#define BufSize  SECSIZ*2  /* We'll follow two blocks (for chop) */
#define ERRORMAX 10        /* Max errors before abort */
#define RETRYMAX 10       /* Maximum retrys before abort */
#define SOH      1       /* Start of sector char */
#define EOT      4      /* end of transmission char */
#define ACK      6     /* acknowledge sector transmission */
#define NAK      21   /* error in transmission detected */
#define CAN      24
#define CR       13
#define BELL    'G'&0x1f
#define CPM_EOF  26

  /* things for function keys */
#define NUMKEYS 10                  /* number of function keys */
#define KEYSIZE 40                  /* maximum length of any one string */
#define BUFSIZE KEYSIZE * NUMKEYS   /* size of total buffer */
#define HEADER 0xaa                 /* must be first byte of file */

UBYTE
    c,character,prompt,
    bufr[BufSize], numstring[5],
    *keybuf,*KeyOffset;
static int
    fd,
    timeout = FALSE,
    cancel,
    LOCAL, ECHO,
    waiting,
    OnKey=FALSE;
static long
    bytes_xferred;

UBYTE ModemRead();  /* C demands this non-integer declaration */
                    /* 'cause ModemRead is used before it is defined */

struct IOStdReq *KeyPort;

/*   Intuition always wants to see these declarations */
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;



/* my window structure */
struct NewWindow NewWindow = {
   0,0,640,200, /* left, top, width, height */
   0,1,         /* detail, block pens */
   CLOSEWINDOW | MENUPICK,
   WINDOWCLOSE | SMART_REFRESH | ACTIVATE | WINDOWDEPTH | BORDERLESS,
   NULL,       /* firstgadget */
   NULL,       /* checkmark */
   (UBYTE *)"Terminal", /* title */
   0,          /* screen */
   NULL,       /* bitmap */
   0,0,        /* min x,y */
   640, 200,   /* max x,y */
   WBENCHSCREEN  /* type */
};

struct Window *mywindow;             /* ptr to applications window */
struct IntuiMessage *NewMessage;    /* msg structure for GetMsg() */

/*****************************************************
*                     File Menu
*****************************************************/

/* define maximum number of menu items */
#define FILEMAX 5

/*   declare storage space for menu items and
 *   their associated IntuiText structures
 */
struct MenuItem FileItem[FILEMAX];
struct IntuiText FileText[FILEMAX];

/*****************************************************************/
/*    The following function initializes the structure arrays    */
/*   needed to provide the File menu topic.                      */
/*****************************************************************/
InitFileItems()
{
short n;

/* initialize each menu item and IntuiText with loop */
for( n=0; n<FILEMAX; n++ )
   {
   FileItem[n].NextItem = &FileItem[n]+1;
   FileItem[n].LeftEdge = 0;
   FileItem[n].TopEdge = 11 * n;
   FileItem[n].Width = 135 + COMMWIDTH;
   FileItem[n].Height = 11;
   FileItem[n].Flags = ITEMTEXT | ITEMENABLED | HIGHBOX | COMMSEQ;
   FileItem[n].MutualExclude = 0;
   FileItem[n].ItemFill = (APTR)&FileText[n];
   FileItem[n].SelectFill = NULL;
   FileItem[n].Command = 0;
   FileItem[n].SubItem = NULL;
   FileItem[n].NextSelect = 0;

   FileText[n].FrontPen = 0;
   FileText[n].BackPen = 1;
   FileText[n].DrawMode = JAM2;     /* render in fore and background */
   FileText[n].LeftEdge = 0;
   FileText[n].TopEdge = 1;
   FileText[n].ITextFont = NULL;
   FileText[n].NextText = NULL;
   }
FileItem[FILEMAX-1].NextItem = NULL;

/* initialize text for specific menu items */
FileText[0].IText = (UBYTE *)"Ascii Capture";
FileText[1].IText = (UBYTE *)"Ascii Transmit";
FileText[2].IText = (UBYTE *)"Xmodem Receive";
FileText[3].IText = (UBYTE *)"Xmodem Send";
FileText[4].IText = (UBYTE *)"Prompt";

FileItem[0].Command = 'c';
FileItem[1].Command = 't';
FileItem[2].Command = 'r';
FileItem[3].Command = 's';
FileItem[4].Command = 'p';

return( 0 );
}

/*****************************************************/
/*                BaudRate  Menu                     */
/*****************************************************/

/* define maximum number of menu items */
#define RSMAX 5

/*   declare storage space for menu items and
 *   their associated IntuiText structures
 */
struct MenuItem RSItem[RSMAX];
struct IntuiText RSText[RSMAX];

/*****************************************************************/
/*    The following function initializes the structure arrays    */
/*   needed to provide the BaudRate menu topic.                  */
/*****************************************************************/
InitRSItems()
{
short n;

/* initialize each menu item and IntuiText with loop */
for( n=0; n<RSMAX; n++ )
   {
   RSItem[n].NextItem = &RSItem[n]+1;
   RSItem[n].LeftEdge = 0;
   RSItem[n].TopEdge = 11 * n;
   RSItem[n].Width = 85;
   RSItem[n].Height = 11;
   RSItem[n].Flags = ITEMTEXT | ITEMENABLED | HIGHBOX | CHECKIT;
   RSItem[n].MutualExclude = (~(1 << n));
   RSItem[n].ItemFill = (APTR)&RSText[n];
   RSItem[n].SelectFill = NULL;
   RSItem[n].Command = 0;
   RSItem[n].SubItem = NULL;
   RSItem[n].NextSelect = 0;

   RSText[n].FrontPen = 0;
   RSText[n].BackPen = 1;
   RSText[n].DrawMode = JAM2;     /* render in fore and background */
   RSText[n].LeftEdge = 0;
   RSText[n].TopEdge = 1;
   RSText[n].ITextFont = NULL;
   RSText[n].NextText = NULL;
   }
RSItem[RSMAX-1].NextItem = NULL;
/* 300 baud item checked */
RSItem[0].Flags = ITEMTEXT | ITEMENABLED | HIGHBOX | CHECKIT | CHECKED;

/* initialize text for specific menu items */
RSText[0].IText = (UBYTE *)"   300";
RSText[1].IText = (UBYTE *)"   1200";
RSText[2].IText = (UBYTE *)"   2400";
RSText[3].IText = (UBYTE *)"   4800";
RSText[4].IText = (UBYTE *)"   9600";

return( 0 );
}

/***************************************************
*                 Mode menu         (feb.86)
****************************************************/
struct MenuItem ModeItem[3];
struct IntuiText Modetext[3];

initmode()
{
short n;

for ( n=0; n<3; n++)

{
 ModeItem[n].NextItem = &ModeItem[n+1];
 ModeItem[n].LeftEdge = 0;
 ModeItem[n].TopEdge = 11 * n;
 ModeItem[n].Width = 85 + COMMWIDTH;
 ModeItem[n].Height = 11;
 ModeItem[n].Flags = ITEMTEXT | ITEMENABLED | HIGHBOX | CHECKIT | COMMSEQ;
 ModeItem[n].MutualExclude = (~(1 << n));
 ModeItem[n].ItemFill = (APTR) &Modetext[n];
 ModeItem[n].SelectFill = NULL;
 ModeItem[n].Command = 0;
 ModeItem[n].SubItem = NULL;
 ModeItem[n].NextSelect = 0;

 Modetext[n].FrontPen = 0;
 Modetext[n].BackPen = 1;
 Modetext[n].DrawMode = JAM2;
 Modetext[n].LeftEdge = 0;
 Modetext[n].TopEdge = 1;
 Modetext[n].ITextFont = NULL;
 Modetext[n].NextText = NULL;
}

ModeItem[2].NextItem = NULL;

/* default is Terminal mode  (full duplex)  */
ModeItem[1].Flags |= CHECKED;
LOCAL = 0;
ECHO = 0;

Modetext[0].IText = (UBYTE *)"    Half";
Modetext[1].IText = (UBYTE *)"    Full";
Modetext[2].IText = (UBYTE *)"    Echo";

ModeItem[0].Command = 'h';
ModeItem[1].Command = 'f';
ModeItem[2].Command = 'e';

}     /* end initmode */

/******************** Initialize FKey Menu **********************/
#define FKMAX 1

struct MenuItem FKitem[FKMAX];
struct IntuiText FKtext[FKMAX];

InitFKey()
{
int x;
for (x=0;x<FKMAX;x++)
{
 FKitem[x].NextItem = NULL;
 FKitem[x].LeftEdge = 0;
 FKitem[x].TopEdge = 12 * x;
 FKitem[x].Width = 100 + COMMWIDTH;
 FKitem[x].Height = 12;
 FKitem[x].Flags = ITEMENABLED | HIGHCOMP | ITEMTEXT | COMMSEQ;
 FKitem[x].MutualExclude = NULL;
 FKitem[x].ItemFill = (APTR) &FKtext[x];
 FKitem[x].SelectFill = NULL;
 FKitem[x].Command = NULL;
 FKitem[x].SubItem = NULL;
 FKitem[x].NextSelect = NULL;

 FKtext[x].FrontPen = 0;
 FKtext[x].BackPen = 1;
 FKtext[x].DrawMode = JAM2;
 FKtext[x].LeftEdge = 0;
 FKtext[x].TopEdge = 1;
 FKtext[x].ITextFont = NULL;
 FKtext[x].NextText = NULL;
}
FKtext[0].IText = (UBYTE *)"F-Key Load";
FKitem[0].Command = 'k';
}

/***************************************************/
/*                Menu Definition                  */
/*                                                 */
/*      This section of code is where the simple   */
/*   menu definition goes.                         */
/***************************************************/

/* current number of available menu topics */
#define MAXMENU 4

/*   declaration of menu structure array for
 *   number of current topics.  Intuition
 *   will use the address of this array to
 *   set and clear the menus associated with
 *   the window.
 */
struct Menu menu[MAXMENU];

/**********************************************************************/
/*   The following function initializes the Menu structure array with */
/*  appropriate values for our simple menu strip.  Review the manual  */
/*  if you need to know what each value means.                        */
/**********************************************************************/
InitMenu()
{
menu[0].NextMenu = &menu[1];
menu[0].LeftEdge = 5;
menu[0].TopEdge = 0;
menu[0].Width = 50;
menu[0].Height = 10;
menu[0].Flags = MENUENABLED;
menu[0].MenuName = "File";           /* text for menu-bar display */
menu[0].FirstItem = &FileItem[0];    /* pointer to first item in list */

menu[1].NextMenu = &menu[2];
menu[1].LeftEdge = 65;
menu[1].TopEdge = 0;
menu[1].Width = 85;
menu[1].Height = 10;
menu[1].Flags = MENUENABLED;
menu[1].MenuName = "BaudRate";        /* text for menu-bar display */
menu[1].FirstItem = &RSItem[0];    /* pointer to first item in list */

menu[2].NextMenu = &menu[3];
menu[2].LeftEdge = 150;
menu[2].TopEdge = 0;
menu[2].Width = 60;
menu[2].Height = 10;
menu[2].Flags = MENUENABLED;
menu[2].MenuName = "Mode";
menu[2].FirstItem = &ModeItem[0];

menu[3].NextMenu = NULL;
menu[3].LeftEdge = 210;
menu[3].TopEdge = 0;
menu[3].Width = 50;
menu[3].Height = 10;
menu[3].Flags = MENUENABLED;
menu[3].MenuName = "FKeys";
menu[3].FirstItem = &FKitem[0];
}

/* declarations for the serial stuff */
struct IOExtSer *Read_Request;
UBYTE rs_in[2];
struct IOExtSer *Write_Request;
UBYTE rs_out[2];

/******************************************************/
/*                   Main Program                     */
/*                                                    */
/*      This is the main body of the program.         */
/******************************************************/

main()
{
ULONG class,waitmask;
USHORT code,menunum,itemnum;
int KeepGoing,capture,send;
UBYTE c,name[32];
FILE *tranr,*trans;

IntuitionBase = OpenLibrary("intuition.library", INTUITION_REV);
if( IntuitionBase == NULL )
   {
   puts("can't open intuition\n");
   exit(TRUE);
   }

GfxBase = OpenLibrary("graphics.library",GRAPHICS_REV);
if( GfxBase == NULL )
   {
   puts("can't open graphics library\n");
   exit(TRUE);
   }


if(( mywindow = OpenWindow(&NewWindow) ) == NULL)
   {
   puts("can't open window\n");
   exit(TRUE);
   }

if(( KeyPort = InitConsole(mywindow) ) == FALSE)
   { puts("Can't open console\n");
     goto q7;
    }

Read_Request = (struct IOExtSer *)AllocMem((long)sizeof(*Read_Request),
  (long)MEMF_PUBLIC|MEMF_CLEAR);
if (Read_Request == NULL) goto q6;

Read_Request->io_SerFlags = SERF_SHARED;
Read_Request->io_CtlChar = 0x11130501;
Read_Request->IOSer.io_Message.mn_ReplyPort = CreatePort("Read_RS",NULL);
if(OpenDevice(SERIALNAME,NULL,Read_Request,NULL))
   {
   puts("Can't open Read device\n");
   goto q4;
   }

Read_Request->io_Baud = 300;
Read_Request->io_ReadLen = 8;
Read_Request->io_WriteLen = 8;
Read_Request->IOSer.io_Command = SDCMD_SETPARAMS;
DoIO(Read_Request);
Read_Request->IOSer.io_Command = CMD_READ;
Read_Request->IOSer.io_Length = 1;
Read_Request->IOSer.io_Data = &rs_in[0];


Write_Request = (struct IOExtSer *)AllocMem((long)sizeof(*Write_Request),
  (long)MEMF_PUBLIC|MEMF_CLEAR);
if (Write_Request == NULL) goto q3;

Write_Request->io_SerFlags = SERF_SHARED | SERF_XDISABLED;
Write_Request->io_CtlChar = 0x11130501;
Write_Request->IOSer.io_Message.mn_ReplyPort = CreatePort("Write_RS",NULL);
if(OpenDevice(SERIALNAME,NULL,Write_Request,NULL))
   {
   puts("Can't open Write device\n");
   goto q1;
   }
Write_Request->io_Baud = 300;
Write_Request->io_ReadLen = 8;
Write_Request->io_WriteLen = 8;
Write_Request->IOSer.io_Command = SDCMD_SETPARAMS;
DoIO(Write_Request);

Write_Request->IOSer.io_Command = CMD_WRITE;
Write_Request->IOSer.io_Length = 1;
Write_Request->IOSer.io_Data =  &rs_out[0];

if (( keybuf= AllocMem((long)BUFSIZE,
     (long)MEMF_PUBLIC | MEMF_CLEAR))== NULL)
 { puts("Can't get memory for function keys\n");
   CloseDevice(Write_Request);
q1:   DeletePort(Write_Request->IOSer.io_Message.mn_ReplyPort);
q2:   FreeMem(Write_Request,(long)sizeof(*Write_Request));
q3:   CloseDevice(Read_Request);
q4:   DeletePort(Read_Request->IOSer.io_Message.mn_ReplyPort);
q5:   FreeMem(Read_Request,(long)sizeof(*Read_Request));
q6:   CloseConsole();
q7:   CloseWindow( mywindow );
      CloseLibrary(GfxBase);
      CloseLibrary(IntuitionBase);
   exit(TRUE);
  }


InitFileItems();
InitRSItems();
initmode();
InitMenu();
InitFKey();
SetMenuStrip(mywindow,&menu[0]);

capture=FALSE;
waiting=send=FALSE;
prompt = 0;

BeginIO(Read_Request);

PutChar(12);

 waitmask = (1L << Read_Request->IOSer.io_Message.mn_ReplyPort->mp_SigBit)
 | ( 1L << mywindow->UserPort->mp_SigBit)
 | (1L << KeyPort->io_Message.mn_ReplyPort->mp_SigBit);

if (LoadKeys("init.key"))           /* did the default f-keys load? */
PutString("Function Keys Loaded...\n");

KeepGoing = TRUE;
while( KeepGoing )
     {

     if ((send | OnKey) == 0)
     Wait(waitmask);

     if (send)
       {

         if (waiting == FALSE)
          {  int c;
             if ((c=getc(trans)) != EOF)
              { if (c == '\n')
                 { sendchar(CR);  /* switch in carriage return  */
                   if (prompt) waiting=TRUE;
                  }
                else sendchar(c);
               }
            else
              {
                fclose(trans);
                PutString("\nFile Sent\n");
                send=waiting=FALSE;
               }
           }     /* end (if not waiting) */
         }       /* end (if send) */

     if (OnKey)  /* if doing function-key  (Mar.86) */
       {
    if ((c=*KeyOffset++)== 0) /* then end of this F-key string */
             OnKey=FALSE;
         else sendchar(c);
        }


   while (c=CheckKey())
   {                      /*  User has touched the keyboard */
     if (c == CSI)
      { character = GetKey();
        while ((c=GetKey()) != '~')
              ;

        if (character == '?')
         {
           PutString("AMIGA Term ATERM6\n\n");

           PutString("\nPrompt character is ");
           if (prompt == 0) PutChar('0');
            else keychar(prompt);
           PutString("\n\n");
           DoContents();              /* display function keys */
          }
         else if ((character >= 0x30) && (character < 0x3a))
          { KeyOffset = keybuf+(KEYSIZE*(character-'0'));
            OnKey = TRUE;
           }
        }    /* end if CSI */
      else
       {
         rs_out[0] = c;
         DoIO(Write_Request);
         if (LOCAL) PutChar(c=='\r'?'\n':c);
        }
    }      /* end while CheckKey() */

     if(CheckIO(Read_Request))
       {
        WaitIO(Read_Request);
        c=rs_in[0] & 0x7f;
        BeginIO(Read_Request);
        if (c!='\n')             /* don't print LF */
        PutChar(c=='\r'?'\n':c); /* convert CR into CR/LF */
        if (ECHO)
         {
          rs_out[0] = c;       /* must do this way: sendchar() also */
          DoIO(Write_Request); /* PutChar()s if LOCAL, and calling sendchar() */
          }                    /* would do a double-PutChar() in EchoPlex */

        if (waiting) if (c==prompt) waiting = FALSE;
        if (capture)
            if (c > 31 && c < 127 || c == '\n') /* assumes CR/LF sequence */
                                   /* trash them mangy ctl chars */
                putc(c , tranr);
        }

     while( NewMessage=(struct IntuiMessage *)GetMsg(mywindow->UserPort) )
          {
          class = NewMessage->Class;
          code = NewMessage->Code;
          ReplyMsg( NewMessage );
          switch( class )
                {
                case CLOSEWINDOW:
                   /*   User is ready to quit, so indicate
                   *   that execution should terminate
                   *   with next iteration of the loop.
                   */
                   KeepGoing = FALSE;
                break;

                case NEWSIZE:
                   PutChar(12);
                break;

                case MENUPICK:
                   if ( code != MENUNULL )
                       {
                       menunum = MENUNUM( code );
                       itemnum = ITEMNUM( code );
                       switch( menunum )
                             {
                             case 0:
                                switch( itemnum )
                                      {
                                      case 0:
                                         if (capture)
                                          {
                                             capture=FALSE;
                                             fclose(tranr);
                                             PutString("\nEnd File Capture\n");
                                            }
                                         else
                                           {
                                             if (GetLine("\nAscii Capture: ",name))
                                              {
                                                if ((tranr=fopen(name,"w")) == 0)
                                                    {
                                                      capture=FALSE;
                                                      PutString("\nError Opening File\n");
                                                      break;
                                                     }
                                                 capture=TRUE;
                                                }
                                              else PutString("\nAborted\n");
                                             }
                                      break;
                                      case 1:
                                         if (send)
                                             {
                                             send=waiting=FALSE;
                                             fclose(trans);
                                             PutString("\nFile Send Cancelled\n");
                                             }
                                         else
                                           if (GetLine("\nAscii Send: ",name))
                                              {
                                                if ((trans=fopen(name,"r")) == 0)
                                                 { send=FALSE;
                                                   PutString("\nError Opening File\n");
                                                   break;
                                                  }
                                                send=TRUE;
                                                waiting=FALSE;
                                               }
                                            else PutString("\nAborted\n");
                                      break;
                                      case 2:
                                         if (GetLine("\nXmodem Receive:",name))
                                          {

                                         /* disable ^Q/^S trapping */
                                            AbortIO(Read_Request);
                                            Read_Request->io_SerFlags |= SERF_XDISABLED;
                                            Read_Request->IOSer.io_Command = SDCMD_SETPARAMS;
                                            DoIO(Read_Request);
                                            Read_Request->IOSer.io_Command = CMD_READ;
                                            BeginIO(Read_Request);

                                           XMODEM_Read_File(name);

                                         /* Re-enable ^Q/^S trapping */
                                            AbortIO(Read_Request);
                                            Read_Request->io_SerFlags &=(~SERF_XDISABLED);
                                            Read_Request->IOSer.io_Command = SDCMD_SETPARAMS;
                                            DoIO(Read_Request);
                                            Read_Request->IOSer.io_Command = CMD_READ;
                                            BeginIO(Read_Request);
                                           DisplayBeep(NULL);
                                            } else PutString("\nAborted\n");
                                      break;
                                      case 3:
                                       if (GetLine("\nXmodem Send: ",name))
                                        { if (XMODEM_Send_File(name))
                                             {
                                             PutString("\nSent File\n");
                                             DisplayBeep(NULL);
                                             }
                                           else
                                             {
                                             close(fd);
                                             PutString("\nXmodem Send Failed\n");
                                             DisplayBeep(NULL);
                                             }
                                         }
                                       else PutString("\nAborted\n");
                                      break;
                                      case 4:      /* new prompt */
                                         PutString("New Prompt Character: ");
                                         if ((c = GetKey()) == '0')
                                          prompt = 0;
                                          else prompt = c;
                                         keychar(c);
                                         PutChar('\n');
                                       break;
                                      }
                             break;

                             case 1:
                                AbortIO(Read_Request);
                                switch( itemnum )
                                      {
                                      case 0:
                                         Read_Request->io_Baud = 300;
                                      break;
                                      case 1:
                                         Read_Request->io_Baud = 1200;
                                      break;
                                      case 2:
                                         Read_Request->io_Baud = 2400;
                                      break;
                                      case 3:
                                         Read_Request->io_Baud = 4800;
                                      break;
                                      case 4:
                                         Read_Request->io_Baud = 9600;
                                      break;
                                      }
                                Read_Request->IOSer.io_Command = SDCMD_SETPARAMS;
                                DoIO(Read_Request);
                                Read_Request->IOSer.io_Command = CMD_READ;
                                BeginIO(Read_Request);
                             break;
                            case 2:                /*  "Mode" */
                              switch( itemnum)
                              {
                               case 0:  /* Half Duplex */
                                ECHO = 0;
                                LOCAL = 1;
                                break;
                               case 1:  /* Full Duplex */
                                ECHO = 0;
                                LOCAL = 0;
                                break;
                               case 2:  /* EchoPlex  */
                                ECHO = 1;
                                LOCAL = 1;
                               }
                             break;
                             case 3:    /* load function-keys (mar.86) */
                               if (GetLine("Function-Key file name: ",name))
                                { if (LoadKeys(name))
                                  PutString("Loaded\n");
                                  else PutString("Couldn't load that file\n");
                                 } else PutChar('\n');
                             } /* end of switch ( menunum ) */
                       }    /*  end of if ( not null ) */
                }   /* end of switch (class) */
          }   /* end of while ( newmessage )*/
     }  /* end while ( keepgoing ) */

/*   It must be time to quit, so we have to clean
*   up and exit.
*/

CloseDevice(Read_Request);
DeletePort(Read_Request->IOSer.io_Message.mn_ReplyPort);
FreeMem(Read_Request,(long)sizeof(*Read_Request));
FreeMem(keybuf,(long)BUFSIZE);
CloseDevice(Write_Request);
DeletePort(Write_Request->IOSer.io_Message.mn_ReplyPort);
FreeMem(Write_Request,(long)sizeof(*Write_Request));
ClearMenuStrip( mywindow );
CloseWindow( mywindow );
CloseConsole();
CloseLibrary(GfxBase);
CloseLibrary(IntuitionBase);
exit();
} /* end of main */

/********************************************************************/
/*           terminal-mode character send                           */
/********************************************************************/

sendchar(ch)
  UBYTE ch;
  {
    rs_out[0] = ch;
    DoIO(Write_Request);
    if (LOCAL)
    if(ch!='\n')
    PutChar(ch=='\r'?'\n':ch);  /* if Half-Duplex or Echo-plex */
   }

/**************************************************************/
/* send char and read char functions for the xmodem function */
/************************************************************/

/*    purge() is called whenever an error is detected.
     Its function is to flush the input buffer, and wait
     until the sender has finished sending a block.
     This ensures that the sender will see the NAK
     sent by the error routine.
*/

void purge(purgetime)
    int purgetime;      /* number of ticks @ 50 per second */
   {

    do  ModemRead(purgetime);
    while (timeout == FALSE);       /* Wait for the line to clear */
   }



     /* ModemSend() is simply sendchar() without local echo
      */

void ModemSend(c)
    UBYTE c;
    { rs_out[0] = c;
      DoIO(Write_Request);
     }

/*     ModemRead()  gets a character from the serial port within a
      specified number of ticks (tick = 1/50 second). DateStamp()
      returns a 3-byte integer date consisting of day, minute, ticks.
      ModemRead() will function correctly as long as the date is set
      and a day boundary isn't crossed.
*/


    UBYTE
ModemRead(MAXTICKS)
   int MAXTICKS;              /* number of ticks @ 50 per second */
  {
   static long startime[3], endtime[3];
   UBYTE c;

   timeout = FALSE;
   DateStamp(startime);
   do {
       if ((c=CheckKey()) == 0x1b)  /* ESC cancels xmodem tranxfer */
             { cancel = TRUE;
               return (0);  /* This will tend to force an error        */
              }             /* when <esc> pressed 'midst block receive */
       if (CheckIO(Read_Request))
        { WaitIO(Read_Request);
          c = rs_in[0];
          BeginIO(Read_Request);
          return c;             /* Character came in on time */
         }
       DateStamp(endtime);
       endtime[2] -= startime[2];
        if ( endtime[1] -= startime[1] ) /* did minutes count change? */
        endtime[2] += endtime[1] * 3000; /* number of ticks in minute */
      }
   while (endtime[2] < MAXTICKS);
     /* else character not received on time */
   timeout = TRUE;
   return (0);
  }



/*   CloseEmUp() closes a receive file, ending Xmodem receive.
     It also performs a 'chop' function, testing for
     zero, 0xff, and CP/M_EOF (ctrol-z).
     To implement the chop function, a two-block buffer
     is maintained, with the basic pointer being bufr[offset],
     with offset toggled 0,SECSIZ,0, etc.
     During file receive, writes lag reads by one block: e.g.,
     when block # 3 is received, block # 2 is written.
     If block # 3 is the final block, CloseEmUp() will
     chop any invalid characters, write the block, and
     close the file.
*/

CloseEmUp(fd,bufr,offset,ourblock)

int fd,offset, ourblock;
UBYTE *bufr;

{
   int i,x;
   ULONG size=0;
   UBYTE c,numstring[7];

 offset = offset ? 0 : SECSIZ ;  /* toggle offset */

 if (ourblock)                     /* skip if no data at all */
  {
    i = SECSIZ - 1;
    c = bufr[offset + i];       /* c gets last char in block */
    if ( (c == 0) || (c == CPM_EOF) || (c == 0xff) )
      {
        while (bufr[ offset+(--i) ] == c)  /* Chop those useless bytes! */
                   ;
       }
    if ((c = write(fd, bufr+offset, ++i)) != i)
       PutString("Error writing final block\n");
    x = SECSIZ - i;
    format(PutChar,"%d bytes chopped from final block\n",&x);

    size=(--ourblock)*(long)SECSIZ;
    size += i;
    format(PutChar,"File size: %ld\n",&size);
   }

 close(fd);
}


/**************************************/
/* xmodem send and receive functions */
/************************************/

            /* These time constants are for benefit of
               CompuServe and other packet-switching networks
               that must be allowed more "slack time"
               due to inherent delays in system.
             */

#define Blocktime 20*50 /* Wait 20 seconds for start of block */
#define Chartime   2*50 /* Wait 2 seconds for chars when getting block */


XMODEM_Read_File(file)

char *file;

{
int ourblock, errors, checksum, offset, x,i;
UBYTE c,blockin;

if ( (fd = creat(file,0)) == -1)
   { PutString("Cannot open file\n");
     return FALSE;
    }
else PutString("Receiving file \n");

         /* Input file now open */

offset = ourblock = errors = cancel = 0;


ModemSend(NAK);
                        /*  MAIN LOOP STARTS HERE  */
loop:

c = ModemRead(Blocktime);

   if (cancel)
   { PutString("Xmodem cancelled\n");
     close(fd);
     purge(Chartime);
     ModemSend(CAN);
     return(FALSE);
    }

   if (timeout)
   { PutString("timed out waiting for block start\n");
     goto error;
    }

 if(c == CAN)    /*Sender just quit*/
   { purge(Chartime);
     PutString("Sender aborted transfer\n");
     CloseEmUp(fd,bufr,offset,ourblock);
     return(FALSE);
    }
 if (c == EOT)   /*Sender is finished */
   {
     CloseEmUp(fd,bufr,offset,ourblock);   /* chop and write last block */
     PutString("Transfer Completed\n");
     ModemSend(ACK);
     return(TRUE);
    }
 if (c != SOH)
   { purge(Chartime);
     PutString("not a valid header\n");
     goto error;
    }
      /*Sender has a block for us */
      /* block receive follows here */
blockin = ModemRead(Chartime);  /* get block number */
   if (timeout)
   { purge(Chartime);
     PutString("Didn't get block# in time\n");
     goto error;
    }
c = ModemRead(Chartime);       /* get 1/blocknumber */
   if (timeout)
   { purge(Chartime);
     PutString("Timed out waiting for 1/block#\n");
     goto error;
    }

if ( ( (UBYTE)~c) != blockin)  /* block # got lunched */
   { purge(Chartime);
     PutString("block complement didn't match\n");
     goto error;
    }
            /* Here comes a block of data  */

checksum = 0;

for (i = 0; i < SECSIZ; i++ )
 {
   c = ModemRead(Chartime);
     if (timeout)
      { purge(Chartime);
        PutString("Timed out during block\n");
        goto error;
       }
   bufr[offset+i] = c;
   checksum += c;
  }

            /* Here comes the checksum */
c = ModemRead(Chartime);
   if (timeout)
   { purge(Chartime);
     PutString("Timed out waiting for checksum\n");
     goto error;
    }
if (c != (checksum & 0xff) ) /* checksums don't match */
  {
   purge(Chartime);
   PutString("checksum error\n");
   goto error;
   }

      /* Block has been received OK: Now check back on blocknumber */

switch ( (UBYTE) (blockin - (ourblock & 0xff)) )
{
 case (0):       /* Duplicate block */
  {
   format(PutChar,"\nDuplicate block # %d\n",&ourblock);
   errors = 0;
   ModemSend(ACK);
   goto loop;
  }
 case (1):      /* New block */
  {
   ourblock++;           /* Update our block count */
   PutChar(0x0b);        /* do a vertical tab */
   format(PutChar,"Received block # %d     \n",&ourblock);

   offset = offset ? 0 : SECSIZ;                  /* toggle offset */
   if (ourblock != 1)
   {                                              /* Write it out */
     if ( (x = (write(fd, bufr+offset, SECSIZ)) ) != SECSIZ)
       { close(fd);
         PutString("Error writing to file.\n");
         PutString("Transfer aborted.\n");
         ModemSend(CAN);
         return(FALSE);
        }
    }
   errors = 0;
   ModemSend(ACK);
   goto loop;
  }
default:
    break;     /* fall through to error: */
}
purge(Chartime);
PutString("Block numbers didn't agree\n");

                  /* ERROR ROUTINE STARTS HERE */
error:

if (cancel)
   { PutString("XModem cancelled\n");
     close(fd);
     ModemSend(CAN);
     return(FALSE);
    }

 errors++;

 if (errors == 10) /* then abort */
   {
     PutString(" 10 Errors. Transfer cancelled.\n");
     close(fd);
     ModemSend(CAN);
     return(FALSE);
    }
 format(PutChar,"Error # %d\n",&errors);
 ModemSend(NAK);
 goto loop;

} /* end of XMODEM_Read_File()  */


#define WaitTime 20*50  /* Wait this long for NAKs (20 seconds)*/

XMODEM_Send_File(file)
    char *file;
{
    int i, x, j,
         size,
         errors=0, blocknum=1;
    char numb[10];
    UBYTE sectnum=1,c,checksum;
    cancel=FALSE;
    if ((fd = open(file, O_RDONLY)) == -1)
      {
         PutString("Cannot Open Send File\n");
         ModemSend(CAN);
         return FALSE;
        }
    else
        PutString("Sending File\n");

       for (errors=0;errors<10;errors++)
        { c=ModemRead(WaitTime);
          if (cancel)
           { PutString("Aborted\n");
             return(FALSE);
            }
          if (timeout)
           { PutString("...nothing yet...\n");
            }
          else if (c == CAN)
           { PutString("Receiver has cancelled.\n");
             return(FALSE);
            }
          else if (c != NAK)
           { PutString("Got something, but not NAK\n");
            }
          else break;        /* got first NAK */
         }
           if (errors == 10)
            { PutString("Quitting after 10 errors\n");
              ModemSend(CAN);
              return(FALSE);
             }
   XS0:    /* get a block into the buffer */

#asm
      move.l   #_bufr,a0      ; zero out the memory buffer
      move.w   #31,d0         ; set up for 128 bytes
loop  clr.l    (a0)+
      dbf      d0,loop
#endasm

      size = read(fd,bufr,SECSIZ);
      if (size == 0)
       { ModemSend(EOT);
         close(fd);
         return(TRUE);
        }
      if (size == -1)
       { PutString("Error reading file\n");
         close(fd);
         ModemSend(CAN);
         return(FALSE);
        }
      errors = 0;
   XS1:          /* send a block */

         ModemSend(SOH);
         ModemSend(sectnum);
         ModemSend(~sectnum);
         checksum = 0;

         for (j=0;j<SECSIZ;j++)
          {
             ModemSend(bufr[j]);
             checksum += bufr[j];
            }
         ModemSend(checksum);

         c = ModemRead(WaitTime);
         if (cancel)
           { ModemSend(CAN);
             PutString("Aborted.\n");
             return(FALSE);
            }
         if (c==CAN)
           { PutString("Receiver cancelled.\n");
             return(FALSE);
            }
         if ((timeout) || (c != ACK))
          { errors++;
            format(PutChar,"Error # %d\n",&errors);
            if (errors == 10)
             { ModemSend(CAN);
               return(FALSE);
              }
            goto XS1;       /*  try again  */
           }

            PutChar(0x0b);           /* do a reverse linefeed */
            PutSequence("K");
            format(PutChar,"Block %d sent\n",&blocknum);
            sectnum++;
            blocknum++;
            goto XS0;
 }                    /* end XMODEM_send */

/*********************************************************************
**        Some routines concerned with the function keys            **
*********************************************************************/

/************* Attempt to load a Function-Key file ******************/

LoadKeys(keyfile)    /* returns (BOOL) success  */

   char *keyfile;  /* the filename */
 {
      FILE *fp;
      UBYTE c;
      int i;
   if ((fp=fopen(keyfile,"r")) == NULL)
     return(FALSE);
   if ((c=fgetc(fp)) != HEADER)  /* gotta be a valid key file! */
    { fclose(fp);
      return(FALSE);
     }
   for (i=0;i<BUFSIZE;i++)
    {
      if ((c=fgetc(fp)) == EOF)
       { fclose(fp);
         return(FALSE);
        }
      else keybuf[i] = c;
     }
    fclose(fp);
    return(TRUE);    /* ahh, success! */
  }
/************ Display Function-Key contents **********/

DoContents()

 {
   int x;
   UBYTE c;
  for (x=0;x<NUMKEYS;x++)
  {
    KeyOffset = keybuf+(KEYSIZE * x);
    PrintKeyNum(x);
    while ( c = *KeyOffset++ )
      keychar(c);
    PutChar('\n');
   }
 }

/************ Print the function-key number **************/
/*              format example: F7:                     */

PrintKeyNum(keynum)
    int keynum;
 {
   keynum++;
   PutChar('F');
  if (keynum == 10)
   PutString("10: ");
  else
   { PutChar(keynum + '0');
     PutString(":  ");
    }
  }

/*********** Print a character from function-key string ************/

keychar(key)
   UBYTE key;
 {
   if (key < 32) /* control character? */
    {
      switch (key)
      { case 0:
        PutChar('0');
        break;
        case 8:
        PutString("<BS>");
        break;
        case 9:
        PutString("<TAB>");
        break;
       case 10:
        PutString("<NL>");
        break;
       case 13:
        PutString("<CR>");
        break;
       case 27:
        PutString("<ESC>");
        break;
       default:
        PutChar('^');
        PutChar(key + 0x40);
       }
      }
   else PutChar(key);
  }
