/* Screen editor:  buffer module
 *                 C/80 version
 *
 * Source:  ed10.c
 * Version: May 15, 1981.
 */

/* define globals */

#include ed1.h

/* globals used by this module -----

int bufcflag;		main buffer changed flag
char *bufp;		start of current line
char *bufpmax;		end of last line
char *buffer;		start of buffer
char *bufend;		last byte of buffer
int bufline;		current line number
int bufmaxln;		number of lines in buffer

----- */

/* This code is built around several invariant
 * assumptions:
 * First, the last line is always completely empty.
 * When bufp points to the last line there is NO
 * CR following it.
 * Second, bufp points to the last line if and only if
 * bufline==bufmaxln+1.
 * Third, bufline is always greater than zero.
 * Line zero exists only to make scanning for the
 * start of line one easier.
 */


/* Clear the main buffer */

bufnew()
{
int k;
	/* do initial allocation of memory */
	if (buffer==0) {
		k=60;
		while ((buffer=alloc(k*1024))==-1)
			k--;
		bufend=buffer+k*1024;
	}
	/* point past line 0 */
	bufp=bufpmax=buffer+1;
	/* at line one. no lines in buffer */
	bufline=1;
	bufmaxln=0;
	/* line zero is always a null line */
	buffer[0]=CR;
	/* indicate no need to save file yet */
	bufcflag=NO;
}

/* return current line number */

bufln()
{
	return(bufline);
}

/* return YES if the buffer (i.e., file) has been
 * changed since the last time the file was saved.
 */

bufchng()
{
	return(bufcflag);
}

/* the file has been saved.  clear bufcflag. */

bufsaved()
{
	bufcflag=NO;
}

/* return number of bytes left in the buffer */

buffree()
{
	return(bufend-bufp);
}

/* Position buffer pointers to start of indicated line */

bufgo(line) int line;
{
	/* put request into range. prevent extension */
	line=min(bufmaxln+1,line);
	line=max(1,line);
	/* already at proper line? return. */
	if (line==bufline) {
		return(OK);
	}
	/* move through buffer one line at a time */
	while (line<bufline) {
		if (bufup()==ERR) {
			return(ERR);
		}
	}
	while (line>bufline) {
		if (bufdn()==ERR) {
			return(ERR);
		}
	}
	/* we have reached the line we wanted */
	return(OK);
}

/* move one line closer to front of buffer, i.e.,
 * set buffer pointers to start of previous line.
 */

bufup()
{
char *oldbufp;
	oldbufp=bufp;
	/* can't move past line 1 */
	if (bufattop()) {
		return(OK);
	}
	/* move past CR of previous line */
	if (*--bufp!=CR) {
		syserr("bufup: missing CR");
		bufp=oldbufp;
		return(ERR);
	}
	/* move to start of previous line */
	while (*--bufp!=CR) {
		;
	}
	bufp++;
	/* make sure we haven't gone too far */
	if (bufp<(buffer+1)) {
		syserr("bufup: bufp underflow");
		bufp=oldbufp;
		return(ERR);
	}
	/* success!  we ARE at previous line */
	bufline--;
	return(OK);
}

/* Move one line closer to end of buffer, i.e.,
 * set buffer pointers to start of next line.
 */

bufdn()
{
char *oldbufp;
	oldbufp=bufp;
	/* do nothing silly if at end of buffer */
	if (bufatbot()) {
		return(OK);
	}
	/* scan past current line and CR */
	while (*bufp++!=CR) {
		;
	}
	/* make sure we haven't gone too far */
	if (bufp>bufpmax) {
		syserr("bufdn: bufp overflow");
		bufp=oldbufp;
		return(ERR);
	}
	/* success! we are at next line */
	bufline++;
	return(OK);
}

/* Insert a line before the current line.
 * p points to a line of length n to be inserted.
 * Note: n does not include trailing CR.
 */

bufins(p,n) char *p; int n;
{
int k;
	/* make room in the buffer for the line */
	if (bufext(n+1)==ERR) {
		return(ERR);
	}
	/* put the line and CR into the buffer */
	k=0;
	while (k<n) {
		*(bufp+k)= *(p+k);
		k++;
	}
	*(bufp+k)=CR;
	/* increase number of lines in buffer */
	bufmaxln++;
	/* special case: inserting a null line at
	 * end of file is not a significant change.
	 */
	if ((n==0)ff(bufnrbot())) {
		;
	}
	else {
		bufcflag=YES;
	}
	return(OK);
}

/* delete the current line */

bufdel()
{
	return(bufdeln(1));
}

/* delete n lines, starting with the current line */

bufdeln(n) int n;
{
int oldline;
int k;
char *oldbufp;
	/* remember current buffer parameters */
	oldline=bufline;
	oldbufp=bufp;
	/* scan for first line after deleted lines */
	k=0;
	while ((n--)>0) {
		if (bufatbot()) {
			break;
		}
		if (bufdn()==ERR) {
			bufline=oldline;
			oldbufp=bufp;
			return(ERR);
		}
		k++;
	}
	/* compress buffer.  update pointers */
	bufmovup(bufp,bufpmax-1,bufp-oldbufp);
	bufpmax=bufpmax-(bufp-oldbufp);
	bufp=oldbufp;
	bufline=oldline;
	bufmaxln=bufmaxln-k;
	bufcflag=YES;
	return(OK);
}

/* replace current line with the line that
 * p points to.  The new line is of length n.
 */

bufrepl(p,n) char *p; int n;
{
int oldlen, k;
char *nextp;
	/* do not replace null line.  just insert */
	if (bufatbot()) {
		return(bufins(p,n));
	}
	/* point nextp at start of next line */
	if (bufdn()==ERR) {
		return(ERR);
	}
	nextp=bufp;
	if (bufup()==ERR) {
		return(ERR);
	}
	/* allow for CR at end */
	n=n+1;
	/* see how to move buffer below us;
	 * up, down, or not at all.
	 */
	oldlen=nextp-bufp;
	if (oldlen<n) {
		/* move buffer down */
		if (bufext(n-oldlen)==ERR) {
			return(ERR);
		}
		bufpmax=bufpmax+n-oldlen;
	}
	else if (oldlen>n) {
		/* move buffer up */
		bufmovup(nextp,bufpmax-1,oldlen-n);
		bufpmax=bufpmax-(oldlen-n);
	}
	/* put new line in the hole we just made */
	k=0;
	while (k<(n-1)) {
		bufp[k]=p[k];
		k++;
	}
	bufp[k]=CR;
	bufcflag=YES;
	return(OK);
}

/* copy current line into buffer that p points to.
 * the maximum size of that buffer is n.
 * return k=length of line in the main buffer.
 * if k>n then truncate n-k characters and only
 * return n characters in the caller's buffer.
 */

bufgetln(p,n) char *p; int n;
{
int k;
	/* last line is always null */
	if (bufatbot()) {
		return(0);
	}
	/* copy line as long as it not too long */
	k=0;
	while (k<n) {
		if (*(bufp+k)==CR) {
			return(k);
		}
		*(p+k)= *(bufp+k);
		k++;
	}
	/* count length but move no more chars */
	while (*(bufp+k)!=CR) {
		k++;
	}
	return(k);
}

/* move buffer down (towards HIGH addresses) */

bufmovdn(from,to,length) char *from, *to; int length;
{
	/* this code needs to be very fast.
	 * use an assembly language routine.
	 */

	sysmovdn(to-from+1,from+length,from);
}

/* the call to sysmovdn() is equivalent to the following code:

int k;
	k=to-from+1;
	while ((k--)>0) {
		*(to+length)= *to;
		to--;
	}

 */

/* move buffer up (towards LOW addresses) */

bufmovup(from,to,length) char *from, *to; int length;
{
	/* this code must be very fast.
	 * use an assembly language routine.
	 */

	sysmovup(to-from+1,from-length,from);
}

/* the call to sysmovup() is equivalent to the following code:

int k;
	k=to-from+1;
	while ((k--)>0) {
		*(from-length)= *from;
		from++;
	}

 */

/* return true if at bottom of buffer.
 * NOTE 1: the last line of the buffer is always null.
 * NOTE 2: the last line number is always bufmaxln+1.
 */

bufatbot()
{
	return(bufline>bufmaxln);
}

/* return true if at bottom or at the last
 * real line before the bottom.
 */

bufnrbot()
{
	return(bufline>=bufmaxln);
}

/* return true if at top of buffer */

bufattop()
{
	return(bufline==1);
}

/* put nlines lines from buffer starting with
 * line topline at position topy of the screen.
 */

bufout(topline,topy,nlines) int topline, topy, nlines;
{
int l,p;
	/* remember buffer's state */
	l=bufline;
	p=bufp;
	/* write out one line at a time */
	while ((nlines--)>0) {
		outxy(0,topy++);
		bufoutln(topline++);
	}
	/* restore buffer's state */
	bufline=l;
	bufp=p;
}

/* print line of main buffer on screen */

bufoutln(line) int line;
{
	/* error message does NOT go on prompt line */
	if (bufgo(line)==ERR) {
		fmtsout("disk error: line deleted",0);
		outdeol();
		return;
	}
	/* blank out lines below last line of buffer */
	if (bufatbot()) {
		outdeol();
	}
	/* write one formatted line out */
	else {
		fmtsout(bufp,0);
		outdeol();
	}
}

/* simple memory version of bufext.
 * create a hole in buffer at current line.
 * length is the size of the hole.
 */

bufext(length) int length;
{
	/* make sure there is room for more */
	if ((bufpmax+length)>=bufend) {
		error("main buffer is full");
		return(ERR);
	}
	/* move lines below current line down */
	bufmovdn(bufp,bufpmax-1,length);
	bufpmax=bufpmax+length;
	return(OK);
}
