[net.micro.amiga] Amiga terminal driver for MicroEmacs

mic@ut-ngp.UUCP (Mic Kaczmarczik) (04/23/86)

[.... If I had a hammer, I'd hammer on the line eater bug ....]

Greetings,

This file, and two more, comprise an Amiga terminal driver for the
version of MicroEmacs that was posted to the net last week.  I compiled
it with Manx Aztec C, but it should not be that hard to move to Lattice,
since Aztec seems to have more restrictions (16-bit ints, no structure
assignment).

There are two versions of the terminal driver:  one that uses
AmigaDOS for vanilla terminal I/O, and one that uses Intuition
for terminal I/O, mouse clicks, and menu selections.  The
function keys work with both drivers; using the Intuition
driver, clicking the mouse in a window moves dot to that
position, just as if you'd moved to that window and typed ^F
and ^N to get there.

Enjoy!

Mic Kaczmarczik
...!ihnp4!seismo!ut-sally!ut-ngp!mic
cc.kaczmarczik@a20.utexas.edu


-----------------------------CUT HERE----------------------------------------
-h- Readme	Wed Apr 23 08:52:51 1986	Readme
This distribution is in three files, in portable archive format:

	1) System routines (subdirectory sys/amiga).  Abort.c
	   is there because the Aztec library doesn't provide it.
	2) The AmigaDOS terminal interface (subdirectory
	   tty/amigados).  It basically opens a RAW: window
	   and reads characters one at a time from the window.
	3) The Intuition terminal interface (tty/intuition).
	   This one demonstrates the use of the console device
	   with an Intuition window, and translates non-keyboard
	   events into key sequences that the rest of the driver
	   code can later parse.


To get this going, you need the MicroEmacs distribution from
mod.sources.  Unpack this file and one of the terminal drivers into the
main directory, compile and link.

Happy hacking,

Mic Kaczmarczik
UUCP:	...!ihnp4!seismo!ut-sally!ut-ngp!mic
ARPA:	mic@a20.cc.utexas.edu
Mail:	313B W. 35th
	Austin, TX 75752

-h- abort.c	Wed Apr 23 08:52:51 1986	abort.c
/*
 * Name:	MicroEmacs
 *		Amiga dummy abort function
 * Version:	31
 * Created:	18-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 */

extern	int	ttclose();

abort()
{
	ttclose();
	exit(1000);
}

-h- fileio.c	Wed Apr 23 08:52:51 1986	fileio.c
/*
 * Name:	MicroEMACS
 * Version:	31
 *		Commodore Amiga file I/O.
 * Last edit:	15-Apr-86
 * By:		Mic Kaczmarczik
 *		...ihnp4!seismo!ut-sally!ut-ngp!mic
 *
 * Read and write ASCII files. All
 * of the low level file I/O knowledge is here.
 * Pretty much vanilla standard I/O.
 */
#include	"def.h"

static	FILE	*ffp;

/*
 * Open a file for reading.
 */
ffropen(fn)
char	*fn;
{
	if ((ffp=fopen(fn, "r")) == NULL)
		return (FIOFNF);
	return (FIOSUC);
}

/*
 * Open a file for writing.
 * Return TRUE if all is well, and
 * FALSE on error (cannot create).
 */
ffwopen(fn)
char	*fn;
{
	register int	fd;
	FILE *fdopen();

	if ((fd=creat(fn, 0)) < 0
	|| (ffp=fdopen(fd, "w")) == NULL) {
		eprintf("Cannot open file for writing");
		return (FIOERR);
	}
	return (FIOSUC);
}

/*
 * Close a file.
 * Should look at the status.
 */
ffclose()
{
	fclose(ffp);
	return (FIOSUC);
}

/*
 * Write a line to the already
 * opened file. The "buf" points to the
 * buffer, and the "nbuf" is its length, less
 * the free newline. Return the status.
 * Check only at the newline.
 */
ffputline(buf, nbuf)
register char	buf[];
{
	register int	i;

	for (i=0; i<nbuf; ++i)
		putc(buf[i]&0xFF, ffp);
	putc('\n', ffp);
	if (ferror(ffp) != FALSE) {
		eprintf("Write I/O error");
		return (FIOERR);
	}
	return (FIOSUC);
}

/*
 * Read a line from a file, and store the bytes
 * in the supplied buffer. Stop on end of file or end of
 * line. Don't get upset by files that don't have an end of
 * line on the last line; this seem to be common on CP/M-86 and
 * MS-DOS (the suspected culprit is VAX/VMS kermit, but this
 * has not been confirmed. If this is sufficiently researched
 * it may be possible to pull this kludge). Delete any CR
 * followed by an LF.
 */
ffgetline(buf, nbuf)
register char	buf[];
{
	register int	c;
	register int	i;

	i = 0;
	for (;;) {
		c = getc(ffp);
		if (c == '\r') {		/* Delete any non-stray	*/
			c = getc(ffp);		/* carriage returns.	*/
			if (c != '\n') {
				if (i >= nbuf-1) {
					eprintf("File has long line");
					return (FIOERR);
				}
				buf[i++] = '\r';
			}
		}
		if (c==EOF || c=='\n')		/* End of line.		*/
			break;
		if (i >= nbuf-1) {
			eprintf("File has long line");
			return (FIOERR);
		}
		buf[i++] = c;
	}
	if (c == EOF) {				/* End of file.		*/
		if (ferror(ffp) != FALSE) {
			eprintf("File read error");
			return (FIOERR);
		}
		if (i == 0)			/* Don't get upset if	*/
			return (FIOEOF);	/* no newline at EOF.	*/
	}
	buf[i] = 0;
	return (FIOSUC);
}

/*
 * Make a file name for a backup copy.
 */
fbackupfile(fname)
char	*fname;
{
	strcat(fname,"~");
	return (TRUE);
}

/*
 * The string "fn" is a file name.
 * Perform any required case adjustments. All sustems
 * we deal with so far have case insensitive file systems.
 * We zap everything to lower case. The problem we are trying
 * to solve is getting 2 buffers holding the same file if
 * you visit one of them with the "caps lock" key down.
 * On UNIX file names are dual case, so we leave
 * everything alone.
 */
adjustcase(fn)
register char	*fn;
{
	register int	c;

	while ((c = *fn) != 0) {
		if (c>='A' && c<='Z')
			*fn = c + 'a' - 'A';
		++fn;
	}
}
-h- sleep.c	Wed Apr 23 08:52:51 1986	sleep.c
/*
 * Name:	MicroEmacs
 *		AmigaDOS sleep function
 * Version:	31
 * Last Edit:	18-Apr-86
 * Created:	18-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 */

/* There are really 60 ticks/second, but I don't want to wait that 	*/
/* long when matching parentheses... */
#define	TICKS	45
extern	long Delay();

sleep(n)
int n;
{
	if (n > 0)
		Delay((long) n * TICKS);
}
-h- spawn.c	Wed Apr 23 08:52:51 1986	spawn.c
/*
 * Name:	MicroEMACS
 *		Spawn an AmigaDOS subprocess
 * Version:	31
 * Last edit:	17-Apr-86
 * By:		...!ihnp4!seismo!ut-sally!ut-ngp!mic
 */
#include	"def.h"

#include <libraries/dos.h>
#include <libraries/dosextens.h>

/*
 * Create a subjob with a copy
 * of the command intrepreter in it. Since the command
 * interpreter doesn't run in the same window, there's no
 * need for a refresh.  Ah, the beauties of windowing system...
 */
spawncli(f, n, k)
{
        struct FileHandle *newcli, *Open();

        eprintf("[Starting new CLI]");
	newcli = Open("CON:1/1/639/199/MicroEmacs Subprocess",
			MODE_NEWFILE);
	if (newcli == (struct FileHandle *) 0) {
		eprintf("Can't create new CLI window");
		return (FALSE);
	}
        Execute("", newcli, 0L);
        Close(newcli);
        return (TRUE);
}

mic@ut-ngp.UUCP (Mic Kaczmarczik) (04/23/86)

This is the file 2 of 3 of the Amiga code for MicroEmacs.
It contains the AmigaDOS terminal driver.

Mic Kaczmarczik
...!ihnp4!seismo!ut-sally!ut-ngp!mic

-------------------------------amigados.ar------------------------------------
-h- tty.c	Wed Apr 23 08:53:26 1986	tty.c
/*
 * Name:	MicroEMACS
 *		Amiga console device virtual terminal display
 * Version:	31
 * Last Edit:	21-Apr-86
 * Created:	19-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 * Modified:	21-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 *		In ttresize() use ttmove(HUGE,HUGE) and cursor
 *		position report to get the new window size 
 */

#include	"def.h"


#define	BEL	0x07			/* BEL character.		*/
#define	ESC	0x1B			/* ESC character.		*/

extern	int	ttrow;
extern	int	ttcol;
extern	int	tttop;
extern	int	ttbot;
extern	int	tthue;

int	tceeol	=	3;		/* Costs, ANSI display.		*/
int	tcinsl	= 	17;
int	tcdell	=	16;

/*
 * Initialize the terminal when the editor
 * gets started up. This is a no-op on the Amiga.
 */
ttinit()
{
}

/*
 * Clean up the terminal, in anticipation of
 * a return to the command interpreter. This is a no-op
 * on the Amiga.
 */
tttidy()
{
}

/*
 * Move the cursor to the specified
 * origin 0 row and column position. Try to
 * optimize out extra moves; redisplay may
 * have left the cursor in the right
 * location last time!
 */
ttmove(row, col)
{
	if (ttrow!=row || ttcol!=col) {
		ttputc(ESC);
		ttputc('[');
		asciiparm(row+1);
		ttputc(';');
		asciiparm(col+1);
		ttputc('H');
		ttrow = row;
		ttcol = col;
	}
}

/*
 * Erase to end of line.
 */
tteeol()
{
	ttputc(ESC);
	ttputc('[');
	ttputc('K');
}

/*
 * Erase to end of page.
 */
tteeop()
{
	ttputc(ESC);
	ttputc('[');
	ttputc('J');
}

/*
 * Make a noise.
 */
ttbeep()
{
	ttputc(BEL);
	ttflush();
}

/*
 * Convert a number to decimal
 * ascii, and write it out. Used to
 * deal with numeric arguments.
 */
asciiparm(n)
register int	n;
{
	register int	q;

	q = n/10;
	if (q != 0)
		asciiparm(q);
	ttputc((n%10) + '0');
}

/*
 * Insert a block of blank lines onto the
 * screen, using a scrolling region that starts at row
 * "row" and extends down to row "bot". Deal with the one
 * line case, which is a little bit special, with special
 * case code.
 *
 * Since we don't really have a scrolling region,
 * delete the block of lines that would have been deleted if
 * we'd had one, then insert blank lines to move the rest
 * of the screen back to where it belongs.  This idea from
 * the Heath driver.
 */
ttinsl(row, bot, nchunk)
{
	register int	i;

	if (row == bot) {			/* Funny case.		*/
		if (nchunk != 1)
			abort();
		ttmove(row, 0);
		tteeol();
		return;
	} 
	ttmove(1+bot-nchunk, 0);
	if (nchunk > 0) {		/* Delete a chunk of lines	*/
		ttputc(ESC);		/* nchunk in size.  Rest of	*/
		ttputc('[');		/* screen moves up.		*/
		asciiparm(nchunk);
		ttputc('M');
	}
	ttmove(row, 0);
	if (nchunk > 0) {		/* Insert a chunk nchunk in size*/
		ttputc(ESC);		/* before current line,	sliding	*/
		ttputc('[');		/* rest of screen down.		*/
		asciiparm(nchunk);
		ttputc('L');
	}
	ttrow = row;			/* End up on current line	*/
	ttcol = 0;
}

/*
 * Delete a block of lines, with the uppermost
 * line at row "row", in a screen slice that extends to
 * row "bot". The "nchunk" is the number of lines that have
 * to be deleted.  This is done by deleting nchunk lines at
 * the appropriate spot, then inserting nchunk lines to make
 * up for the empty space at the bottom of the virtual scrolling
 * region.
 */
ttdell(row, bot, nchunk)
{
	register int	i;

	if (row == bot) {		/* One line special case	*/
		ttmove(row, 0);
		tteeol();
		return;
	}
	if (nchunk > 0) {
		ttmove(row, 0);
		ttputc(ESC);
		ttputc('[');
		asciiparm(nchunk);
		ttputc('M');
	}
	ttmove(1+bot-nchunk,0);
	if (nchunk > 0) {
		ttputc(ESC);		/* For all lines in chunk	*/
		ttputc('[');		/* INS line before bottom	*/
		asciiparm(nchunk);	/* Bottom of window (and rest 	*/
		ttputc('L');		/* of screen) moves down */
	}
	ttrow = HUGE;			/* Force ttmove() to do the move*/
	ttcol = HUGE;
	ttmove(bot-nchunk,0);
}

/*
 * No-op.
 */
ttwindow(top,bot)
{
}

/*
 * No-op.
 */
ttnowindow()
{
}

/*
 * Set the current writing color to the
 * specified color. Watch for color changes that are
 * not going to do anything (the color is already right)
 * and don't send anything to the display.
 */
ttcolor(color)
register int	color;
{
	if (color != tthue) {
		if (color == CTEXT) {		/* Normal video.	*/
			ttputc(ESC);
			ttputc('[');
			ttputc('m');
		} else if (color == CMODE) {	/* Reverse video.	*/
			ttputc(ESC);
			ttputc('[');
			ttputc('7');
			ttputc('m');
		}
		tthue = color;			/* Save the color.	*/
	}
}

/*
 * This routine is called by the
 * "refresh the screen" command to try and resize
 * the display. The new size, which must be deadstopped
 * to not exceed the NROW and NCOL limits, is stored
 * back into "nrow" and "ncol". Display can always deal
 * with a screen NROW by NCOL. Look in "window.c" to
 * see how the caller deals with a change.
 */
#define	CSI	0x9B

ttresize()
{
	register int newnrow, newncol;

	ttmove(HUGE,HUGE);		/* Go to bottom right		*/
	ttputc(CSI);			/* See where cursor went to	*/
	ttputc('6');
	ttputc('n');
	ttflush();

	if (ttgetc() != CSI)			/* parse the report 	*/
		returnf;
	if ((newnrow = readparm(';')) == -1)
		return;
	if ((newncol = readparm('R')) == -1)
		return;

	if (newnrow < 1)			/* check limits.	*/
		newnrow = 1;
	else if (newnrow > NROW)
		newnrow = NROW;
	if (newncol < 1)
		newncol = 1;
	else if (newncol > NCOL)
		newncol = NCOL;
	nrow = newnrow;
	ncol = newncol;
}

/*
 * Read an unsigned number from the terminal
 */
static readparm(delim)
char delim;
{
	register int 	parm;
	register int	c;

	parm = 0;
	while ((c=ttgetc()) >= '0' && c <= '9')
		parm = 10 * parm + c - '0';
	return (c == delim) ? parm : -1;
}

-h- ttydef.h	Wed Apr 23 08:53:26 1986	ttydef.h
/*
 * Name:	MicroEMACS
 *		Amiga console device virtual terminal header file
 * Version:	31
 * Last edit:	20-Apr-86
 * Created:	20-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 *
 */
#define	GOSLING	1			/* Compile in fancy display.	*/
#define	MEMMAP	0			/* Not memory mapped video.	*/

#define	NROW	23			/* Rows.			*/
#define	NCOL	77			/* Columns (AmigaDOS)		*/

/*
 * Special keys for the default Amiga console device keymap.
 * Function key codes are in the form <CSI>v~
 * where v is a 1 or 2-digit code between 0 and 19,
 * so they comprise the first 20 entries in the key
 * table.  The next 12 entries are for the help and
 * arrow keys.  There is no shifted value for the
 * HELP key.
 */
#define	KF1	K01
#define	KF2	K02
#define	KF3	K03
#define	KF4	K04
#define	KF5	K05
#define	KF6	K06
#define	KF7	K07
#define	KF8	K08
#define	KF9	K09
#define	KF10	K0A
#define	KSF1	K0B
#define	KSF2	K0C
#define	KSF3	K0D
#define	KSF4	K0E
#define	KSF5	K0F
#define	KSF6	K10
#define	KSF7	K11
#define	KSF8	K12
#define	KSF9	K13
#define	KSF10	K14
#define	KUP	K15
#define	KSUP	K16
#define	KDOWN	K17
#define	KSDOWN	K18
#define	KLEFT	K19
#define	KSLEFT	K1A
#define	KRIGHT	K1B
#define	KSRIGHT	K1C
#define	KHELP	K1D
-h- ttyio.c	Wed Apr 23 08:53:26 1986	ttyio.c
/*
 * Name:	MicroEMACS
 *		AmigaDOS terminal I/O
 * Version:	31
 * Compiler:	Manx Aztec C
 * Last edit:	19-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 * Created:	19-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 */
#include	"def.h"
#include	"sysdef.h"
#include	<libraries/dos.h>
#include	<libraries/dosextens.h>


#define	NIBUF	128			/* Probably excessive.		*/
#define	NOBUF	512			/* Not too big for 750/730.	*/

struct	FileHandle	*tty;
struct	FileHandle	*Open();
char	obuf[NOBUF];			/* Output buffer		*/
int	nobuf;				/* # of bytes in above		*/
char	ibuf[NIBUF];			/* Input buffer			*/
int	nibuf;				/* # of bytes in above		*/
int	nrow;				/* Terminal size, rows.		*/
int	ncol;				/* Terminal size, columns.	*/
#if	MANX
extern	int	Enable_Abort;
#endif

extern	char	*version[];	/* Defined in "version.c"	*/
/*
 * This routine gets called once, to set up the
 * terminal channel.
 */
ttopen()
{
	char WindowName[80];
	
	nrow = 23;
	ncol = 77;
	nobuf = nibuf = 0;
#if	MANX
	Enable_Abort = 0;	/* Disable ^C during file I/O */
#endif
	strcpy(WindowName,"RAW:1/1/639/199/");
	strcat(WindowName,version[0]);
	tty = Open(WindowName,MODE_NEWFILE);
	if (tty == (struct FileHandle *) 0) {
		printf("Can't open Emacs window!\n");
		exit(200);
	}
}

/*
 * This function gets called just
 * before we go back home to the command interpreter.
 * On the Amiga it closes up the virtual terminal window.
 */
ttclose()
{
	if (tty != (struct FileHandle *) 0L) {
		ttflush();
		Close(tty);
	}
	tty = (struct FileHandle *) 0L;
	Enable_Abort = 1;
}

/*
 * Write a character to the display.
 * On the Amiga, terminal output is buffered, and
 * we just put the characters in the big array,
 * after cheching for overflow.
 */
ttputc(c)
{
	if (nobuf >= NOBUF)
		ttflush();
	obuf[nobuf++] = c;
}

/*
 * This function does the real work of
 * flushing out buffered I/O on the Amiga. All
 * we do is blast out the block with a write call.
 */
ttflush()
{
	if (nobuf > 0) {
		Write(tty,obuf,(long) nobuf);
		nobuf = 0;
	}
}

/*
 * Read a character from the terminal,
 * performing no editing and doing no echo at all.
 */
ttgetc()
{
	unsigned char c;	/* must be unsigned! */

	Read(tty,&c,1L);
	return ((int) c);
}
-h- ttykbd.c	Wed Apr 23 08:53:26 1986	ttykbd.c
/*
 * Name:	MicroEMACS
 * 		Amiga virtual terminal keyboard, default console keymap.
 * Version:	31
 * Last edit:	20-Apr-86
 * Created:	19-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 *			Supports all the default console device
 *			(RAW:) input codes for the Amiga keyboard.
 * Modified:	20-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 *		[UT-14A] Added processing for GR_to_META, so
 *			characters in the alternate character set
 *			can automatically map to META characters.
 *			This is a rather nice thing to have...
 */
#include	"def.h"

#define	ESC	0x1B			/* Escape, arrows et al.	*/
#define	CSI	0x9B			/* Amiga CSI			*/

/*
 * The function keys on the Amiga send back
 * escape sequences of the form <ESC>[code~, where code
 * is a one or two-character code for the key.  To make
 * it easier to decode, we place the internal key values
 * for these codes in this table.
 */

short	consolemap[] = {
	KF1,		KF2,		KF3,		KF4,
	KF5,		KF6,		KF7,		KF8,
	KF9,		KF10,		KSF1,		KSF2,
	KSF3,		KSF4,		KSF5,		KSF6,
	KSF7,		KSF8,		KSF9,		KSF10
};
#define	NFUNCKEYS ((sizeof consolemap)/(sizeof consolemap[0]))

/*
 * Names for the keys with basic keycode
 * between KFIRST and KLAST (inclusive). This is used by
 * the key name routine in "kbd.c".  KFIRST is KRANDOM, which
 * we don't bind anything useful to.
 */
char	*keystrings[] = {
	NULL,		"F1",		"F2",		"F3",
	"F4",		"F5",		"F6",		"F7",
	"F8",		"F9",		"F10",		"Shift-F1",
	"Shift-F2",	"Shift-F3",	"Shift-F4",	"Shift-F5",
	"Shift-F6",	"Shift-F7",	"Shift-F8",	"Shift-F9",
	"Shift-F10",	"Up",		"Shift-Up",	"Down",
	"Shift-Down",	"Left",		"Shift-Left",	"Right",
	"Shift-Right",	"Help",		NULL,		NULL
};

/*
 * Read in a key, doing the low level mapping
 * of ASCII code to 11 bit code. This level deals with
 * mapping the special keys into their spots in the C1
 * control area. The C0 controls go right through, and
 * get remapped by "getkey".
 *
 * If GR_to_META is TRUE, characters in the Amiga alternate
 * character set get mapped to META characters.  If FALSE,
 * they get sent straight through.
 */
extern	int	GR_to_META;	/* defined in "kbd.c" */

getkbd()
{
	register int	c;
	register int	n;
loop:
	c = ttgetc();
	if (c == CSI) {
		c = ttgetc();
		/* Unshifted arrow keys */
		if (c == 'A')
			return (KUP);
		if (c == 'B')
			return (KDOWN);
		if (c == 'C')
			return (KRIGHT);
		if (c == 'D')
			return (KLEFT);
		if (c == 'T')
			return (KSUP);
		if (c == 'S')
			return (KSDOWN);
		if (c == '?') {
			ttgetc();		/* discard '~' */
			return (KHELP);
		}

		/* Shifted left, right */
		if (c == ' ') {
			c = ttgetc();
			if (c == 'A' || c == '@')
				return (c == 'A') ? (KSLEFT) : (KSRIGHT);
			goto loop;		/* try again, sucker */
		}

		/* Function keys	*/
		if (c >= '0' && c <= '9') {
			n = 0;
			do {
				n = 10*n + c - '0';
				c = ttgetc();
			} while (c>='0' && c<='9');
			if (c == '~' && n < NFUNCKEYS) {
				c = consolemap[n];
				if (c != KRANDOM)
					return (c);
				goto loop;
			}
			else 
				goto loop;	/* Try again */
		}
		goto loop;		/* Try again */
	}

	/* Meta keys -- also check for high bit set */
	if ((c == ESC) || (GR_to_META && (c & 0x80))) {
		if (c == ESC)
			c = ttgetc();
		c &= 0x7F;			/* Strip high bit	*/
		if (ISLOWER(c) != FALSE)	/* Copy the standard	*/
			c = TOUPPER(c);		/* META code.		*/
		if (c>=0x00 && c<=0x1F)
			c = KCTRL | (c+'@');
		return (KMETA | c);
	}
	return (c);
}

/*
 * Terminal specific keymap initialization.
 * Attach the special keys to the appropriate built
 * in functions. Bind all of the assigned graphics in the
 * Amiga alternate character set to self-insert.
 * As is the case of all the keymap routines, errors
 * are very fatal.
 */
ttykeymapinit()
{
	register SYMBOL	*sp;
	register int	i;

	keydup(KUP,	"back-line");
	keydup(KDOWN,	"forw-line");
	keydup(KRIGHT,	"forw-char");
	keydup(KLEFT,	"back-char");
	keydup(KHELP,	"help");

	keydup(KSUP,	"goto-bob");
	keydup(KSDOWN,	"goto-eob");
	keydup(KSRIGHT,	"goto-eop");
	keydup(KSLEFT,	"goto-bop");

	keydup(KF1,	"down-window");
	keydup(KF2,	"forw-page");
	keydup(KF3,	"enlarge-window");
	keydup(KF4,	"forw-window");
	keydup(KF5,	"split-window");
	keydup(KF6,	"file-visit");
	keydup(KF7,	"file-save");
	keydup(KF8,	"start-macro");
	keydup(KF9,	"bind-to-key");
	keydup(KF10,	"display-version");

	keydup(KSF1,	"up-window");
	keydup(KSF2,	"back-page");
	keydup(KSF3,	"shrink-window");
	keydup(KSF4,	"back-window");
	keydup(KSF5,	"only-window");
	keydup(KSF6,	"file-read");
	keydup(KSF7,	"file-write");
	keydup(KSF8,	"end-macro");
	keydup(KSF9,	"display-bindings");
	keydup(KSF10,	"quit");

	/*
	 * Bind all positions that correspond
	 * to characters in the Amiga alternate
	 * character set to "ins-self". These characters may
	 * be used just like any other character.
	 */

	if ((sp=symlookup("ins-self")) == NULL)
		abort();
	for (i=0xA0; i<0xFF; ++i) {
		if (binding[i] != NULL)
			abort();
		binding[i] = sp;
		++sp->s_nkey;
	}
}

mic@ut-ngp.UUCP (Mic Kaczmarczik) (04/23/86)

This file is part 3 of 3 the MicroEmacs Amiga code.
It contains the Intuition version of the terminal driver.

Mic Kaczmarczik
...!ihnp4!seismo!ut-sally!ut-ngp!mic
mic@a20.cc.utexas.edu

---------------------------------CUT HERE-------------------------------------
-h- console.c	Wed Apr 23 08:53:04 1986	console.c
/*
 * These functions are taken directly from the
 * console.device chapter in the Amiga V1.1
 * ROM Kernel Manual.
 */
#include <exec/types.h>
#include <exec/io.h>
#include <devices/console.h>
#include <libraries/dos.h>
#include <intuition/intuition.h>

extern	LONG	OpenDevice();
extern	LONG	DoIO();
extern	LONG	SendIO();

/*
 * Open a console device, given a read request
 * and a write request message.
 */

int OpenConsole(writerequest,readrequest,window)
struct IOStdReq *writerequest;
struct IOStdReq *readrequest;
struct Window *window;
{
	LONG error; 
	writerequest->io_Data = (APTR) window;
	writerequest->io_Length = (ULONG) sizeof(*window);
	error = OpenDevice("console.device", 0L, writerequest, 0L);

	/* clone required parts of the request */
	readrequest->io_Device = writerequest->io_Device;
	readrequest->io_Unit   = writerequest->io_Unit;
	return((int) error);
}

/*
 * Output a single character	
 * to a specified console
 */ 

int ConPutChar(request,character)
struct IOStdReq *request;
char character;
{
	request->io_Command = CMD_WRITE;
	request->io_Data = (APTR)&character;
	request->io_Length = (ULONG)1;
	DoIO(request);
	/* caution: read comments in manual! */
	return(0);
}
 
/*
 * Output a NULL-terminated string of
 * characters to a console
 */ 

int ConPutStr(request,string)
struct IOStdReq *request;
char *string;
{
	request->io_Command = CMD_WRITE;
	request->io_Data = (APTR)string;
	request->io_Length = (LONG)-1;
	DoIO(request);
	return(0);
}

/*
 * Write out a string of predetermined
 * length to the console
 */
 
int ConWrite(request,string,len)
struct IOStdReq *request;
char *string;
int len;
{
	request->io_Command = CMD_WRITE;
	request->io_Data = (APTR)string;
	request->io_Length = (LONG)len;
	DoIO(request);
	return(0);
}

/*
 * Queue up a read request 
 * to a console
 */

int QueueRead(request,whereto)
struct IOStdReq *request;
char *whereto;
{
	request->io_Command = CMD_READ;
	request->io_Data = (APTR)whereto;
	request->io_Length = (LONG)1;
	SendIO(request);
	return(0);
}

-h- menu.c	Wed Apr 23 08:53:04 1986	menu.c
/*
 * Dynamic Intuition menu functions
 * from the author of PDTerm
 */

#include "exec/types.h"
#include "exec/memory.h"
#include "graphics/text.h"
#include "intuition/intuition.h"
#define	MEMFLAGS (LONG) (MEMF_PUBLIC | MEMF_CHIP | MEMF_CLEAR)

extern	char	*AllocMem();
extern	LONG	FreeMem();

/*
 * Create an instance of an Intuitext structure
 * containing text, positioned at (left,top)
 */

struct IntuiText *NewIText(text, left, top)
char *text;
int left, top;
{
	struct IntuiText *newtext = NULL;

	newtext = (struct IntuiText *)
		AllocMem((LONG) sizeof(*newtext), MEMFLAGS);
	newtext->IText = (UBYTE *)text;
	newtext->FrontPen = 0;
	newtext->BackPen = 1;
	newtext->DrawMode = JAM2;
	newtext->LeftEdge = left;
	newtext->TopEdge = top;
	newtext->ITextFont = NULL;
	newtext->NextText = NULL;

	return(newtext);
}

/*
 * Add an Intuitext structure to the end of
 * list of them, returning a pointer to the
 * new Intuitext.
 */

struct IntuiText *AddIText(IText, text)
struct IntuiText *IText;
char *text;
{
	struct IntuiText *newtext = NULL;

	newtext = (struct IntuiText *)
		AllocMem((LONG) sizeof(*newtext), MEMFLAGS);
                
	newtext->IText = (UBYTE *)text;
	newtext->FrontPen = IText->FrontPen;
	newtext->BackPen  = IText->BackPen;
	newtext->DrawMode = IText->DrawMode;
	newtext->LeftEdge = IText->LeftEdge;
	newtext->TopEdge  = IText->TopEdge + 11;
	newtext->ITextFont = IText->ITextFont;
	newtext->NextText = NULL;
	IText->NextText   = newtext;

	return(newtext);
}

/*
 * Dispose of a lise of IntuiText structures
 */

DisposeIText(IText)
struct IntuiText *IText;
{
	struct IntuiText *cur, *next;

	cur = IText;
	for(next = cur->NextText; cur != NULL; next = next->NextText) {
		FreeMem(cur,(LONG) sizeof(*cur));
		cur = next;
	}
}


/*
 * Dynamic menu constructor functions
 */

#define InterMenuWidth 15

/*
 * Create a new menu structure.
 */

struct Menu *NewMenu(menuname, width, height)
char *menuname;
int width, height;
{
	struct Menu *menu = NULL;

	menu = (struct Menu *)
		AllocMem((LONG) sizeof(*menu), MEMFLAGS);

	menu->NextMenu = NULL;
	menu->LeftEdge = 0;
	menu->TopEdge = 0;
	menu->Width = width;
	menu->Height = height;
	menu->Flags = MENUENABLED;
	menu->MenuName = menuname;
	menu->FirstItem = NULL;

	return(menu);
}

/*
 * Create a new menu and add it to
 * the end of the list of menus.
 */
struct Menu *AddMenu(menus, MenuName, width, height)
struct Menu *menus;
char *MenuName;
int width, height;
{
	struct Menu *newmenu;

	newmenu = NewMenu(MenuName, width, height);
	newmenu->LeftEdge = menus->LeftEdge + menus->Width + InterMenuWidth;
	menus->NextMenu = newmenu;
	return(newmenu);
}

/*
 * Create a new menu item
 */

struct MenuItem *NewMenuItem(name, width, height)
char *name;
int width, height;
{
	struct MenuItem *newitem = NULL;
	struct IntuiText *NewIText(), *newtext = NULL;

	newitem = (struct MenuItem *)
		AllocMem((LONG) sizeof(*newitem), MEMFLAGS);

	newtext = NewIText(name,0,1);

	newitem->NextItem = NULL;
	newitem->ItemFill = (APTR) newtext;
	newitem->LeftEdge = 0;
	newitem->TopEdge = 0;
	newitem->Width = width;
	newitem->Height = height;
	newitem->Flags = ITEMTEXT | ITEMENABLED | HIGHCOMP;
	newitem->MutualExclude = 0;
	newitem->SelectFill = NULL;
	newitem->Command = 0;
	newitem->SubItem = NULL;
	newitem->NextSelect = 0;

	return(newitem);
}

/*
 * Start the menu item list for a menu.
 */

struct MenuItem *AddNewMenuItem(menu, name, width, height)
struct Menu *menu;
char *name;
int width, height;
{
	struct MenuItem *newitem, *NewMenuItem();

	newitem = NewMenuItem(name, width, height);
	menu->FirstItem = newitem;
	return(newitem);
}

/*
 * Add to a menu item list
 */

struct MenuItem *AddItem(items, name)
struct MenuItem *items;
char *name;
{
	struct MenuItem *newitem, *NewMenuItem();

	newitem = NewMenuItem(name, items->Width, items->Height);
	newitem->TopEdge = items->TopEdge + items->Height;
	newitem->LeftEdge = items->LeftEdge;
	items->NextItem = newitem;
	return(newitem);
}

/*
 * Start a list of subitems for a menu item.
 */

struct MenuItem *AddNewSubItem(item, name, width, height)
struct MenuItem *item;
char *name;
int width, height;
{
	struct MenuItem *newitem, *NewMenuItem();

	newitem = NewMenuItem(name, width, height);
	item->SubItem = newitem;
	newitem->LeftEdge = item->Width;
	return(newitem);
}

/*
 * Dispose of a menu item.
 */

DisposeItem(item)
struct MenuItem *item;
{
	DisposeIText((struct ItuiText *)item->ItemFill);
	FreeMem(item,(LONG) sizeof(*item));
}

/*
 * Dispose of a list of menu items.
 */

DisposeItems(items)
struct MenuItem *items;
{
	struct MenuItem *cur, *next;

	cur = items;
	for(next = cur->NextItem; cur != NULL; next = next->NextItem){
		DisposeItem(cur);
		cur = next;
	}
}

/*
 * Dispose of a single menu
 */
DisposeMenu(menu)
struct Menu *menu;
{
	DisposeItems(menu->FirstItem);
	FreeMem(menu,(LONG) sizeof(*menu));
}

/*
 * Get rid of a menu list.
 */
DisposeMenus(menus)
struct Menu *menus;
{
	struct Menu *cur, *next;

	cur = menus;
	for(next = cur->NextMenu; cur != NULL; next = next->NextMenu){
		DisposeMenu(cur);
		cur = next;
	}
}

-h- tty.c	Wed Apr 23 08:53:04 1986	tty.c
/*
 * Name:	MicroEMACS
 *		Amiga console device virtual terminal display
 * Version:	31
 * Last Edit:	19-Apr-86
 * Created:	19-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 *
 * Drives the Amiga console device display.  The code is a combination
 * of the Heath and ANSI terminal drivers.
 */
#include	"def.h"


#define	BEL	0x07			/* BEL character.		*/
#define	ESC	0x1B			/* ESC character.		*/

extern	int	ttrow;
extern	int	ttcol;
extern	int	tttop;
extern	int	ttbot;
extern	int	tthue;

int	tceeol	=	3;		/* Costs, ANSI display.		*/
int	tcinsl	= 	17;
int	tcdell	=	16;

/*
 * Initialize the terminal when the editor
 * gets started up. This is a no-op on the Amiga.
 */
ttinit()
{
}

/*
 * Clean up the terminal, in anticipation of
 * a return to the command interpreter. This is a no-op
 * on the Amiga.
 */
tttidy()
{
}

/*
 * Move the cursor to the specified
 * origin 0 row and column position. Try to
 * optimize out extra moves; redisplay may
 * have left the cursor in the right
 * location last time!
 */
ttmove(row, col)
{
	if (ttrow!=row || ttcol!=col) {
		ttputc(ESC);
		ttputc('[');
		asciiparm(row+1);
		ttputc(';');
		asciiparm(col+1);
		ttputc('H');
		ttrow = row;
		ttcol = col;
	}
}

/*
 * Erase to end of line.
 */
tteeol()
{
	ttputc(ESC);
	ttputc('[');
	ttputc('K');
}

/*
 * Erase to end of page.
 */
tteeop()
{
	ttputc(ESC);
	ttputc('[');
	ttputc('J');
}

/*
 * Make a noise.
 */
ttbeep()
{
	ttputc(BEL);
	ttflush();
}

/*
 * Convert a number to decimal
 * ascii, and write it out. Used to
 * deal with numeric arguments.
 */
asciiparm(n)
register int	n;
{
	register int	q;

	q = n/10;
	if (q != 0)
		asciiparm(q);
	ttputc((n%10) + '0');
}

/*
 * Insert a block of blank lines onto the
 * screen, using a scrolling region that starts at row
 * "row" and extends down to row "bot". Deal with the one
 * line case, which is a little bit special, with special
 * case code.
 * Since we don't really have a scrolling region,
 * delete the block of lines that would have been deleted if
 * we'd had one, then insert blank lines to move the rest
 * of the screen back to where it belongs.  This idea from
 * the Heath driver.
 */
ttinsl(row, bot, nchunk)
{
	register int	i;

	if (row == bot) {			/* Funny case.		*/
		if (nchunk != 1)
			abort();
		ttmove(row, 0);
		tteeol();
		return;
	} 
	ttmove(1+bot-nchunk, 0);
	if (nchunk > 0) {		/* Delete a chunk of lines	*/
		ttputc(ESC);		/* nchunk in size.  Rest of	*/
		ttputc('[');		/* screen moves up.		*/
		asciiparm(nchunk);
		ttputc('M');
	}
	ttmove(row, 0);
	if (nchunk > 0) {		/* Insert a chunk nchunk in size*/
		ttputc(ESC);		/* before current line,	sliding	*/
		ttputc('[');		/* rest of screen down.		*/
		asciiparm(nchunk);
		ttputc('L');
	}
	ttrow = row;			/* End up on current line	*/
	ttcol = 0;
}

/*
 * Delete a block of lines, with the uppermost
 * line at row "row", in a screen slice that extends to
 * row "bot". The "nchunk" is the number of lines that have
 * to be deleted.  This is done by deleting nchunk lines at the
 * appropriate spot, then inserting nchunk lines to make up for
 * the empty space at the bottom of the virtual scrolling region.
 */
ttdell(row, bot, nchunk)
{
	register int	i;

	if (row == bot) {		/* One line special case	*/
		ttmove(row, 0);
		tteeol();
		return;
	}
	if (nchunk > 0) {
		ttmove(row, 0);
		ttputc(ESC);
		ttputc('[');
		asciiparm(nchunk);
		ttputc('M');
	}
	ttmove(1+bot-nchunk,0);
	if (nchunk > 0) {
		ttputc(ESC);		/* For all lines in chunk	*/
		ttputc('[');		/* INS line before bottom	*/
		asciiparm(nchunk);	/* Bottom of window (and rest 	*/
		ttputc('L');		/* of screen) moves down */
	}
	ttrow = HUGE;
	ttcol = HUGE;
	ttmove(bot-nchunk,0);
}

/*
 * No-op.
 */
ttwindow(top,bot)
{
}

/*
 * No-op.
 */
ttnowindow()
{
}

/*
 * Set the current writing color to the
 * specified color. Watch for color changes that are
 * not going to do anything (the color is already right)
 * and don't send anything to the display.
 */
ttcolor(color)
register int	color;
{
	if (color != tthue) {
		if (color == CTEXT) {		/* Normal video.	*/
			ttputc(ESC);
			ttputc('[');
			ttputc('m');
		} else if (color == CMODE) {	/* Reverse video.	*/
			ttputc(ESC);
			ttputc('[');
			ttputc('7');
			ttputc('m');
		}
		tthue = color;			/* Save the color.	*/
	}
}

/*
 * This routine is called by the
 * "refresh the screen" command to try and resize
 * the display. The new size, which must be deadstopped
 * to not exceed the NROW and NCOL limits, is stored
 * back into "nrow" and "ncol". Display can always deal
 * with a screen NROW by NCOL. Look in "window.c" to
 * see how the caller deals with a change.
 */
#define	CSI	0x9B

extern	int	ttsize();		/* Defined by "ttyio.c"	*/

ttresize()
{
	int newnrow, newncol;

	ttsize(&newnrow,&newncol);		/* ask tty how big it is */

	if (newnrow < 1)			/* check limits.	 */
		newnrow = 1;
	else if (newnrow > NROW)
		newnrow = NROW;
	if (newncol < 1)
		newncol = 1;
	else if (newncol > NCOL)
		newncol = NCOL;
	nrow = newnrow;
	ncol = newncol;
}

/*
 * Read an unsigned parameter from the console
 */
static readparm(delim)
char delim;
{
	register int 	parm;
	register int	c;

	parm = 0;
	while ((c=ttgetc()) >= '0' && c <= '9')
		parm = 10 * parm + c - '0';
	return (c == delim) ? parm : -1;
}
-h- ttydef.h	Wed Apr 23 08:53:04 1986	ttydef.h
/*
 * Name:	MicroEMACS
 *		Amiga console device virtual terminal header file
 * Version:	31
 * Last edit:	20-Apr-86
 * Created:	20-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 *			Add Intuition menu support
 *
 */
#define	GOSLING	1			/* Compile in fancy display.	*/
#define	MEMMAP	0			/* Not memory mapped video.	*/

#define	NROW	23			/* Rows.			*/
#define	NCOL	77			/* Columns (AmigaDOS)		*/

/*
 * Special keys for the default Amiga console device keymap.
 * Function key codes are in the form <CSI>v~
 * where v is a 1 or 2-digit code between 0 and 19,
 * so they comprise the first 20 entries in the key
 * table.  The next 12 entries are for the help and
 * arrow keys.  There is no shifted value for the
 * HELP key.
 */
#define	KF1	K01
#define	KF2	K02
#define	KF3	K03
#define	KF4	K04
#define	KF5	K05
#define	KF6	K06
#define	KF7	K07
#define	KF8	K08
#define	KF9	K09
#define	KF10	K0A
#define	KSF1	K0B
#define	KSF2	K0C
#define	KSF3	K0D
#define	KSF4	K0E
#define	KSF5	K0F
#define	KSF6	K10
#define	KSF7	K11
#define	KSF8	K12
#define	KSF9	K13
#define	KSF10	K14
#define	KUP	K15
#define	KSUP	K16
#define	KDOWN	K17
#define	KSDOWN	K18
#define	KLEFT	K19
#define	KSLEFT	K1A
#define	KRIGHT	K1B
#define	KSRIGHT	K1C
#define	KHELP	K1D

/* The 'menu' key doesn't really appear on the
 * Amiga keyboard.  When ttgetc() sees a menu
 * event, it saves the menu number and item,
 * then stuffs the sequence for KMENU into
 * the input buffer.
 */
#define	KMENU	K1E

/*
 * Similarly, this is the way we shoehorn the
 * mouse into the input stream, using the last
 * function key value for this purpose.
 */
#define	KMOUSE	K1F

/*
 * Intuition menu interface.  Each set of menu items
 * kept in a table of MenuBinding structures, which
 * is in turn kept in a table of MenuInfo structures.
 *
 * These tables are indexed via the menu and item
 * numbers to find the internal extended name of
 * the function associated with a certain item.
 */
struct MenuBinding {
	char *Command;
	char *Binding;
};

struct MenuInfo {
	char *Name;			/* name of menu			*/
	short NumItems;			/* # of items			*/
	short MenuWidth;		/* width, in CHARACTERS		*/
	struct MenuBinding *Items;	/* item name, internal binding	*/
};

#define NITEMS(arr) (sizeof(arr) / (sizeof(arr[0])))
-h- ttyio.c	Wed Apr 23 08:53:04 1986	ttyio.c
/*
 * Name:	MicroEmacs
 *		Amiga terminal-dependent I/O (Intuition)
 *	 	Strategy and much code borrowed from the Lattice C
 *		example in the ROM Kernel manual.
 * Version:	31
 * Last edit:	21-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 * Created:	21-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 */
 
/*
 * Lots of includes.
 */

#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/tasks.h>
#include <exec/ports.h>
#include <exec/io.h>
#include <devices/console.h>
#include <libraries/dos.h>
#include <graphics/clip.h>
#include <graphics/view.h>
#include <graphics/rastport.h>
#include <graphics/layers.h>
#include <graphics/text.h>
#include <intuition/intuition.h>
#undef	TRUE
#undef	FALSE
#include "def.h"
#include "sysdef.h"

/*
 * External functions.  Declared explicitly
 * to avoid problems with different compilers.
 */

extern	char			*OpenLibrary();
extern	int			OpenConsole();
extern	struct	Window		*OpenWindow();
extern	struct	MsgPort		*CreatePort();
extern	struct	IOStdReq	*CreateStdIO();
extern	struct	Menu		*InitEmacsMenu();
extern	struct	IntuiMessage	*GetMsg();
extern	LONG			AbortIO();
extern	LONG			CloseDevice();
extern	LONG			ReplyMsg();
extern	LONG			SetMenuStrip();
extern	LONG			Wait();

/*
 * Terminal I/O global variables
 */

#define	NIBUF	128			/* Probably excessive.		*/
#define	NOBUF	512			/* Not too big for 750/730.	*/

unsigned char	obuf[NOBUF];		/* Output buffer		*/
int		nobuf;			/* # of bytes in above		*/
unsigned char	ibuf[NIBUF];		/* Input buffer			*/
int		ibufo, nibuf;		/* head, # of bytes in ibuf	*/
int		nrow;			/* Terminal size, rows.		*/
int		ncol;			/* Terminal size, columns.	*/
extern char	*version[];		/* Version information		*/
					/*   (I cheat and use it for	*/
					/*    the window title)		*/
/*
 * Intuition global variables
 */

#define WINDOWGADGETS (WINDOWSIZING | WINDOWDRAG | WINDOWDEPTH | WINDOWCLOSE)

struct NewWindow MicroEMACS = {
	0,	0,			/* start position       	*/
	640,	200,			/* width, height       		*/
	0,	1,	     		/* detail pen, block pen	*/
	MENUPICK | CLOSEWINDOW |	/* IDCMP flags			*/
	MOUSEBUTTONS | NEWSIZE,
	WINDOWGADGETS | ACTIVATE,	/* window flags			*/
	NULL,				/* pointer to first user gadget */
	NULL,				/* pointer to user checkmark	*/ 
	NULL,				/* title (filled in later)	*/
	NULL,				/* pointer to screen (none)	*/
	NULL,				/* pointer to superbitmap	*/
	360,99,639,199,			/* sizing limits min and max	*/
	WBENCHSCREEN			/* screen in which to open	*/ 
};

struct	IntuitionBase	*IntuitionBase;		/* library bases	*/
struct	GfxBase		*GfxBase;
struct	Window		*EmacsWindow;		/* Our window		*/
struct	Menu		*EmacsMenu;		/* Our menu		*/
struct	MsgPort		*consoleWritePort;	/* I/O ports 		*/
struct	MsgPort		*consoleReadPort;	
int			intuitionMsgBit,	/* Signal bits		*/
			consoleMsgBit;
struct	IOStdReq	*consoleWriteMsg;	/* I/O messages		*/
struct	IOStdReq	*consoleReadMsg;
unsigned char		letter;			/* Console input buffer	*/
USHORT			class,			/* Intuition event	*/
			code,			/*   information	*/
			qualifier;
APTR			address;
SHORT			x, y;

/*
 * Some definitions
 */

#define INTUITION_MESSAGE ((LONG) 1 << intuitionMsgBit)
#define CONSOLE_MESSAGE ((LONG) 1 << consoleMsgBit)

/*
 * Open up the virtual terminal MicroEMACS communicates with.
 * Set up the window, console, and menu strip.
 */

extern	int	Enable_Abort;		/* Do NOT allow abort!	*/

ttopen()
{
	Enable_Abort = 0;		/* Disable ^C 		*/

	GfxBase = (struct GfxBase *)
		OpenLibrary("graphics.library", (LONG) 0);
	if (GfxBase  == NULL)				/* Graphics lib	*/
		cleanup(1);

	IntuitionBase = (struct IntuitionBase *)	/* Intuition	*/
		OpenLibrary("intuition.library", (LONG) 0);
	if (IntuitionBase == NULL)
		cleanup(2);

	/* Create our window */
	MicroEMACS.Title = (UBYTE *) version[0];
	EmacsWindow = OpenWindow(&MicroEMACS);
	if (EmacsWindow == NULL)
		cleanup(2);

	/* Ports for reading and writing */
	consoleWritePort = CreatePort("Emacs.con.write",(LONG) 0);
	if (consoleWritePort == NULL)
		cleanup(5);
	consoleWriteMsg = CreateStdIO(consoleWritePort);
	if (consoleWriteMsg == NULL)
		cleanup(6);

	consoleReadPort = CreatePort("Emacs.con.read",(LONG) 0);
	if (consoleReadPort == NULL)
		cleanup(7);
	consoleReadMsg = CreateStdIO(consoleReadPort);
	if (consoleReadMsg == NULL)
		cleanup(8);

	/* attach the console device to our window */
	if (OpenConsole(consoleWriteMsg,consoleReadMsg,EmacsWindow) != 0)
		cleanup(10);

	/* attach a menu strip to the window */
	EmacsMenu = InitEmacsMenu();
	SetMenuStrip(EmacsWindow, EmacsMenu);

	/* determine signal bit #'s	*/
	intuitionMsgBit = EmacsWindow->UserPort->mp_SigBit;
	consoleMsgBit = consoleReadPort->mp_SigBit;

	/* initialize console read and input buffer */
	QueueRead(consoleReadMsg,&letter);
	nibuf = ibufo = 0;

	/* Determine initial size of virtual terminal.	*/
	ttsize(&nrow,&ncol);
	return (0);
}

/*
 * Close the virtual terminal, de-allocating
 * everything we have allocated.
 */

ttclose()
{
	ttflush();
	AbortIO(consoleReadMsg);
	CloseDevice(consoleWriteMsg);
	cleanup(0);
	Enable_Abort = 1;
}

/*
 * Write a single character to the screen.
 * Buffered for extra speed, so ttflush()
 * does all the work.
 */

ttputc(c)
unsigned char c;
{
	obuf[nobuf++] = c;
	if (nobuf >= NOBUF)
		ttflush();
}

/*
 * Flush characters from the output buffer.
 * Just blast it out with a console write call.
 */
ttflush()
{
	if (nobuf > 0) {
		ConWrite(consoleWriteMsg, obuf, nobuf);
		nobuf = 0;
	}
}

/*
 * Input buffer management.
 *
 * The input buffer is a circular queue of characters
 * that is updated and manipulated by the macros and
 * functions below.   This allows multiple Intuition
 * events (menus, console input, etc.) to be buffered
 * up until Emacs asks for them.
 */

#define CharsBuffered()  (nibuf > 0 )
#define TypeInChar(c)  if (nibuf < (NIBUF - 1))\
				ibuf[(ibufo + nibuf++) % NIBUF] = c;

/*
 * Return the next character in the input buffer.
 */
static GetInputChar()
{
	unsigned char ch;

	if (nibuf <= 0)			/* this shouldn't happen.	*/
		return 0;
	ch = ibuf[ibufo++];
	ibufo %= NIBUF;
	nibuf--;
	return (int) ch;
}

/*
 * Get a character for Emacs, without echo or
 * translation.  Basically, handle Intuition
 * events until we get one that signifies
 * a character was typed in some way.
 */

ttgetc()
{
	register struct	IntuiMessage *message;		/* IDCMP message */
	register LONG	wakeupmask;
	register int	charfound;	/* have we got a character yet?	*/


	if (CharsBuffered())		/* check input buffer		*/
		return GetInputChar();	/* return immediately have one	*/

	charfound = FALSE;
	do {
		wakeupmask = Wait(INTUITION_MESSAGE|CONSOLE_MESSAGE);

		if (wakeupmask & CONSOLE_MESSAGE) {	/* Keyboard	*/
			GetMsg(consoleReadPort);	/* free message	*/
			TypeInChar(letter);		/* do this FIRST */
			QueueRead(consoleReadMsg, &letter);
			charfound = TRUE;
		}

		if (wakeupmask & INTUITION_MESSAGE) {	/* Intuition	*/
			while(message =	GetMsg(EmacsWindow->UserPort)) {
				class =	message->Class;
				code = message->Code;
				qualifier = message->Qualifier;
				address = message->IAddress;
				x = message->MouseX;
				y = message->MouseY;

				ReplyMsg(message);
				/* Need ||= here because next event may */
				/* not result in a character... */
				charfound = charfound || HandleEvent();
			} /* while (GetMsg()) */
		} /* if Intuition event */
	} while (charfound == FALSE);

	return GetInputChar();		/* finally got a character.	*/
}

/*
 * Handle the events we handle...
 * The result indicates if we've
 * put a character in the input buffer.
 * All the event information is global,
 * because there's a lot of it...
 */

extern	int	quit();				/* Defined by "main.c"	*/

static HandleEvent()
{
	switch(class) {
	case MENUPICK:				/* fake the menu key	*/
		if (code != MENUNULL)
			return (DoMenu());
		else
			return (FALSE);		/* No character found	*/
		break;
	case MOUSEBUTTONS:			/* fake the mouse key */
		return (DoMouse());
		break;
        case CLOSEWINDOW:			/* Call quit() directly	*/
		quit(FALSE, 1, KRANDOM);	/* This loses if in a 	*/
		return (FALSE);			/*   dialogue...	*/
                break;
	}
	return(FALSE);				/* No char found	*/
}

/*
 * Handle a menu selection by hand-crafting
 * an escape sequence that looks like a function
 * key to the terminal driver.  Save the
 * menu number and item number until the
 * menu execution function can ask for it.
 */

int	LastMenuNum;		/* Menu number for KMENU `key'		*/
int	LastItemNum;		/* Menu item for KMENU `key'		*/
int	LastSubItem;		/* Subitem number (for completeness)	*/

#define	CSI	0x9b		/* Amiga command sequence introducer	*/

static DoMenu()
{
	struct	MenuItem *item, *ItemAddress();

	while (code != MENUNULL) {
		item = ItemAddress(EmacsMenu,(LONG) code);
		LastMenuNum = MENUNUM(code);	/* number of menu	*/
		LastItemNum = ITEMNUM(code);	/* item number		*/
		LastSubItem = SUBNUM(code);	/* subitem code		*/
		code = item->NextSelect;
	}
	TypeInChar(CSI);			/* fake the MENU key	*/
	TypeInChar('M');
	TypeInChar('~');
	return (TRUE);				/* found a character!	*/
}

/*
 * Return the last menu selection numbers to
 * the caller.  Used by "ttymenu.c".
 */

ttmenu(menu,item,subitem)
int *menu, *item, *subitem;
{
	*menu = LastMenuNum;
	*item = LastItemNum;
	*subitem = LastSubItem;
	LastMenuNum = (USHORT)-1;	/* Forget the values		*/
	LastItemNum = (USHORT)-1;	/* so they don't get re-used	*/
	LastSubItem = (USHORT)-1;
}


/*
 * Handle a mouse selection by inserting
 * a "MOUSE key" (teer) sequence into the
 * input buffer and saving the x, y and
 * qualifier values for later.
 */

SHORT	LastMouseX, LastMouseY;	/* Position of mouse			*/
USHORT	LastQualifier;		/* Qualifier (shift key?)		*/

static DoMouse()
{
	/* Save last mouse position */
	if (code != SELECTDOWN)
		return (FALSE);
	LastMouseX = x - EmacsWindow->BorderLeft;
	LastMouseY = y - EmacsWindow->BorderTop;
	LastQualifier = qualifier;

	TypeInChar(CSI);			/* fake the MOUSE key	*/
	TypeInChar('P');			/* P for Pointer	*/
	TypeInChar('~');
	return (TRUE);				/* found a character!	*/
}

/*
 * Return the last mouse click values to
 * the caller.   X and Y are translated
 * so that (0,0) is at the edge of the
 * top and left borders. Used by "ttymouse.c".
 */

ttmouse(x,y,qualifier)
SHORT *x, *y;
USHORT *qualifier;
{
	*x = LastMouseX;
	*y = LastMouseY;
	*qualifier = LastQualifier;
	LastMouseX = (SHORT)-1;
	LastMouseY = (SHORT)-1;
	LastQualifier = (USHORT)-1;
}

/*
 * Return the current size of the virtual
 * terminal to the caller.  Placed in
 * ttyio.c because it uses information
 * that is only available to the virtual
 * terminal handler.
 *
 * Assumes the WorkBench screen default
 * font is TOPAZ_EIGHTY (8 wide by 8 high).
 */

ttsize(rows,cols)
int *rows, *cols;
{
	*rows = (EmacsWindow->Height - 		/* have to take borders	*/
		 EmacsWindow->BorderTop -	/* into account.	*/
		 EmacsWindow->BorderBottom) / 8;
	*cols = (EmacsWindow->Width -
		 EmacsWindow->BorderLeft -
		 EmacsWindow->BorderRight) / 8;
}

/*
 * Clean up.
 * Fall through all the possible cases (0 means
 * get rid of everything and start with the case
 * that fits the error situation.
 */

extern	LONG	ClearMenuStrip();
extern	LONG	DeleteStdIO();
extern	LONG	DeletePort();
extern	LONG	CloseWindow();
extern	LONG	CloseLibrary();

static cleanup(prob)
{
	
	switch (prob) {
	case 0:
		ClearMenuStrip(EmacsWindow);
		DisposeMenus(EmacsMenu);
	case 10:
	case 8:
		DeleteStdIO(consoleReadMsg);
	case 7:
		DeletePort(consoleReadPort);
	case 6:
		DeleteStdIO(consoleWriteMsg);
	case 5:
		DeletePort(consoleWritePort);
	case 4:
		CloseWindow(EmacsWindow);
	case 2:
		if (GfxBase != NULL) CloseLibrary(GfxBase);
	case 1:
		if (IntuitionBase != NULL) CloseLibrary(IntuitionBase);
		break;
	}
        return(0);
}

-h- ttykbd.c	Wed Apr 23 08:53:04 1986	ttykbd.c
/*
 * Name:	MicroEMACS
 * 		Amiga virtual terminal keyboard, default console keymap.
 * Version:	31
 * Last edit:	20-Apr-86
 * Created:	19-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 *			Supports all the default console device
 *			(RAW:) input codes for the Amiga keyboard.
 * Modified:	20-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 *		[UT-14]	Added processing for GR_to_META, so
 *			characters in the alternate character set
 *			can automatically map to META characters.
 *			This is a rather nice thing to have...
 *		Added KMENU to key definitions, bound to amigamenu().
 *		This goes with the Intuition terminal driver.
 */
#include	"def.h"

#define	ESC	0x1B			/* Escape, arrows et al.	*/
#define	CSI	0x9B			/* Amiga CSI			*/

/*
 * The function keys on the Amiga send back
 * escape sequences of the form <ESC>[code~, where code
 * is a one or two-character code for the key.  To make
 * it easier to decode, we place the internal key values
 * for these codes in this table.
 */

short	consolemap[] = {
	KF1,		KF2,		KF3,		KF4,
	KF5,		KF6,		KF7,		KF8,
	KF9,		KF10,		KSF1,		KSF2,
	KSF3,		KSF4,		KSF5,		KSF6,
	KSF7,		KSF8,		KSF9,		KSF10
};
#define	NFUNCKEYS ((sizeof consolemap)/(sizeof consolemap[0]))

/*
 * Names for the keys with basic keycode
 * between KFIRST and KLAST (inclusive). This is used by
 * the key name routine in "kbd.c".  KFIRST is KRANDOM, which
 * we don't bind anything useful to.  "Amiga Menus" is
 * special; we implement menus as another function key,
 * but there isn't really any "MENU" key on the keyboard.
 */
char	*keystrings[] = {
	NULL,		"F1",		"F2",		"F3",
	"F4",		"F5",		"F6",		"F7",
	"F8",		"F9",		"F10",		"Shift-F1",
	"Shift-F2",	"Shift-F3",	"Shift-F4",	"Shift-F5",
	"Shift-F6",	"Shift-F7",	"Shift-F8",	"Shift-F9",
	"Shift-F10",	"Up",		"Shift-Up",	"Down",
	"Shift-Down",	"Left",		"Shift-Left",	"Right",
	"Shift-Right",	"Help",		"The Menu",	"The Mouse"
};

/*
 * Read in a key, doing the low level mapping
 * of ASCII code to 11 bit code. This level deals with
 * mapping the special keys into their spots in the C1
 * control area. The C0 controls go right through, and
 * get remapped by "getkey".
 *
 * If GR_to_META is TRUE, characters in the Amiga alternate
 * character set get mapped to META characters.  If FALSE,
 * they get sent straight through.
 */
extern	int	GR_to_META;	/* defined in "kbd.c" */

getkbd()
{
	register int	c;
	register int	n;
loop:
	c = ttgetc();
	if (c == CSI) {
		c = ttgetc();
		if (c == 'P') {			/* mouse sequence	*/
			ttgetc();		/* First because it's   */
			return (KMOUSE);	/* used a lot.		*/
		}
		if (c == 'M') {			/* (fake) menu key	*/
			ttgetc();		/* discard '~'		*/
			return (KMENU);
		}
		/* Arrow keys */
		if (c == 'A')
			return (KUP);
		if (c == 'B')
			return (KDOWN);
		if (c == 'C')
			return (KRIGHT);
		if (c == 'D')
			return (KLEFT);
		if (c == 'T')
			return (KSUP);
		if (c == 'S')
			return (KSDOWN);
		if (c == '?') {			/* HELP key		*/
			ttgetc();		/* discard '~'		*/
			return (KHELP);
		}
		/* Shifted left, right arrow */
		if (c == ' ') {
			c = ttgetc();
			if (c == 'A' || c == '@')
				return (c == 'A') ? (KSLEFT) : (KSRIGHT);
			goto loop;		/* try again, sucker */
		}

		/* Function keys	*/
		if (c >= '0' && c <= '9') {
			n = 0;
			do {
				n = 10*n + c - '0';
				c = ttgetc();
			} while (c>='0' && c<='9');
			if (c == '~' && n < NFUNCKEYS) {
				c = consolemap[n];
				if (c != KRANDOM)
					return (c);
				goto loop;
			}
			else 
				goto loop;	/* Try again */
		}
		goto loop;		/* Try again */
	}

	/* Meta keys -- also check for high bit set */
	if ((c == ESC) || (GR_to_META && (c & 0x80))) {
		if (c == ESC)
			c = ttgetc();
		c &= 0x7F;			/* Strip high bit	*/
		if (ISLOWER(c) != FALSE)	/* Copy the standard	*/
			c = TOUPPER(c);		/* META code.		*/
		if (c>=0x00 && c<=0x1F)
			c = KCTRL | (c+'@');
		return (KMETA | c);
	}
	return (c);
}

/*
 * Terminal specific keymap initialization.
 * Attach the special keys to the appropriate built
 * in functions. Bind all of the assigned graphics in the
 * Amiga alternate character set to self-insert.
 *
 * Bind the fake KMENU code to amigamenu() to do
 * menu selection as transparently as possible.
 * Ditto with KMOUSE. As is the case of all the
 * keymap routines, errors are very fatal.
 */

extern	int	amigamenu();		/* Defined by "ttymenu.c" 	*/
extern	int	amigamouse();		/* Defined by "ttymouse.c"	*/

ttykeymapinit()
{
	register SYMBOL	*sp;
	register int	i;

	/* ADD these bindings instead of duplicating it */
	keyadd(KMENU,	amigamenu,	"amiga-menu");
	keyadd(KMOUSE,	amigamouse,	"amiga-mouse");

	keydup(KUP,	"back-line");
	keydup(KDOWN,	"forw-line");
	keydup(KRIGHT,	"forw-char");
	keydup(KLEFT,	"back-char");
	keydup(KHELP,	"help");
	keydup(KSUP,	"goto-bop");
	keydup(KSDOWN,	"goto-eop");
	keydup(KSRIGHT,	"forw-word");
	keydup(KSLEFT,	"back-word");

	keydup(KF1,	"down-window");
	keydup(KF2,	"forw-page");
	keydup(KF3,	"enlarge-window");
	keydup(KF4,	"forw-window");
	keydup(KF5,	"split-window");
	keydup(KF6,	"file-visit");
	keydup(KF7,	"file-save");
	keydup(KF8,	"start-macro");
	keydup(KF9,	"bind-to-key");
	keydup(KF10,	"display-version");

	keydup(KSF1,	"up-window");
	keydup(KSF2,	"back-page");
	keydup(KSF3,	"shrink-window");
	keydup(KSF4,	"back-window");
	keydup(KSF5,	"only-window");
	keydup(KSF6,	"file-read");
	keydup(KSF7,	"file-write");
	keydup(KSF8,	"end-macro");
	keydup(KSF9,	"display-bindings");
	keydup(KSF10,	"quit");

	/*
	 * Bind all positions that correspond
	 * to characters in the Amiga alternate
	 * character set to "ins-self". These characters may
	 * be used just like any other character.
	 */

	if ((sp=symlookup("ins-self")) == NULL)
		abort();
	for (i=0xA0; i<0xFF; ++i) {
		if (binding[i] != NULL)
			abort();
		binding[i] = sp;
		++sp->s_nkey;
	}
}
-h- ttymenu.c	Wed Apr 23 08:53:04 1986	ttymenu.c
/*
 * Name:	MicroEmacs
 * 		Commodore Amiga Intuition menus
 * Version:	31
 * Last edit:	20-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 * Created:	20-Apr-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <graphics/text.h>
#include <libraries/diskfont.h>
#include <intuition/intuition.h>
#undef	TRUE
#undef	FALSE
#include "def.h"	/* Also includes "sysdef.h" and "ttydef.h" */

/*
 * When ttgetc() sees a menu selection event, it stuffs
 * the sequence <CSI>M~ into the input buffer, and
 * caches the menu number and item number for later.
 * This sequence is translated into the internal key code
 * KMENU, similar to KHELP and the other function keys.
 *
 * The menu item names are chosen to be relatively close
 * to the extended function names, so a user can usually
 * figure out the key binding of a menu item by searching
 * through the "display-bindings" buffer for something
 * that's close.
 */

/*
 * Commands for managing files
 */

static struct MenuBinding FileItems[] = {
	{ "Read File",		"file-read",		},
	{ "Save File",		"file-save",		},
	{ "Visit File",		"file-visit",		},
	{ "Write File",		"file-write"		},
	{ "Insert File",	"file-insert"		},
	{ "Set File Name",	"set-file-name"		},
	{ "Quit",		"quit",			}
};

/*
 * Commands for various editing functions
 */

static struct MenuBinding EditItems[] = {
	{ "Forward Del Char",	"forw-del-char"		},
	{ "Kill Line",		"kill-line"		},
	{ "Yank",		"yank"			},
	{ "Delete Blank Lines",	"del-blank-lines"	},
	{ "NL And Indent",	"ins-nl-and-indent"	},
	{ "Twiddle",		"twiddle"		},
	{ "Quote Character",	"quote"			}
};

/*
 * Movement commands
 */

static struct MenuBinding MoveItems[] = {
	{ "Beg. Of  Line",	"goto-bol"		},
	{ "Beg. Of  Paragraph",	"goto-bop"		},
	{ "Beg. Of  Buffer",	"goto-bob"		},
	{ "End Of   Line",	"goto-eol"		},
	{ "End Of   Paragraph",	"goto-eop"		},
	{ "End Of   Buffer",	"goto-eob"		},
	{ "Goto Line",		"goto-line"		},
	{ "Show Cursor Pos",	"display-position"	}
};

/*
 * Commands for searching and replacing
 */

static struct MenuBinding SearchItems[] = {
	{ "Search Again",	"search-again"	},
	{ "Forward  I-Search",	"forw-i-search"	},
	{ "Backward I-Search",	"back-i-search"	},
	{ "Forward  Search",	"forw-search"	},
	{ "Backward Search",	"back-search"	},
	{ "Query Replace",	"query-replace"	},
};

/*
 * Commands that manipulate words
 */
static struct MenuBinding WordItems[] = {
	{ "Forward  Word",	"forw-word"		},
	{ "Backward Word",	"back-word"		},
	{ "Forward Del Word",	"forw-del-word" 	},
	{ "Backward Del Word",	"back-del-word" 	},
	{ "Capitalize Word",	"cap-word"		},
	{ "Lowercase  Word",	"lower-word"		},
	{ "Uppercase  Word",	"upper-word"		},
	{ "Fill Paragraph",	"fill-paragraph"	},
	{ "Set Fill Column",	"set-fill-column"	}
};

/*
 * Region management
 */

static struct MenuBinding RegionItems[] = {
	{ "Set Mark",		"set-mark"		},
	{ "Swap Dot And Mark",	"swap-dot-and-mark"	},
	{ "Kill Region",	"kill-region"		},
	{ "Copy Region",	"copy-region"		},
	{ "Lowercase Region",	"lower-region"		},
	{ "Uppercase Region",	"upper-region"		}
};

/*
 * Buffer management
 */

static struct MenuBinding BufferItems[] = {
	{ "Use Buffer",		"use-buffer" 		},
	{ "Kill Buffer",	"kill-buffer"		},
	{ "Display Buffers",	"display-buffers"	},
	{ "Rename Buffer",	"rename-buffer"		}
};

/*
 * Commands for manipulating windows
 */

static struct MenuBinding WindowItems[] = {
	{ "Refresh Screen",	"refresh"		},
	{ "Reposition Window",	"reposition-window"	},
	{ "Split Window",	"split-window"		},
	{ "One Window",		"only-window"		},
	{ "Next Window",	"forw-window"		},
	{ "Previous Window",	"back-window"		},
	{ "Move Window Down",	"down-window"		},
	{ "Move Window Up",	"up-window"		},
	{ "Enlarge Window",	"enlarge-window"	},
	{ "Shrink Window",	"shrink-window"		}
};

/*
 * Miscellaneous commands
 */

static struct MenuBinding MiscItems[] = {
	{ "About MicroEMACS",	"display-version"	},
	{ "Start Macro",	"start-macro"		},
	{ "End Macro",		"end-macro"		},
	{ "Execute Macro",	"execute-macro"		},
	{ "Extended Command",	"extended-command"	},
	{ "Bind Key",		"bind-to-key"		},
	{ "Display Bindings",	"display-bindings"	},
	{ "CLI Subprocess",	"spawn-cli"		}
};

/*
 * The following table contains the titles, number of
 * items, and pointers to, the individual menus.
 *
 * The MenuWidth field is the number of characters
 * in the longest item name.
 */

static struct MenuInfo EMInfo[] = {
	{ "File",	NITEMS(FileItems),	13,	&FileItems[0]	},
	{ "Edit",	NITEMS(EditItems),	18,	&EditItems[0]	},
	{ "Move", 	NITEMS(MoveItems),	18,	&MoveItems[0]	},
	{ "Search",	NITEMS(SearchItems),	17,	&SearchItems[0] },
	{ "Word",	NITEMS(WordItems),	17,	&WordItems[0]	},
	{ "Region",	NITEMS(RegionItems),	17,	&RegionItems[0]	},
	{ "Buffer",	NITEMS(BufferItems),	15,	&BufferItems[0]	},
	{ "Window",	NITEMS(WindowItems),	17,	&WindowItems[0] },
	{ "Misc",	NITEMS(MiscItems),	16,	&MiscItems[0]	}
};

/*
 * Initialize the MicroEmacs menus.
 *
 * Returns a pointer to the first menu header in
 * the menu list back to the caller.
 *
 * Makes the assumption that the font for
 * the window being used is 8 pixels wide --
 * a safe bet with the version 1.1 ROM Kernel.
 */

struct Menu *InitEmacsMenu()
{
	struct Menu *EMMenu, *NewMenu(), *AddMenu();
	struct MenuItem *AddNewMenuItem(), *AddItem(), *AddNewSubItem();

	register struct Menu *cm;
	register struct MenuInfo *cinf, *lastinfo;/* current menu info	*/
	register struct MenuBinding *cb;	/* current ``binding''	*/

	lastinfo = &EMInfo[NITEMS(EMInfo)];	/* sentinel for loop	*/

	for (cinf = EMInfo; cinf < lastinfo; cinf++) {
		if (cinf == EMInfo)		/* create menu header	*/
			EMMenu = cm = NewMenu(cinf->Name,
				strlen(cinf->Name) * 8 + 6, 10);
		else
			cm = AddMenu(cm,
				cinf->Name,strlen(cinf->Name) * 8 + 6, 10);
		MakeMenu(cinf,cm,cinf->MenuWidth * 8 + 2, 10);
	}
	return EMMenu;
}

/*
 * Put menu items in a menu.  Width and
 * height control the respective
 * sizes of the menu items.
 */

static	MakeMenu(cinf,cm,width,height)
struct MenuInfo *cinf;
struct Menu *cm;
int width, height;
{
	struct Menu *NewMenu(), *AddMenu();
	struct MenuItem *AddNewMenuItem(), *AddItem(), *AddNewSubItem();

	register struct MenuBinding *cb, *lastbinding;
	register struct MenuItem *ci;

	lastbinding = &cinf->Items[cinf->NumItems];
	for (cb = cinf->Items; cb < lastbinding; cb++) {
		if (cb == cinf->Items)
			ci = AddNewMenuItem(cm, cb->Command, width, height);
		else
			ci = AddItem(ci, cb->Command);
	}
}

/*
 * Menu command.   Get the name of the command to
 * perform from the menu binding table, then
 * run the command if it is found and has the
 * right type. Print an error if there is anything
 * wrong.
 */
extern ttmenu();			/* Defined in "ttyio.c" 	*/
					/* Returns last menu selection	*/
amigamenu(f, n, k)
{
	register SYMBOL	*sp, *symlookup();
	register int	s;
	char		*xname;
	int MenuNum, ItemNum, SubItem;

	ttmenu(&MenuNum,&ItemNum,&SubItem);

	/* check for spurious numbers */
	if ((MenuNum < 0) || (MenuNum >= NITEMS(EMInfo)))
		return (FALSE);
	if ((ItemNum < 0) || (ItemNum >= EMInfo[MenuNum].NumItems))
		return (FALSE);	

	xname = EMInfo[MenuNum].Items[ItemNum].Binding;

	if ((sp=symlookup(xname)) != NULL)
		return ((*sp->s_funcp)(f, n, KRANDOM));
	eprintf("Unknown menu command");
	return (ABORT);
}
-h- ttymouse.c	Wed Apr 23 08:53:04 1986	ttymouse.c
/*
 * Name:	MicroEmacs
 *		(Simple) Commodore Amiga mouse handling
 * Version:	31
 * Last edit:	21-Apr-86
 * Created:	21-Apr-86  ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 */

#include <exec/types.h>
#include <intuition/intuition.h>
#undef	TRUE
#undef	FALSE
#include "def.h"

extern	int	ttmouse();		/* Defined by "ttyio.c"		*/
extern	int	forwline();		/* Defined by "line.c"		*/
extern	int	forwchar();		/* Defined by "basic.c"		*/

/*
 * Handle the mouse click that's been passed
 * by ttgetc() and position dot where the
 * user pointed at.
 */

amigamouse(f, n, k)
{
	register WINDOW *wp;
	register int	dot;
	register int	c;
	register int	col;
	register int	newrow, newcol;
	SHORT	x, y;
	USHORT	qualifier;

	/* figure out new screen position of dot */
	ttmouse(&x, &y, &qualifier);
	newcol = x / 8;
	newrow = y / 8;

	/* find out which window was clicked in	*/
	for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
		if ((newrow >= wp->w_toprow) && 
			(newrow < (wp->w_toprow + wp->w_ntrows)))
			break;

	/* make sure click was in a bona fide window */
	if (wp == NULL)
		return (ABORT);

	/* move dot to top left of selected window */
	curwp = wp;
	curbp = wp->w_bufp;
	curwp->w_dotp = wp->w_linep;
	curwp->w_doto = 0;

	/* go forward the correct # of lines 		*/
	forwline(FALSE, newrow - curwp->w_toprow, KRANDOM);
	
	/* go forward the correct # of characters	*/
	/* need to count them out because of tabs	*/
	col = dot = 0;
	while ((col < newcol) && (dot < llength(curwp->w_dotp))) {
		c = lgetc(curwp->w_dotp, dot++);
		if (c == '\t')
			col |= 0x07;
		else if (ISCTRL(c) != FALSE)
			++col;
		++col;
	}
	forwchar(FALSE, dot, KRANDOM);

	return (TRUE);
}