[comp.sources.amiga] v02i016: dres - an object-oriented resource library, Part01/03

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

Submitted-by: dillon@cory.berkeley.edu (Matt Dillon)
Posting-number: Volume 2, Issue 16
Archive-name: util/dres.1

# 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/ipc.c
#	src/misc.c
#	src/newres.c
#	src/mem.asm
#	util/gtd.c
#	util/testipc.c
#	util/test.c
#	util/qtest.c
#	util/testarg.c
#	util/x.c
#
if `test ! -d src`
then
  mkdir src
  echo "mkdir src"
fi
if `test ! -s src/ipc.c`
then
echo "writing src/ipc.c"
cat > src/ipc.c << '\Rogue\Monster\'

/*
 *  IPC.C   25 September 1988
 *
 *  NOTE!   the BSS segment is not initialized to 0.
 */

#include <local/typedefs.h>
#include <local/ipc.h>

extern EXECBASE *SysBase;

static long  IPCLock[2] = { 0, 0 };
static MLIST IPCList = { (MNODE *)&IPCList.mlh_Tail, NULL, (MNODE *)&IPCList.mlh_Head };

extern void *FindName();
extern IPCMSG *lSendIPC2();
extern char *CCall();
extern APTR Duplicate();

/*
 *  Three-pass command parser.
 *  (1) Determine # of arguments and allocate argv array, resolve variables
 *  (2) Determine length of each argument and allocate entries in the array
 *  (3) Fill entries in the array
 *
 *  On return, an filled ARGV array will be returned.  To free the argv
 *  array and elements, call FreeParseCmd(argv).  Individual argv[]
 *  entries may be modified, including stuffing different pointers in,
 *  without effecting FreeParseCmd().  Note that offset -1 in each entry
 *  contains a status byte, currently only bit 0 (was quoted) is used.
 */

lParseCmd(buf, pav, varget, varfree, error, reserved)
char *buf;
char ***pav;
char *((*varget)());
char *((*varfree)());
long *error;
{
    short numac;	    /*	# of argv entries	*/
    char **av = NULL;	    /*	argv array		*/
    long *arglen = NULL;    /*	array of entry lengths	*/
    MLIST VList;	    /*	Hold $variable contents */
    char *endstrvar();      /*  extracts variable name  */
    short ttllen = 0;	    /*	sum of lengths of entries   */

    NewList(&VList);
    *error = 0;

    /*
     *	PASS1, determine # of arguments and retrieve variables
     */

    {
	register char *ptr;
	register short ac = 0;
	register short quo = 0;

	for (ptr = buf; *ptr == ' ' || *ptr == 9; ++ptr);
	for (; *ptr; ++ptr) {
	    switch(*ptr) {
	    case '(':
		++quo;
		break;
	    case ')':
		--quo;
		break;
	    case '\\':
	    case '^':
		if (ptr[1])
		    ++ptr;
		break;
	    case ' ':
	    case 9:
		if (!quo) {
		    while (*ptr == ' ' || *ptr == 9)
			++ptr;
		    if (*ptr)
			++ac;
		    --ptr;
		}
		break;
	    case '$':
		{
		    short len;
		    char tmp[64];
		    char *str;
		    MNODE *vl;

		    ++ptr;
		    str = endstrvar(ptr, &len);
		    if (*ptr == '(')
			++ptr;
		    BMov(ptr, tmp, len);
		    tmp[len] = 0;
		    if (varget && (ptr = (char *)CCall(varget, tmp))) {
			vl = AllocMem(8 + strlen(ptr) + 1, MEMF_PUBLIC);
			if (!vl) {
			    *error = (PERR_NOMEM << 16) | (ptr - buf);
			    goto fail;
			}
			strcpy(vl+1, ptr);
			if (varfree)
			    CCall(varfree, ptr);
		    } else {
			*error = (PERR_NOVAR << 16) | (ptr - buf);
			goto fail;
		    }
		    AddTail(&VList, vl);
		    ptr = str - 1;
		}
		break;
	    }
	}
	++ac;
	numac = ac;
    }

    /*
     *	av[-1]	= master string pointer (see below)
     *	av[-2]	= sizeof av array
     *	av[ac]	= NULL
     *
     *	Pass 2, allocate argv array and storage for the lengths of each
     *		entry.	Determine the length of each entry.
     */

    av = AllocMem(sizeof(char *) * (numac + 3), MEMF_PUBLIC);
    if (!av) {
	*error = (PERR_NOMEM << 16);
	goto fail;
    }
    av += 2;
    av[-2] = (char *)(sizeof(char *) * (numac + 3));
    arglen = AllocMem(sizeof(long) * numac, MEMF_PUBLIC);
    if (!arglen) {
	*error = (PERR_NOMEM << 16);
	goto fail;
    }

    {
	register char *ptr;
	register short ac = 0;
	register short quo = 0;
	register short len = 0;

	for (ptr = buf; *ptr == ' ' || *ptr == 9; ++ptr);
	for (; *ptr; ++ptr) {
	    switch(*ptr) {
	    case '(':
		++quo;
		if (quo != 1)
		    ++len;
		break;
	    case ')':
		--quo;
		if (quo)
		    ++len;
		break;
	    case '\\':
	    case '^':
		if (ptr[1]) {
		    ++ptr;
		    ++len;
		}
		break;
	    case ' ':
	    case 9:
		if (!quo) {
		    while (*ptr == ' ' || *ptr == 9)
			++ptr;
		    if (*ptr) {
			arglen[ac] = len;
			ttllen += len + 2;
			++ac;
			len = 0;
		    }
		    --ptr;
		} else {
		    ++len;
		}
		break;
	    case '$':
		{
		    register MNODE *node = RemHead(&VList);
		    if (node) {
			len += strlen(node+1);
			AddTail(&VList, node);
		    }
		    ptr = endstrvar(ptr + 1, NULL) - 1;
		}
		break;
	    case '\n':
		break;
	    default:
		++len;
		break;
	    }
	}
	arglen[ac] = len;
	ttllen += len + 2;		/* 1 byte status & 1 byte separator */
	++ac;
    }
    {					/* allocate space for entries */
	register short i;
	register char *mem = AllocMem(4+ttllen, MEMF_PUBLIC);
	register char *ptr = mem + 4;

	if (!mem) {
	    *error = (PERR_NOMEM << 16);
	    goto fail;
	}

	*(long *)mem = 4 + ttllen;
	for (i = 0; i < numac; ++i) {
	    *ptr++ = 0; 		/*  status byte */
	    av[i] = ptr;
	    ptr[arglen[i]] = 0;
	    ptr += arglen[i] + 1;
	}
	av[-1] = mem;
    }
    {
	register char *ptr;
	register char *avs = av[0];
	register short ac = 0;
	short quo = 0;

	for (ptr = buf; *ptr == ' ' || *ptr == 9; ++ptr);
	for (; *ptr; ++ptr) {
	    switch(*ptr) {
	    case '\\':
		if (ptr[1])
		    *avs++ = *++ptr;
		break;
	    case '^':
		if (ptr[1])
		    *avs++ = *++ptr & 0x1F;
		break;
	    case '(':
		++quo;
		if (quo != 1)
		    *avs++ = '(';
		else if (avs == av[ac])     /*  quoted argument */
		    avs[-1] = 1;
		break;
	    case ')':
		--quo;
		if (quo) {
		    *avs++ = ')';
		    break;
		}   /* fall through */
	    case ' ':
	    case 9:
		if (!quo) {
		    ++ptr;
		    while (*ptr == ' ' || *ptr == 9)
			++ptr;
		    if (*ptr) {
			*avs = 0;
			avs = av[++ac];
		    }
		    --ptr;
		} else {
		    *avs++ = *ptr;
		}
		break;
	    case '$':
		{
		    register MNODE *node = RemHead(&VList);
		    if (node) {
			strcpy(avs, node + 1);
			avs += strlen(avs);
			FreeMem(node, 8 + strlen(node+1) + 1);
		    }
		    ptr = endstrvar(ptr + 1, NULL) - 1;
		}
		break;
	    case '\n':
		break;
	    default:
		*avs++ = *ptr;
		break;
	    }
	}
	*avs = 0;
	++ac;
    }
    FreeMem(arglen, sizeof(long) * numac);
    *pav = av;
    av[numac] = NULL;
    return(numac);

fail:
    {
	register MNODE *node;

	if (arglen)
	    FreeMem(arglen, sizeof(long) * numac);
	if (av)
	    FreeMem(av - 2, (long)av[-2]);
	while (node = RemHead(&VList))
	    FreeMem(node, 8 + strlen(node+1) + 1);
    }
    *pav = NULL;
    return(0);
}

lFreeParseCmd(av)
char **av;
{
    FreeMem(av[-1], *(long *)av[-1]);
    FreeMem(av - 2, (long)av[-2]);
}

char *
endstrvar(ptr, plen)
short *plen;
register char *ptr;
{
    register short len = 0;
    if (*ptr == '(') {
	++ptr;
	while (*ptr && *ptr != ')') {
	    ++ptr;
	    ++len;
	}
	if (*ptr == ')')
	    ++ptr;
    } else {
	while ((*ptr >= 'a' && *ptr <= 'z') ||
	       (*ptr >= 'A' && *ptr <= 'Z') ||
	       (*ptr >= '0' && *ptr <= '9') ||
	       (*ptr == '_')) {
	    ++len;
	    ++ptr;
	}
    }
    if (len > 62)
	len = 62;
    if (plen)
	*plen = len;
    return(ptr);
}

IPCPORT *
lOpenIPC(name, flags)
char *name;
{
    register IPCPORT *port;
    register char *ptr;

    port = AllocMem(sizeof(IPCPORT), MEMF_PUBLIC|MEMF_CLEAR);
    if (!port)
	goto fail;
    ptr = AllocMem(strlen(name)+1, MEMF_PUBLIC);
    if (!ptr)
	goto fail;
    strcpy(ptr, name);
    port->Flags = flags;
    port->Port.mp_Node.ln_Type = NT_MSGPORT;
    port->Port.mp_Node.ln_Name = ptr;
    port->Port.mp_Flags = PA_SIGNAL;
    port->Port.mp_SigBit = AllocSignal(-1);
    port->Port.mp_SigTask = FindTask(NULL);
    NewList(&port->Port.mp_MsgList);

    LockAddr(IPCLock);
    Insert(&IPCList, port, FindName(&IPCList, name));
    UnLockAddr(IPCLock);
    return(port);
fail:
    if (port) {
	FreeMem(port, sizeof(IPCPORT));
	if (ptr)
	    FreeMem(ptr, strlen(ptr)+1);
    }
    return(NULL);
}

void
lCloseIPC(port)
register IPCPORT *port;
{
    register IPCMSG *msg;

    LockAddr(IPCLock);              /*  remove port (no new messages)   */
    Remove(port);
    UnLockAddr(IPCLock);
    while (msg = GetMsg(port))      /*  reply to pending msgs w/error   */
	ReplyIPC(msg, NULL, 0, IF_NOTFND);
    FreeMem(port->Port.mp_Node.ln_Name, strlen(port->Port.mp_Node.ln_Name)+1);
    if (port->Port.mp_Flags == PA_SIGNAL)
	FreeSignal(port->Port.mp_SigBit);
    FreeMem(port, sizeof(IPCPORT));
}

/*
 *  msg = SendIPC(appname, buf, len, flags)
 */

IPCMSG *
lSendIPC(name, buf, len, flags)
char *name;
APTR buf;
register long len, flags;
{
    register IPCMSG *msg;
    register PORT *rport;

    msg = AllocMem(sizeof(IPCMSG)+sizeof(PORT), MEMF_PUBLIC|MEMF_CLEAR);
    if (msg == NULL)
	return(NULL);
    flags |= IF_ALLOCMSG;	    /*	auto-deallocate later on    */
    rport = (PORT *)(msg + 1);
    if (buf && !(flags & IF_NOCOPY) && !(flags & IF_ALLOC)) {
	if (!(buf = Duplicate(buf, len))) {
	    FreeMem(msg, sizeof(IPCMSG)+sizeof(PORT));
	    return(NULL);
	}
	flags |= IF_ALLOC;
    }
    rport->mp_Node.ln_Type = NT_MSGPORT;
    rport->mp_Flags = PA_SIGNAL;
    rport->mp_SigBit = 4;
    rport->mp_SigTask = SysBase->ThisTask;
    NewList(&rport->mp_MsgList);

    msg->Msg.mn_ReplyPort = rport;
    msg->Msg.mn_Length = sizeof(IPCMSG) + sizeof(PORT);
    msg->TBuf = buf;
    msg->TLen = len;
    msg->TFlags = flags;
    return(lSendIPC2(name, msg));
}

IPCMSG *
lSendIPC2(name, msg)
char *name;
register IPCMSG *msg;
{
    register IPCPORT *port;

    msg->RBuf = NULL;
    msg->RLen = 0;
    msg->RFlags = 0;
    msg->Error	= 0;
    msg->Confirm = NULL;

    LockAddr(IPCLock);
    if (name) {
	port = (IPCPORT *)FindName(&IPCList, name);
    } else {
	port = GetHead(&IPCList);
    }
    msg->ToPort = port;
    if (port) {
	if ((port->Flags & IF_ALLOC) && !(msg->TFlags & IF_ALLOC)) {
	    if ((msg->TBuf = Duplicate(msg->TBuf, msg->TLen)) == NULL) {
		msg->Error = PERR_NOMEM;
		lReplyIPC(msg, NULL, 0, IF_ERROR);
		return(msg);
	    }
	    msg->TFlags |= IF_ALLOC;
	}
	PutMsg(port, msg);
	UnLockAddr(IPCLock);
    } else {
	UnLockAddr(IPCLock);
	lReplyIPC(msg, NULL, 0, IF_ERROR|IF_NOTFND|IF_NOAPP);
    }
    return(msg);
}

/*
 *  DoIPC2(name, msg, collfunc, collport)
 *
 *  Synchronous IPC routine.  To prevent lockouts in case the calling task
 *  also owns an IPC port, if this port is provided 'collfunc' will
 *  automatically be called if new requests arrive while we are waiting
 *  for our request to complete.  This call is reentrant.
 */

void
lDoIPC2(name, msg, func, port)
char *name;
IPCMSG *msg;
void (*func)();
PORT *port;
{
    SendIPC2(name, msg);        /*  send the message            */
    if (port) {
	register long mask1 = 1 << port->mp_SigBit;
	register long mask2 = 1 << msg->Msg.mn_ReplyPort->mp_SigBit;

	while (!CheckMsg(msg)) {
	    if (CheckPort(port)) {
		CCall(func, port);
		continue;
	    }
	    Wait(mask1 | mask2);
	}
    }
    WaitMsg(msg);
}

/*
 *  (void )ReplyIPC(msg, buf, len, flags)
 */

lReplyIPC(msg, buf, len, flags)
register IPCMSG *msg;
APTR buf;
long len, flags;
{
    if (buf && !(flags & (IF_NOCOPY|IF_ALLOC|IF_NOTFND))) {
	buf = Duplicate(buf, len);
	flags |= IF_ALLOC;
    }
    if (buf || !(flags & IF_NOTFND)) {
	if ((msg->RFlags & IF_ALLOC) && msg->RBuf && buf != msg->RBuf)
	    FreeMem(msg->RBuf, msg->RLen);
	msg->RBuf = buf;
	msg->RLen = len;
    }
    msg->RFlags = flags;
    LockAddr(IPCLock);
    if (msg->RFlags & IF_GLOBAL) {
	register IPCPORT *next;

	do {
	    next = GetSucc(next);
	} while (next && !(next->Flags & IF_GLOBAL));
	if (next) {
	    msg->ToPort = next;
	    PutMsg(next, msg);
	} else {
	    if ((msg->TFlags & IF_ALLOC) && msg->TBuf) {
		FreeMem(msg->TBuf, msg->TLen);
		msg->TBuf = NULL;
	    }
	    ReplyMsg(msg);
	}
	UnLockAddr(IPCLock);
	return;
    }
    if (flags & IF_NOTFND) {
	register IPCPORT *next = GetSucc(msg->ToPort);
	if (next && strcmp(msg->ToPort->Port.mp_Node.ln_Name, next->Port.mp_Node.ln_Name) == 0) {
	    msg->ToPort = next;
	    PutMsg(next, msg);
	} else {
	    if ((msg->TFlags & IF_ALLOC) && msg->TBuf) {
		FreeMem(msg->TBuf, msg->TLen);
		msg->TBuf = NULL;
	    }
	    ReplyMsg(msg);
	    msg->RFlags |= IF_ERROR;
	}
	UnLockAddr(IPCLock);
	return;
    }
    UnLockAddr(IPCLock);
    if ((msg->TFlags & IF_ALLOC) && msg->TBuf) {
	FreeMem(msg->TBuf, msg->TLen);
	msg->TBuf = NULL;
    }
    ReplyMsg(msg);
    return;
}

/*
 * (void) lFreeIPC(msg)
 *
 *  Free space associated with the reply buffer and possibly the message
 *  itself.  This call MUST be made after you receive a reply to your
 *  message.
 */

void
lFreeIPC(msg)
register IPCMSG *msg;
{
    if ((msg->RFlags & IF_ALLOC) && msg->RBuf) {
	FreeMem(msg->RBuf, msg->RLen);
	msg->RBuf = NULL;
    }
    if (msg->Confirm)                   /*  Confirmation requested  */
	CCall(msg->Confirm, msg);
    if (msg->TFlags & IF_ALLOCMSG)      /*  Message was allocated   */
	FreeMem(msg, msg->Msg.mn_Length);
}

APTR
Duplicate(buf, len)
APTR buf;
long len;
{
    APTR newbuf;
    if (newbuf = AllocMem(len, TypeOfMem(buf))) {
	BMov(buf, newbuf, len);
	return(newbuf);
    }
    return(NULL);
}

#asm

	    ;	CCall(funcptr, argument)

_CCall:     movem.l 4(sp),A0/A1
	    movem.l D2/D3/A4/A5/A6,-(sp)
	    move.l  A1,-(sp)
	    jsr     (A0)
	    addq.l  #4,sp
	    movem.l (sp)+,D2/D3/A4/A5/A6
	    rts
#endasm

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

/*
 *  MISC.C
 *
 *  General Stuff... mostly in assembly.
 */

#include <local/typedefs.h>

typedef struct {
    NODE     ml_Node;
    uword    ml_NumEntries;
    MEMENTRY ml_ME[2];
} MYMEMLIST;

APTR
lGetTaskData(name, bytes)
char *name;
long bytes;
{
    extern EXECBASE *SysBase;
    extern void *FindName2();
    register LIST *list;
    register MEMLIST *ml;

    list = &SysBase->ThisTask->tc_MemEntry;
    if (ml = FindName2(list, name))
	return(ml->ml_ME[0].me_Un.meu_Addr);
    if (!list->lh_Head)
	NewList(list);
    {
	MYMEMLIST Ml;

	Ml.ml_NumEntries = 2;
	Ml.ml_ME[0].me_Un.meu_Reqs = MEMF_PUBLIC|MEMF_CLEAR;
	Ml.ml_ME[0].me_Length = bytes;
	Ml.ml_ME[1].me_Un.meu_Reqs = MEMF_PUBLIC;
	Ml.ml_ME[1].me_Length = strlen(name)+1;
	if (ml = AllocEntry(&Ml)) {
	    ml->ml_Node.ln_Name = (char *)ml->ml_ME[1].me_Un.meu_Addr;
	    strcpy(ml->ml_Node.ln_Name, name);
	    AddHead(list, ml);
	    return(ml->ml_ME[0].me_Un.meu_Addr);
	}
    }
    return(NULL);
}

lFreeTaskData(name)
char *name;
{
    extern EXECBASE *SysBase;
    extern void *FindName2();
    register MEMLIST *ml;

    if (ml = FindName2(&SysBase->ThisTask->tc_MemEntry, name)) {
	Remove(ml);
	FreeEntry(ml);
	return(1);
    }
    return(0);
}


#asm


		include "exec/types.i"
		include "exec/ports.i"
		include "exec/tasks.i"
		include "exec/execbase.i"
		include "exec/ables.i"
		include "exec/memory.i"

		;   NOTE:   LockAddr/UnLockAddr referenced elsewhere in
		;	    this library

		;   MISC.ASM

		public	_lWildCmp	; Wildcard compare
		public	_lWaitMsg	; Wait for a message to be replied
		public	_lCheckMsg	; Check if message has been returned
		public	_lCheckPort	; Check if message pending on port
		public	_lDoSyncMsg	; Put and Wait for a message
		public	_lLockAddr
		public	_lLockAddrB
		public	_lUnLockAddr
		public	_lUnLockAddrB
		public	_lFindName2

		public	_LVOWait
		public	_LVORemove
		public	_LVODisable
		public	_LVOEnable
		public	_LVOFindTask
		public	_LVOSignal
		public	_LVOForbid
		public	_LVOPermit
		public	_LVOPutMsg


_lDoSyncMsg:	movem.l A2/A3/A6,-(sp)              ; A0=port, A1=msg
		move.l	4,A6			    ; A6=execbase

		sub.w	#MP_SIZE,sp		    ; initialize reply port
		move.b	#NT_MSGPORT,LN_TYPE(sp)
		move.b	#PA_SIGNAL,MP_FLAGS(sp)
		move.b	#4,MP_SIGBIT(sp)            ; EXEC semaphore signal
		move.l	ThisTask(A6),MP_SIGTASK(sp)
		lea	MP_MSGLIST(sp),A2
		lea	MP_MSGLIST+4(sp),A3
		move.l	A3,(A2)                     ; &tail -> head
		move.l	#0,(A3)                     ; NULL  -> tail
		move.l	A2,8(A2)                    ; &head -> tailpred

		move.l	sp,MN_REPLYPORT(A1)
		move.l	A1,A2			    ; save message
		jsr	_LVOPutMsg(A6)              ; send the message
		move.l	A2,A0
		bsr	_lWaitMsg		    ; wait for reply

		add.w	#MP_SIZE,sp
		movem.l (sp)+,A2/A3/A6
		rts

_lCheckMsg:	moveq.l #0,D0
		cmp.b	#NT_MESSAGE,LN_TYPE(A0) ;NT_MESSAGE == not replied
		beq	.lcm1
		move.l	A0,D0
.lcm1		rts

_lCheckPort:	moveq.l #0,D0
		move.l	MP_MSGLIST+LH_HEAD(A0),A0
		tst.l	(A0)                    ;list empty?
		beq	.lcp1
		move.l	A0,D0			;no, return first element
.lcp1		rts

_lWaitMsg:					;A0 = message to wait for
		movem.l A2/A3/A6,-(sp)
		move.l	A0,A2			;A2 = message
		move.l	MN_REPLYPORT(A0),A3     ;A3 = replyport
		move.l	4,A6			;A6 = execbase
.wmloop 	cmp.b	#NT_MESSAGE,LN_TYPE(A2) ;while msg not replied
		bne	.wm1
		move.b	MP_SIGBIT(A3),D1        ;Wait on port signal
		moveq.l #0,D0
		bset.l	D1,D0
		jsr	_LVOWait(A6)
		bra	.wmloop
.wm1		jsr	_LVODisable(A6)         ;remove from port
		move.l	A2,A1			;A1 = message (A2)
		jsr	_LVORemove(A6)
		jsr	_LVOEnable(A6)
		move.l	A2,D0			;return message
		movem.l (sp)+,A2/A3/A6
		rts


		;WILDCMP(wild:D0, name:D1)
		;
		;   Handles * and ?
		;
		;result:  D0, 0 = no match, 1 = match
		;
		;auto:
		;   D2	bi
		;   A2	wildcard string
		;   A3	name	 string
		;   A4	back-array (of size MAXB * 2 * 4)


MAXB		EQU	8

_lWildCmp:	movem.l D2/A2-A4,-(sp)
		move.l	D0,A2
		move.l	D1,A3
		sub.l	#MAXB*2*8,sp
		move.l	sp,A4

		moveq.l #0,D2

.wcloop 	moveq.l #1,D0
		move.b	(A2),D1
		bne	.w1
		tst.b	(A3)
		beq	.wcdone

.w1		cmp.b	#'*',D1
		bne	.w10
		cmp.w	#MAXB,D2
		bne	.w2
		moveq.l #-1,D0		; error
		bra	.wcdone
.w2		move.w	D2,D0		; back[bi][0] = w  i.e. back+bi*8
		asl.w	#3,D0		; back[bi][1] = n
		move.l	A2,(A4,D0.w)
		move.l	A3,4(A4,D0.w)
		addq.w	#1,D2
		addq.l	#1,A2
		bra	.wcloop

.wgoback	subq.w	#1,D2
		bmi	.w5
		move.w	D2,D0
		asl.w	#3,D0
		move.l	4(A4,D0.w),A0
		tst.b	(A0)
		beq	.wgoback
.w5		tst.w	D2
		bmi	.wcret0
		move.w	D2,D0
		asl.w	#3,D0
		move.l	(A4,D0.w),A2
		addq.l	#1,A2
		add.l	#1,4(A4,D0.w)
		move.l	4(A4,D0.w),A3
		addq.l	#1,D2
		bra	.wcloop

.w10		cmp.b	#'?',D1
		bne	.w20
		tst.b	(A3)
		bne	.wcbreak
		tst.w	D2
		bne	.wgoback
		bra	.wcret0

.w20		move.b	(A3),D0
		cmp.b	#'A',D0
		blo	.w21
		cmp.b	#'Z',D0
		bhi	.w21
		or.b	#$20,D0
.w21		move.b	(A2),D1
		cmp.b	#'A',D1
		blo	.w22
		cmp.b	#'Z',D1
		bhi	.w22
		or.b	#$20,D1
.w22		cmp.b	D0,D1
		beq	.wcbreak
		tst.w	D2
		bne	.wgoback
		bra	.wcret0

.wcbreak	tst.b	(A2)+
		bne	.wcb1
		subq.l	#1,A2
.wcb1		tst.b	(A3)+
		bne	.wcb2
		subq.l	#1,A3
.wcb2		bra	.wcloop

.wcret0 	moveq.l #0,D0
.wcdone 	add.l	#MAXB*2*8,sp
		movem.l (sp)+,D2/A2-A4
		rts

		;   long var[2] = { 0, 0 };
		;
		;   These routines provide fast exclusive
		;   locks.  Up to 8 independant locks may be used for
		;   each 4 byte address.
		;
		;   LockAddr(&var[0]:A0)
		;   LockAddrB(bit:D0, &var[0]:A0)
		;   UnLockAddr(&var[0]:A0)
		;   UnLockAddrB(bit:D0, &var[0]:A0)


_lLockAddr:	moveq.l #0,D0			; bit 0
_lLockAddrB:	bset.b	D0,4(A0)                ; attempt to gain lock
		bne	.la10
		rts				; was clear, so got it
.la10		movem.l A2/A6,-(sp)             ; else failed,
		move.l	4,A6			; A6 = SYSBase
		FORBID
		bset.b	D0,4(A0)                ; try again
		beq	.la20			; got it!

		move.w	D0,-(sp)                ; bit#    12(sp)
		move.l	ThisTask(A6),-(sp)      ; task#    8(sp)
		move.l	A0,-(sp)                ; &var     4(sp)
		move.l	(A0),-(sp)              ; Next      (sp)
		move.l	sp,(A0)                 ; (put at head of list)
.la15		moveq.l #$10,D0 		; wait (semaphore signal)
		jsr	_LVOWait(A6)

		move.w	12(sp),D0               ; try for lock
		move.l	4(sp),A0
		bset.b	D0,4(A0)
		bne	.la15			; loop until get it

.la16		cmp.l	(A0),sp                 ; unlink, find our node!
		beq	.la18
		move.l	(A0),A0
		bra	.la16
.la18		move.l	(sp),(A0)
		add.w	#14,sp
.la20
		PERMIT
		movem.l (sp)+,A2/A6
		rts

_lUnLockAddr:	moveq.l #0,D0			; bit 0
_lUnLockAddrB:	bclr.b	D0,4(A0)                ; clear lock bit
		move.l	(A0),D1                 ; anybody waiting?
		beq	.ulrts			; no, rts

		movem.l D2/A2/A6,-(sp)          ; yes, wake 'm all up
		move.b	D0,D2			; D2 = bit#
		move.l	4,A6			; A6 = SYSBase
		FORBID

		move.l	(A0),D1                 ; get pointer again after FORBID
		beq	.ul20			; no, rts (close a window)

.ul10		move.l	D1,A2			; A2 = node
		cmp.b	13(A2),D2               ; waiting on our bit #?
		bne	.ul18			; no
		move.l	8(A2),A1                ; yes, signal the node
		moveq.l #$10,D0
		jsr	_LVOSignal(A6)          ; signal EVERYONE waiting
.ul18		move.l	(A2),D1                 ; next
		bne	.ul10

.ul20
		PERMIT
		movem.l (sp)+,D2/A2/A6
.ulrts		rts

		;   FindName2(list:D0, name:A0)
		;
		;   Search the node list as in FindName(), but also ignore
		;   NULL ln_name entries, which FindName() does not do.  This
		;   routine will also return NULL if given an uninitialized
		;   list header (completely zero'd).  Finally, it will not
		;   bother to do a string compare if the two pointers are
		;   the same.

_lFindName2:	movem.l A2/A3,-(sp)
		move.l	D0,A1
		tst.l	(A1)                        ; uninitialized list header
		beq	.fn2fail
.fn2loop	move.l	(A1),A1                     ; get first/next node
		tst.l	(A1)                        ; end of list?
		beq	.fn2fail
		move.l	LN_NAME(A1),D0              ; name
		beq	.fn2loop		    ; NULL, skip
		cmp.l	D0,A0			    ; pointers are the same!
		beq	.fn2succ		    ;  don't bother w/cmp.
		move.l	D0,A2
		move.l	A0,A3
.fn2l2		cmpm.b	(A2)+,(A3)+
		bne	.fn2loop
		tst.b	-1(A2)
		bne	.fn2l2
.fn2succ	move.l	A1,D0
		movem.l (sp)+,A2/A3
		rts
.fn2fail	moveq.l #0,D0
		movem.l (sp)+,A2/A3
		rts

#endasm

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


/*
 *FUNC= GetRes		A0
 *FUNC= FreeRes 	A0
 *FUNC= FreeAllRes	A0
 *FUNC= ChownRes	D0/D1/A0
 *FUNC= UnLinkAllRes	A0
 *FUNC= ReLinkAllRes	A0
 *FUNC= SetResFlags	D0/D1/A0
 *FUNC= AddRes		D0/D1/D2/A0
 *FUNC= RemRes		A0
 *FUNC= GetResInfo	D0/D1/A0
 *FUNC= GetResList	D0/D1/D2/A0
 *FUNC= GetFileList	D0/D1/D2/A0
 *FUNC= AddPrivResFile	D0/D1
 *FUNC= RemPrivResFiles A0
 *FUNC= AddGlobResFile	D0/D1
 *FUNC= RemGlobResFiles A0
 *FUNC= AddResSwapDir	D0/D1/A0
 *FUNC= RemResSwapDirs	A0
 *FUNC= NULL		-
 *FUNC= NULL		-
 *FUNC= NULL		-
 *FUNC= NULL		-
 *FUNC= NULL		-
 *FUNC= NULL		-
 *FUNC= NULL		-
 *FUNC= NULL		-
 *FUNC= NULL		-
 *FUNC= NULL		-
 *FUNC= NULL		-
 *FUNC= NULL		-
 *FUNC= NULL		-
 *FUNC= NULL		-
 *
 */

#include <local/typedefs.h>
#include <local/res.h>
#include "file.h"

/*
 *  resptr = GetRes(resnametype)        char *resnametype;
 *
 *  (1) The resource is SHARED, is tagged with its name, then
 *	(a) if swapped, swap in
 *	(b) incr. ref. count.
 *  (2) The resource is not shared, re-load from disk
 *
 */

GetRes(rnt)
register char *rnt;
{
    RMSG    RMsg;
    register char *str;

    for (str = rnt + strlen(rnt); str >= rnt && *str != '.'; --str);
    RMsg.Arg1 = (long)rnt;
    RMsg.Arg2 = (long)str + 1;
    if ((RMsg.Arg3 = str - rnt) < 0)
	RMsg.Arg3 = 0;
    RMsg.Arg4 = strlen(str + 1);
    DoSyncMsg(&ResPort, &RMsg);
    return(RMsg.Res1);
}

    resptr= GetRes(resnametype)             char *resnametype;

	This function retrieves the requested resource, doing any
	translations required to get the resource into the requested
	type.  NULL is returned if the resource could not be found
	or could not be translated to the requested type.

	In-Memory private resources are searched first, then the private
	resource files for the task, then In-Memory system resources, then
	the global resource files for the system.  Openning an already-open
	resource causes one of two actions depending on whether the resource
	is shared or not.  If shared, the reference count is simply
	incremented, otherwise a private copy of the resource is made.

	Example:    Win = GetRes("Charlie.Window");

    error = FreeRes(resptr)

	Free a resource that you retrieved via GetRes().  Only the task
	that owns the resource may free it (though several tasks may own
	a resource through duplication and ownership changes).	1 is
	returned on success, 0 on error.

    numres= FreeAllRes(task)

	Free all resources associated with the specified task (NULL for
	self).

    error = ChownRes(resptr, fromtask, totask)

	Change ownership of a resource from the source task to the
	destination task.  The resource must be owned by the source
	task or an error will occur (0 return value).  1 is returned
	on success.  NULL may be specified for either or both tasks
	meaning the calling task.

    handle= UnLinkAllRes(task)

	Unlink all resources associated with the specified task (NULL
	for self) and return a handle representing those resources.

	This is useful for shells and such to keep their resources from
	getting removed by commands they run.  Combined with the NUNLINK
	flag one can pass resources to a Command and keep the rest out of
	reach.

    (void)  ReLinkAllRes(handle, task)

	Link all the resources represented by the handle to the specified
	task (NULL for self).

    oldfl = SetResFlags(resptr, newflags, flagsmask)

	Modify the flags associated with a resource.  NOTE:  If multiple
	references to a shared resource exist, all are modified.  Some
	modifications may be disallowed by the system.	This call is
	normally used to modify the LOCKED and SWAPABLE flags (note that
	the SWAPABLE flag can be changed back and forth only for resource
	which support it).

    error = AddRes(resnametype, flags, ptr, ctlcode);
						    char *resnametype;
						    long flags;
						    APTR ptr;
						    long (*ctlcode)();

	Add a resource to the system.  The resource is placed either in
	the task's privately accessable in-memory resource list or
	in the system global accessable in-memory resource list depending
	on the specified flags.  One may now GetRes() the resource.

	ONLY VIRTUAL RESOURCES MAY BE ADDED IN THIS WAY.  The VIRTUAL
	and LOCKED flags are automatically set.

	If flags specifies a VIRTUAL resource

    error = RemRes(resname)

	Remove the specified resource.	An error will occur and the resource
	will not be removed (1) if it does not exist in memory, or (2) it
	is currently being referenced.	(Note: the resource is removed even
	if it is swapped or locked).

    error = GetResInfo(resname, &flags, &bytes)

	Get information on a resource.	Information may not exist for a
	resource if it is not currently in memory and would have to be
	translated to get to the right type.

    error = GetResList(wildcard, from, &av, &ac);

	from:	mask, bit 0  search private list
			  1  search system list
			  2  <not used>
			  3  search in-memory private list
			  4  search in-memory global list

	Return an ARGC/ARGV list of resource names.  Note that some names
	might be duplicated if searching multiple lists.  Restricting the
	search to in-memory lists give resources which are already in
	memory (but might be swapped out or removed at any time for those
	with no references)

			     RESOURCE FILES

    error = GetFileList(wildcard, from, &av, &ac);

	from:	mask, bit 0  search private list
			  1  search system list
			  2  search swap list

	Return an ARGC/ARGV list of the files which match the specified
	wildcard from the private list, system list, or system swap dir
	list (in which case you get directory names).  This list has been
	allocated, and can be freed as follows:

	    Loop through all entries for (i = 0; i < ac; ++i)
					FreeMem(av[i], strlen(av[i])+1);
	    Free the array itself:  FreeMem(av, sizeof(char *) * (ac+1));

    num   = AddPrivResFile(filename, pri)

	Add a file name to the list of resource files for this task.  These
	are scanned before global files when a resource is requested.  All
	files in the list are write protected (i.e. a shared lock is kept
	for each file).  Thus, such files cannot be updated until removed
	from the list.

	Can also be used to modify the priority of an existing file

    num   = RemPrivResFiles(wildcard)

	Remove zero or more file names from the list of resource files for
	this task.  A wildcard pattern (* and ?) is accepted.

	Note for command processors:	commands you run might execute
	this command for *.

    num   = AddGlobResFile(filename, pri)

	Same as AddPrivResFile() but applies to the system list, which is
	searched last and by any requesting task.  Wildcard file names are
	NOT accepted.  The file need not exist at this time, and references
	to unmounted volumes are allowed.

	Can also be used to modify the priority of an existing entry

    num   = RemGlobResFiles(wildcard)

	Remove zero or more resource files from the global list.  Again,
	a wildcard filename is accepted.

    num   = AddResSwapDir(dirname, pri, maxkbytes)      char *dirname;
							char pri;
							long maxkbytes

	Add a directory to the list of directories the resource system
	can swap to.  The maximum number of KBytes of material allowed
	in the directory should be specified.  You can also use this
	call to modify the priority and maxkbytes for an entry.

	The highest priority directories are used before lower priority
	directories.  Not all directories need be mounted, but if a swapin
	occurs from an unmounted directory a requester will appear.

	A lock is kept on each specified directory.

    num   = RemResSwapDirs(wildcard)

	Remove directories associated with the resource swap areas.


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

		;   MEM.ASM

		public	_lBZero 	; Zero a block of memory
		public	_lBSet		; Set a block of memory to (byte val)
		public	_lBMov		; move a block of memory
		public	_lBCmp		; compare two blocks of memory

		;   BSET(buffer:D0, len:D1, byte:A0.B)
		;   BZERO(buffer:D0, len:D1)

_lBZero:	move.w	#0,A0
_lBSet: 	exg	A0,D0	    ; A0 = buffer   (D0=byte)
		exg	D0,D1	    ; D0 = length   (D1=byte)
		add.l	D0,A0	    ; start at end of address
		cmp.l	#40,D0	    ; unscientifically chosen
		bls	.bs2
		bra	.bs10
.bs1		move.b	D1,-(A0)    ; any count < 65536
.bs2		dbf	D0,.bs1
		rts

				    ; at least 2 bytes in count (D0)
.bs10		movem.l D2-D7/A2-A6,-(sp)   ;ant count > 4
		move.l	A0,D2
		btst.l	#0,D2	    ; is it aligned?
		beq	.bs22
		move.b	D1,-(A0)    ; no, copy one byte
		subq.l	#1,D0

.bs22		andi.l	#$FF,D1     ; expand data D1.B -> D2-D7/A1-A6
		move.l	D1,D2	    ; D1 000000xx   D2 000000xx
		asl.w	#8,D2	    ;		       0000xx00
		or.w	D2,D1	    ;	 0000xxxx
		move.w	D1,D2	    ;	 0000xxxx      0000xxxx
		swap	D2	    ;	 0000xxxx      xxxx0000
		or.l	D1,D2	    ; D2.L
		move.l	D2,D3
		move.l	D2,D4
		move.l	D2,D5
		move.l	D2,D6
		move.l	D2,D7
		move.l	D2,A1
		move.l	D2,A2
		move.l	D2,A3
		move.l	D2,A4
		move.l	D2,A5
		move.l	D2,A6	    ; D2-D7/A1-A6   (12 registers)
		move.l	#12*4,D1    ; bytes per transfer (48)
.bs30		sub.l	D1,D0	    ; pre subtract
		bmi	.bs40
.bs31		movem.l D2-D7/A1-A6,-(A0)
		sub.l	D1,D0
		bpl	.bs31
.bs40		add.w	D1,D0	    ; less than 48 bytes remaining

		move.w	#4,D1	    ; by 4's
		sub.w	D1,D0
		bmi	.bs50
.bs41		move.l	D2,-(A0)
		sub.w	D1,D0
		bpl	.bs41
.bs50		add.w	D1,D0
		bra	.bs52
.bs51		move.b	D2,-(A0)    ; by 1's
.bs52		dbf	D0,.bs51
		movem.l (sp)+,D2-D7/A2-A6
		rts

		;   BCMP(src:D0, dst:D1, len:A0)

_lBCmp: 	exg	A0,D0	    ;A0 = src, D0 = len
		exg	A1,D1	    ;A1 = dst

		tst.l	D0
		beq	.bcsucc
		cmp.w	D0,D0	    ;force Z bit
		bra	.bc2
.bc1		cmpm.b	(A0)+,(A1)+
.bc2		dbne	D0,.bc1
		bne	.bcfail
		sub.l	#$10000,D0
		bcc	.bc1
.bcsucc 	moveq.l #1,D0	    ;success!
		rts
.bcfail 	moveq.l #0,D0	    ;failure!
		rts

		;   BMOV(src:D0, dst:D1, len:A0)
		;
		;   The memory move algorithm is somewhat more of a mess
		;   since we must do it either ascending or decending.

_lBMov: 	exg	A0,D0		;A0 = src, D0 = len
		exg	A1,D1		;A1 = dst
		cmp.l	A0,A1
		beq	.bmend
		bls	.bmup
.bmdown 	adda.l	D0,A0		;descending copy
		adda.l	D0,A1
		move.w	A0,D1		;CHECK WORD ALIGNED
		btst.l	#0,D1
		bne	.bmdown1
		move.w	A1,D1
		btst.l	#0,D1
		bne	.bmdown1
		cmp.l	#259,D0 	    ;chosen by calculation.
		blo	.bmdown8

		move.l	D0,D1		    ;overhead for bmd44: ~360
		divu	#44,D1
		bvs	.bmdown8	    ;too big (> 2,883,540)
		movem.l D2-D7/A2-A6,-(sp)   ;use D2-D7/A2-A6 (11 regs)
		move.l	#11*4,D0
		bra	.bmd44b
.bmd44a 	sub.l	D0,A0		    ;8		total 214/44bytes
		movem.l (A0),D2-D7/A2-A6    ;12 + 8*11  4.86 cycles/byte
		movem.l D2-D7/A2-A6,-(A1)   ; 8 + 8*11
.bmd44b 	dbf	D1,.bmd44a	    ;10
		swap	D1		    ;D0<15:7> already contain 0
		move.w	D1,D0		    ;D0 = remainder
		movem.l (sp)+,D2-D7/A2-A6

.bmdown8	move.w	D0,D1		    ;D1<2:0> = #bytes left later
		lsr.l	#3,D0		    ;divide by 8
		bra	.bmd8b
.bmd8a		move.l	-(A0),-(A1)         ;20         total 50/8bytes
		move.l	-(A0),-(A1)         ;20         = 6.25 cycles/byte
.bmd8b		dbf	D0,.bmd8a	    ;10
		sub.l	#$10000,D0
		bcc	.bmd8a
		move.w	D1,D0		    ;D0 = 0 to 7 bytes
		and.l	#7,D0
		bne	.bmdown1
		rts

.bmd1a		move.b	-(A0),-(A1)         ;12         total 22/byte
.bmdown1				    ;		= 22 cycles/byte
.bmd1b		dbf	D0,.bmd1a	    ;10
		sub.l	#$10000,D0
		bcc	.bmd1a
		rts

.bmup		move.w	A0,D1		    ;CHECK WORD ALIGNED
		btst.l	#0,D1
		bne	.bmup1
		move.w	A1,D1
		btst.l	#0,D1
		bne	.bmup1
		cmp.l	#259,D0 	    ;chosen by calculation
		blo	.bmup8

		move.l	D0,D1		    ;overhead for bmu44: ~360
		divu	#44,D1
		bvs	.bmup8		    ;too big (> 2,883,540)
		movem.l D2-D7/A2-A6,-(sp)   ;use D2-D7/A2-A6 (11 regs)
		move.l	#11*4,D0
		bra	.bmu44b
.bmu44a 	movem.l (A0)+,D2-D7/A2-A6   ;12 + 8*11  ttl 214/44bytes
		movem.l D2-D7/A2-A6,(A1)    ;8  + 8*11  4.86 cycles/byte
		add.l	D0,A1		    ;8
.bmu44b 	dbf	D1,.bmu44a	    ;10
		swap	D1		    ;D0<15:7> already contain 0
		move.w	D1,D0		    ;D0 = remainder
		movem.l (sp)+,D2-D7/A2-A6

.bmup8		move.w	D0,D1		    ;D1<2:0> = #bytes left later
		lsr.l	#3,D0		    ;divide by 8
		bra	.bmu8b
.bmu8a		move.l	(A0)+,(A1)+         ;20         total 50/8bytes
		move.l	(A0)+,(A1)+         ;20         = 6.25 cycles/byte
.bmu8b		dbf	D0,.bmu8a	    ;10
		sub.l	#$10000,D0
		bcc	.bmu8a
		move.w	D1,D0		    ;D0 = 0 to 7 bytes
		and.l	#7,D0
		bne	.bmup1
		rts

.bmu1a		move.b	(A0)+,(A1)+
.bmup1
.bmu1b		dbf	D0,.bmu1a
		sub.l	#$10000,D0
		bcc	.bmu1a
.bmend		rts

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

/*
 *  GTD     -GetTaskData/FreeTaskData tester
 *
 *  Matthew Dillon, 30 Sep 1988
 *
 *  gtd 		    - show task memlist entries
 *  gtd <name>		    - remove an entry
 *  gtd <name> <bytes>	    - add/retrieve an entry, display it, then
 *			      store sequential values into the buffer.
 */

#include <stdio.h>
#include <local/typedefs.h>
#include <local/xmisc.h>

extern int Enable_Abort;
extern char *GetTaskData();

main(ac,av)
char *av[];
{
    char *ptr;
    long bytes;
    short i;
    TASK *task = FindTask(NULL);

    Enable_Abort = 0;

    if (openlibs(DRES_LIB) == 0)
	exit(1);
    if (ac == 1) {
	MEMLIST *ml;
	for (ml = GetHead(&task->tc_MemEntry); ml; ml = GetSucc(ml)) {
	    printf("memlist %08lx %ld %s\n", ml, ml->ml_NumEntries,
		((ml->ml_Node.ln_Name) ? ml->ml_Node.ln_Name : "NULL")
	    );
	}
	goto fail;
    }
    bytes = atoi(av[2]);
    printf("%s,%ld :", av[1], bytes);
    fflush(stdout);
    if (ac == 2) {
	printf("result %ld\n", FreeTaskData(av[1]));
    } else {
	ptr = GetTaskData(av[1], bytes);
	if (!ptr) {
	    puts("failed");
	    goto fail;
	}
	printf("%08lx ", ptr);
	fflush(stdout);
	for (i = 0; i < bytes; ++i) {
	    printf("%02x ", ptr[i]);
	    ptr[i] = i;
	}
    }
fail:
    closelibs(-1);
}

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

/*
 *  TESTIPC <appname> <file> <file> <file> ...	<inputfile
 *
 *  input file format:
 *
 *  appname delay file text
 *
 */

#include <local/typedefs.h>
#include <local/ipc.h>
#include <local/xmisc.h>
#include <stdio.h>

extern int Enable_Abort;

extern PORT *OpenIPC();

char **Av;
short Ac;

main(ac, av)
char *av[];
{
    PORT *port;

    Av = av;
    Ac = ac;
    if (ac == 1) {
	puts("TESTIPC <appname> <subname> ... <subname>  <inputfile");
	puts("input file format:");
	puts("<appname> <delay-1/50ths> <subname> <text>");
	exit(1);
    }
    Enable_Abort = 0;
    if (openlibs(DRES_LIB) <= 0) {
	puts("Unable to open dres.library");
	exit(1);
    }
    port = OpenIPC(av[1], 0);
    if (port == NULL) {
	puts("unable to open port");
	closelibs(-1);
	exit(1);
    }
    printf("port: %08lx\n", port);
    {
	char apname[32];
	long delay;
	char subname[32];
	char text[64];
	char tbuf[128];
	IPCMSG msg;
	extern void handler();

	BZero(&msg, sizeof(msg));
	msg.Msg.mn_ReplyPort = CreatePort(NULL, 0);

	puts("scanf");
	while (scanf("%s %ld %s %s", apname, &delay, subname, text) == 4) {
	    printf("DO(%s %ld %s %s) ", apname, delay, subname, text);
	    fflush(stdout);
	    strcpy(tbuf, subname);
	    strcpy(tbuf+strlen(subname)+1, text);
	    msg.TBuf = (APTR)tbuf;
	    msg.TLen = strlen(subname)+strlen(text)+2;
	    DoIPC2(apname, &msg, handler, port);
	    if (msg.RFlags & IF_NOTFND) {
		if (msg.RFlags & IF_NOAPP)
		    puts("Application not found");
		else
		    puts("subname not found");
	    } else {
		if (msg.RFlags & IF_ERROR)
		    printf("Error: %s\n", msg.RBuf);
		else
		    printf("OK: %s\n", msg.RBuf);
	    }
	    FreeIPC(&msg);
	    if (delay)
		Delay(delay);
	    if (CheckPort(port))
		handler(port);
	}
	DeletePort(msg.Msg.mn_ReplyPort);
    }
    CloseIPC(port);
    closelibs(-1);
}

void
handler(port)
PORT *port;
{
    IPCMSG *msg;
    short i, j;

    while (msg = GetMsg(port)) {
	register char *ptr = (char *)msg->TBuf;
	printf("Received IPC command: %s  ", ptr);
	for (j = 0; ptr[j] && ptr[j] != ' '; ++j);
	for (i = 2; i < Ac; ++i) {
	    if (strlen(Av[i]) == j && strncmp(Av[i], ptr, j) == 0)
		break;
	}
	if (i < Ac) {
	    puts("MATCH");
	    ReplyIPC(msg, "GotIt!", 7, 0);
	} else {
	    puts("PASSED");
	    ReplyIPC(msg, "Failed", 7, IF_NOTFND);
	}
    }
}

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

/*
 *  TEST.C
 *
 *  Test Library Functions.
 */

#include <typedefs.h>
#include <stdio.h>

typedef struct Library LIB;

LIST ListA;
LIST ListB;
NODE Node1;
NODE Node2;
NODE Node3;
short Glob;

extern int Enable_Abort;

LIB *DMiscSupportBase;

extern LIB *OpenLibrary();
extern char *AllocMem();
extern char *GetHead();
extern char *GetTail();
extern char *GetSucc();
extern char *GetPred();
extern char *GetHeadOff();
extern char *GetTailOff();
extern char *GetSuccOff();
extern char *GetPredOff();
extern long srch();

main()
{
    Enable_Abort = 0;
    DMiscSupportBase = OpenLibrary("dmiscsup.library", 0);
    if (!DMiscSupportBase) {
	puts("Unable to open library");
	exit(1);
    }
    printf("library at: %08lx\n", DMiscSupportBase);
    printf("library flags: %02lx  Rev: %ld  OpenCnt: %ld\n",
	DMiscSupportBase->lib_Flags,
	DMiscSupportBase->lib_Revision,
	DMiscSupportBase->lib_OpenCnt
    );
    NewList(&ListA);
    NewList(&ListB);
    AddTail(&ListA, &Node1);
    AddTail(&ListA, &Node2);
    AddTail(&ListA, &Node3);
    printf("\nGetHead()         "); fflush(stdout);
    results((NODE *)GetHead(&ListA) == &Node1);
    results(GetHead(&ListB) == NULL);
    printf("\nGetTail()         "); fflush(stdout);
    results((NODE *)GetTail(&ListA) == &Node3);
    results(GetTail(&ListB) == NULL);
    printf("\nGetSucc()         "); fflush(stdout);
    results((NODE *)GetSucc(&Node1) == &Node2);
    results((NODE *)GetSucc(&Node3) == NULL);
    printf("\nGetPred()         "); fflush(stdout);
    results((NODE *)GetPred(&Node3) == &Node2);
    results((NODE *)GetPred(&Node1) == NULL);
    printf("\nGetHeadOff()      "); fflush(stdout);
    results(GetHeadOff(&ListA, 3) == (char *)&Node1 - 3);
    results(GetHeadOff(&ListB, 3) == NULL);
    printf("\nGetTailOff()      "); fflush(stdout);
    results(GetTailOff(&ListA, 3) == (char *)&Node3 - 3);
    results(GetTailOff(&ListB, 3) == NULL);
    printf("\nGetSuccOff()      "); fflush(stdout);
    results(GetSuccOff((char *)&Node1 - 3, 3) == (char *)&Node2 - 3);
    results(GetSuccOff((char *)&Node3 - 3, 3) == NULL);
    printf("\nGetPredOff()      "); fflush(stdout);
    results(GetPredOff((char *)&Node2 - 3, 3) == (char *)&Node1 - 3);
    results(GetPredOff((char *)&Node1 - 3, 3) == NULL);
    printf("\nSearchFwdNode()   "); fflush(stdout);
    Glob = 0; SearchFwdNodeOff(NULL, srch, 3, 27); results(Glob == 0);
    Glob = 0; SearchFwdNodeOff(GetHeadOff(&ListA, 3), srch, 3, 27); results(Glob == 3);
    printf("\nSearchRvsNode()   "); fflush(stdout);
    Glob = 0; SearchRvsNodeOff(NULL, srch, 3, 27); results(Glob == 0);
    Glob = 0; SearchRvsNodeOff(GetTailOff(&ListA, 3), srch, 3, 27); results(Glob == 3);
    puts("");
    wildtest();
    memory_test();
    RemLibrary(DMiscSupportBase);
    CloseLibrary(DMiscSupportBase);
}

results(bool)
{
    if (bool)
	printf("   ok");
    else
	printf(" fail");
    fflush(stdout);
}

srch(sptr, arg)
{
    ++Glob;
    printf(".");
    fflush(stdout);
    if (arg != 27)      /*  failure */
	return(1);
    return(NULL);
}

/*
 *  TEST:   WildCmp()
 */

wildtest()
{
    printf("WildCmp()         "); fflush(stdout);
    results(WildCmp("a??b", "axeb") == 1);
    results(WildCmp("a??b", "axebx")== 0);
    results(WildCmp("*/x*y*b", "a/x/u/xcharliey/b") == 1);
    results(WildCmp("*/x*y*b", "a/x/u/xcharlieq/b") == 0);
    puts("");
}

/*
 *  TEST:   BZero(), BSet(), BMov(), and BCmp();
 */

memory_test()
{
    long Size = 80000;	    /*	MUST BE 80000	*/
    short result;
    short i;
    char *ary1 = AllocMem(Size, MEMF_PUBLIC);
    char *ary2 = AllocMem(Size, MEMF_PUBLIC);

    printf("\nTEST MEMORY SUBROUTINES:\n");
    if (!ary1 || !ary2) {
	puts("    UNABLE TO ALLOCATE TWO CONTIGUOUS 80K SEGMENTS");
	puts("    THUS, CANNOT PERFORM MEMORY ROUTINES TEST.");
	goto fail;
    }

    /*
     *	Test BZero/BSet (both are the same subroutine, essentially).
     *	Test:	>64K, odd start, odd length.
     */

    printf(" BZero():   "); fflush(stdout);

    xbset(ary1, Size, 43);
    xbset(ary2, Size, 43);
    printf("."); fflush(stdout);
    BZero(ary1 + 1000, 20);
    BZero(ary1 + 1200, 70000);
    BZero(ary2 + 512 + 3, 70000);
    printf("."); fflush(stdout);
    results(xbcheck(ary1, 1000, 43));
    results(xbcheck(ary1 + 1000, 20, 0));
    results(xbcheck(ary1 + 1020, 1200 - 1020, 43));
    results(xbcheck(ary1 + 1200, 70000, 0));
    results(xbcheck(ary1 + 1200 + 70000, Size - 1200 - 70000, 43));

    printf("\n BSet():   "); fflush(stdout);
    xbset(ary1, Size, 43);
    xbset(ary2, Size, 43);
    printf("."); fflush(stdout);
    BSet(ary1 + 999, 21, 5);
    BSet(ary1 + 1201, 70001, 6);
    BSet(ary2 + 512 + 1, 69999, 7);
    printf("."); fflush(stdout);
    results(xbcheck(ary1, 999, 43));
    results(xbcheck(ary1 + 999, 21, 5));
    results(xbcheck(ary1 + 999 + 21, 1201 - 999 - 21, 43));
    results(xbcheck(ary1 + 1201, 70001, 6));
    results(xbcheck(ary1 + 1201 + 70001, Size - 1201 - 70001, 43));
    results(xbcheck(ary2, 512 + 1, 43));
    results(xbcheck(ary2 + 512 + 1, 69999, 7));
    results(xbcheck(ary2 + 512 + 1 + 69999, Size - 69999 - 512 - 1, 43));

    /*
     *	TEST BMov().  Odd start, overlapping blocks in forward and
     *	reverse.
     */

    printf("\n BMov(): 1"); fflush(stdout);
    xbset(ary1, Size, 43);
    xbset(ary2, Size, 43);
    xbiset(ary1 + 1001, 69999, 1);
    xbiset(ary2 + 1003, 70001, 1);
    BMov(ary1 + 1001, ary1 + 997, 69999);
    BMov(ary2 + 1003, ary2 + 1008, 70001);
    results(xbicheck(ary1 + 997, 69999, 1));
    results(xbicheck(ary2 + 1008, 70001, 1));
    results(xbcheck(ary1, 997, 43));
    results(xbcheck(ary1 + 1001 + 69999, Size - 1001 - 69999, 43));
    results(xbcheck(ary2, 1003, 43));
    results(xbcheck(ary2 + 1008 + 70001, Size - 1008 - 70001, 43));
    printf("\n BMov(): 2"); fflush(stdout);
    xbset(ary1, Size, 43);
    xbset(ary2, Size, 43);
    xbiset(ary1 + 1024, 70000, 1);
    xbiset(ary2 + 1024, 70000, 1);
    BMov(ary1 + 1024, ary1 + 1004, 70000);
    BMov(ary2 + 1024, ary2 + 1044, 70000);
    results(xbicheck(ary1 + 1004, 70000, 1));
    results(xbicheck(ary2 + 1044, 70000, 1));
    results(xbcheck(ary1, 1004, 43));
    results(xbcheck(ary1 + 1024 + 70000, Size - 70000 - 1024, 43));
    results(xbcheck(ary2, 1024, 43));
    results(xbcheck(ary1 + 1044 + 70000, Size - 70000 - 1044, 43));
    puts("");
    printf(" SPEEDTEST (K/sec BMov lw bndry, fastmem): %ld K/sec\n", speedtest(MEMF_FAST, 0, 0));
    printf(" SPEEDTEST (K/sec BSet lw bndry, fastmem): %ld K/sec\n", speedtest(MEMF_FAST, 1, 0));
    printf(" SPEEDTEST (K/sec BMov lw bndry, chipmem): %ld K/sec\n", speedtest(MEMF_CHIP, 0, 0));
    printf(" SPEEDTEST (K/sec BSet lw bndry, chipmem): %ld K/sec\n", speedtest(MEMF_CHIP, 1, 0));
    puts("");
    printf(" SPEEDTEST (K/sec BMov by bndry, fastmem): %ld K/sec\n", speedtest(MEMF_FAST, 0, 1));
    printf(" SPEEDTEST (K/sec BMov by bndry, chipmem): %ld K/sec\n", speedtest(MEMF_CHIP, 0, 1));
fail:
    if (ary1)
	FreeMem(ary1, Size);
    if (ary2)
	FreeMem(ary2, Size);
}

speedtest(memtype, zero, off)
{
    char *buf = AllocMem(65536+256, memtype);
    short i;
    long s;

    if (!buf)
	return(0);
    s = ltm();
    for (i = 0; i < 128; ++i) {
	if (zero)
	    BSet(buf + off, 65536, 1);
	else
	    BMov(buf + off, buf + 128 + off, 65536);
    }
    s = ltm() - s;          /*  1/50ths second for 64K * 128 */
    FreeMem(buf, 65536+256);
    return(64 * 128 * 50 / s);
}

ltm()
{
    long st[3];

    DateStamp(st);
    return(st[2] + 50 * 60 * st[1] + 50 * 60 * 60 * 24 * st[0]);
}

xbset(s, n, v)
register char *s;
register long n;
register char v;
{
    while (n--)
	*s++ = v;
}

xbiset(s, n, v)
register char *s;
register long n;
register char v;
{
    while (n--) {
	++v;
	*s++ = v;
    }
}


xbcheck(s, n, v)
register char *s;
register long n;
register char v;
{
    char *orig = s;
    short errs = 255;
    while (n--) {
	if (*s++ != v) {
	    if (errs) {
		--errs;
		printf("(%ld)", s - orig - 1);
	    } else {
		printf("giveup");
		return(0);
	    }
	}
    }
    return(errs == 255);
}

xbicheck(s, n, v)
register char *s;
register long n;
register char v;
{
    char *orig = s;
    short errs = 255;
    while (n--) {
	++v;
	if (*s++ != v) {
	    if (errs) {
		--errs;
		printf("(%ld)", s - orig);
	    } else {
		printf("giveup");
		return(0);
	    }
	}
    }
    return(errs == 255);
}


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

/*
 *  QTEST.C
 */

#include <typedefs.h>
#include <stdio.h>

typedef struct Library LIB;

LIB *DMiscSupportBase;
long QHan;
long Count;
long EnErr;
long Error;

extern LIB *OpenLibrary();
extern TASK *FindTask();

handler(arg)
{
    printf("ARG: %ld ************\n", arg);
}

main()
{
    int i;

    DMiscSupportBase = OpenLibrary("dmiscsup.library", 0);
    if (!DMiscSupportBase) {
	puts("Unable to open library");
	exit(1);
    }
    puts("Q-TEST");
    QHan = OpenQInts();
    if (!QHan) {
	puts("Unable to alloc. q ints");
	CloseLibrary(DMiscSupportBase);
	exit(1);
    }
    printf("QHAn=  %08lx\n", QHan);
    puts("UnSetting NULL vector");
    SetQVector(QHan, NULL, SIGBREAKB_CTRL_E, 0, 0);
    puts("Setting ^D vector");
    SetQVector(QHan, handler, SIGBREAKB_CTRL_D, 30, -20);
    puts("UnSetting NULL vector");
    SetQVector(QHan, NULL, SIGBREAKB_CTRL_E, 0, 0);
    puts("Loop");
    for (i = 0; i < 100; ++i) {
	char oldpri = SetQPri(QHan, 127);
	EnErr = 1;
	printf("%ld  cnt = %ld  error = %ld (old=%ld) %08lx %08lx\n", i, Count, Error, oldpri,
	    SetSignal(0,0), FindTask(NULL)->tc_SigExcept
	);
	EnErr = 0;
	SetQPri(QHan, oldpri);
    }
    puts("UnSetting NULL vector");
    SetQVector(QHan, NULL, SIGBREAKB_CTRL_E, 0, 0);
    printf("%ld  cnt = %ld  error = %ld\n", i, Count, Error);
    puts("closing");
    CloseQInts(QHan);
    puts("closelib");
    RemLibrary(DMiscSupportBase);
    CloseLibrary(DMiscSupportBase);
}


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

/*
 *
 *
 */

#include <local/xmisc.h>

extern int Enable_Abort;

char *
getv(name)
char *name;
{
    printf("GET: '%s'\n", name);
    return("VAR");
}

freev(str)
char *str;
{
    printf("FREE: %s\n", str);
}

main(ac,av)
char *av[];
{
    char buf[256];
    char **Av;
    short Ac;
    long error;

    Enable_Abort = 0;
    openlibs(DRES_LIB);

    while (gets(buf)) {
	printf("Cmd: %s\n", buf);
	Ac = ParseCmd(buf, &Av, getv, freev, &error, NULL);
	printf("%08lx %ld\n", Av, Ac);
	if (error) {
	    printf("ERROR!: %08lx\n", error);
	}
	if (Av) {
	    short i;
	    for (i = 0; i < Ac; ++i)
		printf("%2ld: (%ld) \"%s\"\n", i, Av[i][-1], Av[i]);
	    puts("free");
	    FreeParseCmd(Av);
	    puts("ok");
	}
    }

    closelibs(-1);
}

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


long Base;

main(ac,av)
char *av[];
{
    int ver = atoi(av[1]);

    Base = OpenLibrary("dres.library", ver);
    if (Base) {
	CloseLibrary(Base);
	puts("openned ok");
    } else {
	puts("open failed");
    }
}

\Rogue\Monster\
else
  echo "will not over write util/x.c"
fi
if [ `wc -c util/x.c | awk '{printf $1}'` -ne 215 ]
then
echo `wc -c util/x.c | awk '{print "Got " $1 ", Expected " 215}'`
fi
echo "Finished archive 1 of 3"
# 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.