/* ------------------------- ue18.c ------------------- */

#include "uedit.h"

#define HYPERWORDSIZE	17  			  /* max size of hyper word/phrase */

struct HTENTRY {							/* hyper table entry structure */
	UCHAR	word[18];							  /* the hyper word/phrase */
	short	level;  			 /* Adam is level 0, children level 1, etc */
	ULONG	offset; 		/* byte offset of this hyper word sect in file */
	USHORT  parent; 			   /* parent hyper word of this hyper word */
	USHORT  child;  								  /* child of this one */
	USHORT  nextSibling;			   /* next sibling of this one, if any */
	USHORT  lastSibling;						   /* last sibling, if any */
};

FhyperUp()
{
	return((hyperText.table==NULL) ? FALSE : TRUE);
}

FhyperClose()
{
register struct HYPER	 *h;
register long			 i;

	h = &hyperText;
	if (h->table==NULL) return(FALSE);
	i = 1L + h->topEntry;
	FreeMem(h->table,i << 5L);
	if (h->text) FreeMem(h->text,h->fileSize);  		/* free the buffer */
	else if (h->fd!=(BPTT)NULL) Close(h->fd);
	h->table = NULL;
	return(TRUE);
}

FhyperOpen()
{
register struct HYPER	*h;
register BPTT           fd;
register short  		flag;
register short  		k;
register ULONG  		t,f;
ULONG					filesize,tablesize;

	f = 0L; t = 0L; fd = (BPTT)NULL;
	h = &hyperText;
	if (h->table!=NULL) goto BADOUT2;

	h->text = NULL;
	h->fd = (BPTT)NULL;
	flag = *longPtrArgs[2];

	fd = (BPTT)openOld(fixString(' ',1,80));  		 /* load the table into memory */
	if (fd==(BPTT)NULL) {
BRTN:	oneMsg("Invalid table or name - rebuild table");
		goto BADOUT;
	}
	if (Read((BPTR)fd,&tablesize,4L)!=4L || Read((BPTR)fd,&filesize,4L)!=4L) goto BRTN;

	t = (ULONG)safeMem(tablesize);  						 /* read table */
	if (t==0L) {
BRTN2:  oneMsg("Not enough memory for hyper text");
		goto BADOUT;
	}
	if (Read((BPTR)fd,(void *)t,tablesize)!=tablesize) goto BRTN;
	Close((BPTR)fd);
	fd = (BPTT)NULL;
												 /* update hyper structure */
	h->table = (struct HTENTRY *)t;
	h->topEntry = (USHORT)(tablesize >> 5) - 1;
	h->fileSize = filesize;

	fd = (BPTT)openOld(fixString(' ',0,80));  	 /* try to open hyper text file */
	if (fd==(BPTT)NULL) {
BRTN3:  oneMsg("Can't find hyper text file");
		goto BADOUT;
	}
	if (flag==2) goto ONDISK;				  /* will be accessing on disk */
	h->fd = (BPTT)NULL;

	f = (ULONG)safeMem(filesize);
	if (f!=0L) {
		if (Read((BPTR)fd,(void *)f,filesize)!=filesize) {
			oneMsg("Hyper text file size wrong - rebuild table");
			goto BADOUT;
		}
		h->text = (UCHAR *)f;
		Close((BPTR)fd);
	} else {
		if (flag==0) goto BRTN2;			/* flag=0 must load & couldn't */
ONDISK: h->fd = fd;
	}
	for (k=0; k<20; k++) h->history[k] = 0;
	return(TRUE);

BADOUT:
	if (fd) Close((BPTR)fd);
	if (f) FreeMem((void *)f,filesize);
	if (t) FreeMem((void *)t,tablesize);
	h->table = NULL;
BADOUT2:
	return(FALSE);
}


	/* Hyper(outBufPtr,wordBufPtr,index,flag)
		outBufPtr	list 2
		wordBufPtr  list 0
		index		list 10
		flag		list 10

		fetches a hyper word's description, putting it into outbuf.  The word
		fetched depends on the word, index, and flag variables.

		The size of the memory block is the return value, size=HyperFetch().

		The index of the word found is returned in index.

		If HyperFetch() is unsuccessful, FALSE is returned and outbuf is
		unused.

		Searching methods:

			If *word==0x00 (no word):
				if flag=0,  look up index hyper word
				if flag=1,  look up parent of index
				if flag=2,  look up child of index
				if flag=3,  look up next sibling of index
				if flag=4,  look up prev sibling of index
				if flag=5,  step backward thru lookup history

			If *word is not empty:
				if flag=0,  find word starting from beginning of table
				if flag=1,  find word reading table starting at index
				if flag=2,  find word using algorithm starting at index

		The lookup algorithm, given a current table index, is to search
		through the:
			1. children 	(immediate, not grand children etc)
			2. siblings 	(entries with same parent as current hyper word)
			3. parent & parent's siblings
			4. grandparent & grandparent's siblings
			5. etc, on to end of tree.

		NOTES:
		1. index is used in ALL cases except when word exists and flag is 0.
		2. when wordBuf is not empty and the above algorithm is used, if the
		word is at index (the current index value), it is ignored and a new
		match for
		it is sought using the above algorithm.
		3. upper/lower case is ignored in the search.
		4. hyper words are looked up using 17 characters max; any extra
		characters are not used in the search.
		5. output buffer has a copy of the hyper text section;
		it has the Title Line, Parent Line, Hyper Line, and text area.
		6. the hyper word found is returned in the first 17 bytes of wordBuf.
	*/

Fhyper()
{
register struct HTENTRY *t,*tt;
register USHORT top,i;
register short  history;
register long	j;
struct CB		*outBufPtr;
struct HYPER	*h;
UCHAR			*x;
short			flag,lenx;
UCHAR			newword[80];

	h = &hyperText;
	if (h->table==NULL) goto BRTN;

	i = (USHORT)*longPtrArgs[0];
	if (!fixBuf(i)) goto BRTN;
	outBufPtr = buf[i];
	if (outBufPtr==curBuf) goto BRTN;

	x = fixString(0,1,18);
	top = h->topEntry;
	i = (USHORT)*longPtrArgs[2];
	if (i>top) { i = top; *longPtrArgs[2] = top; }
	flag = (USHORT)*longPtrArgs[3];

	t = h->table;
	history = FALSE;
	if (x!=NULL) {  						/* do lookup using word & index */
		x[17] = 0x00;
		toLower(x);
		switch (flag) {
		case 0: /* find word starting from beginning of table */
				i = 0;
		case 1: /* find word reading table starting at index */
				lenx = (short)strlen(x);
				j = -1L;
				while (1) {
					tt = t + i;
					if (*x==*(tt->word)) {
						if (strcmp(tt->word,x)==0) break;
						if (j==-1L && strncmp(tt->word,x,(size_t)lenx)==0) j = i;
					}
					if (i==top) {
						if (j>=0L) i = (USHORT)j;
						else return(FALSE);
						break;
					}
					i++;
				}
				break;
		case 2: /* find word using algorithm starting at index */
				j = hAlgo((ULONG)x,i);
				if (j>=0L) { i = j;  break; }
		default:  goto BRTN;
		}
	} else {
		tt = t + i;
		switch(flag) {
		case 1: /* look up parent of index */
				if (i==0) goto BRTN;
				i = tt->parent;
				break;
		case 2: /* look up child of index */
				i = tt->child;
				if (i==0) goto BRTN;
				break;
		case 3: /* look up next sibling of index */
				i = tt->nextSibling;
				if (i==0) goto BRTN;
				break;
		case 4: /* look up prev sibling of index */
				i = tt->lastSibling;
				if (i==0) goto BRTN;
				break;
		case 5: /* step backward thru lookup history */
				history = TRUE;
				i = h->current;
                i--;
				if (i<0) i = 19;
				h->current = i;
				i = h->history[i];
				break;
		}
		/* default is look up index hyper word */
	}

	if (hSelect(outBufPtr,i)) { 			   /* store in history */
		getNewWord(outBufPtr,newword);
		*longPtrArgs[2] = i;
		tt = t + i;
		strcpy(outBufPtr->FileName,newword);
		splitFlag(outBufPtr->BufNum,PAGE_);
		if (!history) {
			h->history[h->current] = i;
			i = h->current;
            i++;
			if (i>19) i = 0;
			h->current = i;
		}
		return(TRUE);
	}
BRTN:
	return(FALSE);
}

void getNewWord(	struct CB		*bufPtr,
					register UCHAR  *word)
{
register UCHAR  *p;
register short  i;
register UCHAR  ch;

	p = bufPtr->NextBlock->Data;
	bufPtr->SHBlock = bufPtr->EHBlock = (struct DB *)p;
	while (*p!=eol) p++;
	bufPtr->SHPtr = p+1;
	p += 2;
	i = 0;
	do {
		ch = *(p++);
		if (ch=='>') break;
		*(word++) = ch;
	} while (++i<80);
	*word = 0x00;
	bufPtr->EHPtr = p;
}

hSelect(	struct CB		*bufPtr,
			USHORT  		index)
{
register UCHAR  		*x,*p;
register long			j;
register BPTT           fd;
register UCHAR  		ch;
register long			offset,nextoffset;

struct HTENTRY  		*t;
struct HYPER			*h;
struct DB				*b;
short					inmem;
UCHAR					buffer[DATASIZE];

	h = &hyperText;
	if (index>h->topEntry) return(FALSE);
	t = h->table + index;

	/* get starting offset and size */
	if (index<h->topEntry)  nextoffset = (t+1)->offset - 1;
	else					nextoffset = h->fileSize;
	offset = t->offset + 1;

	/* load the section */
	if (h->text) {
		inmem = TRUE;
		x = h->text + offset;
		j = DATASIZE;
	} else {
		inmem = FALSE;
		x = buffer;
		fd = h->fd;
		Seek((BPTR)fd,offset,(long)OFFSET_BEGINNING);
		j = Read((BPTR)fd,x,(long)DATASIZE);
		if (j<=0L) goto BADOUT;
	}
	b = bufPtr->NextBlock;
	p = b->Data;
	while (1) {
		ch = *(x++);
		if (ISWEIRD(ch)) ch = charMappings[ch];
		*(p++) = ch;
		if (--j==0) {
			if (!inmem) {
				x = buffer;
				j = Read((BPTR)fd,x,(long)DATASIZE);
				if (j<=0L) break;
			} else j = (long)DATASIZE;
			if (!newBlock(bufPtr,&b,b)) {
BADOUT: 		freeBuf(bufPtr->BufNum);
				return(FALSE);
			}
			p = b->Data;
		}
		if (++offset>=nextoffset) break;
	}
	*p = EOFILE;
	return(TRUE);
}


long hAlgo( register ULONG  		word,
			USHORT  				index)
{
register struct HTENTRY *t,*tt;
register USHORT 		child,parent;
register long			i;

	t = hyperText.table;

	tt = t + index;

	child = tt->child;  							/* look among children */
	if (child) {
		i = hSiblings(word,child);
		if (i>=0L) return(i);
	}

	i = hSiblings(word,index);  				 /* look among siblings */
	if (i>=0L && (USHORT)i!=index) return(i);

	parent = index; 	/* now start looking at parents, grandparents, etc */
	while (1) {
		if (parent==0) return(-1L);
		tt = t + parent;
		parent = tt->parent;
		i = hSiblings(word,parent);
		if (i>=0L) return(i);
	}
}

long hSiblings( register ULONG  		word,
				register USHORT 		index)
{
register struct HTENTRY *t,*tt;
register USHORT 		parent,child;

	t = hyperText.table;
	if (index==0) { 	 /* first entry in table, trunk of tree, no parent */
		if (strcmp(t->word,(UCHAR *)word)==0) return(0);
		return(-1L);
	}
	tt = t + index; 					 /* find parent & then first child */
	parent = tt->parent;
	tt = t + parent;
	child = tt->child;  			/* first child, first sibling of index */
	while (child) {
		tt = t + child;
		if (strcmp(tt->word,(UCHAR *)word)==0) return((long)child);
		child = tt->nextSibling;
	}
	return(-1L);
}


/* --------------------------------- grep ------------------------------ */

#define G_MAXGREP		127
#define G_MAXSEARCH 	63
#define G_MAXREPLACE	63

struct GR {
	short	*gre;										/* ptr into grep[] */
	UCHAR	greLevel[10];							/* RE at nested levels */
	short	greMatched; 						   /* bits for REs matched */
	UCHAR	glevel; 								   /* RE nesting level */
	UCHAR	pad[3];
};

#define GR_SIZE  (short)sizeof(struct GR)

static short  	gsimple;

static struct GR gr;
static struct DB *greSBlock[10],*greEBlock[10];
static UCHAR	 *greSPtr[10],*greEPtr[10];
static short	grep[G_MAXGREP+2],gnumSet;
static UCHAR	gset[10][256];
static UCHAR	gSearch[G_MAXSEARCH+1] = {0x00};
static UCHAR	gReplace[G_MAXREPLACE+1] = {0x00};
static UCHAR	grepDelim = '/';


Fgrep()
{
register UCHAR	*x,*y;
register short	ch,i,j,flag;

struct CB	*bufPtr;
short		rgn;
struct DB	*sB;
UCHAR		*sP,*ff,**ovl;
short		r,found;

	bufPtr = bufPtrArgs[0];
	rgn = args[1];
	flag = *longPtrArgs[2];
	if (bufPtr==NULL || rgn<hilite_
			|| rgn>locPC_ || gSearch[0]==0) return(FALSE);

	for (i=0; i<10; i++) {
		for (j=0; j<256; j++) gset[i][j] &= 1;
	}

	if (searchCaps==0) {
		for (i=0; i<10; i++) {
			for (j=0; j<256; j++) {
				ch = gset[i][j];
				if (ch) {
					if (ch>='a' && ch<='z') gset[i][j-32] |= 2;
					if (ch>='A' && ch<='Z') gset[i][j+32] |= 2;
				}
			}
		}
	}
	pushFast(bufPtr,&bufPtr->Block,&bufPtr->Ptr);
	ff = bufPtr->NextBlock->Data;
	found = FALSE;
	i = *grep;
	ch = i >> 8;	/* ch is UPPER */
	i &= 0xff;  	/* i is LOWER  */
	y = (UCHAR *)searchCaps;
	if (ch < 18 && gsimple) {
		if (y==NULL && i<='Z' && i>='A') i |= 32;
	} else i = 0;
	if (flag>=-1 && flag<=1) goto BUMP;
	j = *fastP;

	while (1) {
		if (i) {
			if (ch==0) {
				if (y==NULL && j<='Z' && j>='A') j |= 32;
				if (i!=j) goto BUMP;
			} else  if (!GSelect_(ch,i,j)) goto BUMP;
		}
		greSPtr[0]   = fastP;
		greSBlock[0] = fastB;
		x = (UCHAR *)&gr;
		((struct GR *)x)->gre = grep;	   /* ptr to current position in the grep[] array */
		((struct GR *)x)->greMatched = 0;	 /* bitmask indicating which REs were matched */
		((struct GR *)x)->glevel = 0;
		((struct GR *)x)->greLevel[0] = 0;
		if (Gre_()) {
			found = TRUE;
			splitFlag(bufPtr->BufNum,PAGE_ | SHOWCURSOR_);
			x = greSPtr[0];
			y = fastP;
			sP = x;
			sB = greSBlock[0];
			bufPtr->Ptr = x;						  /* place the cursor */
			bufPtr->Block = sB;
			greEPtr[0]   = y;					/* store end of rgn */
			greEBlock[0] = fastB;
			ovl = (UCHAR **)bufPtr; 			  /* mark the found region */
			r	= ((rgn - hilite_) << 1) + sHilite_ - atCursor_;
			j	= r + 1;
			ovl[r]  	 = (UCHAR *)sB;
			ovl[r+CBOVL] = x;						 /* mark start of rgn */
			ovl[j]  	 = (UCHAR *)fastB;
			ovl[j+CBOVL] = y;						/* mark end of rgn */
			break;
		}
		fastP = greSPtr[0];
		fastB = greSBlock[0];
BUMP:
		if (flag>=0) {
			x = incFast();
			j = *x;
			if (j==EOFILE) break;
		} else {
			if (fastP==ff) break;
			x = decFast();
			j = *x;
		}
		if (x==(UCHAR *)fastB && stop()) goto BRTN;
	}
	if (found==0) {
BRTN:
		popFast();
		return(FALSE);
	}
	if (flag==2 || flag==-2) {  					 /* if doing a replace */
		y = gReplace;
		if (y[0]=='&' && y[1]==0x00) goto TRTN;
		if (!canChange(bufPtr)) goto BRTN;
		x = (UCHAR *)safeMem(1024L);					  /* scratch area */
		if (x ==NULL) goto BRTN;
		ff = x;
		i = 0;
		while (1) {
			ch = *(y++);
			if (ch==0) break;
			if (ch=='&') {
				j = 0;  			/* RE number 0, the whole matched area */
				goto INSERT_RE;
			}
			if (ch=='\\') { 					   /* a regular expression */
				ch = *(y++);
				if (ch>'9' || ch<'1') goto INCHAR;
				j = ch - '0';
				if (!(gr.greMatched & (1 << j) )) continue;/* RE unmatched */
INSERT_RE:
				fastP = greSPtr[j];
				fastB = greSBlock[j];
				ch = *fastP;
				while (1) {
					if (ch<=EOFILE) break;
					*(x++) = ch;
					if (++i>1023) break;
					ch = *incFast();
					if (fastP==greEPtr[j]) break;
				}
			} else {							  /* an ordinary text byte */
INCHAR:
				*(x++) = ch;
				i++;
			}
			if (i>1023) { FreeMem(ff,1024L); goto BRTN; }
		}
		*(x++) = 0x00;

		FclearRgn();		/* bufPtr,rgn are same as above */
		if (*ff) {			/* bufPtr is same:  bufPtr->BufNum,cursor_,ff,0 */
			args[1] = atCursor_;
			args[3] = 0;				/* for literal text */
			ptrArgs[2] = ff;
			FinsertRgn();
		}
		FreeMem(ff,1024L);

		/* now bracket inserted text with rgn's locs.  Leave cursor after
		insertion, if fwd searching.  (Cursor and end of rgn are already
		correct here because FinsertRgn() bumps them upward.)  Put cursor just
		before insertion if bkwd searching.

		WARNING:  sP/sB is an invalid location due to the clear & insert
		above; it may be pointing to a null byte.  (Buffer locs are tracked
		and maintained during buffer mods, but these grep locs aren't in the
		buffer header so they aren't tracked.)  Neither FclearRgn()() nor
		FinsertRgn() deletes blocks from the chain, so the block sB points to
		still exists; but sP may be pointing to a null byte after the above
		calls.  */

		fastBufPtr = bufPtr;
		x = sP;
		fastB = sB; 					/* may point to null byte - fix it */
		fastP = x;
		if (*x==0x00) x = incFast();			   /* first non-null byte */
		ovl[r]  	 =   (UCHAR *)fastB;
		ovl[r+CBOVL] =   x; 					   /* set start of rgn */

		if (flag<0) {				/* bkwd replace: put cursor before rgn */
			bufPtr->Ptr = decFast();
			bufPtr->Block = fastB;
		}
	}
TRTN:
	popFast();
	return(TRUE);
}

	/* Gre_()
		is called by Grep() to match the grep string.  It may be called
		recursively to resolve multipliers (*).

		Returns FALSE if unsuccessful.
	*/

Gre_()
{
register struct GR  *grp;
register short  	curG;
register short  	upper,lower,ch;
UCHAR				multi,renum;
UCHAR				*ff;

	grp = &gr;
	ff = fastBufPtr->NextBlock->Data;		  /* to test for start of file */
	renum = grp->greLevel[grp->glevel];

DO_OR:  				/* come here if failed but there's an OR in the RE */
	while (1) {
		curG = *(grp->gre++);
		if (curG==0) return(TRUE);  				  /* successful search */
		lower = curG & 0x00ff;
		upper = curG >> 8;
		multi = upper & 0x80;					 /* get multiplier, if any */
		upper &= 0x7f;
		ch = *fastP;
		if (multi) {  /* multiplier: finish out the search using recursion */
			if (GreMulti_(upper,lower,ch)) return(TRUE);
			goto BRTN;
		}
		if (upper==0) { 					 /* match individual character */
			if (searchCaps==0) {
				if (lower<='Z' && lower>='A') lower |= 32;
				if (ch<='Z' && ch>='A') ch |= 32;
			}
			if (lower!=ch) goto BRTN;
			incFast();
			goto DO_OR;
		}
		if (upper<19) { 			/* process character selecting symbols */
			if (!GSelect_(upper,lower,ch)) goto BRTN;
			incFast();
			goto DO_OR;
		}
		switch (upper) {				   /* process nonselecting symbols */
		case 19:	/* \) end RE */
END_RE: 		lower = renum;
				greEPtr[lower] = fastP; 		/* store end of matched RE */
				greEBlock[lower] = fastB;
				grp->greMatched |= (1 << lower);
				renum = grp->greLevel[--grp->glevel];
				goto DO_OR;
		case 30:	/* \( start RE */
				renum = lower;
				grp->greLevel[++grp->glevel] = lower;
				greSPtr[lower] = fastP; 	  /* store start of matched RE */
				greSBlock[lower] = fastB;
				goto DO_OR;
		case 31:	 /* ^ start of a line (for end of line, use Enter key) */
				if (fastP!=ff) {
					lower = *decFast();
					if (lower!=eol && lower!=12) goto BRTN;
					incFast();
				}
				goto DO_OR;
		case 32:	/* | the OR  (When used inside RE it divides RE ONLY)  */
				while (1) { 				  /* skip to end of current RE */
					curG = *(grp->gre++);
					upper = curG >> 8;
					lower = curG & 0x00ff;
					if (curG==0 && renum==0) return(TRUE);
					if (upper==19 && lower==renum) goto END_RE;
				}
				goto DO_OR;
		case 33:	/* \< start of word (prev char not alphadigit)  */
				if (!alphaDigitFast()) goto BRTN;
				if (fastP!=ff) {
					decFast();
					if (alphaDigitFast()) goto BRTN;
					incFast();
				}
				goto DO_OR;
		case 34:	/* \> end of word (next char not alphadigit)  */
				if (alphaDigitFast()) goto BRTN;
				decFast();
				if (!alphaDigitFast()) goto BRTN;
				incFast();
				goto DO_OR;
		case 35:	/* :< prev char  */
		case 36:	/* :> next char  */
				if (upper==35) {							  /* prev char */
					if (fastP==ff) goto BRTN;
					ch = *decFast();
					incFast();
				} else {									  /* next char */
					ch = *fastP;
					if (ch==EOFILE) goto BRTN;
				}
				curG = *(grp->gre++);
				if (curG==0) return(TRUE);
				lower = curG & 0x00ff;
				upper = curG >> 8;
				if (upper>17 || !GSelect_(upper,lower,ch)) goto BRTN;
				goto DO_OR;
		case 37:	/* :e end of text  */
				if (*fastP!=EOFILE) goto BRTN;
				goto DO_OR;
		case 38:	/* :s start of text  */
				if (fastP!=ff) goto BRTN;
				goto DO_OR;
		default:	goto BRTN;
		}
	}
BRTN:
	/* failed:  scan fwd to look for an OR so can continue the search */
	if (Gor_()) {
		renum = grp->greLevel[grp->glevel]; 			/* found an OR can use */
		fastP = greSPtr[renum];
		fastB = greSBlock[renum];
		goto DO_OR;
	}
	return(FALSE);
}


	/* GreMulti_(upper,lower,ch)
		is called by Gre_() to handle a multiplier (*).  It calls Gre_()
		recursively to finish out the search.  Thus recursion occurs for each
		multiplier in the search string.

		Returns FALSE if unsuccessful.

		Returns TRUE if successful and the search is finished.
	*/

GreMulti_(  short upper,
			short lower,
			register short ch)
{
register UCHAR  *sP1;
register short  *sgre;
register short	i,j,rtnM;

struct DB		*sB,*sB1,*sB2;
struct GR		sgr;
UCHAR			*sP,*sP2;
short			rtn0,rtnN,*sgm,saveg;

	sgm = gr.gre - 1;
	*sgm &= 0x7fff; 					/* turn off multiplier temporarily */
	sP = fastP;
	sB = fastB;
	movMeml_(&gr,&sgr);					/* save search status */

	if (upper<19) { 				/* match 0 or more of selecting symbol */
		rtn0 = TRUE;
		sgre = sgr.gre;
		if (*sgre) rtn0 = Gre_();					   /* try for match 0 */

		sP1 = sP; sB1 = sB;
		j = 0;
		if (*sgre==0) j = 1;
		for (i=1; i<=127; i++) {					  /* try match for 1-N */
			fastP = sP1; fastB = sB1;
			rtnM = GSelect_(upper,lower,ch);
			if (!rtnM) break;
			sP1 = incFast();
			ch = *sP1;
			sB1 = fastB;
			if (*sgre) {
				movMeml_(&sgr,&gr);
				rtnN = Gre_();
				if (rtnN) { sP=sP1; sB=sB1; j = i; }
			} else { sP = sP1; sB = sB1; }
		}
		if (((i==1 || j==0) && rtn0==0)) goto BRTN;
		fastP = sP; fastB = sB;
		movMeml_(&sgr,&gr);			  /* refind match for i-1 */
		if (*sgre) Gre_();
		goto TRTN;
	}
/* ----------------------
NOTE:  after matching an RE multiple times, greSPtr stores the start
	   of the first matched copy and greEPtr stores the end of the last
	   matched copy.  Thus all contiguous matches of the RE are included when
	   you reference the matched area using \1...\9.
------------------------- */

	if (upper==30) {									/* multiples of RE */
		sgr.gre--;  							 /* back up to start of RE */
		while (1) { 								/* find end of this RE */
			i = *(gr.gre++);
			if (i==0) goto BRTN;
			if ((i >> 8)==19 && (i & 255)==lower) break;
		}
		sgre = gr.gre;  						   /* save ptr past end RE */
		saveg = *sgre;
		rtn0 = TRUE;
		if (saveg) rtn0 = Gre_();						/* try for match 0 */

		sP1 = sP; sB1 = sB;
		rtnN = 0;
		for (i=1; i<=127; i++) {					  /* try match for 1-N */
			fastP = sP1; fastB = sB1;
			movMeml_(&sgr,&gr);
			*sgre = 0;  					  /* terminate search after RE */
			if (!Gre_()) break; 		  /* seeking match on RE only here */
			sP1 = greEPtr[lower]; sB1 = greEBlock[lower];
			*sgre = saveg;
			if (saveg) {					/* seek match on rest after RE */
				movMeml_(&sgr,&gr);
				gr.gre = sgre;
			}
			if (saveg==0 || Gre_()) {
				rtnN = i;
				sP2 = greSPtr[lower];
				sB2 = greSBlock[lower];
			}
		}
		*sgre = saveg;
		if (rtnN==0) {
			if (rtn0==0) goto BRTN;
			fastP = sP; fastB = sB;
			if (saveg) {
				movMeml_(&sgr,&gr);
				gr.gre = sgre;
				Gre_(); 								 /* refind match 0 */
			}
			goto TRTN;
		}
		fastP = sP2; fastB = sB2;
		movMeml_(&sgr,&gr);			 /* refind match for rtnN */
		Gre_();
		greSPtr[lower] = sP; greSBlock[lower] = sB;
TRTN:
		*sgm |= 0x8000; 						   /* reset multiplier bit */
		return(TRUE);
	}
BRTN:
	*sgm |= 0x8000;
	return(FALSE);
}


	/* Gor_()
		is called when the grep search fails.  It scans forward in grep[]
		looking for an OR that allows continuing the search using an
		alternative search string.  The OR must be in the current RE or in a
		lower level RE containing the current RE.  If nested (higher level)
		REs are encountered they are skipped over.

		Returns TRUE if it finds an OR in the current or a lower level RE.

		Returns FALSE if it reaches end of grep[] without finding a usable OR.
	*/

Gor_()
{
register struct GR *grp;
register short   curG;
register UCHAR   upper,lev;

	grp = &gr;
	lev = grp->glevel;  					/* current level (nesting depth) */
	while (1) {
		curG = *(grp->gre++);
		if (curG==0) return(FALSE); 					  /* end of grep[] */
		upper = curG >> 8;
		switch (upper) {
			case 30:	grp->glevel++; continue;			  /* start of RE */
			case 19:										  /* end of RE */
						if (grp->glevel==0) return(FALSE);
						if (--grp->glevel<lev) lev = grp->glevel;
						continue;
			case 32:											  /* an OR */
						if (grp->glevel==lev) return(TRUE); /* eq/lower level*/
						continue;
		}
	}
}


	/* GSelect_(upper,lower,ch)
		is called by Gre_() to take care of the selecting grep codes.  The
		nonselecting codes are handled by Gre_().  upper is the upper byte in
		grep[i] and lower is the lower byte.  ch is the current character
		pointed to by fastP.
	*/

GSelect_(	register short upper,
			register short lower,
			register short ch)
{
register struct DB  *sB;
register UCHAR  	*sP;

	if (searchCaps==0) {
		if (lower<='Z' && lower>='A') lower += 32;
		if (ch<='Z' && ch>='A') ch += 32;
	}
	switch (upper) {
		case 0: 	/* match individual character */
				if (lower!=ch) break;
				return(TRUE);
		case 1: 	/* a set; lower byte is SETNUMBER  */
				if (!gset[lower][ch]) break;
				return(TRUE);
		case 2: 	/* . single wildcard (any char except eol or ff)  */
				if (ch==eol || ch==12) break;
				return(TRUE);
		case 3: 	/* :c any control character  */
				if (ch>EOFILE && ch<' ') return(TRUE);
				if (ch>=128 && ch<=131)  return(TRUE);
				break;
		case 4: 	/* :@ any control char EXCEPT eol or ff  */
				if ((ch>EOFILE && ch<' ') || (ch>=128 && ch<=131)) {
					if (ch!=eol && ch!=12) return(TRUE);
				}
				break;
		case 5: 	/* :w whitespace (space or tab)  */
				if (ch==' ' || ch==9) return(TRUE);
				break;
		case 6: 	/* :b backspace character (ASCII 8)  */
				if (ch==8) return(TRUE);
				break;
		case 7: 	/* :! any (ASCII 0-31 128-255) except eol or FF  */
				if ((ch>EOFILE && ch<' ') || (ch>127 && ch<256)) {
					if (ch==eol || ch==12) break;
					return(TRUE);
				}
				break;
		case 8: 	/* :? single wildcard (any char INCLUDING eol)  */
				return(TRUE);
		case 9: 	/* :A any alpha (upper/lower case) or apostrophe  */
				if (ch=='\'') return(TRUE);
		case 10:	/* :a any alpha (upper/lower case) */
				if (ch>='a' && ch<='z') return(TRUE);
				if (ch>='A' && ch<='Z') return(TRUE);
				break;
		case 11:	/* :l any lower case alpha  */
				if (ch>='a' && ch<='z') return(TRUE);
				break;
		case 12:	/* :u any uppercase alpha  */
				if (ch>='A' && ch<='Z') return(TRUE);
				if (searchCaps==0 && ch>='a' && ch<='z') return(TRUE);
				break;
		case 13:	/* :N any alpha or digit (upper/lower) or apostrophe  */
				if (ch=='\'') return(TRUE);
		case 14:	/* :n any alpha or digit (upper/lower) */
				if (ch>='a' && ch<='z') return(TRUE);
				if (ch>='A' && ch<='Z') return(TRUE);
				if (ch>='0' && ch<='9') return(TRUE);
				break;
		case 15:	/* :d or :# any digit  */
				if (ch>='0' && ch<='9') return(TRUE);
				break;
		case 16:	/* :o octal digit (0-7)  */
				if (ch>='0' && ch<='7') return(TRUE);
				break;
		case 17:	/* :x or :h hex digit (0-9 A-F or a-f)  */
				if (ch>='0' && ch<='9') return(TRUE);
				if (ch>='A' && ch<='F') return(TRUE);
				if (ch>='a' && ch<='f') return(TRUE);
				break;
		case 18:	/* \1...\9  (lower byte is RE#)  */
				sP = fastP;
				sB = fastB;
				if (GMatchRE_(lower)) return(TRUE);
				fastB = sB;
				fastP = sP;
				break;
	}
	return(FALSE);
}


	/* GMatchRE_(renum)
		seeks another match to previously matched RE number renum.  The start
		& end of the previously matched RE are stored in greSPtr[renum]/
		greSBlock & greEPtr[renum]/greEBlock[renum].

		returns FALSE if unsuccessful.
	*/

GMatchRE_(register short  renum)
{
register struct DB 	*sB;
register UCHAR  	*sP;
register ULONG		eP;

	if ((gr.greMatched & (1<<renum))==0) return(FALSE);  /* re not matched */
	sP = greSPtr[renum];
	sB = greSBlock[renum];
	eP = (ULONG)greEPtr[renum];
	while (1) {
		if (sP==(UCHAR *)eP) { decFast(); return(TRUE); }
		if (*sP != *fastP) return(FALSE);
		if (*incFast()==EOFILE) return(FALSE);
BUMP:	sP++;
		if (*sP==EOB) { sB=sB->NextBlock; sP=sB->Data; }
		if (*sP==0x00) goto BUMP;
	}
}


FsetGrep()
{
register UCHAR   *z;
register UCHAR   *x;
register short   ch,i,backslash;

	z = fixString(0,0,127);
	if (z==NULL) return(FALSE);						  /* 2: no grep string */

	grepDelim = *(z++); 			/* 1st char is taken as delimiter char */

	if (grepDelim==0 || *z==0x00) { /* delete existing search/repl strings */
		gSearch[0] = gReplace[0] = 0;
		return(TRUE);
	}

		/* get new search string, if one is delimited.  if none delimited,
		   fall thru keeping old search str if exists. */
	i=0;
	if (*z!=grepDelim && (i=Gs_(z))==0) {
		gSearch[0] = 0x00;
		return(FALSE);
	}
	z += i;
								 /* no repl str delimited, keep old if any */
	if (*(z++)==0x00 || *z==0x00) return(TRUE);

	i = 0;  	  /* store new replace string or delete old one if just // */
	backslash = FALSE;
	x = gReplace;
	while (1) {
		ch = *(z++);
		if (backslash) {
			backslash = FALSE;
			if (ch=='\\' || (ch>='1' && ch<='9')) goto TAKEIT;
			x--;				/* store literally, toss earlier backslash */
			i--;
		} else {
			if (ch==grepDelim) break;
			if (ch=='\\') backslash = TRUE;
		}
TAKEIT:
		i++;
		*(x++) = ch;
		if (*z==0x00) break;
		if (i>=G_MAXREPLACE) {  			  /* replace string too long */
			gReplace[0] = 0x00; 							/* undefine it */
			return(FALSE);
		}
	}
	*x = 0x00;
/*    x = gReplace; */
	return(TRUE);
}


	/* Gs_(z)
		is called by SetGrep() to store a search string.  The literal search
		string is stored in gSearch[] (character array) and an encoded form of
		it is stored in grep[] (short integer array).

		The upper bytes in grep[], if non-zero, are special codes.  The lower
		bytes in grep[] are the characters in the search string or special
		integers such as set number.

		Character sets in the search string are stored in the gset[][] array.
		Up to 10 sets are allowed.

		Gs_() calls GSet_() to store character sets [] in gset[].

		Gs_() returns FALSE if the search string is invalid.

		If successful Gs_() returns the index into the z array just past the
		end of the search string; i.e., at z[index] there should either be a
		delimiter or null byte indicating end of string.
	*/

Gs_(register UCHAR *z)
{
register UCHAR  *x;
register short	ch,i,ndx,upper;

short			*g,j,backslash,lower,lastupper,rehi;
UCHAR			*y;
UCHAR	 		gmulti[G_MAXGREP+2];

	/* store the string in gSearch() */
	i = ndx = 0;
	x = gSearch;
	backslash = FALSE;
	while (1) {
		ch = *z;
		if (ch==0) {
			if (backslash) x--;
			break;
		}
		if (backslash) {
			backslash = FALSE;
			if (ch==grepDelim) {
				x--;
				i--;
				goto TAKEIT;
			}
		} else if (ch=='\\') backslash = TRUE;

		if (ch==grepDelim) break;
TAKEIT:
		z++;
		ndx++;
		*(x++) = ch;
		if (++i>=G_MAXSEARCH) return(FALSE);
	}
	*x = 0x00;

	/* load grep variables */
	gsimple = TRUE;
	gnumSet = 0;
	x = gSearch;
	i = 0;
	lastupper = 99;
	while (1) {
		ch = *(x++);
		if (ch==0) break;
		upper = lower = 0;
		switch (ch) {
			case '[':	upper = 1;
						if (gnumSet>=9 || (x=GSet_(x))==NULL) return(FALSE);
						lower = gnumSet++;  		   /* store set number */
						break;
			case '.':	upper = 2; break;
			case '^':	if ((x-1)==gSearch) {	  /* is 1st char in string */
							upper = 31;
						} else {		  /* otherwise ^ is treated as eol */
							upper = 0;
							lower = eol;
						}
						break;
			case '|':	if (lastupper==99 || lastupper==30 || *x==0x00) {
							return(FALSE);
						}
						upper = 32;
						gsimple=FALSE;
						break;
			case '*':	upper = 50; break;
			case '\\':  ch = *(x++);
						switch(ch) {
							case '(':	upper = 30; break;
							case ')':	upper = 19; break;
							case '<':	upper = 33; break;
							case '>':	upper = 34; break;
							case 'n':
							case 'r':	upper = 0; lower = eol; break;
							default:	if (ch>='1' && ch<='9') {
											upper = 18;
											lower = ch - '0';	   /* RE # */
										} else {
											/* all other chars take literal */
											lower = ch;
											upper = 0;
										}
										break;
						}
						break;
			case ':':	ch = *(x++);
						if (ch=='x') ch = 'h';
						if (ch=='#') ch = 'd';

						y = (UCHAR *)"c@wb!?AaluNndoh";
						j = 0;
						while (y[j]) {
							if (y[j]==ch) {
								upper = j + 3;
								goto N_GREP;
							}
							j++;
						}
						switch (ch) {
						case '<':	upper = 35; break;
						case '>':	upper = 36; break;
						case 'e':	upper = 37; break;
						case 's':	upper = 38; break;
						default:	goto BAD_STR;
						}
						break;
			default:	lower = ch; break;
		}
		if (upper>=50 && (lastupper>19 || i==0)) {   /* misused multiplier */
BAD_STR:
			return(FALSE);
		}
N_GREP:
		if (i>=G_MAXGREP) return(FALSE);
		if (upper<50) { 							   /* not a multiplier */
			gmulti[i]  = 0x00;
			grep[i++] = (upper << 8) | lower;
		} else gmulti[i-1] = upper; 	  /* multiplier, store in gmulti[] */
		lastupper = upper;
	}
	grep[i] = 0;

	g = grep;
	rehi = 0;
	if (!GVerify_(&rehi,&g)) {  			  /* validate & number the REs */
BADRE:
		return(FALSE);
	}

	i = 0;
	while (1) { 		   /* RE #s have been put in so fix RE multipliers */
		if (grep[i]==0) break;
		if (gmulti[i]) {
			if ((grep[i]>>8) == 19) {			  /* end RE, find start RE */
				lower = grep[i] & 255;  				  /* the RE number */
				j = 0;
				while (j<i) {
					if ((grep[j] >> 8)==30 && (grep[j] & 255)==lower) {
						grep[j] |= 0x8000;  		 /* set multiplier bit */
						break;
					}
					j++;
				}
				if (j==i) goto BADRE;			 /* couldn't find start RE */
			} else grep[i] |= 0x8000;
		}
		i++;
	}
	return(ndx);
}


GVerify_(	register short   *rehi,
			register short   **g)
{
register short   i,myhi,myre;

	myre = myhi = *rehi;
	if (myre>9) return(FALSE);
	while (1) {
		i = **g;
		if (i==0) {
			if (myre==0) return(TRUE);
			return(FALSE);
		}
		switch (i) {
			case (30 << 8): *rehi += 1; 					   /* start RE */
							**g |= *rehi;
							(*g)++;
							if (!GVerify_(rehi,g)) return(FALSE);
							if (myhi < *rehi) myhi = *rehi;
							(*g)++;
							continue;
			case (19 << 8): if (myre==0) return(FALSE); 		 /* end RE */
							**g |= myre;
							*rehi = myhi;
							return(TRUE);
			case (32 << 8): *rehi = myre;							 /* OR */
			default:		(*g)++;
							continue;
		}
	}
}


	/* UCHAR *GSet_(x)
		gets the grep character set [...] from char string x and stores it in
		gset[gnumSet][char].  x points to the first character inside the set
		brackets [] initially and x is incremented as the set is processed.
		If the set is valid a pointer to the first byte in x after the set is
		returned.

		Returns NULL, if the set is invalid.
	*/

UCHAR *GSet_(register UCHAR *x)
{
register UCHAR  *s;
register short 	i;
register short  ch;
register short  cch,notting,j;

	notting = 0;
	if (*x=='^') {
		notting = 1;
		x++;
	}
	if (*x==']' || *x==0x00) return(NULL);  		 /* set can't be empty */

	s = gset[gnumSet];

	for (i=0; i<256; i++) s[i] = notting;			 /* initialize the bits */
	notting ^= 1;

	while (1) {
		ch = *(x++);
		if (ch==']') break; 								 /* end of set */
		if (ch=='\\') {
			ch = *(x++);
			if (ch==0x00) return(NULL);
			if (ch=='n' || ch=='r') ch=eol;
			s[ch] = notting;							/* set byte for ch */
			continue;
		}
		if (*x=='-') {
			cch = x[1];
			x += 2;
			if (cch<ch) return(NULL);
			for (j=ch; j<=cch; j++) s[j] = notting; /* set bytes for ch-cch */
			continue;
		}
		s[ch] = notting;
	}
	return(x);
}


void movMeml_(  register ULONG *x,
				register ULONG *y)
{
register short amt;

	amt = GR_SIZE >> 2;
	do {
		*(y++) = *(x++);
	} while (--amt>0);
}


FgetGrep()
{
register short     bufnum;
register UCHAR     *x,*y;
UCHAR 			   z[2];

	bufnum = *longPtrArgs[0];
	if (!fixBuf(bufnum)) goto BRTN;
	y = buf[bufnum]->NextBlock->Data;
	x = z;
	*x = grepDelim;
	x[1] = 0x00;
	strcpy(y,x);
	strcat(y,gSearch);
	strcat(y,x);
	strcat(y,gReplace);
	strcat(y,x);
	*x=EOFILE;
	strcat(y,x);
	return(TRUE);
BRTN:
	return(FALSE);
}

static struct CB   *bpt,bf;
static struct DB   *b;
static long 	   llen;
static short	   bufnum,len,foundone,checkdates;
static UCHAR	   *x,*fn;
static struct DateStamp dstamp;

FsaveState()
{
register short  	i;
register long		j;
register struct ST  *ss;
struct  ST  		*sss;
register BPTT       fd;

	while (saves) doSaving();		 /* so get/setProtect no longer active */

	fn=fixString(' ',0,NAMELEN-1);
	fd = (long)openNew((UCHAR *)fn); 								 /* state file */
	if (fd==(BPTT)NULL) return(FALSE);

	len = STATE_ID;
	Write((BPTR)fd,&len,2L);
											 /* save search & grep strings */
	llen = ALLBUFS;
	longPtrArgs[0] = &llen;
	args[0] = buf_;
	if (!FgetSearch()) goto BADOUT;
	x = buf[ALLBUFS]->NextBlock->Data;
	len = (short)strlen(x);
	Write((BPTR)fd,&len,2L);
	if (len>1) {
		x[len-1] = 0x00;
		Write((BPTR)fd,x,(long)len);
	}

	if (!FgetGrep()) goto BADOUT;
	x = buf[ALLBUFS]->NextBlock->Data;
	len = (short)strlen(x);
	Write((BPTR)fd,&len,2L);
	if (len>1) {
		x[len-1] = 0x00;
		Write((BPTR)fd,x,(long)len);
	}
	freeBuf(ALLBUFS);

	/* save various arrays */
	Write((BPTR)fd,bufferFlags,(long)sizeof(bufferFlags));

	/* save current color arrays */
	Write((BPTR)fd,tabTables,(long)sizeof(tabTables));
	Write((BPTR)fd,coloring,(long)sizeof(coloring));

	/* save such[] array */
	for (i=0; i<MAXSUCH; i++) Write((BPTR)fd,such[i],4L);

	/* save nSplit & split window arrays */
	Write((BPTR)fd,&curBuf->BufNum,2L);
	Write((BPTR)fd,&nSplit,2L);
	Write((BPTR)fd,&curSplit,2L);
	Write((BPTR)fd,splitBufNum,(long)sizeof(splitBufNum));
	Write((BPTR)fd,startSplit,(long)sizeof(startSplit));
	Write((BPTR)fd,stopSplit,(long)sizeof(stopSplit));
	Write((BPTR)fd,&foldTests,(long)sizeof(foldTests));
	if (Write((BPTR)fd,&maxTest,2L)<=0) {
BADOUT:
		Close((BPTR)fd);
		DeleteFile(fn);
		return(FALSE);
	}

	i = 0;
												/* save buffers & set ptrs */
	do {
		if (stop()) break;
		if (toTiny) {
			if (!makeTiny()) {
				makeWindow();
				if (edWindow==NULL) badQuit();
			}
		}
		if (tiny && gChar==ESC) closeTiny();

		if (buf[i]) {
			bpt = buf[i];
			len = i;
			if (!saveBufState(fd)) goto BADOUT;
		}
		ss = bStack[i];
		sss = NULL;
		while (ss) {					 /* read stack, make reverse stack */
			j = ss->num;
			if (j) push(&sss,j);
			ss = ss->ptr;
		}
		while (pop(&sss,&bpt)) {   /* pop reverse stack & save its buffers */
			if (bpt) {
				len = i - 200;     /* write bufnum as MINUS for stack bufs */
				if (!saveBufState(fd)) goto BADOUT;
			}
		}
	} while (++i<ALLBUFS);
 	len = 32767;
	if (Write((BPTR)fd,&len,2L)!=2L) goto BADOUT;

	Close((BPTR)fd);
	return(TRUE);
}

saveBufState(register BPTT fd)
{
register UCHAR  		**ovr;
register struct DB  	*b;
register short  		j;
register long			locseek;

	if (bpt->Flags & NEEDPACK) packBuf(bpt,TRUE);
	bpt->Flags &= ~LOCKED;
	Write((BPTR)fd,&len,2L);  									 /* buf num */
	movdat((UCHAR *)bpt,(UCHAR *)&bf,(short)sizeof(struct CB));
	ovr = (UCHAR **)&(bf.Block);
	j = 0;
	do {
		b = (struct DB *)ovr[j];
		locseek = (ULONG)b->BlockNum << 16;
		ovr[j] = ovr[j+CBOVL] - (UCHAR *)b + (UCHAR *)locseek;
		ovr[j+CBOVL] = NULL;
	} while (++j<CBOVL);
	if (Write((BPTR)fd,&bf,(long)sizeof(struct CB))<=0L) goto BADOUT;
	locseek = Seek((BPTR)fd,0L,(ULONG)OFFSET_CURRENT);
	llen = 0L;
	Write((BPTR)fd,&llen,4L);
	b = bpt->NextBlock;
	do {
		if (Write((BPTR)fd,b->Data,(long)DATASIZE)!=(long)DATASIZE) goto BADOUT;
		b = b->NextBlock;
		llen += DATASIZE;
	} while (b);
	Seek((BPTR)fd,locseek,(ULONG)OFFSET_BEGINNING);
	Write((BPTR)fd,&llen,4L);
	Seek((BPTR)fd,0L,(ULONG)OFFSET_END);
	return(TRUE);
BADOUT:
	return(FALSE);
}

FloadState()
{
register BPTT       fd;
register short  	i,rtn;
register UCHAR  	**ovr;
short				num;

	rtn = TRUE;
	while (saves) doSaving();	 /* so get/setProtect no longer active now */

	fn = fixString(' ',0,NAMELEN-1);

	checkdates = foundone = FALSE;
	ds.ds_Days = 0L;
	getProtect(fn);
	if (ds.ds_Days) {
		checkdates = TRUE;
		dstamp.ds_Days = ds.ds_Days;
		dstamp.ds_Minute = ds.ds_Minute;
		dstamp.ds_Tick = ds.ds_Tick;
	}

	fd = (BPTT)openOld(fn);		/* state file */
	if (fd==(BPTT)NULL) return(FALSE);

	Read((BPTR)fd,&num,2L);
	if (num!=STATE_ID) {
		oneMsg("Invalid state file");
		Close((BPTR)fd);
		return(FALSE);
	}
											 /* load search & grep strings */
	ptrArgs[0] = z;
	args[0] = literal_;
	Read((BPTR)fd,&len,2L);
	if (len>1) {
		Read((BPTR)fd,z,(long)len);
		FsetSearch();
	}

	Read((BPTR)fd,&len,2L);
	if (len>1) {
		Read((BPTR)fd,z,(long)len);
		FsetGrep();
	}

	/* save various arrays */
	Read((BPTR)fd,bufferFlags,(long)sizeof(bufferFlags));

	/* load current color arrays */
	Read((BPTR)fd,tabTables,(long)sizeof(tabTables));
	Read((BPTR)fd,coloring,(long)sizeof(coloring));

	/* load such[] array */
	for (i=0; i<MAXSUCH; i++) Read((BPTR)fd,such[i],4L);

	/* load nSplit & split window arrays */
	Read((BPTR)fd,&bufnum,2L);
	Read((BPTR)fd,&nSplit,2L);
	Read((BPTR)fd,&curSplit,2L);
	Read((BPTR)fd,splitBufNum,(long)sizeof(splitBufNum));
	Read((BPTR)fd,startSplit,(long)sizeof(startSplit));
	Read((BPTR)fd,stopSplit,(long)sizeof(stopSplit));
	Read((BPTR)fd,&foldTests,(long)sizeof(foldTests));
	Read((BPTR)fd,&maxTest,2L);
												/* load buffers & set ptrs */
	curBuf = NULL;
	freeBufStacks();
	while (1) {
		if (stop()) break;
		if (toTiny) {
			if (!makeTiny()) {
				makeWindow();
				if (edWindow==NULL) badQuit();
			}
		}
		if (tiny && gChar==ESC) closeTiny();

		if (Read((BPTR)fd,&len,2L)!=2L || len==32767) break;
		
		if (!loadBufState(fd)) { rtn = FALSE; break; }

	}
	/* finish out */
	Close((BPTR)fd);
	curBuf = buf[bufnum];
	if (curBuf==NULL) newCurBuf();
	popFast();
	cleared = 0;
	if (!tiny) makeWindow();
	if (foundone)
		oneOk("WARNING: files marked READONLY are OLDER than the copy on disk!");
	return(rtn);
}

loadBufState(BPTT fd)
{
register short  	i,j,stackit;
register ULONG  	offset;
register UCHAR  	**ovr;
register struct CB  *sbp;

	i = len;
	stackit = -1;
	if (i<0) {
		stackit = i + 200;
		sbp = buf[0];
		buf[0] = NULL;
		if (!getBuf(0)) goto BRTN;
		bpt = buf[0];
		buf[0] = sbp;
	} else {
		if (!fixBuf(i)) goto BRTN;  			 /* frees buffer automatically */
		bpt = buf[i];
	}
	b = bpt->NextBlock;
	Read((BPTR)fd,&bf,(long)sizeof(struct CB));
	bf.NextBlock = b;
	if (Read((BPTR)fd,&llen,4L)!=4L) goto BRTN;

	ovr = (UCHAR **)&(bf.Block);
	while (1) {
		if (Read((BPTR)fd,b->Data,(long)DATASIZE)!=(long)DATASIZE) goto BADOUT;
		llen -= DATASIZE;
		j = 0;
		do {
			if (ovr[j+CBOVL]==NULL) {
				offset = (ULONG)ovr[j];
				if ((offset >> 16)==b->BlockNum) {
					ovr[j] = (UCHAR *)b;
					ovr[j+CBOVL] = b->Data + (USHORT)offset;
				}
			}
		} while (++j<CBOVL);
	
		if (llen<=0) break;
		if (!newBlock(bpt,&b,b)) {
BADOUT: 	freeBuf(i);
			goto BRTN;
		}
	}
	movdat((UCHAR *)&bf,(UCHAR *)bpt,(short)sizeof(struct CB));

	if (stackit>=0) {
		if (!push(&bStack[stackit],(long)bpt)) {
			sbp = buf[0];
			buf[0] = bpt;
			freeBuf(0);
			buf[0] = sbp;
			goto BRTN;
		}
		++(bufPopStack[stackit]);
		goto RTN;
	}

	if (checkdates && bpt->BufNum < MAXFILES) {    /* check datestamps */
		ds.ds_Days = 0L;
		getProtect(bpt->FileName);
		if (  ds.ds_Days > dstamp.ds_Days
			|| (ds.ds_Days == dstamp.ds_Days
				&& (ds.ds_Minute > dstamp.ds_Minute
					|| (ds.ds_Minute == dstamp.ds_Minute
						&& ds.ds_Tick > dstamp.ds_Tick )))) {
							foundone = TRUE;
							bpt->Flags |= READONLY;
		}
	}
RTN:
	return(TRUE);
BRTN:
	return(FALSE);
}

