[net.micro.amiga] AmigaTerm

george@osu-eddie.UUCP (George Jones) (12/16/85)

Hi folks...I know there have been a large number of people working on
terminal programs of one variety or another for the Amiga, but until
now I have not seen any of them.  What follows is a terminal program
in C that supports X-Modem and Capture protocols and works up to (I
believe) 9600.  It works under AmigaDos 1.0 and provides examples of
how to set up pull-down menus.  Michael Mounier posted it last night to
CompuServe's Amiga Forum and I figured people here might be interested.
He had no problem with me forwarding it on, so here it is...

===================CUT SOMEWHERE, HERE LOOKS GOOD TO ME===================
/************************************************************************
*  a terminal program that has ascii and xmodem transfer capability
*
*  use esc to abort xmodem transfer
*
*  written by Michael Mounier
************************************************************************/

/*  compiler directives to fetch the necessary header files */
#include <exec/types.h>
#include <exec/exec.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <graphics/gfxbase.h>
#include <graphics/gfx.h>
#include <graphics/text.h>
#include <graphics/regions.h>
#include <graphics/copper.h>
#include <graphics/gels.h>
#include <devices/serial.h>
#include <devices/keymap.h>
#include <hardware/blit.h>
#include <stdio.h>
#include <ctype.h>
#include <libraries/dos.h>

#define INTUITION_REV 1
#define GRAPHICS_REV  1

/* things for xmodem send and recieve */
#define SECSIZ   0x80
#define TTIME    30          /* number of seconds for timeout */
#define BufSize  0x1000     /* Text buffer */
#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 */

static char
    bufr[BufSize];
static int
    fd,
    timeout = FALSE;
static long
    bytes_xferred;

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

/* my window structure */
struct NewWindow NewWindow = {
   0,
   0,
   639,
   199,
   0,
   1,
   CLOSEWINDOW | RAWKEY | MENUPICK | NEWSIZE,
   WINDOWCLOSE | SMART_REFRESH | ACTIVATE | WINDOWDRAG
   | WINDOWDEPTH | WINDOWSIZING | REPORTMOUSE,
   NULL,
   NULL,
   "AMIGA Terminal",
   NULL,
   NULL,
   100, 35,
   640, 200,
   WBENCHSCREEN,
};

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

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

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

/*   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;
   FileItem[n].Height = 11;
   FileItem[n].Flags = ITEMTEXT | ITEMENABLED | HIGHBOX;
   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 Send";
FileText[2].IText = (UBYTE *)"Xmodem Receive";
FileText[3].IText = (UBYTE *)"Xmodem Send";

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 chekced */
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 );
}


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

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

/*   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 = NULL;
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 */

return( 0 );
}

/* declarations for the serial stuff */
extern struct MsgPort *CreatePort();
struct IOExtSer *Read_Request;
static char rs_in[2];
struct IOExtSer *Write_Request;
static char rs_out[2];

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

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

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

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

if(( mywindow = (struct Window *)OpenWindow(&NewWindow) ) == NULL)
   {
   puts("cant open window\n");
   exit(TRUE);
   }

Read_Request = (struct IOExtSer *)AllocMem(sizeof(*Read_Request),MEMF_PUBLIC|MEMF_CLEAR);
Read_Request->io_SerFlags = SERF_SHARED | SERF_XDISABLED;
Read_Request->IOSer.io_Message.mn_ReplyPort = CreatePort("Read_RS",0);
if(OpenDevice(SERIALNAME,NULL,Read_Request,NULL))
   {
   puts("Cant open Read device\n");
   CloseWindow( mywindow );
   DeletePort(Read_Request->IOSer.io_Message.mn_ReplyPort);
   FreeMem(Read_Request,sizeof(*Read_Request));
   exit(TRUE);
   }
Read_Request->IOSer.io_Command = CMD_READ;
Read_Request->IOSer.io_Length = 1;
Read_Request->IOSer.io_Data = (APTR) &rs_in[0];

Write_Request = (struct IOExtSer *)AllocMem(sizeof(*Write_Request),MEMF_PUBLIC|MEMF_CLEAR);
Write_Request->io_SerFlags = SERF_SHARED | SERF_XDISABLED;
Write_Request->IOSer.io_Message.mn_ReplyPort = CreatePort("Write_RS",0);
if(OpenDevice(SERIALNAME,NULL,Write_Request,NULL))
   {
   puts("Cant open Write device\n");
   CloseWindow( mywindow );
   DeletePort(Write_Request->IOSer.io_Message.mn_ReplyPort);
   FreeMem(Write_Request,sizeof(*Write_Request));
   DeletePort(Read_Request->IOSer.io_Message.mn_ReplyPort);
   FreeMem(Read_Request,sizeof(*Read_Request));
   exit(TRUE);
   }
Write_Request->IOSer.io_Command = CMD_WRITE;
Write_Request->IOSer.io_Length = 1;
Write_Request->IOSer.io_Data = (APTR) &rs_out[0];

Read_Request->io_SerFlags = SERF_SHARED | SERF_XDISABLED;
Read_Request->io_Baud = 300;
Read_Request->io_ReadLen = 8;
Read_Request->io_WriteLen = 8;
Read_Request->io_CtlChar = 1L;
Read_Request->IOSer.io_Command = SDCMD_SETPARAMS;
DoIO(Read_Request);
Read_Request->IOSer.io_Command = CMD_READ;

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

KeepGoing = TRUE;
capture=FALSE;
send=FALSE;
SetAPen(mywindow->RPort,1);
emit(12);
BeginIO(Read_Request);

while( KeepGoing )
     {
     /* wait for window message or serial port message */
     Wait((1 << Read_Request->IOSer.io_Message.mn_ReplyPort->mp_SigBit) | ( 1 << mywindow->UserPort->mp_SigBit));

     if (send)
         {
         if ((c=getc(trans)) != EOF)
             {
             sendchar(c);
             }
         else
             {
             fclose(trans);
             emits("\nFile Sent\n");
             send=FALSE;
             }
         }
     if(CheckIO(Read_Request))
        {
        WaitIO(Read_Request);
        c=rs_in[0] & 0x7f;
        BeginIO(Read_Request);
        emit(c);
        if (capture)
            if (c > 31 && c < 127 || c == 10)  /* 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 RAWKEY:
                /*  User has touched the keyboard */
                switch( code )
                      {
                      case 95: /* help key */
                         emits("ESC Aborts Xmodem Xfer\n");
                         emits("AMIGA Term 1.0\n");
                         emits("Copyright 1985 by Michael Mounier\n");
                      break;
                      default:   
                         c = toasc(code); /* get in into ascii */
                         if (c != 0)
                             {
                             rs_out[0] = c;
                             DoIO(Write_Request);
                             }
                      break;
                      }
                break;

                case NEWSIZE:
                   emit(12);
                break;

                case MENUPICK:
                   if ( code != MENUNULL )
                       {
                       menunum = MENUNUM( code );
                       itemnum = ITEMNUM( code );
                       switch( menunum )
                             {
                             case 0:
                                switch( itemnum )
                                      {
                                      case 0:
                                         if (capture == TRUE)
                                             {
                                             capture=FALSE;
                                             fclose(tranr);
                                             emits("\nEnd File Capture\n");
                                             }
                                         else
                                             {
                                             emits("\nAscii Capture:");
                                             filename(name);
                                             if ((tranr=fopen(name,"w")) == 0)
                                                 {
                                                 capture=FALSE;
                                                 emits("\nError Opening File\n");
                                                 break;
                                                 }
                                             capture=TRUE;
                                             }
                                      break;
                                      case 1:
                                         if (send == TRUE)
                                             { 
                                             send=FALSE;
                                             fclose(trans);
                                             emits("\nFile Send Cancelled\n");
                                             }
                                         else
                                             {
                                             emits("\nAscii Send:");
                                             filename(name);
                                             if ((trans=fopen(name,"r")) == 0)
                                                 {
                                                 send=FALSE;
                                                 emits("\nError Opening File\n");
                                                 break;
                                                 }
                                             send=TRUE;
                                             }
                                      break;
                                      case 2:
                                         emits("\nXmodem Receive:");
                                         filename(name);
                                         if (XMODEM_Read_File(name))
                                            {
                                            emits("\nRecieved File\n");
                                            emit(8);
                                            }
                                         else
                                            {
                                            close(fd);
                                            emits("Xmodem Receive Failed\n");
                                            emit(8);
                                            }
                                      break;
                                      case 3:
                                         emits("\nXmodem Send:");
                                         filename(name);
                                         if (XMODEM_Send_File(name))
                                             {
                                             emits("\nSent File\n");
                                             emit(8);
                                             }
                                         else
                                             {
                                             close(fd);
                                             emits("\nXmodem Send Failed\n");
                                             emit(8);
                                             }
                                      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;
                             } /* 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,sizeof(*Read_Request));
CloseDevice(Write_Request);
DeletePort(Write_Request->IOSer.io_Message.mn_ReplyPort);
FreeMem(Write_Request,sizeof(*Write_Request));
ClearMenuStrip( mywindow );
CloseWindow( mywindow );
exit(FALSE);
} /* end of main */

/*************************************************
*  function to get file name
*************************************************/
filename(name)
char name[];
{
char c;
ULONG class;
USHORT code;
int keepgoing,i;
keepgoing = TRUE;
i=0;
while (keepgoing) {
      while( NewMessage=(struct IntuiMessage *)GetMsg(mywindow->UserPort) )
           {
           class = NewMessage->Class;
           code = NewMessage->Code;
           ReplyMsg( NewMessage );
           if (class=RAWKEY)
              {
              c = toasc(code);
              name[i]=c;
              if (name[i] != 0)
                 {
                 if (name[i] == 13)
                    {
                    name[i]=0;
                    keepgoing = FALSE;
                    }
                 else
                    {
                    if (name[i] == 8)
                       {
                       i -=2;
                       if (i < -1)
                          i = -1;
                       else
                          {
                          emit(8);
                          emit(32);
                          emit(8);
                          }
                       }
                    else
                       emit(c);
                    }
                 i += 1;
                 }
              }
          } /* end of new message loop */
      }   /* end of god knows what */
      emit(13);
} /* end of function */


/*************************************************
*  function to print a string
*************************************************/
emits(string)
char string[];
{
int i;
char c;

i=0;
while (string[i] != 0)
      {
      c=string[i];
      if (c == 10)
         c = 13;
      emit(c);
      i += 1;
      }
}
/**************************************************************/
/* send char and read char functions for the xmodem function */
/************************************************************/
sendchar(ch)
int ch;
{
rs_out[0] = ch;
DoIO(Write_Request);
}

readchar()
{
unsigned char c;
int rd,ch;

rd = FALSE;

while (rd == FALSE)
      {
      Wait((1 << Read_Request->IOSer.io_Message.mn_ReplyPort->mp_SigBit) | ( 1 << mywindow->UserPort->mp_SigBit));
      if(CheckIO(Read_Request))
        {
        WaitIO(Read_Request);
        ch=rs_in[0];
        rd = TRUE;
        BeginIO(Read_Request);
        }
      if (NewMessage=(struct IntuiMessage *)GetMsg(mywindow->UserPort))
         if ((NewMessage->Class) == RAWKEY)
            if ((NewMessage->Code) == 69)
               {
               emits("\nUser Cancelled Transfer");
               break;
               }
      }
if (rd == FALSE)
   {
   timeout = TRUE;
   emits("\nTimeout Waiting For Character\n");
   }
c = ch;
return(c);
}

/**************************************/
/* xmodem send and recieve functions */
/************************************/

XMODEM_Read_File(file)
    char *file;
{
    int firstchar, sectnum, sectcurr, sectcomp, errors, errorflag;
    unsigned int checksum, j, bufptr,i;
    char numb[10];
    bytes_xferred = 0L;

    if ((fd = creat(file, 0)) < 0)
        {
        emits("Cannot Open File\n");
        return FALSE;
        }
    else
        emits("Receiving File\n");

    timeout=FALSE;
    sectnum = errors = bufptr = 0;
    sendchar(NAK);
    firstchar = 0;
    while (firstchar != EOT && errors != ERRORMAX)
        {
        errorflag = FALSE;

        do                                    /* get sync char */
           {
           firstchar = readchar();
           if (timeout == TRUE)
              return FALSE;
           }
        while (firstchar != SOH && firstchar != EOT);

        if  (firstchar == SOH)
            {
            emits("Getting Block ");
            stci_d(numb,sectnum,i);
            emits(numb);
            emits("...");
            sectcurr = readchar();
            if (timeout == TRUE)
               return FALSE;
            sectcomp = readchar();
            if (timeout == TRUE)
               return FALSE;
            if ((sectcurr + sectcomp) == 255)
                {
                if (sectcurr == (sectnum + 1 & 0xff))
                    {
                    checksum = 0;
                    for (j = bufptr; j < (bufptr + SECSIZ); j++)
                        {
                        bufr[j] = readchar();
                        if (timeout == TRUE)
                           return FALSE;
                        checksum = (checksum + bufr[j]) & 0xff;
                        }
                    if (checksum == readchar())
                        {
                        errors = 0;
                        sectnum++;
                        bufptr += SECSIZ;
                        bytes_xferred += SECSIZ;
                        emits("verified\n");
                        if (bufptr == BufSize)
                            {
                            bufptr = 0;
                            if (write(fd, bufr, BufSize) == EOF)
                                {
                                emits("\nError Writing File\n");
                                return FALSE;
                                };
                            };
                        sendchar(ACK);
                        }
                    else
                        {
                        errorflag = TRUE;
                        if (timeout == TRUE)
                           return FALSE;
                        }
                    }
                else
                    {
                    if (sectcurr == (sectnum & 0xff))
                        {
                        emits("\nReceived Duplicate Sector\n");
                        sendchar(ACK);
                        }
                    else
                        errorflag = TRUE;
                    }
                }
            else
                errorflag = TRUE;
            }
        if (errorflag == TRUE)
            {
            errors++;
            emits("\nError\n");
            sendchar(NAK);
            }
        };        /* end while */
            
    if ((firstchar == EOT) && (errors < ERRORMAX))
        {
        sendchar(ACK);
        write(fd, bufr, bufptr);
        close(fd);
        return TRUE;
        }
    return FALSE;
}

XMODEM_Send_File(file)
    char *file;
{
    int sectnum, bytes_to_send, size, attempts, c, i;
    unsigned checksum, j, bufptr;
    char numb[10];

    timeout=FALSE;
    bytes_xferred = 0;
    if ((fd = open(file, 1)) < 0)
        {
        emits("Cannot Open Send File\n");
        return FALSE;
        }
    else
        emits("Sending File\n");
    attempts = 0;
    sectnum = 1;
/* wait for sync char */
    j=1;
    while (((c = readchar()) != NAK) && (j++ < ERRORMAX));
    if (j >= (ERRORMAX))
        {
        emits("\nReceiver not sending NAKs\n");
        return FALSE;
        };

    while ((bytes_to_send = read(fd, bufr, BufSize)) && attempts != RETRYMAX)
        {
        if (bytes_to_send == EOF)
            {
            emits("\nError Reading File\n");
            return FALSE;
            };

        bufptr = 0;
        while (bytes_to_send > 0 && attempts != RETRYMAX)
            {
            attempts = 0;
            do
                {
                sendchar(SOH);
                sendchar(sectnum);
                sendchar(~sectnum);
                checksum = 0;
                size = SECSIZ <= bytes_to_send ? SECSIZ : bytes_to_send;
                bytes_to_send -= size;
                for (j = bufptr; j < (bufptr + SECSIZ); j++)
                    if (j < (bufptr + size))
                        {
                        sendchar(bufr[j]);
                        checksum += bufr[j];
                        }
                    else
                        {
                        sendchar(0);
                        }
                sendchar(checksum & 0xff);
                attempts++;
                c = readchar();
                if (timeout == TRUE)
                   return FALSE;
                }
            while ((c != ACK) && (attempts != RETRYMAX));
            bufptr += size;
            bytes_xferred += size;
            emits("Block ");
            stci_d(numb,sectnum,i);
            emits(numb);
            emits(" sent\n");
            sectnum++;
            }
        }
    close(fd);
    if (attempts == RETRYMAX)
        {
        emits("\nNo Acknowledgment Of Sector, Aborting\n");
        return FALSE;
        }
    else
        {
        attempts = 0;
        do
            {
            sendchar(EOT);
            attempts++;
            }
        while ((readchar() != ACK) && (attempts != RETRYMAX) && (timeout == FALSE));
         if (attempts == RETRYMAX)
            emits("\nNo Acknowledgment Of End Of File\n");
        };
    return TRUE;
}

/*************************************************
*  function to output ascii chars to window
*************************************************/
emit(c)
char c;
{
static short x = 3;
static short y = 17;
short xmax,ymax,cx,cy;

xmax = mywindow->Width;
ymax = mywindow->Height;

/* cursor */
if (x > (xmax-31))
   {
   cx = 9;
   cy = y + 8;
   }
else
  {
  cx = x+8;
  cy = y;
  }
 
if (cy > (ymax-2))
   {
   cx = 9;
   cy -= 8;
   }

SetDrMd(mywindow->RPort,COMPLEMENT);
SetAPen(mywindow->RPort,3);
RectFill(mywindow->RPort,cx-7,cy-6,cx,cy+1);
SetAPen(mywindow->RPort,1);
SetDrMd(mywindow->RPort,JAM2);

if (x > (xmax-31))
   {
   x = 3;
   y += 8;
   }

if (y > (ymax-2))
   {
   x = 3;
   y -= 8;
   }

Move(mywindow->RPort,x,y);

switch( c )
      {
      case '\t':
         x += 60;
         break;
      case '\n':
         break;
      case 13:  /* newline */
         x = 3;
         y += 8;
         break;
      case 8:   /* backspace */
         x -= 8;
         if (x < 3)
            x = 3;
         break;
      case 12:     /* page */
         x = 3;
         y = 17;
         SetAPen(mywindow->RPort,0);
         RectFill(mywindow->RPort,2,10,xmax-19,ymax-7);
         SetAPen(mywindow->RPort,1);
         break;
      case 7:     /* bell */
         ClipBlit(mywindow->RPort,0,0,mywindow->RPort,0,0,xmax,ymax,0x50);
         ClipBlit(mywindow->RPort,0,0,mywindow->RPort,0,0,xmax,ymax,0x50);
         break;
      default:
         Text(mywindow->RPort,&c,1);
         x += 8;
      } /* end of switch */
/* cursor */
if (x > (xmax-31))
   {
   cx = 9;
   cy = y + 8;
   }
else
  {
  cx = x+8;
  cy = y;
  }

if (cy > (ymax-2))
   {
   cx = 9;
   cy -= 8;
   ScrollRaster(mywindow->RPort,0,8,2,10,xmax-20,ymax-2);
   }

SetAPen(mywindow->RPort,3);
RectFill(mywindow->RPort,cx-7,cy-6,cx,cy+1);
SetAPen(mywindow->RPort,1);
}
/*************************************************
*  function to take raw key data and convert it 
*  into ascii chars
**************************************************/
toasc(code)
USHORT code;
{
static int ctrl = FALSE;
static int shift = FALSE;
static int capsl = FALSE;
char c;
static char keys[75] = {
'`' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , '0' , '-' ,
'=' , '\\' , 0 , '0' , 'q' , 'w' , 'e' , 'r' , 't' , 'y' , 'u' , 'i' , 'o' ,
'p' , '[' , ']' , 0 , '1' , '2' , '3' , 'a' , 's' , 'd' , 'f' , 'g' , 'h' ,
'j' , 'k' , 'l' , ';' , '\'' , 0 , 0 , '4' , '5' , '6' , 0 , 'z' , 'x' , 'c' , 'v' ,
'b' , 'n' , 'm' , 44 , '.' , '/' , 0 , '.' , '7' , '8' , '9' , ' ' , 8 ,
'\t' , 13 , 13 , 27 , 127 , 0 , 0 , 0 , '-' } ;

             switch ( code ) /* I didn't know about the Quilifier field when I write this */
                    {
                    case 98:
                       capsl = TRUE;
                       c = 0;
                       break;
                    case 226:
                       capsl = FALSE;
                       c = 0;
                       break;
                    case 99:
                       ctrl = TRUE;
                       c = 0;
                       break;
                    case 227:
                       ctrl = FALSE;
                       c = 0;
                       break;
                    case 96:
                    case 97:
                       shift = TRUE;
                       c = 0;
                       break;
                    case 224:
                    case 225:
                       shift = FALSE;
                       c = 0;
                       break;
                   default:
                       if (code < 75)
                          c = keys[code];
                       else
                          c = 0;
                       }
         
/* add modifiers to the keys */

if (c != 0)
   {

   if (ctrl && (c <= 'z') && (c >= 'a'))
      c -= 96;
   else if (shift)
           {
      if ((c <= 'z') && (c >= 'a'))
         c -= 32;
      else
         switch( c )
               {
          case '[':
             c = '{';
             break;
          case ']':
             c = '}';
             break;
          case '\\':
             c = '|';
             break;
          case '\'':
             c = '"';
             break;
          case ';':
             c = ':';
             break;
          case '/':
             c = '?';
             break;
          case '.':
             c = '>';
             break;
          case ',':
             c = '<';
             break;
          case '`':
             c = '~';
             break;
          case '=':
             c = '+';
             break;
          case '-':
             c = '_';
             break;
          case '1':
             c = '!';
             break;
          case '2':
             c = '@';
             break;
          case '3':
             c = '#';
             break;
          case '4':
             c = '$';
             break;
          case '5':
             c = '%';
             break;
          case '6':
             c = '^';
             break;
          case '7':
             c = '&';
             break;
          case '8':
             c = '*';
             break;
          case '9':
             c = '(';
             break;
          case '0':
             c = ')';
             break;
          default:
          } /* end switch */
      } /* end shift */
   else if (capsl && (c <= 'z') && (c >= 'a'))
           c -= 32;
   } /* end modifiers */
   return(c);
} /* end of routine */
/* end of file */
-- 
-----------
George M. Jones		{cbosgd,ihnp4}!osu-eddie!george
Work Phone:		george@ohio-state.csnet
(614) 457-8600		CompuServe: 70003,2443

clint@ISM780.UUCP (12/19/85)

/* Written 12:57 am  Dec 16, 1985 by george@osu-eddie in ISM780:net.micro.amiga */
/* ---------- "AmigaTerm (LONG SOURCE)" ---------- */
Hi folks...I know there have been a large number of people working on
terminal programs of one variety or another for the Amiga, but until
now I have not seen any of them.  What follows is a terminal program
in C that supports X-Modem and Capture protocols and works up to (I
believe) 9600.  It works under AmigaDos 1.0 and provides examples of
how to set up pull-down menus.  Michael Mounier posted it last night to
CompuServe's Amiga Forum and I figured people here might be interested.
He had no problem with me forwarding it on, so here it is...

===================CUT SOMEWHERE, HERE LOOKS GOOD TO ME===================
/************************************************************************
*  a terminal program that has ascii and xmodem transfer capability
*
*  use esc to abort xmodem transfer
*
*  written by Michael Mounier
************************************************************************/

/*  compiler directives to fetch the necessary header files */
#include <exec/types.h>
#include <exec/exec.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <graphics/gfxbase.h>
#include <graphics/gfx.h>
#include <graphics/text.h>
#include <graphics/regions.h>
#include <graphics/copper.h>
#include <graphics/gels.h>
#include <devices/serial.h>
#include <devices/keymap.h>
#include <hardware/blit.h>
#include <stdio.h>
#include <ctype.h>
#include <libraries/dos.h>

#define INTUITION_REV 1
#define GRAPHICS_REV  1

/* things for xmodem send and recieve */
#define SECSIZ   0x80
#define TTIME    30          /* number of seconds for timeout */
#define BufSize  0x1000     /* Text buffer */
#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 */

static char
    bufr[BufSize];
static int
    fd,
    timeout = FALSE;
static long
    bytes_xferred;

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

/* my window structure */
struct NewWindow NewWindow = {
   0,
   0,
   639,
   199,
   0,
   1,
   CLOSEWINDOW | RAWKEY | MENUPICK | NEWSIZE,
   WINDOWCLOSE | SMART_REFRESH | ACTIVATE | WINDOWDRAG
   | WINDOWDEPTH | WINDOWSIZING | REPORTMOUSE,
   NULL,
   NULL,
   "AMIGA Terminal",
   NULL,
   NULL,
   100, 35,
   640, 200,
   WBENCHSCREEN,
};

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

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

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

/*   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;
   FileItem[n].Height = 11;
   FileItem[n].Flags = ITEMTEXT | ITEMENABLED | HIGHBOX;
   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 Send";
FileText[2].IText = (UBYTE *)"Xmodem Receive";
FileText[3].IText = (UBYTE *)"Xmodem Send";

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 chekced */
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 );
}


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

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

/*   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 = NULL;
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 */

return( 0 );
}

/* declarations for the serial stuff */
extern struct MsgPort *CreatePort();
struct IOExtSer *Read_Request;
static char rs_in[2];
struct IOExtSer *Write_Request;
static char rs_out[2];

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

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

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

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

if(( mywindow = (struct Window *)OpenWindow(&NewWindow) ) == NULL)
   {
   puts("cant open window\n");
   exit(TRUE);
   }

Read_Request = (struct IOExtSer *)AllocMem(sizeof(*Read_Request),MEMF_PUBLIC|MEMF_CLEAR);
Read_Request->io_SerFlags = SERF_SHARED | SERF_XDISABLED;
Read_Request->IOSer.io_Message.mn_ReplyPort = CreatePort("Read_RS",0);
if(OpenDevice(SERIALNAME,NULL,Read_Request,NULL))
   {
   puts("Cant open Read device\n");
   CloseWindow( mywindow );
   DeletePort(Read_Request->IOSer.io_Message.mn_ReplyPort);
   FreeMem(Read_Request,sizeof(*Read_Request));
   exit(TRUE);
   }
Read_Request->IOSer.io_Command = CMD_READ;
Read_Request->IOSer.io_Length = 1;
Read_Request->IOSer.io_Data = (APTR) &rs_in[0];

Write_Request = (struct IOExtSer *)AllocMem(sizeof(*Write_Request),MEMF_PUBLIC|MEMF_CLEAR);
Write_Request->io_SerFlags = SERF_SHARED | SERF_XDISABLED;
Write_Request->IOSer.io_Message.mn_ReplyPort = CreatePort("Write_RS",0);
if(OpenDevice(SERIALNAME,NULL,Write_Request,NULL))
   {
   puts("Cant open Write device\n");
   CloseWindow( mywindow );
   DeletePort(Write_Request->IOSer.io_Message.mn_ReplyPort);
   FreeMem(Write_Request,sizeof(*Write_Request));
   DeletePort(Read_Request->IOSer.io_Message.mn_ReplyPort);
   FreeMem(Read_Request,sizeof(*Read_Request));
   exit(TRUE);
   }
Write_Request->IOSer.io_Command = CMD_WRITE;
Write_Request->IOSer.io_Length = 1;
Write_Request->IOSer.io_Data = (APTR) &rs_out[0];

Read_Request->io_SerFlags = SERF_SHARED | SERF_XDISABLED;
Read_Request->io_Baud = 300;
Read_Request->io_ReadLen = 8;
Read_Request->io_WriteLen = 8;
Read_Request->io_CtlChar = 1L;
Read_Request->IOSer.io_Command = SDCMD_SETPARAMS;
DoIO(Read_Request);
Read_Request->IOSer.io_Command = CMD_READ;

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

KeepGoing = TRUE;
capture=FALSE;
send=FALSE;
SetAPen(mywindow->RPort,1);
emit(12);
BeginIO(Read_Request);

while( KeepGoing )
     {
     /* wait for window message or serial port message */
     Wait((1 << Read_Request->IOSer.io_Message.mn_ReplyPort->mp_SigBit) | ( 1 << mywindow->UserPort->mp_SigBit));

     if (send)
         {
         if ((c=getc(trans)) != EOF)
             {
             sendchar(c);
             }
         else
             {
             fclose(trans);
             emits("\nFile Sent\n");
             send=FALSE;
             }
         }
     if(CheckIO(Read_Request))
        {
        WaitIO(Read_Request);
        c=rs_in[0] & 0x7f;
        BeginIO(Read_Request);
        emit(c);
        if (capture)
            if (c > 31 && c < 127 || c == 10)  /* 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 RAWKEY:
                /*  User has touched the keyboard */
                switch( code )
                      {
                      case 95: /* help key */
                         emits("ESC Aborts Xmodem Xfer\n");
                         emits("AMIGA Term 1.0\n");
                         emits("Copyright 1985 by Michael Mounier\n");
                      break;
                      default:   
                         c = toasc(code); /* get in into ascii */
                         if (c != 0)
                             {
                             rs_out[0] = c;
                             DoIO(Write_Request);
                             }
                      break;
                      }
                break;

                case NEWSIZE:
                   emit(12);
                break;

                case MENUPICK:
                   if ( code != MENUNULL )
                       {
                       menunum = MENUNUM( code );
                       itemnum = ITEMNUM( code );
                       switch( menunum )
                             {
                             case 0:
                                switch( itemnum )
                                      {
                                      case 0:
                                         if (capture == TRUE)
                                             {
                                             capture=FALSE;
                                             fclose(tranr);
                                             emits("\nEnd File Capture\n");
                                             }
                                         else
                                             {
                                             emits("\nAscii Capture:");
                                             filename(name);
                                             if ((tranr=fopen(name,"w")) == 0)
                                                 {
                                                 capture=FALSE;
                                                 emits("\nError Opening File\n");
                                                 break;
                                                 }
                                             capture=TRUE;
                                             }
                                      break;
                                      case 1:
                                         if (send == TRUE)
                                             { 
                                             send=FALSE;
                                             fclose(trans);
                                             emits("\nFile Send Cancelled\n");
                                             }
                                         else
                                             {
                                             emits("\nAscii Send:");
                                             filename(name);
                                             if ((trans=fopen(name,"r")) == 0)
                                                 {
                                                 send=FALSE;
                                                 emits("\nError Opening File\n");
                                                 break;
                                                 }
                                             send=TRUE;
                                             }
                                      break;
                                      case 2:
                                         emits("\nXmodem Receive:");
                                         filename(name);
                                         if (XMODEM_Read_File(name))
                                            {
                                            emits("\nRecieved File\n");
                                            emit(8);
                                            }
                                         else
                                            {
                                            close(fd);
                                            emits("Xmodem Receive Failed\n");
                                            emit(8);
                                            }
                                      break;
                                      case 3:
                                         emits("\nXmodem Send:");
                                         filename(name);
                                         if (XMODEM_Send_File(name))
                                             {
                                             emits("\nSent File\n");
                                             emit(8);
                                             }
                                         else
                                             {
                                             close(fd);
                                             emits("\nXmodem Send Failed\n");
                                             emit(8);
                                             }
                                      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;
                             } /* 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,sizeof(*Read_Request));
CloseDevice(Write_Request);
DeletePort(Write_Request->IOSer.io_Message.mn_ReplyPort);
FreeMem(Write_Request,sizeof(*Write_Request));
ClearMenuStrip( mywindow );
CloseWindow( mywindow );
exit(FALSE);
} /* end of main */

/*************************************************
*  function to get file name
*************************************************/
filename(name)
char name[];
{
char c;
ULONG class;
USHORT code;
int keepgoing,i;
keepgoing = TRUE;
i=0;
while (keepgoing) {
      while( NewMessage=(struct IntuiMessage *)GetMsg(mywindow->UserPort) )
           {
           class = NewMessage->Class;
           code = NewMessage->Code;
           ReplyMsg( NewMessage );
           if (class=RAWKEY)
              {
              c = toasc(code);
              name[i]=c;
              if (name[i] != 0)
                 {
                 if (name[i] == 13)
                    {
                    name[i]=0;
                    keepgoing = FALSE;
                    }
                 else
                    {
                    if (name[i] == 8)
                       {
                       i -=2;
                       if (i < -1)
                          i = -1;
                       else
                          {
                          emit(8);
                          emit(32);
                          emit(8);
                          }
                       }
                    else
                       emit(c);
                    }
                 i += 1;
                 }
              }
          } /* end of new message loop */
      }   /* end of god knows what */
      emit(13);
} /* end of function */


/*************************************************
*  function to print a string
*************************************************/
emits(string)
char string[];
{
int i;
char c;

i=0;
while (string[i] != 0)
      {
      c=string[i];
      if (c == 10)
         c = 13;
      emit(c);
      i += 1;
      }
}
/**************************************************************/
/* send char and read char functions for the xmodem function */
/************************************************************/
sendchar(ch)
int ch;
{
rs_out[0] = ch;
DoIO(Write_Request);
}

readchar()
{
unsigned char c;
int rd,ch;

rd = FALSE;

while (rd == FALSE)
      {
      Wait((1 << Read_Request->IOSer.io_Message.mn_ReplyPort->mp_SigBit) | ( 1 << mywindow->UserPort->mp_SigBit));
      if(CheckIO(Read_Request))
        {
        WaitIO(Read_Request);
        ch=rs_in[0];
        rd = TRUE;
        BeginIO(Read_Request);
        }
      if (NewMessage=(struct IntuiMessage *)GetMsg(mywindow->UserPort))
         if ((NewMessage->Class) == RAWKEY)
            if ((NewMessage->Code) == 69)
               {
               emits("\nUser Cancelled Transfer");
               break;
               }
      }
if (rd == FALSE)
   {
   timeout = TRUE;
   emits("\nTimeout Waiting For Character\n");
   }
c = ch;
return(c);
}

/**************************************/
/* xmodem send and recieve functions */
/************************************/

XMODEM_Read_File(file)
    char *file;
{
    int firstchar, sectnum, sectcurr, sectcomp, errors, errorflag;
    unsigned int checksum, j, bufptr,i;
    char numb[10];
    bytes_xferred = 0L;

    if ((fd = creat(file, 0)) < 0)
        {
        emits("Cannot Open File\n");
        return FALSE;
        }
    else
        emits("Receiving File\n");

    timeout=FALSE;
    sectnum = errors = bufptr = 0;
    sendchar(NAK);
    firstchar = 0;
    while (firstchar != EOT && errors != ERRORMAX)
        {
        errorflag = FALSE;

        do                                    /* get sync char */
           {
           firstchar = readchar();
           if (timeout == TRUE)
              return FALSE;
           }
        while (firstchar != SOH && firstchar != EOT);

        if  (firstchar == SOH)
            {
            emits("Getting Block ");
            stci_d(numb,sectnum,i);
            emits(numb);
            emits("...");
            sectcurr = readchar();
            if (timeout == TRUE)
               return FALSE;
            sectcomp = readchar();
            if (timeout == TRUE)
               return FALSE;
            if ((sectcurr + sectcomp) == 255)
                {
                if (sectcurr == (sectnum + 1 & 0xff))
                    {
                    checksum = 0;
                    for (j = bufptr; j < (bufptr + SECSIZ); j++)
                        {
                        bufr[j] = readchar();
                        if (timeout == TRUE)
                           return FALSE;
                        checksum = (checksum + bufr[j]) & 0xff;
                        }
                    if (checksum == readchar())
                        {
                        errors = 0;
                        sectnum++;
                        bufptr += SECSIZ;
                        bytes_xferred += SECSIZ;
                        emits("verified\n");
                        if (bufptr == BufSize)
                            {
                            bufptr = 0;
                            if (write(fd, bufr, BufSize) == EOF)
                                {
                                emits("\nError Writing File\n");
                                return FALSE;
                                };
                            };
                        sendchar(ACK);
                        }
                    else
                        {
                        errorflag = TRUE;
                        if (timeout == TRUE)
                           return FALSE;
                        }
                    }
                else
                    {
                    if (sectcurr == (sectnum & 0xff))
                        {
                        emits("\nReceived Duplicate Sector\n");
                        sendchar(ACK);
                        }
                    else
                        errorflag = TRUE;
                    }
                }
            else
                errorflag = TRUE;
            }
        if (errorflag == TRUE)
            {
            errors++;
            emits("\nError\n");
            sendchar(NAK);
            }
        };        /* end while */
            
    if ((firstchar == EOT) && (errors < ERRORMAX))
        {
        sendchar(ACK);
        write(fd, bufr, bufptr);
        close(fd);
        return TRUE;
        }
    return FALSE;
}

XMODEM_Send_File(file)
    char *file;
{
    int sectnum, bytes_to_send, size, attempts, c, i;
    unsigned checksum, j, bufptr;
    char numb[10];

    timeout=FALSE;
    bytes_xferred = 0;
    if ((fd = open(file, 1)) < 0)
        {
        emits("Cannot Open Send File\n");
        return FALSE;
        }
    else
        emits("Sending File\n");
    attempts = 0;
    sectnum = 1;
/* wait for sync char */
    j=1;
    while (((c = readchar()) != NAK) && (j++ < ERRORMAX));
    if (j >= (ERRORMAX))
        {
        emits("\nReceiver not sending NAKs\n");
        return FALSE;
        };

    while ((bytes_to_send = read(fd, bufr, BufSize)) && attempts != RETRYMAX)
        {
        if (bytes_to_send == EOF)
            {
            emits("\nError Reading File\n");
            return FALSE;
            };

        bufptr = 0;
        while (bytes_to_send > 0 && attempts != RETRYMAX)
            {
            attempts = 0;
            do
                {
                sendchar(SOH);
                sendchar(sectnum);
                sendchar(~sectnum);
                checksum = 0;
                size = SECSIZ <= bytes_to_send ? SECSIZ : bytes_to_send;
                bytes_to_send -= size;
                for (j = bufptr; j < (bufptr + SECSIZ); j++)
                    if (j < (bufptr + size))
                        {
                        sendchar(bufr[j]);
                        checksum += bufr[j];
                        }
                    else
                        {
                        sendchar(0);
                        }
                sendchar(checksum & 0xff);
                attempts++;
                c = readchar();
                if (timeout == TRUE)
                   return FALSE;
                }
            while ((c != ACK) && (attempts != RETRYMAX));
            bufptr += size;
            bytes_xferred += size;
            emits("Block ");
            stci_d(numb,sectnum,i);
            emits(numb);
            emits(" sent\n");
            sectnum++;
            }
        }
    close(fd);
    if (attempts == RETRYMAX)
        {
        emits("\nNo Acknowledgment Of Sector, Aborting\n");
        return FALSE;
        }
    else
        {
        attempts = 0;
        do
            {
            sendchar(EOT);
            attempts++;
            }
        while ((readchar() != ACK) && (attempts != RETRYMAX) && (timeout == FALSE));
         if (attempts == RETRYMAX)
            emits("\nNo Acknowledgment Of End Of File\n");
        };
    return TRUE;
}

/*************************************************
*  function to output ascii chars to window
*************************************************/
emit(c)
char c;
{
static short x = 3;
static short y = 17;
short xmax,ymax,cx,cy;

xmax = mywindow->Width;
ymax = mywindow->Height;

/* cursor */
if (x > (xmax-31))
   {
   cx = 9;
   cy = y + 8;
   }
else
  {
  cx = x+8;
  cy = y;
  }
 
if (cy > (ymax-2))
   {
   cx = 9;
   cy -= 8;
   }

SetDrMd(mywindow->RPort,COMPLEMENT);
SetAPen(mywindow->RPort,3);
RectFill(mywindow->RPort,cx-7,cy-6,cx,cy+1);
SetAPen(mywindow->RPort,1);
SetDrMd(mywindow->RPort,JAM2);

if (x > (xmax-31))
   {
   x = 3;
   y += 8;
   }

if (y > (ymax-2))
   {
   x = 3;
   y -= 8;
   }

Move(mywindow->RPort,x,y);

switch( c )
      {
      case '\t':
         x += 60;
         break;
      case '\n':
         break;
      case 13:  /* newline */
         x = 3;
         y += 8;
         break;
      case 8:   /* backspace */
         x -= 8;
         if (x < 3)
            x = 3;
         break;
      case 12:     /* page */
         x = 3;
         y = 17;
         SetAPen(mywindow->RPort,0);
         RectFill(mywindow->RPort,2,10,xmax-19,ymax-7);
         SetAPen(mywindow->RPort,1);
         break;
      case 7:     /* bell */
         ClipBlit(mywindow->RPort,0,0,mywindow->RPort,0,0,xmax,ymax,0x50);
         ClipBlit(mywindow->RPort,0,0,mywindow->RPort,0,0,xmax,ymax,0x50);
         break;
      default:
         Text(mywindow->RPort,&c,1);
         x += 8;
      } /* end of switch */
/* cursor */
if (x > (xmax-31))
   {
   cx = 9;
   cy = y + 8;
   }
else
  {
  cx = x+8;
  cy = y;
  }

if (cy > (ymax-2))
   {
   cx = 9;
   cy -= 8;
   ScrollRaster(mywindow->RPort,0,8,2,10,xmax-20,ymax-2);
   }

SetAPen(mywindow->RPort,3);
RectFill(mywindow->RPort,cx-7,cy-6,cx,cy+1);
SetAPen(mywindow->RPort,1);
}
/*************************************************
*  function to take raw key data and convert it 
*  into ascii chars
**************************************************/
toasc(code)
USHORT code;
{
static int ctrl = FALSE;
static int shift = FALSE;
static int capsl = FALSE;
char c;
static char keys[75] = {
'`' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , '0' , '-' ,
'=' , '\\' , 0 , '0' , 'q' , 'w' , 'e' , 'r' , 't' , 'y' , 'u' , 'i' , 'o' ,
'p' , '[' , ']' , 0 , '1' , '2' , '3' , 'a' , 's' , 'd' , 'f' , 'g' , 'h' ,
'j' , 'k' , 'l' , ';' , '\'' , 0 , 0 , '4' , '5' , '6' , 0 , 'z' , 'x' , 'c' , 'v' ,
'b' , 'n' , 'm' , 44 , '.' , '/' , 0 , '.' , '7' , '8' , '9' , ' ' , 8 ,
'\t' , 13 , 13 , 27 , 127 , 0 , 0 , 0 , '-' } ;

             switch ( code ) /* I didn't know about the Quilifier field when I write this */
                    {
                    case 98:
                       capsl = TRUE;
                       c = 0;
                       break;
                    case 226:
                       capsl = FALSE;
                       c = 0;
                       break;
                    case 99:
                       ctrl = TRUE;
                       c = 0;
                       break;
                    case 227:
                       ctrl = FALSE;
                       c = 0;
                       break;
                    case 96:
                    case 97:
                       shift = TRUE;
                       c = 0;
                       break;
                    case 224:
                    case 225:
                       shift = FALSE;
                       c = 0;
                       break;
                   default:
                       if (code < 75)
                          c = keys[code];
                       else
                          c = 0;
                       }
         
/* add modifiers to the keys */

if (c != 0)
   {

   if (ctrl && (c <= 'z') && (c >= 'a'))
      c -= 96;
   else if (shift)
           {
      if ((c <= 'z') && (c >= 'a'))
         c -= 32;
      else
         switch( c )
               {
          case '[':
             c = '{';
             break;
          case ']':
             c = '}';
             break;
          case '\\':
             c = '|';
             break;
          case '\'':
             c = '"';
             break;
          case ';':
             c = ':';
             break;
          case '/':
             c = '?';
             break;
          case '.':
             c = '>';
             break;
          case ',':
             c = '<';
             break;
          case '`':
             c = '~';
             break;
          case '=':
             c = '+';
             break;
          case '-':
             c = '_';
             break;
          case '1':
             c = '!';
             break;
          case '2':
             c = '@';
             break;
          case '3':
             c = '#';
             break;
          case '4':
             c = '$';
             break;
          case '5':
             c = '%';
             break;
          case '6':
             c = '^';
             break;
          case '7':
             c = '&';
             break;
          case '8':
             c = '*';
             break;
          case '9':
             c = '(';
             break;
          case '0':
             c = ')';
             break;
          default:
          } /* end switch */
      } /* end shift */
   else if (capsl && (c <= 'z') && (c >= 'a'))
           c -= 32;
   } /* end modifiers */
   return(c);
} /* end of routine */
/* end of file */
-- 
-----------
George M. Jones		{cbosgd,ihnp4}!osu-eddie!george
Work Phone:		george@ohio-state.csnet
(614) 457-8600		CompuServe: 70003,2443
/* End of text from ISM780:net.micro.amiga */