echo x - Letter
cat >Letter <<'!Funky!Stuff!'

Here is the new version of mackermit.  It differs from the old version
in the following ways:
	1) It has debug option which displays the contents
	   of each packet until a key is pressed.

	2) It remembers terminal settings.

	3) Pressing a key during transfer causes Mackermit to
	   resend the last packet (useful if the packet length
	   is distorted during transmission).

Again, I apologize for taking so long on this, and please let me know
how Mackermit is or is not working.

Steve

!Funky!Stuff!
echo x - Makefile
cat >Makefile <<'!Funky!Stuff!'
.SUFFIXES:  .b .c

OBJECTS = kermit.b term.b

.c.b:
	cc68  -c $<

kermit.rsrc:  kermit.rc b.out 
	rmaker kermit.rc

b.out: kermit.b term.b
	cc68 -z -m ${OBJECTS}
!Funky!Stuff!
echo x - controls.h
cat >controls.h <<'!Funky!Stuff!'
/* File controls.h
*  Constants for use with mackermit communications controls.  Masks
*  allow the changing of one specific communications paramater.
*/




#define NUMCNTLS 24

struct contrec
{
	int andpart, orpart;
} masks[NUMCNTLS-5]={
				/* Baud */
	{0xfd00,0x017c},	/* 300 */
	{0xfd00,0x0bd},		/* 600 */
	{0xfd00,0x05e},		/* 1200 */
	{0xfd00,0x3e},		/* 1800 */
	{0xfd00,0x2e},		/* 2400 */
	{0xfd00,0x1e},		/* 3600 */
	{0xfd00,0x16},		/* 4800 */
	{0xfd00,0xe},		/* 7200 */
	{0xfd00,0xa},		/* 9600 */
	{0xfd00,0x4},		/* 19200 */
	{0xfd00,0x0},		/* 57600 */
				/* Bits sent */
	{0xffff,0x0c00},	/* eight bits */
	{0xf3ff,0x0400},	/* seven bits */
				/* parity */
	{0xffff,0x3000},	/* even */
	{0xcfff,0x1000},	/* odd */
	{0xcfff,0x0000},	/* none */
				/* stop bits */
	{0x3fff,0x4000},	/* one */
	{0xffff,0xc000}		/* two */
};

!Funky!Stuff!
echo x - kermit.c
cat >kermit.c <<'!Funky!Stuff!'

 /*
 *  File Kermit
 *
 *  UNIX Kermit, Columbia University, 1981, 1982, 1983
 *      Bill Catchings, Bob Cattani, Chris Maio, Frank da Cruz, Alan Crosswell
 *
 *  Also:   Jim Guyton, Rand Corporation
 *          Walter Underwood, Ford Aerospace
 *
 *  Macintosh adaptation: Stephen Engel
 */


#include "kermit.h"

/*
*	Main routine:
*			Initialize windows, communications, and wait for a
*		menu selection.
*/

main()
{
   
    int err,code;
    struct QDVar QDVar;

    QD = &QDVar;
    eol = CR;                           /* EOL for outgoing packets */
    quote = '#';                        /* Standard control-quote char "#" */
    pad = 0;                            /* No padding */
    padchar = NULL;                     /* Use null if any padding wanted */

     


    InitGraf(&thePort);
    InitFonts();
    InitWindows();
    InitDialogs((ProcPtr) NULL);
    SetupMenus();
    SetCursor(&QD->arrow);
    ControlWindow=NewWindow(&CWRecord, (Rect *) ControlRect, "Controls",
				0,userKind,(WindowPtr) -1,1,1);
	DebugWindow=NewWindow(&DWRecord, (Rect *) DebugRect, "Debugger",
			0,userKind,(WindowPtr) -1,1,1);
    TabWindow=NewWindow(&TWRecord, (Rect *) TabRect,
		"Tabs--Press any  key to exit",
				0,userKind,(WindowPtr) -1,1,1);
    theWindow=NewWindow(&wRecord, (Rect *)wirect, "MacKermit",VIS,
			DocumentProc, (WindowPtr) -1,1,0);
    SetupControls();
    SetPort(theWindow);
    theWindow->txFont=4;   /* Monaco font with non-proportional spacing */
    theWindow->txSize=9;
    PenSize(1,1);
    FlushEvents(everyEvent,0);
    err=GetVol(volname,&volnum);
    if (err)
	printerr("Bad volume name: ",err);

    config=0x4c0a;	   /* Initial Conditions:
				9600 baud, eight bit, no parity, one stop bit */
	SetupIO();

/* Main loop:  look for events, ignore unless its a menu selection
*              If it is call DoCommand to do the dispatching */

    for(;;)
    {
	SystemTask();
	GetNextEvent(everyEvent,&myEvent);
	switch(myEvent.what)
	{
		case mouseDown:
			code=FindWindow(&myEvent.where,&whichWindow);
			switch(code)
			{
				case inMenuBar:
					DoCommand(MenuSelect(&myEvent.where));
					break;
			}
	}
   }
}

SetupIO()
{
	int err;
			   /* Set up IO drivers */
    innum=OpenDriver(".AIn");
    outnum=OpenDriver(".AOut");
    controlparam.asncConfig=config;
    err=Control(innum,8,&controlparam);
    if (err)
	fatal("Can't open drivers: ",err);
    err=Control(outnum,8,&controlparam);	
    if (err)
	fatal("Can't open drivers: ",err);

    			  /* Replace driver buffer with larger one */
    controlparam.asyncInBuff.asncBPtr=mybuff;
    controlparam.asyncInBuff.asncBLen=(short)MYBUFSIZE;
    err=Control(innum,9,&controlparam);
    if (err)
	printerr("Trouble making io buffer:",err);

			  /* Specify handshake options */
    controlparam.asyncShk.fXOn=(char)FALSE;
    controlparam.asyncShk.fCTS=(char)FALSE;
    controlparam.asyncShk.xon=(char) 17;
    controlparam.asyncShk.xoff=(char) 19;
    controlparam.asyncShk.errs=(char)FALSE;
    controlparam.asyncShk.evts=(char)FALSE;
    controlparam.asyncShk.fInX=(char)FALSE;
    controlparam.asyncShk.filler4=(char)FALSE;
    if (err)
	printerr("Trouble with handshake: ",err);
    err=Control(outnum,10,&controlparam);
    if (err)
	printerr("Trouble with handshake: ",err);
}


/*
*   SetupMenus:
*		Install command menus.
*/

SetupMenus()
{
	int i;
 	char fnamcnv[13];

 	stringcpy(fnamcnv,"Filenamecnv!");
 	fnamcnv[12]=(char) 18;

	InitMenus();
	myMenus[0]=NewMenu(filemenu,"File");
	AppendMenu(myMenus[0], "Exit");
	myMenus[1]=NewMenu(modemenu,"Mode");
	AppendMenu(myMenus[1],"Connect");
	AppendMenu(myMenus[1],"Transmit");
	AppendMenu(myMenus[1],"Receive");
	AppendMenu(myMenus[1],"Controls");
	myMenus[2]=NewMenu(optionmenu,"Option");
	AppendMenu(myMenus[2],"Image");
	AppendMenu(myMenus[2],"Macwrite");
	AppendMenu(myMenus[2],"Executable");
	AppendMenu(myMenus[2],fnamcnv);
	AppendMenu(myMenus[2],"Debug");
	for (i=0;i<numMenu;i++)
		InsertMenu(myMenus[i],0);
	DrawMenuBar();
}


/*
*	SetupControls:
*		Read in and install the terminal controls
*/

SetupControls()
{
	ControlHandle theControl;
	int i;
	Handle tabstring;

	/* Read in controls from resource file */

	for(i=0;i<NUMCNTLS;i++)	
	{
		theControl=GetNewControl(i,ControlWindow);
		if(GetCtlValue(theControl))
		{
			config&= 
				masks[i].andpart;
			config|= 
				masks[i].orpart;
			if(i<11)
				baud=theControl;
			else if(i<13)
				bits=theControl;
			else if (i<16)
				parity=theControl;
			else if (i<18)
				stopb=theControl;
			else if (i<20)
			{
				xon=theControl;
				flowctl=(i==18);
			}
			else if (i < 22)
				online=theControl;
		}
	};
	MoveTo(25,25);
	tabstring=GetResource("TABS",0);
	stringcpy(tabs,*tabstring);
}


/*
*	DoCommand:
*		Either dispatch to a routine or change constant values,
*		according to menu selection.
*/

DoCommand(mResult)
int mResult;
{
	int theMenu=mResult>>16, theItem=(mResult &0xffff);

	if (mResult !=0)
		switch(theMenu)
		{
			case filemenu: /* Exit is only possibility */
				flushio();
				ExitToShell();
				break;
			case modemenu:
					/* Dispatch to the correct routines */
				switch(theItem)
				{
					case CONNECT:
						connection();
						break;
					case TRANSMIT:
						sendsw();
						break;
					case RECEIVE:
						recsw();
						break;
					case CONTROLS:
						DoControls();
						break;
					default:
						break;
				};
				break;
			case optionmenu:
					/* Update the selected constant */
					/* Selecting negates the current setting*/
				switch(theItem)
				{
					case IMAGE:
						image = !image; 

 /* Checking with last paramater=TRUE places checkmark, otherwise its erased */
						CheckItem(myMenus[2],IMAGE,image);
						break;
					case MACWRITE:
						macwrite = !macwrite; 
						CheckItem(myMenus[2],MACWRITE,macwrite);
						break;
					case EXECUTABLE:
						exec=!exec; 
						CheckItem(myMenus[2],EXECUTABLE,exec);
				/* Need all eight bits for executable code */
						image=exec;
						CheckItem(myMenus[2],IMAGE,image);
						break;
 
 				case FILENAMECNV:
 					filnamcnv=!filnamcnv;
 					CheckItem(myMenus[2],FILENAMECNV,filnamcnv);
 					break;
				case DEBUG:
					debug=!debug;
					CheckItem(myMenus[2],DEBUG,debug);
					break;
				};
		};
	HiliteMenu(0);
}		
    
/*
*	DoControls:
*		Draw contol labels, find which control was selected, and
*		update control panel and serial driver accordingly. 
*/
 
DoControls()
{
	int i,res,whichone;
	Handle myResource;

	ShowWindow(ControlWindow);
	SelectWindow(ControlWindow);	
	SetPort(ControlWindow);
	PenNormal();
	DrawControls(ControlWindow);
	TextFace(boldStyle);
	MoveTo(20,20);
	DrawString("Baud Rate");
	MoveTo(190,20);
	DrawString("Bits Sent");
	MoveTo(190,100);
	DrawString("Parity");
	MoveTo(280,20);
	DrawString("Stop Bits");
	MoveTo(280,100);
	DrawString("Xon/Xoff Flow");
	for(;;) /* Loop until exit button is pressed */
	{
		GetNextEvent(everyEvent,&myEvent);
		FindWindow(&myEvent.where,&whichWindow);
		GlobalToLocal(&myEvent.where);
		if (whichWindow==ControlWindow)
		switch(myEvent.what)
		{
		case mouseDown:
			res=FindControl(&myEvent.where,ControlWindow,&theControl);
		/* res=TRUE if a control was selected */
			if ( res)
			{
				TrackControl(theControl,&myEvent.where,(ProcPtr) NULL);
				whichone=(*theControl)->contrlRfCon;

				/* Update apropriate controls */
				if(whichone<11)
					resetcontrol(&baud);
				else if(whichone<13)
					resetcontrol(&bits);
				else if (whichone<16)
					resetcontrol(&parity);
				else if (whichone<18)
					resetcontrol(&stopb);
				else if (whichone<20)
				{
					resetcontrol(&xon);
				/* Need to update global variable since its
					not part of the driver */
					flowctl=(whichone==18);
				}
				else if (whichone < 22)
					resetcontrol(&online);
				else if (whichone==22)
				{
					ShowWindow(TabWindow);
					SelectWindow(TabWindow);
					SetPort(TabWindow);
					change_tabs();
					HideWindow(TabWindow);
					SelectWindow(ControlWindow);
					SetPort(ControlWindow);
				}	
				else /* Must be exit button */
				{
					HideWindow(ControlWindow);
					SelectWindow(theWindow);
					SetPort(theWindow);
					return;
				};

	/* Set selected control, the manager automatically fills it in. */
			if(whichone<22)
			{
				myResource=GetResource("CNTL",(int)(*theControl)->contrlRfCon);
				SetCtlValue(theControl,1);
				(*myResource)[9]=1;
				ChangedResource(myResource);
				WriteResource(myResource);
			}

	/* Reconfigure the drivers */
				if(whichone<18)
				/* Don't reconfigure if xon was thecontrol */
				{
				config&= 
					masks[whichone].andpart;
				config|= 
					masks[whichone].orpart;
				sendbreak();
				controlparam.asncConfig=config;
				Control(innum,8,&controlparam);
				Control(outnum,8,&controlparam);	
				}
		}

	}
	};
}		

resetcontrol(cont)
ControlHandle *cont;
{
	Handle myResource;

	myResource=GetResource("CNTL",(int)(**cont)->contrlRfCon);
	(*myResource)[9]=0;
	ChangedResource(myResource);
	WriteResource(myResource);
	HNoPurge(myResource);
	SetCtlValue(*cont,0); /* Unselect the old control value */
	*cont=theControl;     /* Set it to the selected one */
}
	

		
 		
/*
 *  s e n d s w
 *
 *  Sendsw is the state table switcher for sending files.  It loops until
 *  either it finishes, or an error is encountered.  The routines called
 *  by sendsw are responsible for changing the state.
 *
 */
     
sendsw()
{
    int packno=0;  /* Actual (not mod 64) packet count */
    char sinit(), sfile(), sdata(), seof(), sbreak();
    state = 'S';                        /* Send initiate is the start state */
    n = 0;                              /* Initialize message number */
    fp=NULL;   				/* No file yet */
    numtry = 0;                         /* Say no tries yet */

    EraseRect((Rect *) inrect);
    MoveTo(15,50);
    MDrawString("SENDING...");
    MoveTo(15,200);
    MDrawString("Packet ");
    if(debug)
	ShowWindow(DebugWindow);
		
    while(TRUE)                         /* Do this as long as necessary */
    {
	if(n!=(packno%64))              /* Change in packet number? */
		packno++;
	EraseRect((Rect *) myrect);     /* Erase old packet number */
	MoveTo(80,200);	
	printnum(packno);
        switch(state)
        {
            case 'S':   state = sinit();  break; /* Send-Init */
            case 'F':   state = sfile();  break; /* Send-File */
            case 'D':   state = sdata();  break; /* Send-Data */
            case 'Z':   state = seof();   break; /* Send-End-of-File */
            case 'B':   state = sbreak(); break; /* Send-Break */
            case 'C': 	MoveTo(15,260);          /* Complete */ 
	    		DrawString("File Successfully Transmitted. ");
			return;
            case 'A':   		         /* "Abort" */
            default:	if(fp!=NULL)
			{
				FSClose(fp); 
				FlushVol(volname,volnum);	
			}
	 		MoveTo(15,260);
			MDrawString("File transmission failed."); 
			return;
        }
    }
}
     
     
/*
 *  s i n i t
 *
 *  Send Initiate: send this host's parameters and get other side's back.
 */
     
char sinit()
{
    int num, len;                       /* Packet number, length */
    if (numtry++ > MAXTRY) 
	{
		printerr("In init-- too many tries",0);
		return('A'); /* If too many tries, give up */
	};
	GetNextEvent(everyEvent,&myEvent);
	if(myEvent.what==mouseDown)
	{
		printerr("\nMousedown Aborted\n",0);
		return('A');
	};
    spar(packet);                       /* Fill up init info packet */
     
     
    spack('S',n,6,packet);              /* Send an S packet */
    switch(rpack(&len,&num,recpkt))     /* What was the reply? */
    {
        case 'N':  return(state);       /* NAK, try it again */
     
        case 'Y':                       /* ACK */
            if (n != num)               /* If wrong ACK, stay in S state */
                return(state);          /* and try again */
            rpar(recpkt);               /* Get other side's init info */
     
            if (eol == 0) eol = '\n';   /* Check and set defaults */
            if (quote == 0) quote = '#';
     
            numtry = 0;                 /* Reset try counter */
            n = (n+1)%64;              /*  Bump packet count */
            return('F');                /* OK, switch state to F */
     
        case FALSE: return(state);      /* Receive failure, try again */
     
	    /* Anything else, just "abort" */
        default: printerr("Bad packet type--sinit",0); 
					return('A');       
   }
 }
     
     
/*
 *  s f i l e
 *
 *  Send File Header.
 */
     
char sfile()
{
    int num,len;                   /* Packet number, length */
    char     *cp;                            /* char pointer */
     
    if (numtry++ > MAXTRY) 
	{
		printerr("Too many tries-- in sfile",0);
		return('A'); /* If too many tries, give up */
	};
	GetNextEvent(everyEvent,&myEvent);
	if(myEvent.what==mouseDown)
	{
		printerr("\nMousedown Aborted\n",0);
		return('A');
	};
     
    if (fp == NULL)                     /* If not already open, */
    {
	fp=open_file(filnam);
	if(!fp)
		fatal("Kermit Cancelled",0);
    
    if (filnamcnv)                      /* Convert lower case to upper  */
        for (cp =filnam; *cp != '\0'; cp++)
		{
            if (*cp >= 'a' && *cp <= 'z')
                *cp ^= 040;
		if ((*cp=='\0x20')||(*cp=='0xca'))
			*cp='.';
		};
     
    }
    len = stringlen(filnam);                /* Compute length of new filename */
     
    spack('F',n,len,filnam);         /* Send an F packet */
    switch(rpack(&len,&num,recpkt))     /* What was the reply? */
    {
        case 'N':                       /* NAK, just stay in this state, */
            num = (--num<0 ? 63:num);   /* unless it's NAK for next packet */
            if (n != num)               /* which is just like an ACK for */
                return(state);          /* this packet so fall thru to... */
     
        case 'Y':                       /* ACK */
            if (n != num) return(state); /* If wrong ACK, stay in F state */
            numtry = 0;                 /* Reset try counter */
            n = (n+1)%64;             /*  Bump packet count */
            size = bufill(packet);      /* Get first data from file */
            return('D');                /* Switch state to D */
     
            case FALSE: return(state);  /* Receive failure, stay in F state */
     
 			/* Something else, just "abort" */
            default:    printerr("Bad packet type--in sfile",0);
						return('A');   
        }
}
     
     
/*
 *  s d a t a
 *
 *  Send File Data
 */
     
char sdata()
{
    int num, len;                       /* Packet number, length */
    if (numtry++ > MAXTRY) 
	{
		printerr("Too many tries--in sdata",0);
		return('A'); /* If too many tries, give up */
	};
    GetNextEvent(everyEvent,&myEvent);
    if(myEvent.what==mouseDown)
    {
    	printerr("\nMousedown Aborted\n",0);
    	return('A');
    };
     
    spack('D',n,size,packet);           /* Send a D packet */
    switch(rpack(&len,&num,recpkt))     /* What was the reply? */
    {
        case 'N':                       /* NAK, just stay in this state, */
            num = (--num<0 ? 63:num);   /* unless it's NAK for next packet */
            if (n != num)               /* which is just like an ACK for */
                return(state);          /* this packet so fall thru to... */
     
        case 'Y':                       /* ACK */
            if (n != num) return(state); /* If wrong ACK, fail */
            numtry = 0;                 /* Reset try counter */
            n = (n+1)%64;                /*Bump packet count */
            if ((size = bufill(packet)) == EOF) /* Get data from file */
                return('Z');            /* If EOF set state to that */
            return('D');                /* Got data, stay in state D */
     
        case FALSE: return(state);      /* Receive failure, stay in D */
     
    	/* Anything else, "abort" */
        default: printerr("Bad packet type--sdata",0);
			     return('A');  
    }
}
     
     
/*
 *  s e o f
 *
 *  Send End-Of-File.
 */
     
char seof()
{
    int num, len;                       /* Packet number, length */
    if (numtry++ > MAXTRY) 
	{
		printerr("Too many tries--seof",0);
		return('A'); /* If too many tries, "abort" */
	};
    GetNextEvent(everyEvent,&myEvent);
    if(myEvent.what==mouseDown)
    {
    	printerr("\nMousedown Aborted\n",0);
    	return('A');
    };

     
    spack('Z',n,0,packet);              /* Send a 'Z' packet */
    switch(rpack(&len,&num,recpkt))     /* What was the reply? */
    {
        case 'N':                       /* NAK, just stay in this state, */
            num = (--num<0 ? 63:num);   /* unless it's NAK for next packet, */
            if (n != num)               /* which is just like an ACK for */
                return(state);          /* this packet so fall thru to... */
     
        case 'Y':                       /* ACK */
            if (n != num) return(state); /* If wrong ACK, hold out */
            numtry = 0;                 /* Reset try counter */
            n = (n+1)%64;              	/* and bump packet count */
            FSClose(fp);                /* Close the input file */
	    FlushVol(volname,volnum);	
            fp = NULL;                  /* Set flag indicating no file open */
            return('B');                /* if not, break, EOT, all done */
     
        case FALSE: return(state);      /* Receive failure, stay in Z */
     
   		/* Something else, "abort" */
        default:   printerr("Bad packet type--seof",0);
			 	   return('A');     
    }
}
     
     
/*
 *  s b r e a k
 *
 *  Send Break (EOT)
 */
     
char sbreak()
{
    int num, len;                       /* Packet number, length */
    if (numtry++ > MAXTRY) 
		{
			printerr("Too many tries--sbreak",0);
			return('A'); /* If too many tries "abort" */
		};
     
    GetNextEvent(everyEvent,&myEvent);
    if(myEvent.what==mouseDown)
    {
    	printerr("\nMousedown Aborted\n",0);
    	return('A');
    };
    spack('B',n,0,packet);              /* Send a B packet */
    switch (rpack(&len,&num,recpkt))    /* What was the reply? */
    {
        case 'N':                       /* NAK, just stay in this state, */
            num = (--num<0 ? 63:num);   /* unless NAK for previous packet, */
            if (n != num)               /* which is just like an ACK for */
                return(state);          /* this packet so fall thru to... */
     
        case 'Y':                       /* ACK */
            if (n != num) return(state); /* If wrong ACK, fail */
            numtry = 0;                 /* Reset try counter */
            n = (n+1)%64;                	/*and bump packet count */
            return('C');                /* Switch state to Complete */
     
        case FALSE: return(state);      /* Receive failure, stay in B */
     
        default:    return ('A');       /* Other, "abort" */
   }
}
     
     
/*
 *  r e c s w
 *
 *  This is the state table switcher for receiving files.
 */
     
recsw()
{
    int packno=0;			/* Actual (not mod 64) packet count */
    char rinit(), rfile(), rdata();     /* Use these procedures */
     
    state = 'R';                        /* Receive-Init is the start state */
    n = 0;                              /* Initialize message number */
    numtry = 0;                         /* Say no tries yet */
    fp=NULL; 

    EraseRect((Rect *) inrect);		/* Clear screen */
    MoveTo(15,50);
    MDrawString("RECEIVING...");
    MoveTo(15,200);
    MDrawString("Packet ");
    if(debug)
	ShowWindow(DebugWindow);
		
    while(TRUE)                         /* Do this as long as necessary */
    {
	if (n!=(packno%64))		/* Change in packet number? */
		packno++;
	EraseRect((Rect *) myrect);	/* Erase old packet count */
	MoveTo(80,200);	
	printnum(packno);
        switch(state)                   /* Do until done */
        {
            case 'R':   state = rinit(); break; /* Receive-Init */
            case 'F':   state = rfile(); break; /* Receive-File */
            case 'D':   state = rdata(); break; /* Receive-Data */
            case 'C':  	MoveTo(15,260);	        /* Complete */
 			DrawString("File Successfully Received.");
	 		return; 
            case 'A':  
			if( fp!=NULL)           /* Abort state */
			{
				FSClose(fp); 
				FlushVol(volname,volnum);	
			}
			MoveTo(15,260);
			DrawString("File Transfer Failed."); 
			return;        
        }
    }
}
     
     
/*
 *  r i n i t
 *
 *  Receive Initialization
 */
     
char rinit()
{
    int len, num;                       /* Packet length, number */
     
    if (numtry++ > MAXTRY) 
    {
	printerr("Too many tries--rinit",0);
	return('A'); /* If too many tries, "abort" */
    };
     
    switch(rpack(&len,&num,packet))     /* Get a packet */
    {
        case 'S':                       /* Send-Init */
            rpar(packet);               /* Get the other side's init data */
            spar(packet);               /* Fill up packet with my init info */
            spack('Y',n,6,packet);      /* ACK with my parameters */
            oldtry = numtry;            /* Save old try count */
            numtry = 0;                 /* Start a new counter */
            n = (n+1)%64;             /*  Bump packet number, mod 64 */
            return('F');                /* Enter File-Receive state */
     
        case FALSE:                     /* Didn't get packet */
            spack('N',n,0,(char *)0);           /* Return a NAK */
            return(state);              /* Keep trying */
     
        default:   printerr("Bad packet type--rinit",0);
		  return('A');       /* Some other packet type, "abort" */
    }
}
     
     
/*
 *  r f i l e
 *
 *  Receive File Header
 */
     
char rfile()
{
    int num, len, err;                       /* Packet number, length */
    char *filnam1;                   /* Holds the converted file name */
     
    if (numtry++ > MAXTRY) 
    {
	printerr("Too many tries--rfile",0);
	return('A'); /* "abort" if too many tries */
    };
     
    GetNextEvent(everyEvent,&myEvent);
    if(myEvent.what==mouseDown)
    {
    	printerr("\nMouse Aborted\n",0);
    	return('A');
    };
    switch(rpack(&len,&num,packet))     /* Get a packet */
    {
        case 'S':                       /* Send-Init, maybe our ACK lost */
            if (oldtry++ > MAXTRY) return('A'); /* If too many tries "abort" */
            if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */
            {                           /* Yes, ACK it again with  */
                spar(packet);           /* our Send-Init parameters */
                spack('Y',num,6,packet);
                numtry = 0;             /* Reset try counter */
                return(state);          /* Stay in this state */
            }
            else return('A');           /* Not previous packet, "abort" */
     
        case 'Z':                       /* End-Of-File */
            if (oldtry++ > MAXTRY) return('A');
            if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */
            {                           /* Yes, ACK it again. */
                spack('Y',num,0,(char* )0);
                numtry = 0;
                return(state);          /* Stay in this state */
            }
            else return('A');           /* Not previous packet, "abort" */
     
        case 'F':                       /* File Header (just what we want) */
            if (num != n) return('A');  /* The packet number must be right */
            	stringcpy(filnam, packet);    /* Copy the file name */
     
            if (filnamcnv)              /* Convert upper case to lower */
                for (filnam1=filnam; *filnam1 != '\0'; filnam1++)
                    if (*filnam1 >= 'A' && *filnam1 <= 'Z')
                        *filnam1 |= 040;
		MDrawString("\n\n\n\rFile Name: ");
		MDrawString(filnam);
		if (!exec)
		{	/* Place data in data fork of mac text file */

			err=Create(filnam,0,"KERMIT","TEXT");
			if(err != noErr)
			{
				printerr("Couldn't create file:",err);
				return('A');
			}
			err=FSOpen(filnam,0,&fp);
			if(err !=noErr)
			{
				printerr("Can't open file:",err);
				return('A'); 
			}
		}
		else
		{	/* Executable file---place data in resource fork
			   of application file. */

			err=Create(filnam,0,"KERMIT","APPL");
			if(err != noErr)
			{
				printerr("Couldn't create file:",err);
				return('A');
			}
			err=OpenRF(filnam,0,&fp);
			if(err != noErr)
			{
				printerr("Couldn't open resources: ",err);
				return('A');
			}
			
		};
            spack('Y',n,0,(char *)0);           /* Acknowledge the file header */
            oldtry = numtry;            /* Reset try counters */
            numtry = 0;                 /* ... */
            n = (n+1)%64;                /* Bump packet number, mod 64 */
            return('D');                /* Switch to Data state */
     
        case 'B':                       /* Break transmission (EOT) */
            if (num != n) return ('A'); /* Need right packet number here */
            spack('Y',n,0,(char *)0);   /* Say OK */
            return('C');                /* Go to complete state */
     
        case FALSE:                     /* Didn't get packet */
            spack('N',n,0,(char *)0);           /* Return a NAK */
            return(state);              /* Keep trying */
     
        default:    return ('A');       /* Some other packet, "abort" */
    }
}
     
/*
 *  r d a t a
 *
 *  Receive Data
 */
     
char rdata()
{
    int num, len;                       /* Packet number, length */
    if (numtry++ > MAXTRY) 
	{
		printerr("Too many tries--rdata",0);
		return('A'); /* "abort" if too many tries */
		};
    GetNextEvent(everyEvent,&myEvent);
    if(myEvent.what==mouseDown)
    {
    	printerr("\nMouse Aborted\n",0);
    	return('A');
    };

    switch(rpack(&len,&num,packet))     /* Get packet */
    {
        case 'D':                       /* Got Data packet */
            if (num != n)               /* Right packet? */
            {                           /* No */
                if (oldtry++ > MAXTRY)
                    {
			printerr("Too many tries--rdata",0);
			return('A');        /* If too many tries, abort */
				};
                if (num == ((n==0) ? 63:n-1)) /* Else check packet number */
                {                       /* Previous packet again? */
                    spack('Y',num,6,packet); /* Yes, re-ACK it */
                    numtry = 0;         /* Reset try counter */
                    return(state);      /* Don't write out data! */
                }
                else 
		{
			printerr("Bad Packet no--rdata",0);
			return('A');       /* sorry, wrong number */
		};
            }

	    bufemp(packet,len);
            spack('Y',n,0,(char *)0);   /* Acknowledge the packet */
            oldtry = numtry;            /* Reset the try counters */
            numtry = 0;                 /* ... */
            n = (n+1)%64;               /* Bump packet number, mod 64 */
            return('D');                /* Remain in data state */
     
        case 'F':                       /* Got a File Header */
            if (oldtry++ > MAXTRY)
	    {
		printerr("Too many tries--rdata",0);	
               	return('A');            /* If too many tries, "abort" */
	    };
            if (num == ((n==0) ? 63:n-1)) /* Else check packet number */
            {                           /* It was the previous one */
                spack('Y',num,0,(char *)0);     /* ACK it again */
                numtry = 0;             /* Reset try counter */
                return(state);          /* Stay in Data state */
            }
            else
	    {
		printerr("Not previous packet-rdata",0);
		return('A');           /* Not previous packet, "abort" */
	    };
     
        case 'Z':                       /* End-Of-File */
            if (num != n) return('A');  /* Must have right packet number */
            spack('Y',n,0,(char *)0);           /* OK, ACK it. */
            FSClose(fp);                 /* Close the file */
	    FlushVol(volname,volnum);	
            n = (n+1)%64;               /*  Bump packet number */
            return('F');                /* Go back to Receive File state */
     
        case FALSE:                     /* Didn't get packet */
            spack('N',n,0,(char *)0);           /* Return a NAK */
            return(state);              /* Keep trying */
     
        default:     return('A');       /* Some other packet, "abort" */
    }
}


/*
*   KERMIT Utilities
*/

stringcpy(str1,str2)
char *str1,*str2;
{
	while(*str1++ = *str2++)
	;
}

stringlen(str)
char *str;
{
	int i=0;

	while(*str++)
		i++;
	return(++i);
}

/*
 *  r p a c k
 *
 *  Read a Packet
 */
     
rpack(len,num,data)
int *len, *num;                         /* Packet length, number */
char *data;                             /* Packet data */
{
    int i, done;                /* Data character number, loop exit */
    char chksum,t='a',type;
    ioParam  inparam;

    inparam.ioCompletion=(ProcPtr) NULL;
    inparam.ioRefNum=innum ;
    inparam.ioReqCount=1;
    inparam.ioPosMode=0;
    inparam.ioPosOffset=0;
    inparam.ioBuffer= &t;

    if (debug)
    {
        SelectWindow(DebugWindow);
    	SetPort(DebugWindow);
    	EraseRect((Rect *) inrect);
    	MoveTo(50,12);
    	MDrawString("Packet Coming In\n\n\r");
    }
    while (t != SOH)                    /* Wait for packet header */
    {
        PBRead(&inparam,TRUE);
	while(inparam.ioResult!=0)
	{
		GetNextEvent(everyEvent,&myEvent);
		if (myEvent.what==keyDown)
			return(FALSE);
	};
        t &= 0177;                      /* Handle parity */
    }
    if (debug)
	MDrawString("Got SOH \n\r");
    done = FALSE;                       /* Got SOH, init loop */
    while (!done)                       /* Loop to get a packet */
    {
        PBRead(&inparam,TRUE);
	while(inparam.ioResult!=0)
	{
		GetNextEvent(everyEvent,&myEvent);
		if (myEvent.what==keyDown)
			return(FALSE);
	};
        if (!image) t &= 0177;          /* Handle parity */
        if (t == SOH) continue;         /* Resynchronize if SOH */
        chksum = t;                     /* Start the checksum */
        *len = unchar(t)-3;             /* Character count */
	if (debug)
	{
		MDrawString("Number of characters: ");
		printnum(*len);
	}
        PBRead(&inparam,TRUE);
	while(inparam.ioResult!=0)
	{
		GetNextEvent(everyEvent,&myEvent);
		if (myEvent.what==keyDown)
			return(FALSE);
	};
        if (!image) t &= 0177;          /* Handle parity */
        if (t == SOH) continue;         /* Resynchronize if SOH */
        chksum = chksum + t;            /* Update checksum */
        *num = unchar(t);               /* Packet number */
	if(debug)
	{
		MDrawString("\n\rPacket Number: ");
		printnum(*num);
	}
     
        PBRead(&inparam,TRUE);
	while(inparam.ioResult!=0)
	{
		GetNextEvent(everyEvent,&myEvent);
		if (myEvent.what==keyDown)
			return(FALSE);
	};
        if (!image) t &= 0177;          /* Handle parity */
        if (t == SOH) continue;         /* Resynchronize if SOH */
        chksum = chksum + t;            /* Update checksum */
        type = t;                       /* Packet type */
    	if(debug)
	{
		MDrawString("\n\rPacket type: ");
		MDrawChar(type);
	}
        for (i=0; i<*len; i++)          /* The data itself, if any */
        {                               /* Loop for character count */
        	PBRead(&inparam,TRUE);
		while(inparam.ioResult!=0)
		{
			GetNextEvent(everyEvent,&myEvent);
			if (myEvent.what==keyDown)
				return(FALSE);
		};
            if (!image) t &= 0177;      /* Handle parity */
            if (t == SOH) continue;     /* Resynch if SOH */
            chksum = chksum + t;        /* Update checksum */
            data[i] = t;                /* Put it in the data buffer */
        }
        data[*len] = 0;                 /* Mark the end of the data */
     
        PBRead(&inparam,TRUE);
	while(inparam.ioResult!=0)
	{
		GetNextEvent(everyEvent,&myEvent);
		if (myEvent.what==keyDown)
			return(FALSE);
	};
        if (!image) t &= 0177;          /* Handle parity */
        if (t == SOH) continue;         /* Resynchronize if SOH */
        done = TRUE;                    /* Got checksum, done */
    }

     
                                        /* Fold in bits 7,8 to compute */
    chksum = (((chksum&0300) >> 6)+chksum)&077; /* final checksum */
     
    if (chksum != unchar(t)) 
    {
	if (debug)
	{
		MDrawString("\n\rBad Checksum: ");
		printnum(unchar(t));
	}
	type=FALSE;
    }
    if (debug)
    {
	MDrawString("\n\rHere's the data:\n\n\r");
	MDrawString(data);
	do
	{
		GetNextEvent(everyEvent,&myEvent);
	}
	while(myEvent.what !=mouseDown);
	SelectWindow(theWindow);
	SetPort(theWindow);
    }

     
    return(type);                       /* All OK, return packet type */
}

/*
 *  s p a c k
 *
 *  Send a Packet
 */
     
spack(type,num,len,data)
char type, *data;
int num, len;
{
    int i, count;                            /* Character loop counter */
    char chksum, buffer[100];           /* Checksum, packet buffer */
    register char *bufp;                /* Buffer pointer */
    bufp = buffer;                      /* Set up buffer pointer */
		/* Issue any padding */
    for (i=1; i<=pad; i++) FSWrite(outnum,&count, &padchar); 
     
    *bufp++ = SOH;                      /* Packet marker, ASCII 1 (SOH) */
    *bufp++ = tochar(len+3);            /* Send the character count */
    chksum  = tochar(len+3);            /* Initialize the checksum */
    *bufp++ = tochar(num);              /* Packet number */
    chksum += tochar(num);              /* Update checksum */
    *bufp++ = type;                     /* Packet type */
    chksum += type;                     /* Update checksum */
     
    for (i=0; i<len; i++)               /* Loop for all data characters */
    {
        *bufp++ = data[i];              /* Get a character */
        chksum += data[i];              /* Update checksum */
    }
    chksum = (((chksum&0300) >> 6)+chksum)&077; /* Compute final checksum */
    *bufp++ = tochar(chksum);           /* Put it in the packet */
    *bufp = eol;                        /* Extra-packet line terminator */
    count=bufp-buffer+1;
    FSWrite(outnum,&count, buffer); /* Send the packet */
	if (debug)
	{
	SelectWindow(DebugWindow);
	SetPort(DebugWindow);
	EraseRect((Rect *) inrect);
	MoveTo(50,12);
	MDrawString("Packet To Be Sent Out\n\n\r");
	MDrawString("Packet Number: ");
	printnum(num);
	MDrawString("\n\rPacket Type: ");
	MDrawChar(type);
	MDrawString("\n\rCharacter Count: ");
	printnum(len+3);
	MDrawString(" \n\rThe Packet: \n\n\r");
	MDrawString( buffer );
	do
	{
		GetNextEvent(everyEvent,&myEvent);
	}
	while(myEvent.what !=mouseDown);
	SelectWindow(theWindow);
	SetPort(theWindow);
	}
}
     

/*
 *  b u f i l l
 *
 *  Get a bufferful of data from the file that's being sent.
 *  Only control-quoting is done; 8-bit & repeat count prefixes are
 *  not handled.
 */
     
bufill(buffer)
char buffer[];                          /* Buffer */
{
    int i;                              /* Loop index */
    char t;				/* Character read from file */
    int 
        t7,                             /* 7-bit version of above */
	count=1;
    i = 0;                              /* Init data buffer pointer */
    while( FSRead(fp,&count,&t) != eoFErr)        /* Get the next character */
    {
        t7 = t & 0177;                  /* Get low order 7 bits */
     
        if (t7 < SP || t7==DEL || t7==quote) /* Does this char require */
        {                                   /* special handling? */
    	    if(t7==CR && macwrite)	/* Map CR to LF if macwrite */
		t=t7='\n';
            if (t=='\n' && !image) 
            {                           /* Do LF->CRLF mapping if !image */
                buffer[i++] = quote;
		buffer[i++]=ctl('\r');
            } 
            buffer[i++] = quote;        /* Quote the character */
            if (t7 != quote)
            {
                t = ctl(t);             /* and uncontrolify */
                t7 = ctl(t7);
            }
        }
        if (image)
            buffer[i++] = t;            /* Deposit the character itself */
        else
            buffer[i++] = t7;
        if (i >= spsiz-8) return(i);    /* Check length */
    }
    if (i==0) 
		return(EOF);              /* Wind up here only on EOF */
    return(i);                          /* Handle partial buffer */
}
     
     
/*
 *      b u f e m p
 *
 *  Put data from an incoming packet into a file.
 */
     
bufemp(buffer,len)
char  *buffer;                         /* Buffer */
int   len;                              /* Length */
{
    int i,err,count=1;                              /* Counter */
    char t;                             /* Character holder */
     
    for (i=0; i<len; i++)               /* Loop thru the data field */
    {
        t = buffer[i];                  /* Get character */
        if (t == MYQUOTE)               /* Control quote? */
        {                               /* Yes */
            t = buffer[++i];            /* Get the quoted character */
            if ((t & 0177) != MYQUOTE)  /* Low order bits match quote char? */
                t = ctl(t);             /* No, uncontrollify it */
        }
        if (t=='\n' && macwrite)	/* If macwrite filter out LFs */
            continue;
	err=FSWrite(fp,&count,&t);	/* Write out the character */
	if (err != noErr)
		fatal("Error in writing :", err);
	}
}
     
     
/*
*   SPAR:
*		Fill a packet with our communication paramaters 
*/

spar(data)
char data[];
{
	data[0]=tochar(MAXPACKSIZ);
	data[1]=tochar(MYTIME);
	data[2]=tochar(MYPAD);
	data[3]=ctl(MYPCHAR);
	data[4]=tochar(MYEOL);
	data[5]=MYQUOTE;
}


/*
*   RPAR:
*		Read in the other kermits paramaters
*/

rpar(data)
char data[];
{
	spsiz=unchar(data[0]);
	timint=unchar(data[1]);
	pad=unchar(data[2]);
	padchar=ctl(data[3]);
	eol=unchar(data[4]);
	quote=data[5];
}


/*
*  Printerr:
*	Display error message and number in standard error box
*/

printerr(str,err)
char *str;
int err; 
{
	int i;
	char error[10];

	if (err) /* Err=0 signals message only */
	{
		/* Make sure string will be null terminated */
		for(i=0;i<10;error[i++]='\0');

		NumToString(err,error);  /* Convert err number */
		ParamText(str,error,"",""); /* Insert strings into error box */
	}
	else
		ParamText(str,"","","");
	StopAlert(1,(ProcPtr) NULL);

}


/*
*   Fatal:
*	Printerr, close file, and exit to shell.
*/

fatal(str,err)
char *str;
int err;
{
	printerr(str,err);

	if(fp!=NULL)
	{
		FSClose(fp);
		FlushVol(volname,volnum);	
	}
	ExitToShell();
}

/*
*   Printnum:
*	Print out number as an ascii string.
*/

printnum(num)
int num;
{
	int i;
	char numstr[10];

	/* Make sure string will be null terminated. */

	for(i=0;i<10;numstr[i++]='\0');
	NumToString(num,numstr);
	DrawString(numstr);

}

draw_tabs()
{
	int index,tabindex,lin;

	MoveTo(CHARWIDTH,TOPMARGIN);
	for(index=1;index<=(RIGHTMARGIN-LEFTMARGIN)/CHARWIDTH;index++)
	{
		for (tabindex=0;tabindex<NUMTABS;tabindex++)	
			if(tabs[tabindex]==index)
			{
				DrawChar('T');
				Move(-CharWidth('T'),0);
				break;
			};
		Line(CHARWIDTH,0);
		lin=3+3*((index%5)==0);
		Line(0,-lin);
		Move(0,lin);
	}
}

change_tabs()
{
	int ind;
	Handle tabstring;

	clear_screen();
	draw_tabs();
	FlushEvents(everyEvent,0);
	for (;;)
	{
		GetNextEvent(everyEvent, &myEvent);
		if (myEvent.what==mouseDown)
		{
			GlobalToLocal(&myEvent.where);
			for(ind=0;ind<NUMTABS;ind++)
			{
				if(((myEvent.where.h-tabs[ind]*CHARWIDTH)<CHARWIDTH)&&
				   (myEvent.where.h-tabs[ind]*CHARWIDTH>0))
				{
					SysBeep(3);
					tabs[ind]=0;
					break;
				}
			}
			if (ind>=NUMTABS)
				for(ind=0;ind<NUMTABS;ind++)
				 	if(tabs[ind]==0)
					{
						tabs[ind]= myEvent.where.h/(int) CHARWIDTH;
						break;
					}
		clear_screen();
		draw_tabs();
		}
		else if(myEvent.what==keyDown)
			break;
	};
	tabstring=GetResource("TABS",0);
	stringcpy(*tabstring,tabs);
	ChangedResource(tabstring);
	WriteResource(tabstring);
}

/*
*	open_file  -- used to open the file for reading
*	   returns value of refNum if successful, NULL if not
*	   returns file name in fNamePtr if successful, NULL if not
*/

int open_file (fNamePtr)	/*returns refNum if file opened, NULL if not*/
char fNamePtr[];
{
	SFReply reply;
	Point where;
	int i, err=0, length, refNum;

	/*position of dialog box*/
	SetPt (&where,30,30);

	/*initializes fNamePtr to NULLs for safety*/
	for (i=0; i<64; i++)
		reply.fName[i] = NULL;

	/*handles user interface*/
	SFGetFile (&where, NULL, NULL, -1, NULL, NULL, &reply);

	/*
	*	convert Pascal string to C string --
	*	remove leading number and add a trailing NULL
	*/
	length = reply.fName[0];
	for (i=0; i< length; i++)
		fNamePtr[i] = reply.fName[i + 1];
	fNamePtr[i] = NULL;

	for (i=0; i< length; i++)
		reply.fName[i] = reply.fName[i + 1];
	reply.fName[i] = NULL;

	if (reply.good)		/*user did not press Cancel Button*/
		{
		err = FSOpen (reply.fName, (int) reply.vRefNum, &refNum);
		if(err)
			printerr("Bad SFgetopen",err);
	}

	else			/*user did press Cancel Button*/
		{
		refNum = NULL;
		fNamePtr[0] = NULL;
		}
	
	return (refNum);
}
!Funky!Stuff!
echo x - kermit.h
cat >kermit.h <<'!Funky!Stuff!'
/*
* File kermit.h
*
* Symbol definitions and global variable for kermit.
*/

#include <stdio.h>     		/* Standard io definitions */
#include "mac/quickdraw.h"	/* Macintosh C interface */
#include "mac/osintf.h"
#include "mac/toolintf.h"
#include "mac/packintf.h"
#include "mac/libmac.h"
#include "controls.h"		/* Constants for terminal settings */

     
     
     
     
     
/* Symbol Definitions */
     
#define MAXPACKSIZ  94      /* Maximum packet size */
#define SOH         1       /* Start of header */
#define CR          13      /* ASCII Carriage Return */
#define SP          32      /* ASCII space */
#define DEL         127     /* Delete (rubout) */
     
#define MAXTRY      20      /* Times to retry a packet */
#define MYQUOTE     '#'     /* Standard quoting character */
#define MYPAD       0       /* Number of padding characters I will need */
#define MYPCHAR     0       /* Padding character I need (NULL) */
#define MYEOL       '\n'    /* End-Of-Line character I need */
#define MYTIME      10      /* Seconds after which I should be timed out */
#define MAXTIM      60      /* Maximum timeout interval */
#define MINTIM      2       /* Minumum timeout interval */
     
#define TRUE        -1      /* Boolean constants */
#define FALSE       0

#define	numMenu	    3	    /* Number of menus in program */
#define filemenu    256     /* Reference numbers for menus */
#define modemenu    257
#define optionmenu  255
#define VIS         1       /* Sets window visisble */
#define CONNECT     1       /* Menu item constants */
#define TRANSMIT    2
#define RECEIVE     3
#define CONTROLS    4
#define IMAGE       1
#define MACWRITE    2
#define EXECUTABLE  3
#define FILENAMECNV 4
#define DEBUG	5
#define MYBUFSIZE   2048
#define BUFSIZE	    1024
#define noErr	    0       /* Macintosh error codes */
#define eoFErr      (-39)
#define TOPMARGIN 10	    /* Terminal display constants */
#define BOTTOMMARGIN 286
#define LEFTMARGIN 12
#define RIGHTMARGIN 480
#define CHARWIDTH 6
     
     
/* Macro Definitions */
     
/*
 * tochar: converts a control character to a printable one by adding a space.
 *
 * unchar: undoes tochar.
 *
 * ctl:    converts between control characters and printable characters by
 *         toggling the control bit (ie. ^A becomes A and A becomes ^A).
 */
#define tochar(ch)  ((ch) + ' ')
#define unchar(ch)  ((ch) - ' ')
#define ctl(ch)     ((ch) ^ 64 )
     
     
/* Global Variables */
     
int     size,               /* Size of present data */
        spsiz,              /* Maximum send packet size */
        pad,                /* How much padding to send */
        timint,             /* Timeout for foreign host on sends */
        n,                  /* Packet number */
        numtry,             /* Times this packet retried */
        oldtry,             /* Times previous packet retried */
        filnamcnv = TRUE,   /* -1 means do file name case conversions */
	fp,		    /* Data file reference number (file pointer) */
	innum,		    /* Input driver refnum */
	outnum,		    /* Output driver refnum */

		            /* Mode of data transmission */
        image=FALSE ,       /* 8-bit transmission */
	macwrite=FALSE,     /* Macwrite compatible */
	exec=FALSE,         /* Executable resource file */
	volnum,
    	debug=FALSE,
	flowctl=TRUE;
     
char    state,              /* Present state of the automaton */
        padchar,            /* Padding character to send */
        eol,                /* End-Of-Line character to send */
        escchr,             /* Connect command escape character */
        quote,              /* Quote character in incoming data */
	volname[256],       /* Current volume name */
        filnam[80],            /* Current file name */
        recpkt[MAXPACKSIZ], /* Receive packet buffer */
        packet[522],        /* Packet buffer */
	inbuff[BUFSIZE],    /* buffer for characters not yet printed */
	*endbuff=sizeof(char)*BUFSIZE+inbuff, /* End of buffer */
	mybuff[MYBUFSIZE];  /* Serial drivers new buffer */

MenuHandle myMenus[numMenu];
EventRecord myEvent;
WindowPtr theWindow, whichWindow, ControlWindow,DebugWindow,TabWindow;
WindowRecord wRecord, CWRecord,DWRecord,TWRecord;
			     /* Rectangles */
short wirect[]={40,5,335,505}, /* Kermit window in global */
	inrect[]  = { 0, 0, 390, 500}, /* and local coords */
	myrect[]= {190,80,210,150},    /* To erase packet number */
	ControlRect[]={70,40,280,480}, /* Controls window */
	DebugRect[]={40,15,180,500},
	TabRect[]={310,5,350,505};
OpParamType controlparam;      /* To change serial driver paramaters */
ControlHandle baud,bits,parity,stopb,xon,online,theControl;
short config;  		       /* Current Serial driver configuration */

/* Tab settings */
#define NUMTABS 10

char tabs[NUMTABS];
!Funky!Stuff!
echo x - kermit.rc
cat >kermit.rc <<'!Funky!Stuff!'
*
* File kermit.rc
*
* This file contains the resources for rmaker for the kermit program

* Specify output file of executable code

kermit.rsrc

Type TABS = STR
  ,0
0123456789

* Error box

Type ALRT
,1
90 50 180 400
2
5555

Type DITL
,2
2
BtnItem Enabled
65 20 85 60
OK

StatText Disabled
65 80 85 400
Error: ^0  ^1

* Terminal controls

* Baud rate

Type CNTL
,0
300
30 10 45 60
Visible
2
0
0 0 1

Type CNTL
,1
600
50 10 65 60
Visible
2
1
0 0 1

Type CNTL
,2
1200
70 10 85 60
Visible
2
2
0 0 1


Type CNTL
,3
1800
90 10 105 60
Visible
2
3
0 0 1


Type CNTL
,4
2400
110 10 125 60
Visible
2
4
0 0 1

Type CNTL
,5
3600
130 10 145 60
Visible
2
5
0 0 1

Type CNTL
,6
4800
150 10 165 60
Visible
2
6
0 0 1

Type CNTL
,7
7200
170 10 185 60
Visible
2
7
0 0 1

Type CNTL
,8
9600
30 100 45 150
Visible
2
8
1 0 1

Type CNTL
,9
19200
50 100 65 150
Visible
2
9
0 0 1

Type CNTL
,10
57600
70 100 85 150
Visible
2
10
0 0 1

* Bits sent and received

Type CNTL
,11
8
30 190 45 240
Visible
2
11
1 0 1

Type CNTL
,12
7
50 190 65 240
Visible
2
12
0 0 1

* Parity

Type CNTL
,13
even
110 190 125 240
Visible
2
13
0 0 1

Type CNTL
,14
odd
130 190 145 240
Visible
2
14
0 0 1

Type CNTL
,15
none
150 190 165 240
Visible
2
15
1 0 1

* Stop bits

Type CNTL
,16
1
30 280 45 330
Visible
2
16
1 0 1

Type CNTL
,17
2
50 280 65 330
Visible
2
17
0 0 1

* Xon/Xof flow control

Type CNTL
,18
yes
110 280 125 330
Visible
2
18
1 0 1

Type CNTL
,19
no
130 280 145 330
Visible
2
19
0 0 1

Type CNTL
,20
ON LINE
110 100 125 170
Visible
2
20
1 0 1

Type CNTL
,21
LOCAL
130 100 145 170
Visible
2
21
0 0 1

Type CNTL
,22
Tabs
170 280 185 330
Visible
1
22
0 0 0
* Exit

Type CNTL
,23
exit
160 360 190 420
Visible
0
23
0 0 0


* Compiled program code

Type CODE
  b.out,0


!Funky!Stuff!
echo x - kermitdoc
cat >kermitdoc <<'!Funky!Stuff!'
Mackermit Information:

This is an "alpha" release of the program.  Much of the code is sloppy
and buggy, but I believe that all the program's funadmentals work 
adequately.  This program is cheifly an adaptation of Columbia's Unix
kermit.  The kermit file transfer system is a communication between two 
running programs, one on each computer.  This document is minimal 
information concerning the operation of the Macs end of the program.  
Please see your systems manual pages for kermits operation there.


Any questions or comments can be mailed over the networks to 
engel@harvard.

Kermit is a file transfer program, and has four main modes, which can
be selected by clicking the appropriate item on the mode menu.  

Controls:

This mode allows the user to change the  type of communication to
adequately interface with the other computer.  Selecting this option
and pressing the appropriate buttons on the screen  changes the baud
rate, the number of bits passed, the number  of stop bits, and the type
of parity, if any.


Connect:

Connect mode allows kermit to behave as a dumb terminal.   Control 
characters can be sent using the clover leaf key.  
The enter key sends a break across.  The terminal is 
hardwired for 9600 baud eight bit, one stop bit connection.    Clicking the 
mouse button exits this mode, and breaks communication.  Occasional
characters may be lost.

Transmit:

In this mode the Mackermit sends a file  to the other machine.  After
sending some initial data, it will prompt the suer for a filename.  At
this time the file entered must be on the disk in the internal drive.
The filename must also be compatible with the receiving machine.  
All filenames are translated to lower case, and any spaces are removed.
Once communicating Mackermit prints out a few status messages during 
transmission.






                                                        2



Receive:

Mackermit receives files sent over by other kermits.  They can be of any
name with the exception of names already used on the Mac's 
current disk.

Protocol:

     To send (upload) files:

     Run Mackermit.  Select connect mode, and login to the appropriate
     machine.  Note that this machine must have a working kermit.
     Type to the shell, "kermit r  <cr> ".  Hit the mouse button to exit
     connect mode.  Select the appropriate option. (see below)  Select
     transmit mode.  A prompt for filename will appear.  Enter the
     appropriate one.  Mackermit will report either success or failure.

To receive (download) files:

      Do the same as above until typeing kermit r to the shell.  Instead
      type "kermit s filname <cr> ", where filname can be any readable
      file.  Disconnect by hitting the mouse button, select the appropriate 
     option, and finally select receive mode.  Again mackermit will
     report either success or failure.


Options:

The options menu concerns how kermit handles carriage returns in  files.
The macwrite mode allows kermit to handle those type files nicely,
by mapping CRs to NLs on sending, and doing the reverse upon receiving
a file.  The image mode is a holdover from the Columbia version.  If it
is turned off, NL is mapped to CRLF on output, and CRs are ignored on
input.  If it is selected, NLs and CRs are left alone.   Selecting image
also allows eight data bits to be sent or received. Note that macwrite
may be selected along with an image option, but that its operations are 
executed first.  The third mode, executable, allows the
transfer of executable Macintosh code .  If selected, it places the data
passed over into the resource fork of an "application" file.  If the code
passed over was valid Macintosh machine code and resource data, the
file can be successfully run on the mac.   The code must be sent
over by the non-mac kermit as eight bit data, however.  This is why selecting
executable always also selects image.


Additional features (Kermit 1.1):

	The terminal mode now accepts many escape sequence, and feebly emulates
	a VT100

	Debug option may now be selected.  This causes information about
	all packets sent and received to be displayed.  Each packet
	is displayed until the mouse button is pressed.

	Pressing a key during execution causes the last packet to be
	resent.  This is useful if, for example, a packet length comes
	across distorted, and Mackermit waits for characters that are not
	coming.

!Funky!Stuff!
echo x - term.c
cat >term.c <<'!Funky!Stuff!'
/*
* FILE Term.c
*
* Module of mackermit: contains code for the terminal simulation
* routine.
*/

#include "term.h"  /* Terminal routine constants */

/*
 *  c o n n e c t
 *
 *  Establish a virtual terminal connection with the remote host.
*/

connection()
{
	register int index;
        int halt=FALSE,err=0,printit(), fixbuffer();
	char *addptr();


	initchars(); /* Set up the keyboard configuration */

	dumptr=NewRgn();
	PenMode(patXor);
	if (GetCRefCon(online)==21)
	{
		local();
		return;
	}
	flushio(); /* Get rid of pending characters */
	sendbreak();


	for(;;)
	{

/* Read until buffer is estimated full */
/* or until no input is immeadiately pending. */

	
			inparam.ioBuffer=inbuff;
			inparam.ioReqCount = BUFSIZE;
			PBRead(&inparam,TRUE);


/* Print and read keyboard until new input arrives */

		do
		{
			GetNextEvent(everyEvent, &myEvent);
			if ((myEvent.what==keyDown)||(myEvent.what==autoKey))
			{
				outchar= (myEvent.message & 0xff);

				/* Enter is break key */
				if(outchar==3)
				{
					sendbreak();
					flushio();
				}
				
				/* Translate typed characters */
				/* Clover leaf key is control */
				if(myEvent.modifiers&256)
					outchar=contcharmap[outchar];
				else
					outchar=charmap[outchar];
				err=PBWrite(&outparam,TRUE);
				if(err)
					printerr("Bad Writeout:",err);
			}

			/* Mouse button exits connect mode */
			else if(myEvent.what==mouseDown)
			{
				flushio();
				return;
				}
			if(*outp != 255)
			{
				if(halt<3)
					if(*addptr(outp,FULLENOUGH)!=255)
					{
						halt++;
						PBWrite(&xoffparam,FALSE);
					};
				printit();
			}
			else if (halt)
			{
				halt=FALSE;
				outchar=controlq;
				PBWrite(&outparam,TRUE);
			}
		}
		while  (inparam.ioResult!=0);
	}
}

/*
*  Printit:
*	Draws character and updates buffer
*/
	
printit()
{
	PFI funp, lookup();

	Line(-CHARWIDTH,0);	/* Erase Cursor */
	*outp &=0177;

	if (begptr!=NULL)	/* Are we in an escape sequence? */
	{
		if(funp=lookup(*outp,singescapetable,NUMSINGESCS))
			(*funp)();	/* Do escape sequence function */
		*begptr=255;
		*(++begptr)=255;
		begptr=NULL;
	}
	else if(escseq!=NULL)
	{
		if(funp=lookup(*outp,escapetable,NUMMULTESCS))
		{
			begptr=escseq;
			endptr= outp;
			(*funp)();	/* Do escape sequence function */
			for(;begptr<=endptr;begptr++)
				*begptr=255;
			escseq=NULL;
			begptr=NULL;
			endptr=NULL;
		}
	}
	else if(*outp==ESCAPE)
		begptr=outp;
	else
	{	
		MDrawChar(*outp);
		*outp=255;
		}
	Line(CHARWIDTH,0);	/* Redraw Cursor */
	if(++outp>=endbuff)   /* Find next character to be printed */
		outp=inbuff;
}	


/*
*  Fixbuffer:
*	Update input character buffer.  This is the completion routine for
*	the aynchronous reads.
*/

fixbuffer()
{	

}


	
/*
 *      KERMIT utilities.
 */
  
/*
*   Lookup:
*	Lookup a given character in the apropriate character table, and
*	return a pointer to the appropriate function, if it exists.
*/

 PFI
 lookup(data,table,length)
 char data;
 CMD table[];
 int length;
 {
 	for(length-1;length>=0;length--)
 	{
 		if(table[length].name==data)
 			return(table[length].funct);
 	};
 	return((PFI)NULL);
 }


/*
*   Flushio:
*	Initialize some communications constants, and clear screen and
*	character buffers.
*/

flushio()
{
	int i,err;



	for (i=0;i<=BUFSIZE;i++)
		inbuff[i]=255;
	controls='\023';
	controlq='\021';
	numinbuffer=0;
	begptr=NULL;
	endptr=NULL;
	escseq=NULL;
	outp=inbuff;
	err=KillIO(-6);
	if (err)
		printerr("Bad input clear",err);
	err=KillIO(-7);
	if(err)
		printerr("Bad ouput clear",err);

/* Initialize the asynchronous io paramater blocks */
	
	SetupIO();
	inparam.ioCompletion=fixbuffer;
	inparam.ioRefNum=innum ;
    	inparam.ioActCount=0;
	inparam.ioPosMode=0;
	inparam.ioPosOffset=0;
	inparam.ioResult=0;
	inparam.ioBuffer= inbuff;

	xoffparam.ioCompletion=(ProcPtr) NULL;
	xoffparam.ioRefNum=outnum;
    	xoffparam.ioReqCount=1;
	xoffparam.ioPosMode=0;
	xoffparam.ioPosOffset=0;
	xoffparam.ioBuffer= &controls;

	outparam.ioCompletion = (ProcPtr) NULL;
	outparam.ioRefNum=outnum;
	outparam.ioReqCount=1;
	outparam.ioPosMode=0;
	outparam.ioPosOffset=0;
	outparam.ioBuffer= &outchar;

	home_cursor();

	EraseRect((Rect *)inrect);

}	

   
/*
*  Sendbreak:
*	Sends a break across the communications lines.
*/
 
sendbreak()
{ 
	int xx;

	Control(outnum,12,&controlparam);
	for( xx=0; xx<150;xx++)
		;
	Control(outnum,11,&controlparam);
}

     
MDrawChar(chr)
char chr;
{
	PFI funp;

	/* If its a control char, do the apropriate function. */	
	

	if(chr<'!')   /* Space is a control character, because mac's space
		         does not wipe out the character beneath it. */
	{ 
			
		if (funp=lookup( chr,controltable,NUMSINGCMDS))
			(*funp)();
	}
	else
	{
		GetPen (&p);
		if(p.h>RIGHTMARGIN)
		{
		overrun=FALSE;
			carriage_return();
			line_feed();
			overrun=TRUE;
		}
		if(insert)
			insert_char();
		space();
		back_space(); 
		DrawChar(chr);
	}
}	

MDrawString(str)
char *str;
{
	while (*str)
		MDrawChar(*str++);
}


/* Increment pointer in circular buffer */
char *
addptr(ptr,num)
char *ptr;
int num;
{

	ptr+=num;
	if(ptr>endbuff)
		ptr=(char* )((long)ptr-BUFSIZE);
	else if(ptr<inbuff)
		ptr=(char *)((long)ptr+BUFSIZE);
	return(ptr);
}



myatoi(strptr)
char *strptr;
{
	int sign=1, n=0;

	if(*strptr=='+'||*strptr=='-')
		sign=(*strptr++=='+') ? 1 : -1;

	while( *strptr>='0'&& *strptr<='9')
		{
			n=10*n+*strptr-'0';
			if(++strptr>endbuff)  
				strptr=inbuff;
		};
	return(sign*n);
}
	
local()
{
	char *charp=inbuff;

	flushio();
	SysBeep(10);
	for(;;)
	{
		
			GetNextEvent(everyEvent, &myEvent);
			if ((myEvent.what==keyDown)||(myEvent.what==autoKey))
			{
				GetKeys(&theKeys);
				outchar= (myEvent.message & 0xff);

				
				/* Translate typed characters */
				/* Clover leaf key is control */
				if(theKeys.kmap[1] & (long)  0x8000) 
					outchar=contcharmap[outchar];
				else
					outchar=charmap[outchar];
				*charp=outchar;
				numinbuffer++;
				charp=addptr(charp,1);
				printit();
			}
			else if (myEvent.what==mouseDown)
				return;
	};
}



/*
* Initchars:
*	Do a mock reconfiguration of the keyboard by setting
*	values in an array of ascii chars.
*/

initchars()
{
	int index;


	/* Start with all charcters normal */
	for(index=0;index<256;index++)
	{
		charmap[index]=index;
		contcharmap[index]=index;
	};
	
	charmap['\140']='\033'; /* Replace ` with escape */
	charmap['\010']='\177'; /* Replace backspace with delete */

	/* Insert control characters */
	for(index='a';index<='z';index++)
		contcharmap[index] -=96;

}


/*
*	Control character functions:
*		Each of the following allow the mac to simulate
*		the behavior of a terminal when given the proper
*		control character.
*/

int
back_space()
{
	GetPen( &p);
	if (p.h-CHARWIDTH>0)
		Move(-CHARWIDTH,0);
	else
		carriage_return();
}

int
space()
{
	/* Regular quickdraw space does not cover previous characters */
	Point p; 

	GetPen(&p);
	currect[0]=p.v-LINEHEIGHT+3;
	currect[1]=p.h;
	currect[2]=p.v+3;
	currect[3]=p.h+CHARWIDTH;
	if (invert)
		FillRect((Rect* ) currect,&QD->black);
	else
		EraseRect((Rect*) currect);
	Move(CHARWIDTH,0);

}



tab()
{
	int index, tabstop, remember=999;

	GetPen(&p);

	for(index=0;index<NUMTABS;index++)
	{
		tabstop=(tabs[index])*CHARWIDTH;
		if (tabstop>p.h)
			if(tabstop<remember)
				remember=tabstop;
	};
	if (remember !=999)
		MoveTo(remember,p.v);
}


line_feed()
{
	if(!overrun)
	{
		GetPen(&p);
		if(p.v>=bottommargin)
		{
			ScrollRect((Rect *)scrollrect,0,-LINEHEIGHT,dumptr);
			EraseRgn(dumptr);
		}
		else
			MoveTo(p.h,p.v+LINEHEIGHT);
	}
	else
		overrun=FALSE;
}

carriage_return()
{
	GetPen(&p);
	MoveTo(LEFTMARGIN,p.v);
}

clear_screen()
{
	EraseRect((Rect *)inrect);
}


home_cursor()
{
	MoveTo(LEFTMARGIN,TOPMARGIN);
}


bell()
{
	SysBeep(3);
}





/* Esacpe Sequence Functions---basically behave like vt100 */

clear_line()
{
	GetPen(&p);

	currect[0]= p.v-LINEHEIGHT;
	currect[1]=p.h-CHARWIDTH;
	currect[2]=p.v+3;
	currect[3]=RIGHTMARGIN;

	EraseRect((Rect*) currect);


}

int
erase_display()
{
	int ermode;

	ermode=myatoi(addptr(begptr,2));
	GetPen(&p);

	switch(ermode)
	{
		case 0:
			clear_line();
			currect[0]=p.h+3;
			currect[1]=LEFTMARGIN;
			currect[2]=BOTTOMMARGIN;
			currect[3]=RIGHTMARGIN;
			EraseRect((Rect *) currect);
		case 1:
			currect[0]=p.v-LINEHEIGHT;
			currect[1]=LEFTMARGIN;
			currect[2]=p.v+3;
			currect[3]=p.h;
			EraseRect((Rect *) currect);
			currect[0]=TOPMARGIN;
			currect[2]=p.v-LINEHEIGHT;
			currect[3]=RIGHTMARGIN;
			EraseRect((Rect *) currect);
			break;
		case 2:
			clear_screen();
			break;
		}
}

			
cursor_right()
{
	Move(CHARWIDTH,0);
}

set_scroll_region()
{
	char *tmptr;
	int t,b;
	for(tmptr=begptr;*tmptr!=';';)
	{
		if (tmptr==endptr)
			return;
		tmptr=addptr(tmptr,1);
	};
	t=myatoi(addptr(begptr,2));
	b=myatoi(addptr(tmptr,1));
	if(t<=0)
		t=1;
	if (b<=0)
		b=1;
	topmargin= --t*LINEHEIGHT+TOPMARGIN;
	bottommargin= TOPMARGIN+(--b)*LINEHEIGHT;
	scrollrect[0]= (--t)*LINEHEIGHT+TOPMARGIN;
		if(scrollrect[0]<=0)
			scrollrect[0]=0;
	scrollrect[2]=bottommargin+LINEHEIGHT;
}

cursor_position()
{
	int hort=0,vert=0;
	char *tmptr;

	for(tmptr=begptr;*tmptr!=';';)
	{
		if (tmptr==endptr)
		{
			home_cursor();
			return;
		}
		tmptr=addptr(tmptr,1);
	};
	vert=myatoi(addptr(begptr,2));
	hort=myatoi(addptr(tmptr,1));

	if(--hort<0)
		hort=0;
	if(--vert<0)
		vert=0;	
	MoveTo(LEFTMARGIN+hort*CHARWIDTH,TOPMARGIN+vert*LINEHEIGHT);
}



text_mode()
{
	int style=myatoi(addptr(begptr,2));

	switch(style)
	{
		case 0: 
			invert=FALSE;
			textstyle=0;
			TextFace(0);
			TextMode(srcOr);
			break;
		case 1:
			textstyle +=boldStyle;
			TextFace(textstyle);
			break;
		case 4:
			textstyle+=underlineStyle;
			TextFace(textstyle);
			break;
		case 7:
			invert=TRUE;
			TextMode(srcBic);
			break;
		case 22:
			if (textstyle>=boldStyle)
			{
				TextFace(textstyle-boldStyle);
				textstyle -= boldStyle;
			}
			break;
		case 24:
			if (textstyle>=underlineStyle)
			{
				TextFace(textstyle-underlineStyle);
				textstyle -= underlineStyle;
			}
			break;
		case 27:
			invert=FALSE;
			TextMode(srcOr);
			break;
		default:;
	}
}	

reverse_line_feed()
{
	GetPen(&p);

	
	if(p.v<=topmargin)
	{
		ScrollRect((Rect *)scrollrect,0,LINEHEIGHT,dumptr);
		EraseRgn(dumptr);
	}
	else
		MoveTo(p.h,p.v-LINEHEIGHT);
}

insert_delete_line()
{
	char mode = *endptr;
	int scrolldir;
	int numlin=myatoi(addptr(begptr,2));

	if (numlin==0)
		numlin=1;

	currect[1]=LEFTMARGIN;
	currect[2]=bottommargin;
	currect[3]=RIGHTMARGIN;

	GetPen(&p);
	if(numlin> (bottommargin-p.v)/LINEHEIGHT)
		numlin=(int) ((bottommargin-p.v)/LINEHEIGHT);

	if (mode=='L')
	{
		scrolldir=LINEHEIGHT;
		currect[0]=p.v-LINEHEIGHT+3;
	}
	else
	{
		scrolldir= -LINEHEIGHT;
		currect[0]=p.v-LINEHEIGHT+3;
	}

	while(numlin--)
	{
		ScrollRect((Rect *)currect,0,scrolldir,dumptr);
		EraseRgn(dumptr);
	}
}


delete_char()
{
	int numchars=myatoi(addptr(begptr,2));

	if (numchars==0)
		numchars=1;

	GetPen(&p);

	currect[0]=p.v-LINEHEIGHT+3;
	currect[1]=p.h;
	currect[2]=p.v+3;
	currect[3]=RIGHTMARGIN;

	if(numchars> (RIGHTMARGIN-p.h)/CHARWIDTH)
		numchars =(int) ((RIGHTMARGIN-p.h)/CHARWIDTH);


	while(numchars--)
	{
		ScrollRect((Rect *)currect,-CHARWIDTH,0,dumptr);
		EraseRgn(dumptr);
	}
}

insert_char()
{
	GetPen(&p);
	
	currect[0]=p.v-LINEHEIGHT+3;
	currect[1]=p.h;
	currect[2]=p.v+3;
	currect[3]=RIGHTMARGIN;

	ScrollRect((Rect *) currect, CHARWIDTH,0,dumptr);
	EraseRgn(dumptr);
}

insert_mode()
{
	int mode=myatoi(addptr(begptr,2));
	if(mode==4)
	insert=TRUE;
}

end_insert_mode()
{
	int mode=myatoi(addptr(begptr,2));
	if(mode==4)
	insert=FALSE;
}

unknown()
{
	for(;begptr!=endptr;begptr=addptr(begptr,1))
	{
		MDrawChar(*begptr);
	}
}

dummy()
{
}
csi()
{
	escseq=begptr;
}
!Funky!Stuff!
echo x - term.h
cat >term.h <<'!Funky!Stuff!'

#include <stdio.h>     		/* Standard io definitions */
#include "mac/quickdraw.h"	/* Macintosh C interface */
#include "mac/osintf.h"
#include "mac/toolintf.h"
#include "mac/packintf.h"
#include "mac/libmac.h"
     
#define DEL         127     /* Delete (rubout) */
#define ESCAPE      27      /* Escape character */	
     
#define BUFSIZE 1024
#define TRUE        -1      /* Boolean constants */
#define FALSE       0

#define noErr	    0 	    /* Mac error code */
#define FULLENOUGH  500     /* How long to wait before sending out xoff */
#define TOPMARGIN 10	    /* Terminal display constants */
#define BOTTOMMARGIN 286
#define LEFTMARGIN 12
#define RIGHTMARGIN 480
#define LINEHEIGHT 12
#define CHARWIDTH 6
#define NUMSINGCMDS 9	    /* Number of implemented control character
				   routines */ 
#define NUMMULTESCS  13     /* Number of escape sequences recognised */
#define NUMSINGESCS  6
#define NUMTABS 10
     
     
extern int     
	innum,		    /* Serial driver reference numbers */
	outnum,
	fp,		    /* file refnum */
	flowctl;	    /* xon/xoff flag */


int 
	invert=FALSE,	/* Flag for inverted terminal mode */
	insert=FALSE,
	topmargin=TOPMARGIN,	/* Edges of adjustable window */
	bottommargin=BOTTOMMARGIN,
	textstyle=0,
	overrun=FALSE,
	numinbuffer;	    /* Number of read in characters waiting to be
			       printed */
extern char
	inbuff[],
	tabs[],		/* Tab settings */
	*endbuff;

char
	outchar,	    /* Character sent to host. */
	*outp,		    /* Pointer to next charcter to be printed */
	*begptr,           /* Beginning of escape sequence */
	*endptr,           /* end of escape sequence */
	*escseq,
	charmap[256],	    /* Arrays to "remap" the keyboard */
	contcharmap[256],
	controls,controlq;  /* xon/xoff characters */
extern ControlHandle online;
extern WindowPtr theWindow;
extern EventRecord myEvent;
extern short inrect[];    /* Terminal screen */
short  currect[4],	    /* Rect around current character */
       scrollrect[]  = { 0, 0, 390, 500}; /* Scrolling region */
  /* For terminal asynch. communication */
ioParam xoffparam, outparam, inparam;
extern OpParamType controlparam;
RgnHandle dumptr;	    /* Dummy ptr to satisfy scrollbits */
KeyMap theKeys;		    /* Map of which key has been pressed */
Point p;

/* Structure for the dispatch tables of terminal commands. */

typedef struct cmd_elem{
	char name;
	int(*funct)();
}CMD;

typedef 
int(*PFI)();


/* Terminal function declarations. */

int tab(), back_space(), space(),
	carriage_return(),line_feed(),clear_screen(),
	home_cursor(), bell();

int text_mode(),clear_line(),erase_display(), cursor_position(),cursor_right(),
	set_scroll_region(),reverse_line_feed(), insert_delete_line(),
	dummy(), unknown(),delete_char(),insert_mode(),end_insert_mode(),
	csi();

/* Terminal control character function command table. */

CMD controltable[NUMSINGCMDS]=
{ 
	{'\010',back_space},
	{'\040',space},
	{'\n',line_feed},
	{'\r',carriage_return},
	{'\013',reverse_line_feed},
	{'\014',clear_screen},
	{'\036',home_cursor},
	{'\t',tab},
	{'\007',bell}
};

CMD singescapetable[NUMSINGESCS]=
{
	{'>',dummy},
	{'=',dummy},
	{'8',dummy},
	{'7',dummy},
	{'M',reverse_line_feed},
	{'[',csi}
};

/* Terminal escape sequence function command table */
CMD escapetable[NUMMULTESCS]=
{
	{'\113',clear_line},	/* K */
	{'\110',cursor_position},	/* H */
	{'\112',erase_display},	 /* J */
	{'\103',cursor_right},	/* C */
	{'\101',reverse_line_feed}, /* A */
	{'\155',text_mode},     /* m */
	{'\162',set_scroll_region}, /* r */
	{'M',insert_delete_line},  /* M */
	{'P',delete_char},
	{'h',insert_mode},
	{'l',end_insert_mode},
	{'*',unknown},
	{'L',insert_delete_line}
};

!Funky!Stuff!
