/*
 * Screen editor:  command mode commands -- enhanced
 *
 * Source: ed3.c
 * Version: December 20, 1981.
 * Transliteration of small-C version of September 5, 1981
 *
 */

/* define globals */

#include ed1.h

/* data global to these routines */

/* comment out -----
char filename [SYSFNMAX];
----- end comment out */


/* append command.
 * load a file into main buffer at current location.
 * this command does NOT change the current file name.
 */

append(args) char *args;
{
char buffer [MAXLEN];		/* disk line buffer */
int file;
int n;
int topline;
char locfn [SYSFNMAX];		/* local file name */
	/* get file name which follows command */
	if (name1(args,locfn) == ERR) {
		return;
	}
	if (locfn [0] == EOS) {
		message("no file argument");
		return;
	}
	/* open the new file */
	if ((file=sysopen(locfn,"r")) == ERR) {
		message("file not found");
		return;
	}
	/* read the file into the buffer */
	while ((n=readline(file,buffer,MAXLEN)) >= 0) {
		if (n > MAXLEN) {
			message("line truncated");
			n=MAXLEN;
		}
		if (bufins(buffer,n) == ERR) {
			break;
		}
		if (bufdn() == ERR) {
			break;
		}
	}
	/* close the file */
	sysclose(file);
	/* redraw the screen so topline will be at top
	 * of the screen after command() does a CR/LF.
	 */
	topline=max(1,bufln()-SCRNL2);
	bufout(topline,2,SCRNL2);
	bufgo(topline);
}

/* global change command */

change(args) char *args;
{
char oldline [MAXLEN1];		/* reserve space for EOS */
char newline [MAXLEN1];
char oldpat [MAXLEN1];
char newpat [MAXLEN1];
int from, to, col, n, k;
	if (get2args(args,ffrom,fto) == ERR) {
		return;
	}
	/* get search and change masks into oldpat, newpat */
	fmtsout("search mask ?  ",0);
	getcmnd(oldpat,15);
	fmtcrlf();
	if (oldpat [0] == EOS) {
		return;
	}
	pmtline();
	fmtsout("change mask ?  ",0);
	getcmnd(newpat,15);
	fmtcrlf();
	/* make substitution for lines between from, to */
	while (from <= to) {
		if (chkkey() == YES) {
			break;
		}
		if (bufgo(from++) == ERR) {
			break;
		}
		if (bufatbot() == YES) {
			break;
		}
		n=bufgetln(oldline,MAXLEN);
		n=min(n,MAXLEN);
		oldline [n]=EOS;
		/* '^' anchors search */
		if (oldpat [0] == '^') {
			if (amatch(oldline,oldpat+1,0) == YES) {
				k=replace(oldline,newline,
					oldpat+1,newpat,0);
				if (k == ERR) {
					return;
				}
				fmtcrlf();
				putdec(bufln(),5);
				fmtsout(newline,5);
				outdeol();
				bufrepl(newline,k);
			}
			continue;
		}
		/* search oldline for oldpat */
		col=0;
		while (col < n) {
			if (amatch(oldline,oldpat,col++) == YES){
				k=replace(oldline,newline,
					oldpat,newpat,col-1);
				if (k == ERR) {
					return;
				}
				fmtcrlf();
				putdec(bufln(),5);
				fmtsout(newline,5);
				outdeol();
				bufrepl(newline,k);
				break;
			}
		}
	}
	fmtcrlf();
}

/* clear main buffer and file name */

clear()
{
	/* make sure it is ok to clear buffer */
	if (chkbuf() == YES) {
		filename [0]=0;
		pmtfile("");
		outclr();
		outxy(0,SCRNL1);
		bufnew();
		message("buffer cleared");
	}
}

/* multiple line delete command */

delete(args) char *args;
{
int from, to;
	if (get2args(args,ffrom,fto) == ERR) {
		return;
	}
	if (from > to) {
		return;
	}
	/* go to first line to be deleted */
	if (bufgo(from) == ERR) {
		return;
	}
	/* delete all line between from and to */
	if (bufdeln(to-from+1) == ERR) {
		return;
	}
	/* redraw the screen */
	bufout(bufln(),1,SCRNL1);
}

/* search all lines below the current line for a pattern
 * return -1 if pattern not found.
 * otherwise, return column number of start of pattern.
 */

find()
{
	return(search1(bufln()+1,HUGE,YES));
}

/* list lines to list device */

list(args) char *args;
{
char linebuf [MAXLEN1];
int n;
int from, to, line, oldline;
	/* save the buffer's current line */
	oldline=bufln();
	/* get starting, ending lines to print */
	if (get2args(args,ffrom,fto) == ERR) {
		return;
	}
	/* print lines one at a time to list device */
	line=from;
	while (line <= to) {
		/* make sure prompt goes to console */
		fmtassn(NO);
		/* check for interrupt */
		if (chkkey() == YES) {
			break;
		}
		/* print line to list device */
		fmtassn(YES);
		if (bufgo(line++) != OK) {
			break;
		}
		if (bufatbot()) {
			break;
		}
		n=bufgetln(linebuf,MAXLEN1);
		n=min(n,MAXLEN);
		linebuf [n]=CR;
		fmtsout(linebuf,0);
		fmtcrlf();
	}
	/* redirect output to console */
	fmtassn(NO);
	/* restore cursor */
	bufgo(oldline);
}

/* load file into buffer */

load (args) char *args;
{
char buffer [MAXLEN];	/* disk line buffer */
char locfn  [SYSFNMAX];  /* file name until we check it */
int n;
int file;
int topline;
	/* get filename following command */
	if (name1(args,locfn) == ERR) {
		return;
	}
	if (locfn [0] == EOS) {
		message("no file argument");
		return;
	}
	/* give user a chance to save the buffer */
	if (chkbuf() == NO) {
		return;
	}
	/* open the new file */
	if ((file=sysopen(locfn,"r")) == ERR) {
		message("file not found");
		return;
	}
	/* update file name */
	syscopfn(locfn, filename);
	pmtfile(filename);
	/* clear the buffer */
	bufnew();
	/* read the file into the buffer */
	while ((n=readline(file,buffer,MAXLEN)) >= 0) {
		if (n > MAXLEN) {
			message("line truncated");
			n=MAXLEN;
		}
		if (bufins(buffer,n) == ERR) {
			break;
		}
		if (bufdn() == ERR) {
			break;
		}
	}
	/* close the file */
	sysclose(file);
	/* indicate that the buffer is fresh */
	bufsaved();
	/* set current line to line 1 */
	bufgo(1);
	/* redraw the screen so that topline will be
	 * on line 1 after command() does a CR/LF.
	 */
	topline=max(1,bufln()-SCRNL2);
	bufout(topline,2,SCRNL2);
	bufgo(topline);
}

/* change current file name */

name(args) char *args;
{
	name1(args,filename);
	pmtfile(filename);
}

/* check syntax of args.
 * copy to filename.
 * return OK if the name is valid.
 */

name1(args,filename) char *args, *filename;
{
	/* skip command */
	args=skiparg(args);
	args=skipbl(args);
	/* check file name syntax */
	if (syschkfn(args) == ERR) {
		return(ERR);
	}
	/* copy filename */
	syscopfn(args,filename);
	return(OK);
}

/* save the buffer in an already existing file */

resave()
{
char linebuf [MAXLEN];
int file, n, oldline;
	/* make sure file has a name */
	if (filename [0] == EOS) {
		message("file not named");
		return;
	}
	/* the file must exist for resave */
	if ((file=sysopen(filename,"r")) == ERR) {
		message("file not found");
		return;
	}
	if (sysclose(file) == ERR) {
		return;
	}
	/* open the file for writing */
	if ((file=sysopen(filename,"w")) == ERR) {
		return;
	}
	/* save the current position of file */
	oldline=bufln();
	/* write out the whole file */
	if (bufgo(1) == ERR) {
		sysclose(file);
		return;
	}
	while (bufatbot() == NO) {
		n=bufgetln(linebuf,MAXLEN);
		n=min(n,MAXLEN);
		if (pushline(file,linebuf,n) == ERR) {
			break;
		}
		if (bufdn() == ERR) {
			break;
		}
	}
	/* indicate if all buffer was saved */
	if (bufatbot()){
		bufsaved();
	}
	/* close file and restore line number */
	sysclose(file);
	bufgo(oldline);
}

/* save the buffer in a new file */

save()
{
char linebuf [MAXLEN];
int file, n, oldline;
	/* make sure the file is named */
	if (filename [0] == EOS) {
		message("file not named");
		return;
	}
	/* file must NOT exist for save */
	if ((file=sysopen(filename,"r")) != ERR) {
		sysclose(file);
		message("file exists");
		return;
	}
	/* open file for writing */
	if ((file=sysopen(filename,"w")) == ERR) {
		return;
	}
	/* remember current line */
	oldline=bufln();
	/* write entire buffer to file */
	if (bufgo(1) == ERR) {
		sysclose(file);
		return;
	}
	while (bufatbot() == NO) {
		n=bufgetln(linebuf,MAXLEN);
		n=min(n,MAXLEN);
		if (pushline(file,linebuf,n) == ERR) {
			break;
		}
		if (bufdn() == ERR) {
			break;
		}
	}
	/* indicate buffer saved if good write */
	if (bufatbot()) {
		bufsaved();
	}
	/* restore line and close file */
	bufgo(oldline);
	sysclose(file);
}

/* global search command */

search(args) char *args;
{
int from, to;

	if (get2args(args,ffrom,fto) == ERR) {
		return;
	}
	search1(from, to, NO);
}

/* search lines for a pattern.
 * if flag  ==  YES: stop at the first match.
 *                 return -1 if no match.
 *                 otherwise return column number of match.
 * if flag  ==  NO:  print all matches found.
 */

search1(from, to, flag) int from, to, flag;
{
char pat   [MAXLEN1];		/* reserve space for EOS */
char line  [MAXLEN1];
int col, n;
	/* get search mask into pat */
	fmtsout("search mask ?  ",0);
	getcmnd(pat,15);
	fmtcrlf();
	if (pat [0] == EOS) {
		return;
	}
	/* search all lines between from and to for pat */
	while (from <= to) {
		if (chkkey() == YES) {
			break;
		}
		if (bufgo(from++) == ERR) {
			break;
		}
		if (bufatbot() == YES) {
			break;
		}
		n=bufgetln(line,MAXLEN);
		n=min(n,MAXLEN);
		line [n]=EOS;
		/* ^ anchors search */
		if (pat [0] == '^') {
			if (amatch(line,pat+1,0) == YES) {
				if (flag == NO) {
					fmtcrlf();
					putdec(bufln(),5);
					fmtsout(line,5);
					outdeol();
				}
				else {
					return(0);
				}
			}
			continue;
		}
		/* search whole line for match */
		col=0;
		while (col < n) {
			if (amatch(line,pat,col++) == YES) {
				if (flag == NO) {
					fmtcrlf();
					putdec(bufln(),5);
					fmtsout(line,5);
					outdeol();
					break;
				}
				else {
					return(col-1);
				}
			}
		}
	}
	/* all searching is finished */
	if (flag == YES) {
		return(-1);
	}
	else {
		fmtcrlf();
	}
}

/* set tab stops for fmt routines */

tabs(args) char *args;
{
int n, junk;
	if (get2args(args,fn,fjunk) == ERR) {
		return;
	}
	fmtset(n);
}

/* return YES if buffer may be drastically changed */

chkbuf()
{
	if (bufchng() == NO) {
		/* buffer not changed. no problem */
		return(YES);
	}
	fmtsout("buffer not saved. proceed ?  ",0);
	pmtline();
	if (tolower(syscout(syscin())) != 'y') {
		fmtcrlf();
		message("cancelled");
		return(NO);
	}
	else {
		fmtcrlf();
		return(YES);
	}
}

/* print message from a command */

message(s) char *s;
{
	fmtsout(s,0);
	fmtcrlf();
}

/* get two arguments the argument line args.
 * no arguments imply 1 HUGE.
 * one argument implies both args the same.
 */

get2args(args,val1,val2) char *args; int *val1, *val2;
{
	/* skip over the command */
	args=skiparg(args);
	args=skipbl(args);
	if (*args == EOS) {
		*val1=1;
		*val2=HUGE;
		return(OK);
	}
	/* check first argument */
	if (number(args,val1) == NO) {
		message("bad argument");
		return(ERR);
	}
	/* skip over first argument */
	args=skiparg(args);
	args=skipbl(args);
	/* 1 arg: arg 2 is HUGE */
	if (*args == EOS) {
		*val2=HUGE;
		return(OK);
	}
	/* check second argument */
	if (number(args,val2) == NO) {
		message("bad argument");
		return(ERR);
	}
	else {
		return(OK);
	}
}

/* skip over all except EOS, and blanks */

skiparg(args) char *args;
{
	while (*args != EOS ff *args!=' ') {
		args++;
	}
	return(args);
}

/* skip over all blanks */

skipbl(args) char *args;
{
	while (*args == ' ') {
		args++;
	}
	return(args);
}

/* return YES if the user has pressed any key.
 * blanks cause a transparent pause.
 */
chkkey()
{
int c;
	c=syscstat();
	if (c == 0) {
		/* no character at keyboard */
		return(NO);
	}
	else if (c == ' ') {
		/* pause.  another blank ends pause */
		pmtline();
		if (syscin() == ' ') {
			return(NO);
		}
	}
	/* we got a nonblank character */
	return(YES);
}

/* anchored search for pattern in text line at column col.
 * return YES if the pattern starts at col.
 */

amatch(line,pat,col) char *line, *pat; int col;
{
int k;
	k=0;
	while (pat [k] != EOS) {
		if (pat [k] == line[col]) {
			k++;
			col++;
		}
		else if (pat [k] == '?' ff line[col] != EOS) {
			/* question mark matches any char */
			k++;
			col++;
		}
		else {
			return(NO);
		}
	}
	/* the entire pattern matches */
	return(YES);
}

/* replace oldpat in oldline by newpat starting at col.
 * put result in newline.
 * return number of characters in newline.
 */

replace(oldline,newline,oldpat,newpat,col)
char *oldline, *newline, *oldpat, *newpat; int col;
{
int k;
char *tail, *pat;
	/* copy oldline preceding col to newline */
	k=0;
	while (k < col) {
		newline [k++]= *oldline++;
	}
	/* remember where end of oldpat in oldline is */
	tail=oldline;
	pat=oldpat;
	while (*pat++ != EOS) {
		tail++;
	}
	/* copy newpat to newline.
	 * use oldline and oldpat to resolve question marks
	 * in newpat.
	 */
	while (*newpat != EOS) {
		if (k > MAXLEN-1) {
			message("new line too long");
			return(ERR);
		}
		if (*newpat != '?') {
			/* copy newpat to newline */
			newline [k++]= *newpat++;
			continue;
		}
		/* scan for '?' in oldpat */
		while (*oldpat != '?') {
			if (*oldpat == EOS) {
				message(
				"too many ?'s in change mask"
				);
				return(ERR);
			}
			oldpat++;
			oldline++;
		}
		/* copy char from oldline to newline */
		newline [k++]= *oldline++;
		oldpat++;
		newpat++;
	}
	/* copy oldline after oldpat to newline */
	while (*tail != EOS) {
		if (k >= MAXLEN-1) {
			message("new line too long");
			return(ERR);
		}
		newline [k++]= *tail++;
	}
	newline [k]=EOS;
	return(k);
}

