/* SUPPORT.C - supporting functions for Mince

	This file contains several useful subroutines that can be
generally used. */

#include "mince.gbl"

ArgEcho(targ)	   /* internal routine to display current argument */
	int targ;
{
	int trow, tcol;

#ifdef SUSER
	if (targ==4)
		for (cnt=delaycnt*mhz; cnt>0; --cnt) if (TKbRdy()) return(FALSE);
#endif
	trow=TGetRow();
	tcol=TGetCol();
	Echo("Arg: ");
	itot(targ);
	TCLEOL();
	TSetPoint(trow,tcol);
	TForce();
	return(TRUE);
	}

Ask(mesg)			       /* Ask a yes/no question in the echo line */
	char *mesg;	     /* Space or y or Y mean yes, anything else is no */
{
	char tchar;
	int yes;

	Echo(mesg);
	while (TRUE) {
		if (yes = ((KbWait(),tchar=TGetKb())==' ' || ToUpper(tchar)=='Y'))
			break;
		if (tchar == BELL || tchar == DEL || tchar == BS ||
			ToUpper(tchar)=='N') break;
		TBell();
		}
	ClrEcho();
	return (yes);
	}

BlockMove(from,to)	      /* move a block of characters between Point and
					   From to before To.  Assume Point is before From */
	int from, to;
{
	int tmp;

	while (BIsBeforeMrk(from)) {
		tmp=Buff();
		BSwapPnt(to);
		BInsert(tmp);
		BSwapPnt(to);
		BDelete(1);
		}
	}

change(old,new)	 /* change old string into new */
	char *old, *new;
{
	BMove(-strlen(old));
	BDelete(strlen(old));
	while (*new) BInsert(*new++);
	}

CheckMode(tmname)	       /* see if there is a mode of this name */
	char *tmname;
{
	LowCase(tmname);
#ifdef LARGE
	if (!strcmp(tmname,"c")) return('c');
#endif
	if (!strcmp(tmname,"save")) return('s');
	if (!strcmp(tmname,"wrap")) return('f');
	if (!strcmp(tmname,"overwrite")) return('o');
	if (!strcmp(tmname, "spell")) return('p');
	if (!strcmp(tmname, "view")) return('v');
	return(FALSE);
	}

ClrEcho()			       /* Clear the echo line */
{
	int trow, tcol;

	trow=TGetRow();
	tcol=TGetCol();
	TSetPoint(TMaxRow()-1,0);
	TCLEOL();
	TSetPoint(trow,tcol);
	TForce();
	}

CopyToMrk(tmark,forwdp) /* copy point to mark to delete buffer */
	int tmark, forwdp;
{
	int ltmark;

	BSwitchTo(del_buff);
	if (!DelCmnd(lfunct)) {
		BToEnd();
		ltmark=BCreMrk();
		BToStart();
		BDelToMrk(ltmark);
		BKillMrk(ltmark);
		}
	if (forwdp) BToEnd();
	else BToStart();
	BSwitchTo(buffs[cbuff].bbuff);
	BCopyRgn(tmark,del_buff);
	}

Debug(message,value)    /* useful for debugging commands */
	char *message;
	int value;
{
	char tchar;

	Echo(message);
	itot(value);
	IncrDsp();
	return((tchar=TGetKb())==DEL || tchar==BS);
	}

DelayPrompt(mesg)	       /* echo mesg if no char is typed within interval */
	char *mesg;	     /* return true if mesg printed, else false */
{
	int trow, tcol;

#ifdef SUSER
	for (cnt=delaycnt*mhz; cnt; --cnt) if (TKbRdy()) return (FALSE);
#endif
	trow=TGetRow();
	tcol=TGetCol();
	Echo(mesg);
	TSetPoint(trow,tcol);
	TForce();
	return(TRUE);
	}

DelCmnd(lfunct)	 /* tell if previous command is a delete command */
	int (*lfunct)();
{
	int MDelLin(), MDelWord(), MRDelWord(), MMakeDel();
	int MDelRgn(), MCopyRgn(), MDelELin(), MDelSent();

	return(lfunct==&MDelLin || lfunct==&MDelWord ||
		lfunct==&MRDelWord || lfunct==&MMakeDel ||
		lfunct==&MDelRgn || lfunct==&MDelELin ||
		lfunct==&MDelSent || lfunct==&MCopyRgn);
	}

DoReplace(query)		/* do query replace and replace string */
	int query;
{
	char tchar;
	int exit;
	char old[STRMAX], new[STRMAX];

	arg=0;
	*old = *new = '\0';
	do if (!GetArg(query ? "Query Replace <ESC>: " : "Replace <ESC>: ",ESC,
			old,STRMAX)) return;
		while (*old=='\0');
	if (!GetArg("with <ESC>: ",ESC,new,STRMAX)) return;
	ReplaceIt(old, new, query);
}

ReplaceIt(old,new,query)
char old[], new[];
int	query;
{
	char tchar;
	int exit;

	tmark=BCreMrk();
	if (query) tchar=',';
	else tchar=' ';
	exit=FALSE;

	while (!exit && StrSrch(old,FORWARD)) {
		if (query) {
			if (tchar==',') {
				Echo("Replacing '");
				NLPrnt(old);
				TPrntStr("' with '");
				NLPrnt(new);
				TPrntChar('\'');
				TPrntStr(" Okay?");
				}
			IncrDsp();
			KbWait();
			tchar=TGetKb();
			switch (tchar) {
			case BELL: 
				ClrEcho();
				BKillMrk(tmark);
				return;
			case ' ':
			case ',':		       /* comma's turn comes later... */
			case 'Y':
			case 'y':
				break;
			case '!':
				query=FALSE;
				break;
			case '.':
				exit=TRUE;
				continue;
			default:
				continue;
				}
			}
		change(old,new);
		if (tchar==',') {
			IncrDsp();
			if (!Ask("Confirm Replace?")) change(new,old);
			}
		}
	ClrEcho();
	BPntToMrk(tmark);
	BKillMrk(tmark);
	}

Echo(mesg)		      /* Say something useful in the echo line */
	char *mesg;
{
	TSetPoint(TMaxRow()-1,0);
	TCLEOL();				       /* clear whatever was there */
	TPrntStr(mesg);		 /* display the message */
	TForce();
	}

Error(mesg)		     /* Display an error message */
	char *mesg;
{
	int trow, tcol;

	trow=TGetRow();
	tcol=TGetCol();
	TDisStr(TMaxRow()-1,TMaxCol()-25,mesg); /* Display the message */
	TBell();							/* Ring the terminal bell */
	TForce();
	KbWait();
	TSetPoint(TMaxRow()-1,TMaxCol()-25);
	TCLEOL();
	if(tcol >= TMaxCol())
		tcol = TMaxcol() - 1;
	TSetPoint(trow,tcol);
	TForce();
	}

ForceCol(col,forwardp)	  /* force the column to be col */
	int col, forwardp;
{
	BMakeCol(col);
	if (col<=0) return;
	if (BGetCol()>col && (BMove(-1), Buff()!=TAB)) BMove(1);
	SIndent(col-BGetCol());
	if (BGetCol()>col && !forwardp) BMove(-1);
	}

GetArg(mesg,term,str,len)       /* input a string argument */
	char *mesg, term, *str;
	int len;
{
	char stemp[80], inpt, *nstr, tcol;

	TDisStr(TMaxRow()-1,0,mesg);
	TCLEOL();
	tcol=TGetCol();
	TForce();
	nstr=stemp;
	while ((KbWait(),inpt=TGetKb())!=term) {
		if (inpt==BELL) {
			ClrEcho();
			return(FALSE);
			}
		if (nstr-stemp>=len-1 && inpt!=DEL && inpt!=BS) {
			nstr=RubOut(stemp,nstr,tcol);
			TBell();
			}
		switch (inpt) {
			case CR:
				TPrntStr("<NL>");
				*nstr++=NL;
				break;
			case BS:
			case DEL:
				if (nstr>stemp) nstr=RubOut(stemp,nstr,tcol);
				break;
			case '\21':		     /* ^Q */
				KbWait();
				inpt=TGetKb();
			default:
				TPrntChar(inpt);
				*nstr++=inpt;
				break;
			}
		TForce();
		}
	if (nstr!=stemp) {
		*nstr='\0';
		strcpy(str,stemp);
		}
	else NLPrnt(str);
	TSetPoint(TMaxRow()-1,0);
	TForce();
	return(TRUE);
	}

GetModeId(msg)		  /* read in and check a mode id */
	char *msg;
{
	char *tmname[STRMAX];
	int id;

	*tmname='\0';
	if (!GetArg(msg,CR,tmname,STRMAX)) return(FALSE);
	if (id=CheckMode(tmname)) return(id);
	Error("Unknown Mode");
	return(FALSE);
	}

SIndent(arg)		    /* put in the right number of spaces */
	int arg;
{
#ifndef CPM
	register int cnt;
#endif

	for (cnt=arg; cnt>0; --cnt) {
		BInsert(' ');
		TKbChk();
		}
	}

index(tstr,tchar)		       /* locate character in string */
	char *tstr, tchar;
{
	int cntr;

	for (cntr=0; *tstr; ++cntr) if (*tstr++==tchar) return(cntr);
	return(-1);
	}


IsClose()			       /* do we have a closing character */
{
	return(Buff()==')' || Buff()==']' || Buff()=='}' ||
		Buff()=='"' || Buff()=='\'');
	}

IsGray()				/* tell if current char is white space */
{
	return(IsWhite() || Buff()==NL);
	}

IsNL()			  /* check for Newline */
{
	return(Buff()==NL);
	}

IsNLPunct()		     /* check for Newline or punctuation */
{
	return(IsNL() || Buff()=='.' || Buff()=='?' || Buff()=='!');
	}


IsParaEnd()		     /* check for end of paragraph*/
{
	return(BIsEnd() || Buff()==TAB || IsNL() || Buff()=='@' ||
		Buff()=='.');
	}

IsSentEnd()		     /* check for end of sentence.  you begin
			at, for example, a '.'.  it skips over as many of
			')}]"' or "'" as it finds, and tells you whether you
			wind up at a whitespace character */
{
	int IsClose();

	BMove(1);
	MovePast(IsClose,FORWARD);
	return(BIsEnd() || IsGray());
	}	       

IsToken()			       /* tell if current char is part of a token */
{
	return(isalpha(Buff()) || isdigit(Buff()));
	}

IsWhite()		       /* tell if current char is limited white space */
{
	return(Buff()==' ' || Buff()==TAB);
	}

itot(n)				 /* print a number on the terminal */
	unsigned n;
{
	if (n>9) itot(n/10);
	TPrntChar(n%10+'0');
	}

KbWait()				/* wait for a character to be typed */
{
	unsigned cntr;

	if(swapf < 0){
		TKbWait();
		return;
	}
	for (cntr=15*delaycnt*mhz; cntr>0; --cntr) if (TKbRdy()) return;
	while (!TKbRdy()) BFlush();
	}

KillToMrk(tmark,forwdp) /* delete region directioned */
	int tmark, forwdp;
{
	CopyToMrk(tmark,forwdp);
	BDelToMrk(tmark);
	}

LowCase(str)			    /* convert a string to all lower case */
	char *str;
{
	while (*str=ToLower(*str)) ++str;
	}

#ifndef CPM
ltot(n)			 /* print a number on the terminal */
	long n;
{
	if (n>9) ltot(n/10);
	TPrntChar((char)(n%10)+'0');
	}
#endif

ModeFlags()		     /* Display the mode flags */
{
#ifdef CPM
	unsigned loc, len;
#else
	long loc, len;
#endif

	if (TKbRdy()) return;
	TREV();
	TDisStr(TMaxRow()-2,stat_col," -");
	loc=BLocation();
	len=BLength(buffs[cbuff].bbuff);
#ifdef CPM
	if (loc<655) itot((loc*100)/len);
	else if (loc<6550) itot((loc*10)/(len/10+1));
	else itot(loc/(len/100+1));
#else
	if (len==0) len=1;
	itot((int)((loc*100)/len));
#endif
	TPrntStr("%- ");
	if (TKbRdy()){
		TNORM();
		return;
	}
	TPrntChar(BModp(buffs[cbuff].bbuff) ? '*' : ' ');
	TPrntChar(DelCmnd(lfunct) ? '+' : ' ');
	while(TGetCol() < TMaxCol())
		TPrntChar(' ');
	TNORM();
/*
	TCLEOL();
*/
	}

MovePast(pred,forwdp)   /* go forward or back past a thingy */
	int (*pred)(), forwdp;
{
	if (!forwdp) BMove(-1);
	while (!(forwdp?BIsEnd():BIsStart()) && (*pred)()) {
		BMove(forwdp?1:-1);
		TKbChk();
		}
	if (!forwdp && !BIsStart()) BMove(1);
	}

MoveTo(pred,forwdp)	     /* go forward or back to a thingy */
	int (*pred)(), forwdp;
{
	if (!forwdp) BMove(-1);
	while (!(forwdp?BIsEnd():BIsStart()) && !(*pred)()) {
		BMove(forwdp?1:-1);
		TKbChk();
		}
	if (!forwdp && !BIsStart()) BMove(1);
	}

NLPrnt(str)		     /* print a string with "<NL>"'s */
	char *str;
{
	while (*str)
		if (*str!=NL) TPrntChar(*str++);
		else {
			TPrntStr("<NL>");
			++str;
			}
	TForce();
	}

NLSrch()				/* Search for the next new line combination */
{
	return(BCSearch(NL));
	}

Rebind(from,to)	 /* change all references to From to To */
	int (*from)(), (*to)();
{
	for (cnt=383; cnt>=0; --cnt) {
		if (functs[cnt]==from) functs[cnt]=to;
		TKbChk();
		}
	}

RNLSrch()			       /* Reverse search for the next new line */
{
	return(BCRSearch(NL));
	}

RubOut(ostr,str,tcol)   /* delete a character for getarg */
	char *str, *ostr, tcol;
{
	*--str='\0';
	TSetPoint(TGetRow(),tcol);
	NLPrnt(ostr);
	TCLEOL();
	TForce();
	return(str);
	}

strip(to,from)		  /* get main part of file name */
	char *to, *from;
{
	int left, right;
	char *tptr;

#ifdef YINYANG
	left=0;
	while (index(from+left,'/')>=0) left += index(from+left,'/')+1;
	right=index(from+left,'.');
	if (right<0) right=strlen(from);
	else right += left;
#else
	left=index(from,':')+1;
	right=index(from,'.');
	if (right<0) right=strlen(from);
	TKbChk();
	if (left>=right) {
		left=0;
		right=strlen(from);
		}
#endif
	if (right-left>8) right=left+8;
	for (tptr=from+left; tptr<from+right; ++tptr) *to++ = *tptr;
	*to='\0';
	}

StrSrch(str,forwardp)   /* directioned string search */
	char *str;
	int forwardp;
{
	cnt=0;
	while (str[cnt]!='\0') {
		if (!(forwardp ? BCSearch(*str) : BCRSearch(*str))) break;
		if (!forwardp) BMove(1);
		for (cnt=1; !BIsEnd() && str[cnt]; ++cnt) {
			if (Buff()!=str[cnt] && ToLower(Buff())!=str[cnt]) break;
			BMove(1);
			TKbChk();
			}
		if (!forwardp || str[cnt]!='\0') BMove((forwardp?1:0)-cnt);
		TKbChk();
		}
	return (str[cnt]=='\0');
	}

ToBegLine()		     /* move to the beginning of the line */
{
	if (BCRSearch(NL)) BMove(1);
	}

ToEndLine()		     /* move to the end of the line */
{
	if (BCSearch(NL)) BMove(-1);
	}

TIndent(arg)		    /* put in the right number of tabs and spaces */
	int arg;
{
	for (cnt=arg; cnt>=tabincr; cnt-=tabincr) {
		BInsert(TAB);
		TKbChk();
		}
	SIndent(cnt);
	}

ToNotWhite(forwardp)    /* move to non whitespace */
	int forwardp;
{
	int IsGray();

	MovePast(IsGray,forwardp);
	}

ToSentEnd(forwardp)	     /* move to a sentence end */
	int forwardp;
{
	int IsNLPunct();

	while (TRUE) {
		MoveTo(IsNLPunct,forwardp);
		if (!forwardp) BMove(-1);
		if (BIsEnd() || BIsStart()) return;
		if (IsNL()) {
			BMove(1);
			tmp=IsParaEnd();
			if (forwardp) BMove(-1);
			}
		else {
			tmark=BCreMrk();
			tmp=IsSentEnd();
			BPntToMrk(tmark);
			BKillMrk(tmark);
			}
		if (tmp) return;
		BMove(forwardp ? 1:-1);
		}
	}

ToWhite(forwardp)	       /* Move to whitespace */
	int forwardp;
{
	int IsGray();

	MoveTo(IsGray,forwardp);
	}

ToWord()				/* move to the beginning of a word */
{
	int IsToken();

	MoveTo(IsToken,FORWARD);
	}

UpCase(str)			     /* convert a string to all upper case */
	char *str;
{
	while (*str=ToUpper(*str)) ++str;
	}

VMovCmnd(lfunct)		/* tell if previous command was a vertical move */
	int (*lfunct)();
{
	int MNextLin(), MPrevLin();
	return(lfunct==&MNextLin || lfunct==&MPrevLin);
	}

/* END OF SUPPORT.C - supporting routines for Mince */

PtMkKill(amark)			/* Move point to mark and kill mark */
struct mrk *amark;
{
	BPntToMrk(amark);
	BKillMrk(amark);
	}
