/* VBUFF2.C     This is the buffer abstraction, part two.

	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. */

#include "mince.gbl"	    /* Only when seperate files */

#ifdef CPM
Copyright()		     /* Copyright notice */
{
	puts("Copyright (c) 1981 by Mark of the Unicorn");
	}
#endif

#ifndef CPM
long
#endif
BLocation()		     /* Returns the current position of the point. */
{
#ifdef CPM
	int len;
#else
	long len;
#endif

	len = 0;
	for (tpage=cur_buff->firstp; tpage!=cur_page; tpage=tpage->nextp)
		len += tpage->plen;
	return(len+cur_char);
	}

BToStart()		      /* Set the point to the start of the buffer */
{
	make_cur(cur_buff->firstp);
	make_offset(0);
	savecol=0;
	}

BToEnd()				/* Set the point to the end of the buffer */
{
	make_cur(cur_buff->lastp);
	make_offset(cur_plen);
	savecol = -1;
	}

BShoveIt()		      /* Point to off the end of the buffer */
{
	make_cur(cur_buff->lastp);
	make_offset(cur_plen+1);
	savecol = -1;
	}

#ifdef CPM
BIsStart()		      /* Is the point at the beginning of the buffer? */
{
	return(cur_char==0 && cur_page==cur_buff->firstp);
	}

BIsEnd()				/* Is the point at the end of the buffer? */
{
	return(cur_char>=cur_plen && cur_page==cur_buff->lastp);
	}
#endif

BGetCol()			       /* return current screen column of point */
{
	int tpnt;

	if (savecol>=0) return(savecol);
	tpnt=BCreMrk();
	if (BCRSearch(NL)) BMove(1);
	savecol=0;
	while (BIsBeforeMrk(tpnt)) {
		BMove(1);
		TKbChk();
		}
	BKillMrk(tpnt);
	return(savecol);
	}

BMakeCol(colposs)	       /* put cursor in specific column */
	int colposs;
{
	if (BCRSearch(NL)) BMove(1);
	savecol=0;
	while (savecol<colposs && Buff()!=NL && !BIsEnd()) {
		BMove(1);
		TKbChk();
		}
	}

#ifndef CPM
long
#endif
BLength(tbuff)		  /* Returns the length of the buffer */
	struct buff *tbuff;
{
#ifdef CPM
	int len;
#else
	long len;
#endif

	len=0;
	for (tpage=tbuff->firstp; tpage; tpage=tpage->nextp)
		if (tpage==cur_page) len += cur_plen;
		else len += tpage->plen;
	return(len);
	}

BCSearch(what)		  /* Search for what forward */
	char what;
{
	while (!BIsEnd() && Buff()!=what && ToLower(Buff())!=what) {
		make_offset(cur_char+1);
		if (cur_char>=cur_plen && cur_page->nextp) {
			make_cur(cur_page->nextp);
			make_offset(0);
			}
		TKbChk();
		}
	savecol = -1;
	if (!BIsEnd()) {
		BMove(1);
		return(TRUE);
		}
	else return(FALSE);
	}

BCRSearch(what)	 /* search for what backwards*/
	char what;
{
	do {
		if (BIsStart()) return(FALSE);
		BMove(-1);
		TKbChk();
		} while (Buff()!=what && ToLower(Buff())!=what);
	return(TRUE);
	}

BCreMrk()			       /* Create a mark at the current point */
{
	struct mrk *mptr;

	for (mptr = &marks[MAXMARK-1]; mptr->mpage; --mptr);
	if (mptr<marks) {
		Error("No more marks");
		mptr = marks;
		}
	mptr->mbuff=cur_buff;
	mptr->mpage=cur_page;
	mptr->moffset=cur_char;
	return(mptr);
	}

#ifdef CPM
BScrnMrk(indx)		  /* get the mark for this screen row */
	int indx;
{
	return(&scrnmarks[indx]);
	}
#endif

BTstMrk(tmark)		  /* Test modified flag on screen mark */
	struct mrk *tmark;
{
	int temp;

	temp=tmark->modf;
	tmark->modf=FALSE;
	return(temp);
	}

BSetMod(tmark)		  /* Set the modified flag on screen mark */
	struct mrk *tmark;
{
	tmark->modf=TRUE;
	}

BMrkToPnt(tmark)		/* Put the mark where the point is */
	struct mrk *tmark;
{
	tmark->mbuff=cur_buff;
	tmark->mpage=cur_page;
	tmark->moffset=cur_char;
	}

BPntToMrk(tmark)		/* Put the point to the mark */
	struct mrk *tmark;
{
	if (tmark->mpage==NULL) {
		Error("Invalid mark");
		return;
		}
	if (tmark->mbuff != cur_buff) BSwitchTo(tmark->mbuff);
	make_cur(tmark->mpage);
	make_offset(tmark->moffset);
	savecol= -1;
	}

BSwapPnt(tmark)	 /* swap the point and the mark */
	struct mrk *tmark;
{
	struct mrk tmp;

	tmp.mbuff=cur_buff;
	tmp.mpage=tmark->mpage;
	tmp.moffset=tmark->moffset;
	BMrkToPnt(tmark);
	BPntToMrk(&tmp);
	}

BIsAtMrk(tmark)	 /* Is the point at the mark? */
	struct mrk *tmark;
{
	return(tmark->mpage==cur_page && tmark->moffset==cur_char);
	}

BIsBeforeMrk(tmark)	     /* Is the point before the mark? */
	struct mrk *tmark;
{
	if (tmark->mpage==NULL || tmark->mbuff != cur_buff)
		Error("Mark in wrong buff!");
	if (tmark->mpage==cur_page) return(cur_char<tmark->moffset);
	for (tpage=cur_page; tpage && tpage!=tmark->mpage;
		tpage=tpage->nextp);
	return(tpage!=NULL);
	}

BIsAfterMrk(tmark)	      /* Is the point after the mark? */
	struct mrk *tmark;
{
	if (tmark->mpage==NULL || tmark->mbuff != cur_buff)
		Error("Mark in wrong buff!");
	if (tmark->mpage==cur_page) return(cur_char>tmark->moffset);
	for (tpage=cur_page; tpage && tpage!=tmark->mpage;
		tpage=tpage->prevp);
	return(tpage!=NULL);
	}

BKillMrk(tmark)		 /* Free up the mark */
	struct mrk *tmark;
{
	tmark->mpage = NULL;
	}

BReadFile(tfname)	       /* Load the buffer with the file tfname */
	char *tfname;
{

	int crflag, fd, count;

	make_cur(cur_buff->firstp);
	while (cur_page->nextp) free_page(cur_buff,cur_page->nextp);
	for (btmark = &marks[MAXMARK-1]; btmark>=scrnmarks; --btmark)
		if (btmark->mpage && btmark->mbuff==cur_buff) {
			btmark->mpage=cur_page;
			btmark->moffset=0;
			btmark->modf=TRUE;
		}
	if ((fd=open(tfname, INPUT))==-1) {
		make_offset(0);
		cgstart=cpstart;
		cgend=cpstart+PSIZE;
		cur_plen=0;
		cur_modf=cur_buff->bmodf=FALSE;
		close(fd);
		return(FALSE);
		}
	crflag=FALSE;
	while ((count = read(fd,cpstart,FILE_PSIZE*128))>0) {
		if(count < FILE_PSIZE*128)
			cpstart[count - 1] = '\32';
		cgstart=cpstart+FILE_PSIZE*128;
		cgend=cpstart+PSIZE;
		cur_plen=FILE_PSIZE*128;
		make_offset(0);
		cur_modf=TRUE;
		if (crflag && Buff()==LF) {
			*cpstart=NL;
			--(cur_page->prevp->plen);
			--(cur_page->prevp->gstart);
			}
		crflag=FALSE;
		while (cur_char<cur_plen) {
			if (Buff()=='\32') break;
			if (Buff()==CR) {
				make_offset(cur_char+1);
				GetGap();
				if (cur_char>=cur_plen) crflag=TRUE;
				else if (Buff()==LF) {
					--cgstart;
					--cur_plen;
					*cgend=NL;
					}
				make_offset(cur_char-1);
				}
			make_offset(cur_char+1);
			}
		if (Buff()=='\32') break;
		if (new_page(cur_buff,cur_page,NULL)==NULL) break;
		make_cur(cur_page->nextp);
		}
	if (Buff()!='\32') Error("Read Error or no EOF");
	close(fd);
	GetGap();
	cgend=cpstart+PSIZE;
	cur_plen=cur_char;
	make_cur(cur_buff->firstp);
	make_offset(0);
	savecol=0;
	cur_buff->bmodf=FALSE;
	return(TRUE);
	}

BWriteFile(tfname)	      /* Write the buffer onto the file on fd */
	char *tfname;
{
	int fd, plen;
	struct pim *tpim, *fpim;
	char *cptr, tchar, *wrtbuff;

/*	fpim=get_memp();	*/	/* free up storage with the storage alligator */
/*	free(fpim->pdata);	*/	/* so i/o functions that need a buffer can */

	TKbChk();
	if ((fd=creat(tfname, OUTPUT)) == -1) return(FALSE);
	TKbChk();
	tpim=get_memp();
	tpim->lfile=LOCKED;
	wrtbuff=tpim->pdata;
	btmark=(struct mrk *)BCreMrk();
	cptr=wrtbuff;
	tpage=cur_buff->firstp;
	TKbChk();
	while (tpage) {
		TKbChk();
		make_cur(tpage);
		make_offset(0);
		tpage=cur_page->nextp;
		while (cur_char<cur_plen) {
			tchar=Buff();
			if (tchar==NL) {
				*cptr++=CR;
				if (cptr >= (&wrtbuff[SWAP_PSIZE*128])) {
					write(fd,wrtbuff,SWAP_PSIZE*128);
					cptr=wrtbuff;
					}
				tchar=LF;
				}
			*cptr++=tchar;
			if (cptr >= (&wrtbuff[SWAP_PSIZE*128])) {
				TKbChk();
				write(fd,wrtbuff,SWAP_PSIZE*128);
				cptr=wrtbuff;
				}
			make_offset(cur_char+1);
			}
		}
	*cptr++='\32';
	plen=(cptr-wrtbuff);
	TKbChk();
	if (plen!=write(fd,wrtbuff,plen)) 
		Error("Write Error");
	else cur_buff->bmodf=FALSE;
	tpim->lfile=MEMORY;
	tpim->pim_modf=FALSE;

	close(fd);
	BPntToMrk(btmark);
	BKillMrk(btmark);
	return(TRUE);
	}

BModp(tbuff)		    /* has the buffer been modified? */
	struct buff *tbuff;
{
	return(tbuff->bmodf);
	}

#ifdef CPM
Buff()			  /* Return the character at the point */
{
	return(*cur_cptr);
	}
#endif

	/* local routines: not callable from outside right now */
DskWarn()			       /* print going to disk warning */
{
	TKbChk();
	TDisStr(terminal.nrows-1,terminal.ncols-15,"Swapping...");
#ifdef SUSER
#endif
	}

DskUnWarn(trow,tcol)    /* clear disk warning */
	int trow, tcol;
{
	TKbChk();
	TSetPoint(terminal.nrows-1,terminal.ncols-15);
	TCLEOL();
	TSetPoint(trow,tcol);
	TForce();
#ifdef SUSER
#endif
	}

GetGap()				/* Get the gap at the point */
{
	if (cur_cptr==cgend) return;
	if (cur_cptr<cgstart) {
		cgend -= cgstart-cur_cptr;
		movmem(cur_cptr,cgend,cgstart-cur_cptr);
		cgstart=cur_cptr;
		cur_cptr=cgend;
		}
	else {
		movmem(cgend,cgstart,cur_cptr-cgend);
		cgstart += cur_cptr-cgend;
		cgend=cur_cptr;
		}
	}

VSetMod(flag)			   /* insert the correct modified flags */
	int flag;
{
	if (divide>0) SubSet(0,divide,flag);
	if (divide<TMaxRow()-2) SubSet(divide+1,TMaxRow()-2,flag);
	}

SubSet(from,to,flag)	    /* set one windows modified flags */
	int from, to, flag;
{
#ifdef CPM
	struct mrk *ltmark;
#else
	register struct mrk *btmark, *ltmark;
#endif

	if (scrnmarks[from].mbuff!=cur_buff) return;
	ltmark = &scrnmarks[to];
	for (btmark = &scrnmarks[from]; btmark<=ltmark &&
		btmark->mpage!=cur_page; ++btmark);
	TKbChk();
	if (btmark>ltmark) {
		for (btmark = &scrnmarks[from]; btmark<=ltmark &&
			(btmark->mbuff!=cur_buff || BIsAfterMrk(btmark)); ++btmark);
		if (btmark > &scrnmarks[from]) {
			while ((--btmark)->mbuff!=cur_buff);
			btmark->modf=TRUE;
			}
		}
	else {
		while (btmark->mpage==cur_page && btmark->moffset<=cur_char &&
			btmark<=ltmark) ++btmark;
		if (--btmark >= &scrnmarks[from]) btmark->modf=TRUE;
		if (flag) while (btmark > &scrnmarks[from] &&
			 btmark->mpage==cur_page && btmark->moffset==cur_char)
			(--btmark)->modf=TRUE;
		}
	TKbChk();
	}

make_offset(dist)	       /* make the point be dist chars into the page */
	int dist;
{
	cur_char=dist;
	if ((cur_cptr=cpstart+dist) >= cgstart) cur_cptr += cgend-cgstart;
	}

movmem(from,to,len)	     /* move len bytes, from from to to, don't bash */
	char *from, *to;
	int len;
{
	if (from>to)
		while (len--) *to++ = *from++;
	else {
		from += len;
		to += len;
		while (len--) *--to = *--from;
		}
	}

/* END OF VBUFF2.C - buffer abstraction, part two */
