/* VBUFF1.C     This is the buffer abstraction, part one.

	Copyright (c) 1981 by Mark of the Unicorn
	Updated to version two  5/14/80  JTL
	Updated to virtual buff 8/28/80  JTL
	Updated to version three 1/7/81  JTL

	This supports a general use virtual buffer gap text buffer.
This allows the very rapid insertion and deletion of text in huge
files at a slight expense for moving around in the buffer.
	The buffers are broken up into a series of pages which are
stored on the disk or in memory. Each page has a gap in the
middle. Pages are swapped on an LRU basis with usage being
determined by the active point moving onto the page. The current
page is always in memory. */

#include "mince.gbl"
#define NOMEM	"Out of Memory!"

BInit(swpfd)		    /* set up the universe */
int swpfd;
{
	swapf=swpfd;
	cur_buff=NULL;
	cur_page=NULL;
	for (cnt=MAXBUFF-1; cnt>=0; --cnt) buffers[cnt].page_cnt=0;
	for (cnt=MAXMARK-1; cnt>=0; --cnt) marks[cnt].mpage=NULL;

	for (cnt=npages/8-1; cnt>=0; --cnt) swapmap[cnt]=0;
	pim_cnt=0;
	TKbChk();

	allmem();
	for(tpim = pages; tpim < &pages[MAXPAGES] && sizmem() > 1024; tpim++){
		if((tpim->pdata = (char *) getmem(PSIZE)) == NULL)
			break;
		++pim_cnt;
		tpim->lfile=MEMORY;
		tpim->pim_modf=FALSE;
		tpim->nextu=tpim+1;
		tpim->prevu=tpim-1;
	}
	firstu=pages;
	firstu->prevu=NULL;
	lastu=tpim-1;
	lastu->nextu=NULL;
}

struct buff *BCreate()  /* Create a buffer. Returns a pointer to the
						buffer descriptor. */
{
	int cnt;
	struct buff *bptr;

	for (cnt=MAXBUFF-1; cnt>=0 && buffers[cnt].page_cnt!=0; --cnt);
	if (cnt<0) return(NULL);
	bptr = &buffers[cnt];
	if (!(tpage=new_page(bptr,(struct page_desc *)NULL,(struct page_desc *)NULL))) return(NULL);
	bptr->page_cnt=1;
	bptr->bmodf=FALSE;
	bptr->pnt_page=tpage;
	bptr->pnt_offset=0;
	return(bptr);
}

BSwitchTo(tbuff)		/* Make tbuff the current buffer */
struct buff *tbuff;
{
	if (!tbuff || !tbuff->page_cnt) {
		Error("Invalid Buffer");
		return;
	}
	if (cur_buff) {
		cur_buff->pnt_page=cur_page;
		cur_buff->pnt_offset=cur_char;
	}
	make_cur(tbuff->pnt_page);
	make_offset(tbuff->pnt_offset);
	cur_buff=tbuff;
	savecol = -1;
}

BDelBuff(tbuff)	 /* Delete the buffer and its pages */
struct buff *tbuff;
{
	if (tbuff==cur_buff || tbuff==NULL) {
		Error("Del cur buff");
		return;
	}
	while (tbuff->firstp) free_page(tbuff,tbuff->firstp);
	tbuff->page_cnt=0;
}

BMove(dist)		     /* Move the point relative to its current pos */
int dist;			       /* Distance to move the point */
{
	int toff;

	if (dist==1) {
		if (cur_char>=cur_plen) return;
		if (savecol>=0) savecol += TWidth(savecol,*cur_cptr);
		if (++cur_char<cur_plen) {
			if (++cur_cptr==cgstart) cur_cptr=cgend;
			return;
		}
		--cur_char;
	}
	else savecol = -1;
	toff = cur_char+dist;
	if (toff>=0 && toff<cur_plen) {
		make_offset(toff);
		return;
	}
	if (toff>=cur_plen && cur_buff->lastp==cur_page) {
		make_offset(cur_plen);
		return;
	}
	TKbChk();
	if (toff<0) {
		if (cur_page==cur_buff->firstp) {
			make_offset(0);
			return;
		}
		make_cur(cur_page->prevp);
		make_offset(cur_plen);
	}
	else {
		toff -= cur_plen;		       /* must use this cur_plen */
		make_cur(cur_page->nextp);
		make_offset(0);
	}
	if (toff) BMove(toff);
}

BDelToMrk(tmark)		/* delete from the point to the mark */
struct mrk *tmark;
{
	if (BIsAfterMrk(tmark)) BSwapPnt(tmark);
	if (BIsBeforeMrk(tmark))
		if (cur_page==tmark->mpage) BDelete((unsigned) (tmark->moffset-cur_char));
		else {
			BDelete((unsigned) (cur_plen-cur_char));
			BDelToMrk(tmark);
		}
}

BDelete(quantity)	       /* Delete quantity characters */
unsigned quantity;		      /* Amount to delete to the left or right */
{
	int quan, noffset;
	register struct mrk *btmark;

	if (quantity==0) return;
	GetGap();
	quan=quantity;
	if (cur_char+quantity>cur_plen) quan=cur_plen-cur_char;
	if (quan<0) quan=0;
	cgend += quan;
	cur_plen -= quan;
	if (cur_page==cur_buff->lastp) quantity=quan;

	quantity-=quan;
	cur_buff->bmodf=TRUE;
	cur_modf=TRUE;
	if (cur_plen==0 && (cur_page->nextp || cur_page->prevp)) {
		tpage=cur_page->nextp;
		noffset=0;
		if (tpage==NULL) {
			tpage=cur_page->prevp;
			noffset=tpage->plen;
		}
		TKbChk();
#ifdef CPM
		BDelUp1(tpage,noffset);
#else
		for (btmark = &marks[MAXMARK-1]; btmark>=scrnmarks; --btmark)
			if (btmark->mpage==cur_page) {
				btmark->mpage=tpage;
				btmark->moffset=noffset;
			}
#endif
		free_page(cur_buff,cur_page);
		cur_page=NULL;
	}
	else {
		tpage=cur_page;
		noffset=cur_char;
		if (noffset>=cur_plen && cur_page->nextp) {
			tpage=cur_page->nextp;
			noffset=0;
		}
		TKbChk();
#ifdef CPM
		BDelUp2(tpage,noffset,quan);
#else
		for (btmark = &marks[MAXMARK-1]; btmark>=scrnmarks; --btmark)
			if (btmark->mpage==cur_page && btmark->moffset>=cur_char)
				if (btmark->moffset >= cur_char+quan)
					btmark->moffset-=quan;
				else {
					btmark->mpage=tpage;
					btmark->moffset=noffset;
				}
#endif
	}
	make_cur(tpage);
	make_offset(noffset);
	if (quantity) BDelete(quantity);
	else VSetMod(TRUE);
}

BInsert(new)		    /* Insert new in the buffer */
char new;
{
	if (cgstart==cgend && !page_split(PSIZE/2)) return;
	GetGap();			       
	*cgstart++ = new;
	++cur_plen;
	make_offset(cur_char+1);
	if (savecol>=0) savecol += TWidth(savecol,new);
	cur_buff->bmodf=TRUE;
	cur_modf=TRUE;
	TKbChk();
#ifdef CPM
	BInsUp();				       /* above loop hand-coded */
#else
	for (btmark = &marks[MAXMARK-1]; btmark>=scrnmarks; --btmark)
		if (btmark->mpage==cur_page && btmark->moffset>=cur_char)
			++(btmark->moffset);
#endif
	VSetMod(FALSE);
}

BCopyRgn(tmark,tbuff)   /* copy from point to tmark to tbuff */
struct mrk *tmark;
struct buff *tbuff;
{
	struct buff *sbuff;
	struct mrk *ltmark;
	int flip, src, dst;
	register struct mrk *btmark;
	char *spnt;

	if (tbuff==cur_buff) {
		Error("Can't copy to self");
		return;
	}
	if (flip=BIsAfterMrk(tmark)) BSwapPnt(tmark);
	ltmark=BCreMrk();
	sbuff=cur_buff;
	while (BIsBeforeMrk(tmark)) {
		if (cur_page==tmark->mpage) src=tmark->moffset-cur_char;
		else src=cur_plen-cur_char;
		GetGap();
		cur_modf=TRUE;
		spnt=cgend;

		TKbChk();
		BSwitchTo(tbuff);
		dst=PSIZE-cur_plen;
		if (dst==0)
			if (page_split(cur_char+((cur_char<PSIZE/2)?1:-1)))
				dst=PSIZE-cur_plen;
			else {
				BSwitchTo(sbuff);
				break;
			}
		GetGap();
		if (src<dst) dst=src;
		movmem(spnt,cgstart,dst);
		cur_plen+=dst;
		cgstart+=dst;
		TKbChk();
		for (btmark = &marks[MAXMARK-1]; btmark>=scrnmarks; --btmark)
			if (btmark->mpage==cur_page && btmark->moffset>cur_char)
				btmark->moffset+=dst;
		make_offset(cur_char+dst);
		VSetMod(FALSE);
		cur_modf=TRUE;
		cur_buff->bmodf=TRUE;
		BSwitchTo(sbuff);
		BMove(dst);
	}
	BPntToMrk(ltmark);
	BKillMrk(ltmark);
	if (flip) BSwapPnt(tmark);
}

BFlush()				/* write out a modified page */
{
	int trow, tcol;

	for (tpim=lastu; tpim->prevu && (tpim->lfile!=SWAP || !tpim->pim_modf);
		tpim=tpim->prevu);
	    TKbChk();
	if (!tpim->prevu) return;
	if (tpim->ldesc->plen != 0){
		if(swapf < 0){
			Error(NOMEM);
			return;
		}
		trow=TGetRow();
		tcol=TGetCol();
		DskWarn();
		if (lseek(swapf, (long) (tpim->lpage) * PSIZE + SWAPBASE, 0) == 				-1 || write(swapf,tpim->pdata,PSIZE) == -1) {
			Error("Swap Write Error");
			return;
		}
		DskUnWarn(trow,tcol);
	}
	tpim->pim_modf=FALSE;
}

/* local routines: not callable from outside right now */
make_cur(tpage)	 /* make tpage the current page */
struct page_desc *tpage;
{
	if (cur_page==tpage) return;
	if (cur_page) {
		cur_page->plen=cur_plen;
		cur_page->gstart=cgstart-cpstart;
		pages[cur_page->apage].pim_modf=cur_modf;
	}
	into_mem(tpage);
	cur_page=tpage;
	tpim = &pages[cur_page->apage];
	cpstart=tpim->pdata;
	cur_modf=tpim->pim_modf;
	cur_plen=cur_page->plen;
	cgstart=cpstart+cur_page->gstart;
	cgend=cgstart-cur_plen+PSIZE;

	if (tpim==firstu) return;
	if (tpim->nextu==NULL) lastu=tpim->prevu;
	else tpim->nextu->prevu=tpim->prevu;
	tpim->prevu->nextu=tpim->nextu;
	tpim->prevu=NULL;
	tpim->nextu=firstu;
	firstu->prevu=tpim;
	firstu=tpim;
}

into_mem(tpage)	 /* get the page into memory */
struct page_desc *tpage;
{
	int trow, tcol;

	if (tpage->afile==MEMORY) return;
	tpim=get_memp();
	tpim->lfile=SWAP;
	tpim->lpage=tpage->apage;
	tpim->pim_modf=FALSE;
	tpim->ldesc=tpage;
	if (tpage->plen != 0){
		if(swapf < 0){
			Error(NOMEM);
			goto nomem;
		}
		trow=TGetRow();
		tcol=TGetCol();
		DskWarn();
		if (lseek(swapf, (long) (tpage->apage) * PSIZE + SWAPBASE,0) == -1 ||			 read(swapf,tpim->pdata,PSIZE) == -1)
			Error("Swap Read Error");
		DskUnWarn(trow,tcol);
	}
nomem:
	tpage->afile=MEMORY;
	tpage->apage = tpim - pages;
}

struct pim *
get_memp()		      /* find a free memory page */
{
	struct pim *tpim;
	int cnt;

	if(swapf >= 0)
		cnt=pim_cnt/3+1;
	else
		cnt = pim_cnt;
	for (tpim=lastu; --cnt>0 && (tpim->lfile==LOCKED || tpim->pim_modf);
		tpim=tpim->prevu);
	    TKbChk();
	if (cnt<=0) {
		BFlush();
		for (tpim=lastu; tpim && (tpim->lfile==LOCKED || tpim->pim_modf);
			tpim=tpim->prevu);
		    if (tpim==NULL) {
			Error("Fatal Paging Fault");
			return(NULL);
		}
		TKbChk();
	}
	if (tpim->lfile==SWAP) {
		tpim->ldesc->afile=SWAP;
		tpim->ldesc->apage=tpim->lpage;
	}
	return(tpim);
}

struct page_desc *
new_page(tbuff,ppage,npage)     /* make a new page and link it into the chain */
struct buff *tbuff;
struct page_desc *ppage, *npage;
{
	int cnt;

#ifdef CPM
	for (cnt=0; cnt<npages/8 && swapmap[cnt]==255; ++cnt);
	if (cnt==npages/8) {
#else
		for (cnt=0; cnt<npages/8 && swapmap[cnt] == 0xFF; ++cnt);
		if (cnt==npages/8) {
#endif
			if(swapf < 0)
				Error(NOMEM);
			else
				Error("Swap file full!");
			return(NULL);
		}
		for (cnt <<= 3; swapmap[cnt>>3] & (1<<(cnt&7)); ++cnt);
		swapmap[cnt>>3] |= (1<<(cnt&7));
#ifdef CPM
		tpage=AAlloc(sizeof(*tpage));
		if (tpage==NULL) {
			tpim=get_memp();
			AFree(tpim->pdata);
#endif
#ifdef UNIX
			tpage = (struct page_desc *) getmem(sizeof(*tpage));
			if (tpage == NULL) {
				tpim=get_memp();
				rlsmem(tpim->pdata, PSIZE);
#endif
#ifdef RSX
				tpage=nalloc(sizeof(*tpage),NULL);
				if (tpage==NULL) {
					tpim=get_memp();
					free(tpim->pdata,NULL);
#endif
					tpim->lfile = LOCKED;
					if (tpim->nextu) tpim->nextu->prevu=tpim->prevu;
					else lastu=tpim->prevu;
					if (tpim->prevu) tpim->prevu->nextu=tpim->nextu;
					else firstu=tpim->nextu;				/* link out of LRU chain */
#ifdef CPM
					tpage=AAlloc(sizeof(*tpage));
#endif
#ifdef UNIX
					tpage = (struct page_desc *) getmem(sizeof(*tpage));
#endif
#ifdef RSX
					tpage=nalloc(sizeof(*tpage),NULL);
#endif
				}
				TKbChk();
				tpage->nextp=npage;
				tpage->prevp=ppage;
				if (npage) npage->prevp=tpage;
				else tbuff->lastp=tpage;
				if (ppage) ppage->nextp=tpage;
				else tbuff->firstp=tpage;
				tpage->gstart=tpage->plen=0;
				tpage->afile=SWAP;
				tpage->apage=cnt;
				++(tbuff->page_cnt);
				return(tpage);
			}

			free_page(tbuff,tpage)	  /* free the page */
				struct buff *tbuff;
			struct page_desc *tpage;
			{
				if (tpage->nextp) tpage->nextp->prevp=tpage->prevp;
				else tbuff->lastp=tpage->prevp;
				if (tpage->prevp) tpage->prevp->nextp=tpage->nextp;
				else tbuff->firstp=tpage->nextp;
				--(tbuff->page_cnt);
				if (tpage->afile==MEMORY) {
					tpim = &pages[tpage->apage];
					swapmap[tpim->lpage>>3] &= ~(1<<(tpim->lpage&7));
					pages[tpage->apage].lfile=MEMORY;
					pages[tpage->apage].pim_modf=FALSE;
				}
				else swapmap[tpage->apage>>3] &= ~(1<<(tpage->apage&7));
#ifdef CPM
				AFree(tpage);
#endif
#ifdef UNIX
				rlsmem(tpage, sizeof(struct page_desc));
#endif
#ifdef RSX
				free(tpage,NULL);
#endif
			}

			page_split(amount)	      /* split the curent (full) page */
				int amount;
			{
#ifndef CPM
				register struct mrk *btmark;
#endif

				if ((tpage=new_page(cur_buff,cur_page,cur_page->nextp))==NULL)
					return(FALSE);
				into_mem(tpage);
				tpim = &pages[tpage->apage];
				movmem(cpstart+amount,tpim->pdata,PSIZE-amount);
				tpim->pim_modf=TRUE;
				cur_modf=TRUE;
				cgstart=cpstart+amount;
				cgend=cpstart+PSIZE;
				cur_plen=amount;
				tpage->plen=tpage->gstart=PSIZE-amount;
				TKbChk();
				for (btmark = &marks[MAXMARK-1]; btmark>=scrnmarks; --btmark)
					if (btmark->mpage==cur_page && btmark->moffset>=amount) {
						btmark->mpage=tpage;
						btmark->moffset-=amount;
					}
				if (cur_char>=amount) {
					make_cur(tpage);
					make_offset(cur_char-amount);
				}
				return(TRUE);
			}

			/* END OF VBUFF1.C - virtual buffer abstraction */
