[comp.sources.amiga] v02i012: dme - programmer's text editor V1.31, Part04/06

page@swan.ulowell.edu (Bob Page) (10/22/88)

Submitted-by: dillon@cory.berkeley.edu (Matt Dillon)
Posting-number: Volume 2, Issue 12
Archive-name: editors/dme131.4of6

# This is a shell archive.  Remove anything before this line
# then unpack it by saving it in a file and typing "sh file"
# (Files unpacked will be owned by you and have default permissions).
# This archive contains the following files:
#	./src/menu.c
#	./src/mods.c
#	./src/rexxbind.asm
#	./src/command.c
#	./src/cmd3.c
#	./src/keyboard.c
#
if `test ! -d ./src`
then
  mkdir ./src
  echo "mkdir ./src"
fi
if `test ! -s ./src/menu.c`
then
echo "writing ./src/menu.c"
cat > ./src/menu.c << '\Rogue\Monster\'

/*
 *  MENU.C
 *
 *  Menu routines... made to take up as little space as possible, and
 *  thus uses many tricks which you should watch out for.
 */

#include "defs.h"

typedef struct {
    ITEM item;
    char *com;
} XITEM;

short Menuoff;
short DoMenuoff;

MENU *Menu;

menu_strip(win)
WIN *win;
{
    if (!Menuoff && Menu) {
	SetMenuStrip(win,Menu);
	Forbid();
	win->Flags &= ~RMBTRAP;
	Permit();
    }
}

menu_off()
{
    register ED *ed;
    if (Menuoff == 0) {
	for (ed = (ED *)DBase.mlh_Head; ed->Node.mln_Succ; ed = (ED *)ed->Node.mln_Succ) {
	    ClearMenuStrip(ed->Win);
	    Forbid();
	    ed->Win->Flags |= RMBTRAP;
	    Permit();
	}
    }
    ++Menuoff;
}

menu_on()
{
    register ED *ed;
    if (Menu && Menuoff == 1) {
	fixmenu();
	for (ed = (ED *)DBase.mlh_Head; ed->Node.mln_Succ; ed = (ED *)ed->Node.mln_Succ) {
	    SetMenuStrip(ed->Win,Menu);
	    Forbid();
	    ed->Win->Flags &= ~RMBTRAP;
	    Permit();
	}
    }
    --Menuoff;
}

do_menuoff()
{
    menu_off();
    ++DoMenuoff;
}

do_menuon()
{
    if (DoMenuoff) {
	--DoMenuoff;
	menu_on();
    }
}

char *
menutomacro(str)
char *str;
{
    char header[64];
    char itembuf[64];
    register short i;
    register char *ptr;
    register MENU *menu;
    register ITEM *item;

    for (i = 0; str[i] && str[i] != '-'; ++i);
    if (str[i] == '-') {
	strncpy(header, str, i);
	header[i] = 0;
	strcpy(itembuf, str + i + 1);
	for (menu = Menu; menu; menu = menu->NextMenu) {
	    if (ncstrcmp(header, menu->MenuName) == 0) {
		for (item = menu->FirstItem; item; item = item->NextItem) {
		    ptr = (char *)((ITEXT *)item->ItemFill)->IText;
		    if (ncstrcmp(itembuf, ptr) == 0) {
			ptr = ((XITEM *)item)->com;
			goto done;
		    }
		}
	    }
	}
    }
    ptr = NULL;
done:
    return(ptr);
}

char *
menu_cmd(im)
IMESS *im;
{
    XITEM *item;
    char *ptr;

    if (item = (XITEM *)ItemAddress(Menu, im->Code))
	return(item->com);
    return(NULL);
}

fixmenu()
{
    register MENU *menu;
    register ITEM *item;
    register ITEXT *it;
    register int row, col, maxc, scr;

    col = 0;
    for (menu = Menu; menu; menu = menu->NextMenu) {
	maxc = strlen(menu->MenuName);
	row = 0;
	for (item = menu->FirstItem; item; item = item->NextItem) {
	    it = (ITEXT *)item->ItemFill;
	    item->TopEdge = row;
	    scr = strlen(((ITEXT *)item->ItemFill)->IText);
	    if (scr > maxc)
		maxc = scr;
	    item->Height = 10;
	    row += item->Height;
	}
	maxc = (maxc * 8) + 16;
	for (item = menu->FirstItem; item; item = item->NextItem)
	    item->Width = maxc;
	menu->Width = maxc;
	menu->LeftEdge = col;
	menu->Height = row;
	col += maxc;
    }
}

/*
 *  menuclear
 *  menuadd	header	item	command
 *  menudel	header	item
 *  menudelhdr	header
 */

do_menuclear()
{
    menu_off();
    while (Menu) {
	av[1] = (ubyte *)Menu->MenuName;
	do_menudelhdr();
    }
    menu_on();
}

do_menuadd()
{
    register MENU *menu, **mpr;
    register ITEM *item, **ipr;
    register ITEXT *it;

    menu_off();
    mpr = &Menu;
    for (menu = *mpr; menu; menu = *mpr) {
	if (strcmp(av[1], menu->MenuName) == 0) {
	    ipr = &menu->FirstItem;
	    for (item = *ipr; item; item = *ipr) {
		if (strcmp(av[2], ((ITEXT *)item->ItemFill)->IText) == 0)
		    goto newname;
		ipr = &item->NextItem;
	    }
	    goto newitem;
	}
	mpr = &menu->NextMenu;
    }
newmenu:    /*	create new menu */
    menu = malloc(sizeof(MENU));
    bzero(menu, sizeof(MENU));
    menu->NextMenu = *mpr;
    *mpr = menu;
    menu->Flags = MENUENABLED;
    menu->MenuName = malloc(strlen(av[1])+1);
    strcpy(menu->MenuName, av[1]);
    ipr = &menu->FirstItem;
    *ipr = NULL;
newitem:    /*	create new item */
    it = malloc(sizeof(ITEXT));
    bzero(it, sizeof(ITEXT));
    it->BackPen = 1;
    it->DrawMode = JAM2;
    it->IText = malloc(strlen(av[2])+1);
    strcpy(it->IText, av[2]);
    item = malloc(sizeof(XITEM));
    bzero(item, sizeof(XITEM));
    item->NextItem = *ipr;
    *ipr = item;
    item->ItemFill = (APTR)it;
    item->Flags = ITEMTEXT|ITEMENABLED|HIGHCOMP;
newname:    /*	create new name */
    if (((XITEM *)item)->com)
	free(((XITEM *)item)->com);
    ((XITEM *)item)->com = malloc(strlen(av[3])+1);
    strcpy(((XITEM *)item)->com, av[3]);
    menu_on();
}

do_menudelhdr()
{
    register MENU *menu;
    register MENU **mpr;

    menu_off();
    mpr = &Menu;
    for (menu = *mpr; menu; menu = *mpr) {
	if (strcmp(av[1], menu->MenuName) == 0) {
	    if (menu->FirstItem) {
		while (menu->FirstItem) {
		    av[2] = ((ITEXT *)menu->FirstItem->ItemFill)->IText;
		    if (do_menudel())
			break;
		}
		break;
	    }
	    *mpr = menu->NextMenu;
	    free(menu->MenuName);
	    free(menu);
	    break;
	}
	mpr = &menu->NextMenu;
    }
    menu_on();
}

do_menudel()
{
    register MENU *menu;
    register ITEM *item, **ipr;
    register ITEXT *it;
    short ret = 0;

    menu_off();
    for (menu = Menu; menu; menu = menu->NextMenu) {
	if (strcmp(av[1], menu->MenuName) == 0) {
	    ipr = &menu->FirstItem;
	    for (item = *ipr; item; item = *ipr) {
		it = (ITEXT *)item->ItemFill;
		if (strcmp(av[2], it->IText) == 0) {
		    *ipr = item->NextItem;
		    free(it->IText);
		    free(it);
		    free(((XITEM *)item)->com);
		    free(item);
		    if (!menu->FirstItem) {
			do_menudelhdr();
			ret = 1;
		    }
		    menu_on();
		    return(ret);
		}
		ipr = &item->NextItem;
	    }
	}
    }
    menu_on();
    return(ret);
}

\Rogue\Monster\
else
  echo "will not over write ./src/menu.c"
fi
if [ `wc -c ./src/menu.c | awk '{printf $1}'` -ne 5387 ]
then
echo `wc -c ./src/menu.c | awk '{print "Got " $1 ", Expected " 5387}'`
fi
if `test ! -s ./src/mods.c`
then
echo "writing ./src/mods.c"
cat > ./src/mods.c << '\Rogue\Monster\'

/*
 *  KTS.C
 *
 *  Additional DME commands written by Kevin T. Seghetti fixed up and
 *  incorporated by Matt Dillon 17 April 1988.
 */

#include "defs.h"

#define BLOCKDEPTH  5
#define PINGDEPTH   10

static long BSstack[BLOCKDEPTH];
static long BEstack[BLOCKDEPTH];
static ED   *Bp[BLOCKDEPTH];
static int  CurrDepth = 0;

static long PingLine[PINGDEPTH];
static long PingCol[PINGDEPTH];
static ED   *PingWin[PINGDEPTH];

void
PMAdd()
{
}

void
PMRem()
{
}

void
PMKill(ep)
ED *ep;
{
    register short i, j;

    for (i = 0; i < PINGDEPTH; ++i) {       /*  remove ping-pong marks  */
	if (PingWin[i] == ep)
	    PingWin[i] = NULL;
    }
    for (i = j = 0; i < CurrDepth; ++i) {   /*  remove block marks      */
	Bp[j] = Bp[i];
	if (Bp[i] != ep)
	    ++j;
    }
    CurrDepth = j;
}


do_pushmark()
{
    text_sync();
    if (blockok()) {
	if (CurrDepth == BLOCKDEPTH) {
	    title("pushmark: stack limit reached");
	    return(-1);
	}
	BSstack[CurrDepth] = BSline;
	BEstack[CurrDepth] = BEline;
	Bp[CurrDepth] = BEp;

	++CurrDepth;
	text_redrawblock(0);
    }
    return(0);
}

void
do_popmark()
{
    text_sync();

    if (!CurrDepth) {           /*  no error message on purpose */
	text_redrawblock(0);    /*  remove any existing block   */
	return;
    }
    text_redrawblock(0);
    --CurrDepth;
    BSline = BSstack[CurrDepth];
    BEline = BEstack[CurrDepth];
    BEp = Bp[CurrDepth];
    if (BEp == NULL || BEline >= BEp->Lines) {
	BEp = NULL;
	BSline = BEline = -1;
    } else
	text_redrawblock(1);
}

void
do_swapmark()
{
    register short i;
    register long *ptmp;
    register long tmp;

    if (do_pushmark() < 0)
	return;
    i = CurrDepth - 2;
    if (i >= 0) {
	ptmp = PingLine + i;
	tmp = ptmp[0]; ptmp[0] = ptmp[1]; ptmp[1] = tmp;
	ptmp = PingCol + i;
	tmp = ptmp[0]; ptmp[0] = ptmp[1]; ptmp[1] = tmp;
	ptmp = (long *)PingWin + i;
	tmp = ptmp[0]; ptmp[0] = ptmp[1]; ptmp[1] = tmp;
    }
    do_popmark();
}

do_purgemark()
{
    CurrDepth = 0;
}

void
do_ping()
{
    register uword num = atoi(av[1]);

    if (num >= PINGDEPTH) {
	title("ping: out of range");
	return;
    }
    PingLine[num]= Ep->Line;
    PingCol[num] = Ep->Column;
    PingWin[num] = Ep;
    title("Line marked");
}

void
do_pong()
{
    register uword num = atoi(av[1]);
    extern IBASE *IntuitionBase;

    text_sync();
    if (num < 0 || num >= PINGDEPTH || !PingWin[num]) {
	title("pong: range error or nothing marked");
	return;
    }
    text_cursor(1);
    text_switch(PingWin[num]->Win);
    text_cursor(0);

    if (IntuitionBase->ActiveWindow != Ep->Win) {
	WindowToFront(Ep->Win);
	ActivateWindow(Ep->Win);
    }
    if ((Ep->Line = PingLine[num]) >= Ep->Lines) {
	PingLine[num] = Ep->Line = Ep->Lines - 1;
    }
    Ep->Column = PingCol[num];
    text_load();
    text_sync();
}

void
do_undo()
{
    text_load();
    text_redisplaycurrline();
}

\Rogue\Monster\
else
  echo "will not over write ./src/mods.c"
fi
if [ `wc -c ./src/mods.c | awk '{printf $1}'` -ne 2857 ]
then
echo `wc -c ./src/mods.c | awk '{print "Got " $1 ", Expected " 2857}'`
fi
if `test ! -s ./src/rexxbind.asm`
then
echo "writing ./src/rexxbind.asm"
cat > ./src/rexxbind.asm << '\Rogue\Monster\'
* === rexxbind.asm =====================================================
*
* Copyright (c) 1986, 1987 by William S. Hawes (All Rights Reserved)
*
* ======================================================================
* "Glue" routines for calling functions in the ARexx Systems Library.
* All calls assume that the external _RexxSysBase has been set to the
* ARexx SYstems library base by a call to OpenLibrary.

         INCLUDE  "rexx/storage.i"
         INCLUDE  "rexx/rxslib.i"

         XREF     _RexxSysBase

* First calling convention:
* 1, 2, or 3 parameters in (A0,A1,D0), return value in D0.

         ; msgptr = CreateRexxMsg(&replyport,&fileext,&hostname)

         XDEF     _CreateRexxMsg
_CreateRexxMsg:
         move.w   #_LVOCreateRexxMsg,d1
         bra.s    CallSeq1


         ; DeleteArgstring(argptr)

         XDEF     _DeleteArgstring
_DeleteArgstring:
         move.w   #_LVODeleteArgstring,d1
         bra.s    CallSeq1


         ; DeleteRexxMsg(msgptr)

         XDEF     _DeleteRexxMsg
_DeleteRexxMsg:
         move.w   #_LVODeleteRexxMsg,d1
         bra.s    CallSeq1


         ; FreePort(&msgport)

         XDEF     _FreePort
_FreePort:
         move.w   #_LVOFreePort,d1
         bra.s    CallSeq1


         ; signal = InitPort(&replyport)

         XDEF     _InitPort
_InitPort:
         move.w   #_LVOInitPort,d1
         bra.s    CallSeq1


         ; boolean = IsRexxMsg(msgptr)

         XDEF     _IsRexxMsg
_IsRexxMsg:
         move.w   #_LVOIsRexxMsg,d1
         bra.s    CallSeq1


         ; Load three arguments into (A0,A1,D0)

CallSeq1 movea.l  4(sp),a0
         movea.l  8(sp),a1
         move.l   12(sp),d0


         ; Call the library function

CallFunc move.l   a6,-(sp)
         movea.l  _RexxSysBase,a6
         jsr      0(a6,d1.w)
         movea.l  (sp)+,a6
         rts


* Second calling convention:  2 parameters in (A0,D0), return value in D0.

         ; argptr = CreateArgstring(&string,length)

         XDEF     _CreateArgstring
_CreateArgstring:
         moveq    #_LVOCreateArgstring,d1
         bra.s    CallSeq2


         ; ClearMem(address,length)

         XDEF     _ClearMem
_ClearMem:
         move.w   #_LVOClearMem,d1
         bra.s    CallSeq2


         ; Load two arguments (A0,D0)

CallSeq2 movea.l  4(sp),a0
         move.l   8(sp),d0
         bra      CallFunc

         END

\Rogue\Monster\
else
  echo "will not over write ./src/rexxbind.asm"
fi
if [ `wc -c ./src/rexxbind.asm | awk '{printf $1}'` -ne 2357 ]
then
echo `wc -c ./src/rexxbind.asm | awk '{print "Got " $1 ", Expected " 2357}'`
fi
if `test ! -s ./src/command.c`
then
echo "writing ./src/command.c"
cat > ./src/command.c << '\Rogue\Monster\'

/*
 * COMMAND.C
 *
 *	(C)Copyright 1987 by Matthew Dillon, All Rights Reserved
 *
 * )c		     single character (typing)
 * 'c                single character (typing)
 * `string'          string of characters w/ embedded `' allowed!
 * (string)             same thing w/ embedded () allowed!
 * \c		     override
 *
 * name arg arg      command name. The arguments are interpreted as strings
 *		     for the command.
 *
 * $scanf	     macro insert scanf'd variable
 * $filename	     macro insert current file name
 *
 * Any string arguments not part of a command are considered to be typed
 * text.
 */

#include "defs.h"
#include <stdio.h>

#if AREXX
extern int foundcmd;	   /* control for implicit ARexx macro invocation   */
extern int cmderr;	   /* global command error flag for do_rexx()'s use */
#endif

#define CF_COK	1   /*	Can be executed while in command line mode	*/
#define CF_PAR	2   /*	ESCIMM special flag.. save rest of command line */
		    /*	so it can be executed after user entry		*/

#define CF_ICO	4   /*	OK to execute if iconified, else uniconify first*/

extern char *breakout();

typedef struct {
   char *name;	    /* command name	  */
   ubyte args;
   ubyte flags;
   int (*func)();   /* function           */
} COMM;

extern int  do_map(),       do_unmap(),     do_up(),        do_down(),
	    do_left(),      do_right(),     do_return(),    do_bs(),
	    do_del(),       do_esc(),       do_downadd(),   do_lastcolumn(),
	    do_firstcolumn(),do_edit(),     do_tab(),       do_backtab(),
	    do_save(),      do_saveas(),    do_deline(),    do_insline(),
	    do_top(),       do_bottom(),    do_source(),    do_firstnb(),
	    do_quit(),      do_find(),      do_page(),      do_savetabs(),
	    do_split(),     do_goto(),      do_screentop(), do_screenbottom(),
	    do_join(),      do_repeat(),    do_tabstop(),   do_insertmode(),
	    do_block(),     do_bdelete(),   do_bcopy(),     do_bmove(),
	    do_bsave(),     do_wleft(),     do_wright(),    do_remeol(),
	    do_savemap(),   do_toggle(),    do_if(),        do_tlate(),
	    do_bsource(),   do_findr(),     do_findstr(),   do_newwindow(),
	    do_windowparm(),do_resize(),    do_margin(),    do_wordwrap(),
	    do_reformat(),  do_execute(),   do_chfilename(),do_scrollup(),
	    do_scrolldown(),do_recall(),    do_scanf(),     do_iconify(),
	    do_tomouse(),   do_refs(),      do_arpload(),   do_arpsave(),
	    do_arpinsfile(),do_setfont(),   do_ignorecase(),do_ctags(),
	    do_addpath(),   do_rempath(),   do_set(),       do_setenv(),
	    do_unset(),     do_unsetenv(),  do_ipc(),       do_cd();

extern int  do_menu(), do_menuclear(), do_menuadd(), do_menudel(),
	    do_menudelhdr(), do_menuon(), do_menuoff();

extern int  do_null(), do_rx();

extern int  do_pushmark(),  do_popmark(),   do_swapmark(),  do_purgemark(),
	    do_ping(),      do_pong(),      do_undo();

#if AREXX
extern int  do_rx(),        do_rx1(),       do_rx2();
#endif

/*============================================================================*/

/*
 *  WLEFT/WRIGHT will check command line mode themselves, and thus can
 *  be marked flags=1 even though they can change the line number.
 *
 *  No more than 255 commands may exist unless you change the type of hindex[]
 *
 *  Command names MUST be sorted by their first character
 */

unsigned char hindex[26];   /*	alpha hash into table	*/

	/*	  args flags	*/

COMM Comm[] = {
#ifndef NO_DO2
    "addpath",       1, CF_COK, do_addpath,
#endif
    "arpinsfile",    0,      0, do_arpinsfile,
    "arpload",       0,      0, do_arpload,
    "arpsave",       0,      0, do_arpsave,
    "back",          0, CF_COK, do_bs,
    "backtab",       0, CF_COK, do_backtab,
    "bcopy",         0,      0, do_bcopy,
    "bdelete",       0,      0, do_bdelete,
    "block",         0,      0, do_block,    /* checks com name for mode */
    "bmove",         0,      0, do_bmove,
    "bottom",        0,      0, do_bottom,
    "bs",            0, CF_COK, do_bs,
    "bsave",         1, CF_COK, do_bsave,
    "bsource",       0,      0, do_bsource,
    "cd",            1, CF_COK, do_cd,
    "chfilename",    1,      0, do_chfilename,
#ifndef NO_DO_CTAGS
    "ctags",         0, CF_ICO, do_ctags,
#endif
    "del",           0, CF_COK, do_del,
    "deline",        0,      0, do_deline,
    "down",          0,      0, do_down,
    "downadd",       0,      0, do_downadd,
    "esc",           0, CF_COK, do_esc,
    "escimm",        1, CF_PAR, do_esc,
    "execute",       1, CF_ICO, do_execute,
    "find",          1,      0, do_find,     /* checks com name for mode */
    "findr",         2,      0, do_findr,    /* checks com name for mode */
    "findstr",       1, CF_COK, do_findstr,  /* checks com name for mode */
    "first",         0, CF_COK, do_firstcolumn,
    "firstnb",       0, CF_COK, do_firstnb,
    "goto",          1,      0, do_goto,
    "height",        1, CF_COK, do_windowparm,
    "iconify",       0, CF_ICO, do_iconify,
    "if",            2, CF_COK, do_if,
    "ifelse",        3, CF_COK, do_if,
    "ignorecase",    1, CF_COK, do_ignorecase,
    "insertmode",    1, CF_COK, do_insertmode,
    "insfile",       1,      0, do_edit,
    "insline",       0,      0, do_insline,
    "ipc",           3, CF_COK, do_ipc,
    "join",          0,      0, do_join,
    "last",          0, CF_COK, do_lastcolumn,
    "left",          0, CF_COK, do_left,
    "leftedge",      1, CF_COK, do_windowparm,
    "map",           2, CF_COK, do_map,
    "margin",        1, CF_COK, do_margin,
    "menuon",        0,      0, do_menuon,
    "menuoff",       0,      0, do_menuoff,
    "menuadd",       3,      0, do_menuadd,
    "menudel",       2,      0, do_menudel,
    "menudelhdr",    1,      0, do_menudelhdr,
    "menuclear",     0,      0, do_menuclear,
    "newfile",       1,      0, do_edit,     /* checks com name for mode */
    "newwindow",     0, CF_ICO, do_newwindow,
    "next",          0,      0, do_find,
    "nextr",         0,      0, do_findr,
    "null",          0, CF_COK, do_null,
    "pagedown",      0,      0, do_page,
    "pageset",       1,      0, do_page,
    "pageup",        0,      0, do_page,
    "ping",          1, CF_ICO, do_ping,
    "pong",          1,      0, do_pong,
    "prev",          0,      0, do_find,
    "prevr",         0,      0, do_findr,
    "popmark",       0,      0, do_popmark,
    "purgemark",     0,      0, do_purgemark,
    "pushmark",      0,      0, do_pushmark,
    "quit",          0, CF_ICO, do_quit,
    "recall",        0, CF_COK, do_recall,
#ifndef NO_DO_REF
    "ref",           0,      0, do_refs,
#endif
    "reformat",      0,      0, do_reformat,
    "remeol",        0, CF_COK, do_remeol,
#ifndef NO_DO2
    "rempath",       1, CF_COK, do_rempath,
#endif
    "repeat",        2, CF_ICO|CF_COK, do_repeat,
    "repstr",        1, CF_COK, do_findstr,
    "resettoggle",   1, CF_COK, do_toggle,
    "resize",        2,      0, do_resize,
    "return",        0, CF_COK, do_return,   /* special meaning in command line mode */
    "right",         0, CF_COK, do_right,
#if AREXX
    "rx",            1,      0, do_rx,       /* explicit ARexx macro invocation      */
    "rx1",           2,      0, do_rx1,      /* explicit, with 1 arg  to ARexx macro */
    "rx2",           3,      0, do_rx2,      /* explicit, with 2 args to ARexx macro */
#endif
    "saveas",        1, CF_ICO|CF_COK, do_saveas,
    "savemap",       1, CF_ICO|CF_COK, do_savemap,  /* checks com name for mode */
    "saveold",       0, CF_ICO|CF_COK, do_save,
    "savesmap",      1, CF_ICO|CF_COK, do_savemap,
    "savetabs",      1, CF_ICO|CF_COK, do_savetabs,
    "scanf",         1, CF_COK, do_scanf,
    "screenbottom",  0,      0, do_screenbottom,
    "screentop",     0,      0, do_screentop,
    "scrollup",      0,      0, do_scrollup,
    "scrolldown",    0,      0, do_scrolldown,
    "set",           2, CF_ICO|CF_COK, do_set,
    "setenv",        2, CF_ICO|CF_COK, do_setenv,
    "setfont",       2,      0, do_setfont,
    "settoggle",     1, CF_COK, do_toggle,
    "source",        1, CF_COK, do_source,
    "split",         0,      0, do_split,
    "swapmark",      0,      0, do_swapmark,
    "tab",           0, CF_COK, do_tab,
    "tabstop",       1, CF_COK, do_tabstop,
    "tlate",         1, CF_COK, do_tlate,
    "tmpheight",     1, CF_COK, do_windowparm,
    "tmpwidth",      1, CF_COK, do_windowparm,
    "toggle",        1, CF_COK, do_toggle,
    "tomouse",       0,      0, do_tomouse,
    "top",           0,      0, do_top,
    "topedge",       1, CF_COK, do_windowparm,
    "unblock",       0,      0, do_block,
    "undo",          0,      0, do_undo,
    "unmap",         1, CF_ICO|CF_COK, do_unmap,
    "unset",         1, CF_ICO|CF_COK, do_unset,
    "unsetenv",      1, CF_ICO|CF_COK, do_unsetenv,
    "up",            0,      0, do_up,
    "while",         2, CF_ICO|CF_COK, do_if,
    "width",         1, CF_COK, do_windowparm,
    "wleft",         0, CF_COK, do_wleft,
    "wordwrap",      1, CF_COK, do_wordwrap,
    "wright",        0, CF_COK, do_wright,
    NULL, 0, 0, NULL
};

init_command()
{
    register short hi;
    register COMM *comm;

    hi = sizeof(Comm)/sizeof(Comm[0]) - 2;
    comm = Comm + hi;

    while (hi >= 0) {
	hindex[comm->name[0] - 'a'] = hi;
	--hi;
	--comm;
    }
}

#define MAXIA	5

do_command(str)
char *str;
{
    register char *arg;
    char *aux1, *aux2;
    char *repstr[MAXIA];
    char quoted;
    short repi = 0;
    register short i, j;
    static int level;

    if (++level > 20) {
	title("Recursion Too Deep!");
	--level;
#if AREXX
	foundcmd = 1;	/* to prevent us from trying an ARexx macro */
#endif
	return(0);
    }
    while (arg = breakout(&str, &quoted, &aux1)) {
	if (quoted) {
	    if (Ep->iconmode)
		uniconify();
	    text_write(arg);
	    goto loop;
	}
	for (i = 0; arg[i]; ++i) {
	    if (arg[i] >= 'A' && arg[i] <= 'Z')
		arg[i] += 'a' - 'A';
	}

	if (arg[0] >= 'a' && arg[0] <= 'z') {
	    register COMM *comm = &Comm[hindex[arg[0]-'a']];
	    for (; comm->name && comm->name[0] == arg[0]; ++comm) {
		if (strcmp(arg, comm->name) == 0) {
#if AREXX
		    foundcmd = 1;
#endif
		    av[0] = (ubyte *)comm->name;
		    for (j = 1; j <= comm->args; ++j) {
			av[j] = (ubyte *)breakout(&str, &quoted, &aux2);
			if (aux2) {
			    if (repi == MAXIA) {
				free(aux2);
				title("Command too complex");
				goto fail;
			    } else {
				repstr[repi++] = aux2;
			    }
			}
			if (!av[j]) {
			    title("Bad argument");
			    goto fail;
			}
		    }
		    av[j] = NULL;   /* end of arglist */
		    if ((comm->flags & CF_COK) || !Comlinemode) {
			if (comm->flags & CF_PAR) {
			    if (Partial)
				free(Partial);
			    Partial = (char *)malloc(strlen(str)+1);
			    strcpy(Partial, str);
			    str += strlen(str);     /*  skip string */
			}
			if (Ep->iconmode && !(comm->flags & CF_ICO))
			    uniconify();
			(*comm->func)(-1);
		    }
		    if (Abortcommand)
			goto fail;
		    goto loop;
		}
	    }
	}

	/* Command not found, check for macro	*/

	{
	    char *str;
	    int ret;
	    if ((str = keyspectomacro(arg)) || (str = menutomacro(arg))) {
		str = (char *)strcpy(malloc(strlen(str)+1), str);
		ret = do_command(str);
		free(str);
#if AREXX
		if (ret) {
		    foundcmd = 1;   /* dunno about this yet for ARexx macros */
		    goto loop;
		}
#else
		if (ret)
		    goto loop;
#endif
		goto fail;
	    }
	}

	/* Command still not found, check for public macro  */
	/* code to be added */

#if AREXX
	do_rxImplied(arg, str);
#else
	title("Unknown Command");
#endif
fail:
	--level;
	while (--repi >= 0)
	    free(repstr[repi]);
	if (aux1)
	    free(aux1);
	return(0);
loop:
	if (aux1)
	    free(aux1);
    }
    --level;
    while (--repi >= 0)
	free(repstr[repi]);
    return(1);
}

do_null()
{
}

do_source()
{
    char buf[256];
    long xfi;
    register char *str;
    long oldlock = CurrentDir(DupLock(Ep->dirlock));

    if (xfi = xfopen(av[1], "r", 512)) {
	while (xfgets(xfi, buf, 256) >= 0) {
	    if (buf[0] == '#')
		continue;
	    for (str = buf; *str; ++str) {
		if (*str == 9)
		    *str = ' ';
	    }
	    do_command(buf);
	}
	xfclose(xfi);
    } else {
	if (av[0])
	    title("File not found");
    }
    UnLock(CurrentDir(oldlock));
}


do_quit()
{
    extern char Quitflag;

    Quitflag = 1;
}

do_execute()
{
    long oldlock = CurrentDir(Ep->dirlock);

    Execute(av[1], NULL, NULL);
    CurrentDir(oldlock);
}

/*
 * repeat X command
 *
 * Since repeat takes up 512+ stack, it should not be nested more than
 * twice.
 *
 * (if X is not a number it can be abbr. with 2 chars)
 *
 * X =	N     -number of repeats
 *	line  -current line # (lines begin at 1)
 *	lbot  -#lines to the bottom, inc. current
 *	cleft -column # (columns begin at 0)
 *		(thus is also chars to the left)
 *	cright-#chars to eol, including current char
 *	tr    -#char positions to get to next tab stop
 *	tl    -#char positions to get to next backtab stop
 */

#define SC(a,b) ((a)<<8|(b))

do_repeat()
{
    register ubyte *ptr = av[1];
    register unsigned long n;
    char buf1[256];
    char buf2[256];

    breakreset();
    strcpy(buf1, av[2]);
    switch((ptr[0]<<8)+ptr[1]) {
    case SC('l','i'):
	n = text_lineno();
	break;
    case SC('l','b'):
	n = text_lines() - text_lineno() + 1;
	break;
    case SC('c','l'):
	n = text_colno();
	break;
    case SC('c','r'):
	n = text_cols() - text_colno();
	break;
    case SC('t','r'):
	n = text_tabsize()-(text_colno() % text_tabsize());
	break;
    case SC('t','l'):
	n = text_colno() % text_tabsize();
	if (n == 0)
	    n = text_tabsize();
	break;
    default:
	n = atoi(av[1]);
	break;
    }
    while (n > 0) {
	strcpy(buf2, buf1);
	if (do_command(buf2) == 0 || breakcheck()) {
	    Abortcommand = 1;
	    break;
	}
	--n;
    }
}

/*
 *  BREAKOUT()
 *
 *  Break out the next argument.  The argument is space delimited and
 *  might be quoted with `' or (), or single quoted as 'c or )c
 *
 *  Also:	$var	    -variable insertion
 *		^c	    -control character
 */

char *
breakout(ptr, quoted, paux)
register char **ptr;
char **paux;
char *quoted;
{
    register char *str = *ptr;
    char *base;
    short count = 0;
    char opc = 0;
    char clc = 0;
    char immode = 0;
    char isaux = 0;
    char buf[256];
    short di = 0;

    *quoted = 0;
    *paux = NULL;
    while (*str == ' ')
	++str;
    if (!*str)
	return(NULL);

    *ptr = str;
    base = str;
    while (*str) {
	if (immode) {
	    if (di != sizeof(buf)-1)
		buf[di++] = *str;
	    ++str;
	    continue;
	}
	if (count == 0) {
	    if (*str == ' ')
		break;
	    if (*str == '\'' || *str == ')')
		clc = *str;
	    if (*str == '`') {
		opc = '`';
		clc = '\'';
	    }
	    if (*str == '(') {
		opc = '(';
		clc = ')';
	    }
	}
	if (*str == opc) {
	    ++count;
	    if (str == *ptr) {
		*quoted = 1;
		base = ++str;
		continue;
	    }
	}
	if (*str == clc) {
	    --count;
	    if (count == 0 && *quoted)     /*  end of argument     */
		break;
	    if (str == *ptr && count < 0) {
		immode = 1;
		*quoted = 1;
		base = ++str;
		continue;
	    }
	}

	/*
	 *  $varname $(varname) $`varname'.  I.E. three forms are allowed,
	 *  which allows one to insert the string almost anywhere.  The
	 *  first form names are limited to alpha-numerics, '-', and '_'.
	 */

	if (*str == '$') {
	    register char *ptr;
	    char c, ce;
	    short len;

	    ce = 0;			    /*	first form  */
	    ++str;			    /*	skip $	    */
	    if (*str == '(') {              /*  second form */
		ce = ')';
		++str;
	    } else if (*str == '`') {       /*  third form  */
		ce = '\'';
		++str;
	    }
	    ptr = str;			    /*	start of varname    */
	    if (ce) {                       /*  until end char OR   */
		while (*ptr && *ptr != ce)
		    ++ptr;
	    } else {			    /*	smart end-varname   */
		while ((*ptr >= 'a' && *ptr <= 'z') ||
			(*ptr >= 'A' && *ptr <= 'Z') ||
			(*ptr >= '0' && *ptr <= '9') ||
			*ptr == '-' || *ptr == '_' ) {
		    ++ptr;
		}
	    }
	    len = ptr - str;		    /*	length of variable  */

	    c = *ptr; *ptr = 0; 	    /*	temp. terminate \0  */
	    if (strcmp(str, "scanf") == 0) {
		*ptr = c;
		isaux = 1;
		if (di + strlen(String) < sizeof(buf)-1) {
		    strcpy(buf + di, String);
		    di += strlen(buf + di);
		}
		str += len;		    /*	next string pos     */
		if (ce)
		    ++str;
		continue;
	    }
	    if (strcmp(str, "filename") == 0) {
		*ptr = c;
		isaux = 1;
		if (di + strlen(Ep->Name) < sizeof(buf)-1) {
		    strcpy(buf + di, Ep->Name);
		    di += strlen(buf + di);
		}
		str += len;
		if (ce)
		    ++str;
		continue;
	    }
	    if (strcmp(str, "colno") == 0) {
		*ptr = c;
		isaux = 1;
		if (di < sizeof(buf)-8) {
		    sprintf(buf + di, "%ld", Ep->Column + 1);
		    di += strlen(buf + di);
		}
		str += len;
		if (ce)
		    ++str;
		continue;
	    }
	    if (strcmp(str, "lineno") == 0) {
		*ptr = c;
		isaux = 1;
		if (di < sizeof(buf)-8) {
		    sprintf(buf + di, "%ld", Ep->Line + 1);
		    di += strlen(buf + di);
		}
		str += len;
		if (ce)
		    ++str;
		continue;
	    }
	    if (ptr = getvar(str)) {
		str[len] = c;
		isaux = 1;
		if (di + strlen(ptr) < sizeof(buf)-1) {
		    strcpy(buf + di, ptr);
		    di += strlen(buf + di);
		}
		str += len;
		if (ce)
		    ++str;
		free(ptr);
		continue;
	    }
	    *ptr = c;
	    --str;
	    if (ce)
		--str;
	}
	if (*str == '^' && (str[1] & 0x1F)) {
	    ++str;
	    *str &= 0x1F;
	    isaux = 1;
	}
	if (*str == '\\' && str[1]) {
	    ++str;
	    isaux = 1;
	}
	buf[di++] = *str++;
    }
    buf[di++] = 0;
    if (isaux) {
	*paux = malloc(di);
	strcpy(*paux, buf);
	base = *paux;
    }
    if (*str) {             /*  space ended */
	*str = '\0';
	*ptr = str + 1;     /*	next arg    */
    } else {
	*ptr = str;	    /*	last arg    */
    }
    return(base);
}


\Rogue\Monster\
else
  echo "will not over write ./src/command.c"
fi
if [ `wc -c ./src/command.c | awk '{printf $1}'` -ne 17738 ]
then
echo `wc -c ./src/command.c | awk '{print "Got " $1 ", Expected " 17738}'`
fi
if `test ! -s ./src/cmd3.c`
then
echo "writing ./src/cmd3.c"
cat > ./src/cmd3.c << '\Rogue\Monster\'

/*
 * CMD3.C
 *
 *	(C)Copyright 1988 by Matthew Dillon, All Rights Reserved
 *
 *  SETFONT
 *  IGNORECASE
 *  SET
 *  SETENV
 *  UNSET
 *  UNSETENV
 *  CD
 */

#include "defs.h"
#include <local/xmisc.h>
#include <stdio.h>

#define nomemory()  { memoryfail = 1; }

extern FONT *GetFont();

/*
 *  SETFONT font size
 */

void
do_setfont()
{
    register FONT *font = GetFont(av[1], atoi(av[2]));
    register ED *ep = Ep;
    if (font) {
	if (ep->Font)
	    CloseFont(ep->Font);
	ep->Font = font;
	SetFont(ep->Win->RPort, font);
	SetRast(ep->Win->RPort, 0);
	RefreshWindowFrame(ep->Win);
	set_window_params();
	text_redisplay();
    } else {
	title("Unable to find font");
    }
}

do_ignorecase()
{
    register ED *ep = Ep;

    if (av[1][0]) {
	switch(av[1][1] & 0x1F) {
	case 'n'&0x1F:
	    ep->IgnoreCase = 1;
	    break;
	case 'f'&0x1F:
	    ep->IgnoreCase = 0;
	    break;
	case 'o'&0x1F:
	    ep->IgnoreCase = 1 - ep->IgnoreCase;
	    break;
	}
	if (ep->IgnoreCase)
	    title("Case InSensitive");
	else
	    title("Case Sensitive");
    }
}

/*
 *  av[1]
 */

do_cd()
{
    long oldlock;
    long lock;

    oldlock = CurrentDir(Ep->dirlock);
    if (lock = Lock(av[1], SHARED_LOCK)) {
	UnLock(CurrentDir(oldlock));
	Ep->dirlock = lock;
    } else {
	CurrentDir(oldlock);
	Abortcommand = 1;
	title("Unable to CD");
    }
}

/*
 *  VARIABLE SUPPORT!
 */

#define VARS	struct _VARS
VARS {
    MNODE   Node;
    char    *Name;
    char    *Str;
};

static MLIST SList = { (MNODE *)&SList.mlh_Tail, NULL, (MNODE *)&SList.mlh_Head };

void
do_set()
{
    register VARS *v;

    do_unset();
    if (v = malloc(sizeof(VARS))) {
	if (v->Name = malloc(strlen(av[1])+1)) {
	    if (v->Str = malloc(strlen(av[2])+1)) {
		AddHead(&SList, v);
		strcpy(v->Name, av[1]);
		strcpy(v->Str , av[2]);
		return;
	    }
	    free(v->Name);
	}
	free(v);
    }
    nomemory();
}

do_setenv()
{
    SetDEnv(av[1], av[2]);
}

do_unset()
{
    register VARS *v;

    for (v = (VARS *)SList.mlh_Head; v->Node.mln_Succ; v = (VARS *)v->Node.mln_Succ) {
	if (strcmp(v->Name, av[1]) == 0) {
	    Remove(v);
	    free(v);
	    free(v->Name);
	    free(v->Str);
	    break;
	}
    }
}

do_unsetenv()
{
    register char *ptr = (char *)av[1];
    register char *tmp = malloc(4+strlen(ptr)+1);

    if (tmp) {
	strcpy(tmp, "ENV:");
	strcat(tmp, ptr);
	mountrequest(0);
	DeleteFile(tmp);
	mountrequest(1);
	free(tmp);
    }
}

/*
 *  Search (1) internal list, (2) enviroment, (3) macros.  The variable
 *  is allocated with malloc().  NULL if not found.  ENV: need not exist.
 */

char *
getvar(find)
char *find;
{
    register char *str = NULL;
    {
	register VARS *v;

	for (v = (VARS *)SList.mlh_Head; v->Node.mln_Succ; v = (VARS *)v->Node.mln_Succ) {
	    if (strcmp(v->Name, find) == 0) {
		if (str = malloc(strlen(v->Str)+1)) {
		    strcpy(str, v->Str);
		    return(str);
		}
	    }
	}
    }

    mountrequest(0);
    str = GetDEnv(find);
    mountrequest(1);
    if (str)
	return(str);

    if ((str = keyspectomacro(find)) || (str = menutomacro(find))) {
	register char *ptr = malloc(strlen(str)+1);
	if (ptr) {
	    strcpy(ptr, str);
	    return(ptr);
	}
    }
    return(NULL);
}

\Rogue\Monster\
else
  echo "will not over write ./src/cmd3.c"
fi
if [ `wc -c ./src/cmd3.c | awk '{printf $1}'` -ne 3166 ]
then
echo `wc -c ./src/cmd3.c | awk '{print "Got " $1 ", Expected " 3166}'`
fi
if `test ! -s ./src/keyboard.c`
then
echo "writing ./src/keyboard.c"
cat > ./src/keyboard.c << '\Rogue\Monster\'

/*
 *  KEYBOARD.C
 *
 *	(C)Copyright 1987 by Matthew Dillon
 *
 *  Handle keyboard related stuff such as keyboard mappings.  Every time
 *  a key is pressed, KEYCTL() is called with the code.  KEYCTL() remembers
 *  which qualifier keys are currently held down, and when a non-qualifier
 *  key is pressed finds the hash entry for the key.  If no hash entry
 *  exists (e.g. you type a normal 'a') the default keymap is used.
 */

#include "defs.h"
#include <stdio.h>

extern ubyte *cqtoa();

typedef struct IOStdReq CIO;

#define QUAL_SHIFT   0x01
#define QUAL_CTRL    0x02
#define QUAL_AMIGA   0x04
#define QUAL_ALT     0x08
#define QUAL_LMB     0x10
#define QUAL_MMB     0x20
#define QUAL_RMB     0x40

#define HASHSIZE  64		    /*	power of 2  */
#define HASHMASK  (HASHSIZE-1)

typedef struct _HASH {
    struct _HASH *next;     /* next hash   */
    ubyte code; 	    /* keycode	   */
    ubyte mask; 	    /* qual. mask  */
    ubyte qual; 	    /* qual. comp  */
    ubyte stat; 	    /* string static? */
    char *str;		    /* command string */
} HASH;

HASH *Hash[HASHSIZE];

struct Device *ConsoleDevice;

ubyte	ctoa[128];
ubyte	cstoa[128];

void
keyctl(im, code, qual)
IMESS *im;
register USHORT qual;
{
    ubyte buf[256];
    ubyte c2;
    short blen = 0;

    code &= 0xFF;
    if (im) {
	im->Qualifier &= ~IEQUALIFIER_REPEAT;
	blen = DeadKeyConvert(im, buf+1, 254, NULL);
	if (blen < 0)
	    return;
    }
    c2 = 0;
    if (qual & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT))
	c2 |= QUAL_SHIFT;
    if (qual & (IEQUALIFIER_CONTROL))
	c2 |= QUAL_CTRL;
    if (qual & (IEQUALIFIER_LCOMMAND|IEQUALIFIER_RCOMMAND))
	c2 |= QUAL_AMIGA;
    if (qual & (IEQUALIFIER_LALT|IEQUALIFIER_RALT))
	c2 |= QUAL_ALT;
    if ((qual & IEQUALIFIER_CAPSLOCK) && blen == 1 && buf[1] >= 'a' && buf[1] <= 'z')
	c2 |= QUAL_SHIFT;
    if (qual & IEQUALIFIER_LEFTBUTTON)
	c2 |= QUAL_LMB;
    if (qual & IEQUALIFIER_MIDBUTTON)
	c2 |= QUAL_MMB;
    if (qual & (IEQUALIFIER_RBUTTON))
	c2 |= QUAL_RMB;

    {
	register HASH *hash;
	for (hash = Hash[code&HASHMASK]; hash; hash = hash->next) {
	    if (hash->code == code && (c2 & hash->mask) == hash->qual)
		break;
	}

	/*
	 *  Use hash entry only if not in command line mode, or if the
	 *  entry does not correspond to an alpha key.
	 */

	if (hash) {
	    if (c2 || !Comlinemode || blen > 1 || !ctoa[code]) {
		strcpy(buf, hash->str);
		do_command(buf);
		return;
	    }
	}
    }

    /*
     *	No hash entry
     */

    if (blen == 1) {
	buf[0] = '\'';
	buf[2] = 0;
    } else {
	buf[0] = '\`';
	buf[blen+1] = '\'';
    }
    if (blen)
	do_command(buf);
}

dealloc_hash()
{
    register HASH *hash, *hnext = NULL;
    register short i;

    for (i = 0; i < HASHSIZE; ++i) {
	for (hash = Hash[i]; hash; hash = hnext) {
	    hnext = hash->next;
	    if (!hash->stat)
		FreeMem(hash->str, strlen(hash->str)+1);
	    FreeMem(hash, sizeof(HASH));
	}
	Hash[i] = NULL;
    }
}

resethash()
{
    register short i;
    CIO cio;
    static struct {
	char *from, *to;
    } defmap[] = {
	"esc",      "esc",
	"c-esc",    "recall",
	"return",   "return insline up firstnb down",
	"enter",    "return",
	"up",       "up",
	"down",     "down",
	"right",    "right",
	"left",     "left",
	"bs",       "bs",
	"del",      "del",
	"tab",      "tab",
	"a-up",     "scrollup",
	"a-down",   "scrolldown",
	"a-r",      "nextr",
	"a-u",      "while cl (tlate -32 right)",
	"a-l",      "while cu (tlate +32 right)",
	"s-up",     "top",
	"s-down",   "bottom",
	"s-right",  "last",
	"s-left",   "first",
	"s-tab",    "backtab",
	"s-del",    "deline",
	"s- ",      "( )",              /* shift space to space */
	"c-1",      "goto block",
	"c-c",      "",                 /* break.. map to a nop */
	"c-l",      "wleft",
	"c-r",      "wright",
	"c-i",      "insertmode on",
	"c-o",      "insertmode off",
	"c-j",      "join",
	"c-s",      "split first down",
	"c-del",    "remeol",
	"c-n",      "next",
	"c-p",      "prev",
	"c-/",      "escimm (find )",
	"c-]",      "ref",
	"c-[",      "ctags",
	"c-g",      "escimm (goto )",
	"c-up",     "pageup",
	"c-down",   "pagedown",
	"c-q",      "quit",
	"c-f",      "reformat",
	"c-w",      "wordwrap toggle",
	"f1",       "escimm (insfile )",
	"f2",       "escimm (newfile )",
	"f3",       "escimm (newwindow newfile )",
	"f6",       "saveold iconify",
	"f7",       "escimm (bsave )",
	"f8",       "saveold escimm (newfile )",
	"f9",       "saveold",
	"f10",      "saveold quit",
	"c-b",      "block",
	"c-u",      "unblock",
	"a-d",      "bdelete",
	"a-c",      "bcopy",
	"a-m",      "bmove",
	"a-s",      "bsource",
	"a-S",      "unblock block block bsource",
	"L-lmb",    "tomouse",      /*  left button                 */
	"L-mmo",    "tomouse",      /*  mouse move w/left held down */
	"R-rmb",    "iconify",      /*  right button                */
	NULL, NULL
    };

    dealloc_hash();
    OpenDevice("console.device", -1, &cio, 0);
    ConsoleDevice = cio.io_Device;
    keyboard_init();
    for (i = 0; defmap[i].from; ++i) {
	ubyte code, qual;
	if (get_codequal(defmap[i].from, &code, &qual))
	    addhash(code, 1, 0xFF, qual, defmap[i].to);
    }
}

returnoveride(n)
{
    HASH *hash;
    static ubyte *str;
    static int stat;

    for (hash = Hash[0x44&HASHMASK]; hash; hash = hash->next) {
	if (hash->code == 0x44 && hash->qual == 0) {
	    if (n) {
		str = (ubyte *)hash->str;
		stat= hash->stat;
		hash->str = "return";
		hash->stat = 1;
	    } else {
		if (str == NULL) {
		    remhash(0x44, -1, 0);
		} else {
		    hash->str = (char *)str;
		    hash->stat= stat;
		}
	    }
	    return(0);
	}
    }
    if (n) {
	addhash(0x44, 1, 0xFF, 0, "return");
	str = NULL;
    }
}


addhash(code, stat, mask, qual, str)
ubyte code, stat, mask, qual;
ubyte *str;
{
    register HASH **p, *hash;

    hash = *(p = &Hash[code&HASHMASK]);
    while (hash) {
	if (hash->code == code && hash->qual == qual && hash->mask == mask) {
	    if (!hash->stat)
		FreeMem(hash->str, strlen(hash->str)+1);
	    goto newstr;
	}
	hash = *(p = &hash->next);
    }
    *p = hash = (HASH *)AllocMem(sizeof(HASH), 0);
    hash->next = NULL;
newstr:
    hash->code = code;
    hash->stat = stat;
    hash->mask = mask;
    hash->qual = qual;
    hash->str = (char *)str;
    if (!stat)                  /* if not static */
	hash->str = (char *)strcpy(AllocMem(strlen(str)+1, 0), str);
}


remhash(code, mask, qual)
ubyte code, mask, qual;
{
    register HASH *hash, **p;

    hash = *(p = &Hash[code&HASHMASK]);
    while (hash) {
	if (hash->code == code && hash->qual == qual && hash->mask == mask) {
	    if (!hash->stat)
		FreeMem(hash->str, strlen(hash->str)+1);
	    *p = hash->next;
	    FreeMem(hash, sizeof(HASH));
	    return(1);
	}
	hash = *(p = &hash->next);
    }
    return(0);
}

char *
keyspectomacro(str)
char *str;
{
    HASH *hash;
    ubyte code, qual;

    if (get_codequal(str, &code, &qual)) {
	for (hash = Hash[code&HASHMASK]; hash; hash = hash->next) {
	    if (hash->code == code) {
		if (hash->qual == (qual & hash->mask)) {
		    return(hash->str);
		}
	    }
	}
    }
    return(NULL);
}


do_map()
{
    ubyte code, qual;

    if (get_codequal(av[1], &code, &qual)) {
	addhash(code, 0, 0xFF, qual, av[2]);
    } else {
	title("Unknown Key");
    }
}

do_unmap()        /* key   */
{
    ubyte code, qual;

    if (get_codequal(av[1], &code, &qual)) {
	remhash(code, -1, qual);
    } else {
	title("Unknown Command");
    }
}

do_clearmap()
{
    resethash();
}

/*
 * SAVEMAP  file
 * SAVESMAP file
 */

do_savemap()
{
    char sysalso;
    char err = 0;
    char buf[256];
    long xfi;
    register int i;
    register HASH *hash;
    register ubyte *ptr;

    xfi = xfopen(av[1], "w", 512);
    if (xfi) {
	sysalso = av[0][4] == 's';
	for (i = 0; i < HASHSIZE; ++i) {
	    for (hash = Hash[i]; hash; hash = hash->next) {
		if (hash->stat == 0 || sysalso) {
		    char soc = '(';
		    char eoc = ')';
		    char ksoc = '(';
		    char keoc = ')';
		    short len;

		    for (ptr = (ubyte *)hash->str; *ptr; ++ptr) {
			if (*ptr == '(')
			    break;
			if (*ptr == '\`') {
			    soc = '\`';
			    eoc = '\'';
			    break;
			}
		    }
		    len = strlen(ptr = cqtoa(hash->code, hash->qual)) - 1;
		    if (ptr[len] == '(' || ptr[len] == ')') {
			ksoc = '\`';
			keoc = '\'';
		    }
		    sprintf(buf, "map %c%s%c %c%s%c\n", ksoc, cqtoa(hash->code, hash->qual), keoc, soc, hash->str, eoc);
		    xfwrite(xfi, buf, strlen(buf));
		}
	    }
	}
	xfclose(xfi);
	if (err)
	    title ("Unable to Write");
	else
	    title ("OK");
    } else {
	title("Unable to open file");
    }
}

/*
 *  Nitty Gritty.
 *
 *  keyboard_init:  initialize for get_codequal() and cqtoa()
 *  get_codequal:   convert a qualifier-string combo to a keycode and qual.
 *  cqtoa:	    convert a keycode and qual to a qual & string
 */

#define LN(a,b,c,d)  ((a<<24)|(b<<16)|(c<<8)|d)

long lname[] = {
    LN('e','s','c', 0  ), LN('f','1', 0 , 0  ), LN('f','2', 0 , 0  ),
    LN('f','3', 0 , 0  ), LN('f','4', 0 , 0  ), LN('f','5', 0 , 0  ),
    LN('f','6', 0 , 0  ), LN('f','7', 0 , 0  ), LN('f','8', 0 , 0  ),
    LN('f','9', 0 , 0  ), LN('f','1','0', 0  ), LN('d','e','l', 0  ),
    LN('b','a','c', 0  ), LN('b','s', 0 , 0  ), LN('t','a','b', 0  ),
    LN('h','e','l', 0  ), LN('r','e','t', 0  ), LN('u','p', 0 , 0  ),
    LN('d','o','w', 0  ), LN('r','i','g', 0  ), LN('l','e','f', 0  ),
    LN('e','n','t', 0  ), LN('n','k','-', 0  ), LN('n','k','.', 0  ),
    LN('n','k','0', 0  ),   /* 24 */
    LN('n','k','1', 0  ), LN('n','k','2', 0  ), LN('n','k','3', 0  ),
    LN('n','k','4', 0  ), LN('n','k','5', 0  ), LN('n','k','6', 0  ),
    LN('n','k','7', 0  ), LN('n','k','8', 0  ), LN('n','k','9', 0  ),
    LN('n','k','(', 0  ), LN('n','k',')', 0  ), LN('n','k','/', 0  ), /*34-36*/
    LN('n','k','*', 0  ), LN('n','k','+', 0  ),
    LN('l','m','b',0xE8), LN('m','m','b',0xEA), LN('r','m','b',0xE9),
    LN('m','m','o',QMOVE),
    0
};


/*
 *  ESC:	x1B
 *  FUNCKEYS:	x9B 30 7E to x9B 39 7E
 *  DEL:	x7E
 *  BS: 	x08
 *  TAB:	x09
 *  RETURN:	x0D
 *  HELP	x9B 3F 7E
 *  UP/D/L/R	x9B 41/42/44/43
 *  NK0-9,-,.,ENTER
 *
 *  Mouse buttons
 */

keyboard_init()
{
    static struct InputEvent ievent = { NULL, IECLASS_RAWKEY };
    ubyte buf[32];
    register short i, q, len;

    lname[16] |= 0x44;
    lname[21] |= 0x43;

    for (i = 0; i < 128; ++i) {
	ievent.ie_Code = i;
	ievent.ie_Qualifier = 0;
	ievent.ie_position.ie_addr = NULL;
	len = RawKeyConvert(&ievent,buf,32,NULL);
	switch(len) {
	case 1:     /*	ESC/DEL/BS/TAB/NKx  */
	    if (buf[0] >= 32 && buf[0] < 127)
		ctoa[i] = buf[0];
	    switch(buf[0]) {
	    case 0x1B:	lname[ 0] |= i; break;
	    case 0x7F:	lname[11] |= i; break;
	    case 0x09:	lname[14] |= i; break;
	    case 0x08:	lname[12] |= i; lname[13] |= i; break;
	    case '(': if (i > 0x3A) lname[34] |= i; break;
	    case ')': if (i > 0x3A) lname[35] |= i; break;
	    case '/': if (i > 0x3A) lname[36] |= i; break;
	    case '*': if (i > 0x3A) lname[37] |= i; break;
	    case '-': if (i > 0x3A) lname[22] |= i; break;
	    case '+': if (i > 0x3A) lname[38] |= i; break;
	    case '.': if (i > 0x3A) lname[23] |= i; break;
	    default:
		if (i >= 0x0F && buf[0] >= '0' && buf[0] <= '9')
		    lname[24+buf[0]-'0'] |= i;
	    }
	    break;
	case 2:     /*	cursor		    */
	    if (buf[0] == 0x9B) {
		switch(buf[1]) {
		case 0x41:  lname[17] |= i;  break;
		case 0x42:  lname[18] |= i;  break;
		case 0x43:  lname[19] |= i;  break;
		case 0x44:  lname[20] |= i;  break;
		}
	    }
	    break;
	case 3:     /*	function/help	    */
	    if (buf[0] == 0x9B && buf[2] == 0x7E) {
		if (buf[1] == 0x3F)
		    lname[15] |= i;
		if (buf[1] >= 0x30 && buf[1] <= 0x39)
		    lname[buf[1]-0x30+1] |= i;
	    }
	    break;
	}
    }
    for (i = 0; i < 128; ++i) {
	ievent.ie_Code = i;
	ievent.ie_Qualifier = IEQUALIFIER_LSHIFT;
	ievent.ie_position.ie_addr = NULL;
	len = RawKeyConvert(&ievent,buf,32,NULL);
	if (len == 1)
	    cstoa[i] = buf[0];
    }
    {
	ubyte code, qual;
	get_codequal("c", &code, &qual);
	CtlC = code;
    }
}


ubyte *
cqtoa(code, qual)
register int qual;
{
    static ubyte buf[32];
    register ubyte *ptr = buf;
    register int i;

    if (qual & QUAL_SHIFT)
	*ptr++ = 's';
    if (qual & QUAL_CTRL)
	*ptr++ = 'c';
    if (qual & QUAL_ALT)
	*ptr++ = 'a';
    if (qual & QUAL_AMIGA)
	*ptr++ = 'A';
    if (qual & QUAL_LMB)
	*ptr++ = 'L';
    if (qual & QUAL_MMB)
	*ptr++ = 'M';
    if (qual & QUAL_RMB)
	*ptr++ = 'R';
    if (qual)
	*ptr++ = '-';
    for (i = 0; i < sizeof(lname)/sizeof(lname[0]); ++i) {
	if ((lname[i]&0xFF) == code) {
	    *ptr++ = (lname[i]>>24);
	    *ptr++ = (lname[i]>>16);
	    *ptr++ = (lname[i]>>8);
	    break;
	}
    }
    if (i == sizeof(lname)/sizeof(lname[0]))
	*ptr++ = ctoa[code];
    *ptr++ = 0;
    return(buf);
}


get_codequal(str, pcode, pqual)
ubyte *pcode, *pqual;
register ubyte *str;
{
    register ubyte qual;
    register short i;

    qual = 0;
    if (strlen(str) > 1) {
	for (; *str && *str != '-'; ++str) {
	    if (*str == 's')
		qual |= QUAL_SHIFT;
	    if (*str == 'c')
		qual |= QUAL_CTRL;
	    if (*str == 'a')
		qual |= QUAL_ALT;
	    if (*str == 'A')
		qual |= QUAL_AMIGA;
	    if (*str == 'L')
		qual |= QUAL_LMB;
	    if (*str == 'M')
		qual |= QUAL_MMB;
	    if (*str == 'R')
		qual |= QUAL_RMB;
	    if (!qual)
		goto notqual;
	}
	if (*str)
	    ++str;
    }
notqual:
    if (strlen(str) != 1) {           /* long name   */
	register short shift = 24;
	register long mult = 0;

	*pqual = qual;
	while (*str && shift >= 8) {
	    if (*str >= 'A' && *str <= 'Z')
		*str = *str - 'A' + 'a';
	    mult |= *str << shift;
	    shift -= 8;
	    ++str;
	}
	for (i = 0; lname[i]; ++i) {
	    if (mult == (lname[i] & 0xFFFFFF00)) {
		*pcode = lname[i] & 0xFF;
		return(1);
	    }
	}
    } else {		    /*	single character keycap */
	for (i = 0; i < sizeof(ctoa); ++i) {
	    if (*str == ctoa[i]) {
		*pcode = i;
		*pqual = qual;
		return(1);
	    }
	}
	for (i = 0; i < sizeof(cstoa); ++i) {
	    if (*str == cstoa[i]) {
		*pcode = i;
		*pqual = qual|QUAL_SHIFT;
		return(1);
	    }
	}
    }
    return(0);
}

\Rogue\Monster\
else
  echo "will not over write ./src/keyboard.c"
fi
if [ `wc -c ./src/keyboard.c | awk '{printf $1}'` -ne 14051 ]
then
echo `wc -c ./src/keyboard.c | awk '{print "Got " $1 ", Expected " 14051}'`
fi
echo "Finished archive 4 of 6"
# if you want to concatenate archives, remove anything after this line
exit
-- 
Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
Have five nice days.