ftp.nice.ch/pub/next/unix/editor/xvile-7.0.N.bs.tar.gz#/xvile-7.0.N.bs/fences.c

This is fences.c in view mode; [Download] [Up]

/* 
 *
 *	fences.c
 *
 * Match up various fenceposts, like (), [], {}, */ /*, #if, #el, #en
 *
 * Most code probably by Dan Lawrence or Dave Conroy for MicroEMACS
 * Extensions for vile by Paul Fox
 *
 * $Header: /home/tom/src/vile/RCS/fences.c,v 1.41 1997/02/09 19:41:53 tom Exp $
 *
 */

#include	"estruct.h"
#include	"edef.h"

#if OPT_CFENCE

#define	CPP_UNKNOWN -1
#define	CPP_IF       0
#define	CPP_ELIF     1
#define	CPP_ELSE     2
#define	CPP_ENDIF    3

#define CurrentChar() \
 	(is_at_end_of_line(DOT) ? '\n' : char_at(DOT))
#define InDirection(sdir) \
 	((sdir == REVERSE) ? backchar(FALSE, 1) : forwchar(FALSE, 1))

#define PrevCharIs(c) (DOT.o > 0 && lgetc(DOT.l, DOT.o-1) == c)
#define NextCharIs(c) (DOT.o+1 < llength(DOT.l) && (lgetc(DOT.l, DOT.o+1) == c))

static	int	comment_fence(int sdir);
static	int	getfence (int ch, int sdir);
static	int	simple_fence(int sdir, int ch, int ofence);

static int
cpp_keyword(
LINE	*lp,
int	off)
{
	char	temp[NSTRING];
	register char *d = temp;
	register SIZE_T  n;

	static	const	struct	{
		const char *name;
		int	code;
	} keyword_table[] = {
		{ "if",     CPP_IF },
		{ "ifdef",  CPP_IF },
		{ "ifndef", CPP_IF },
		{ "elif",   CPP_ELIF },
		{ "else",   CPP_ELSE },
		{ "endif",  CPP_ENDIF }
	};

	while (off < llength(lp)) {
		n = lgetc(lp,off++);
		if ((d - temp < sizeof(temp)-2) && isident(n))
			*d++ = (char)n;
		else
			break;
	}
	*d = EOS;

	for (n = 0; n < TABLESIZE(keyword_table); n++)
		if (!strcmp(temp, keyword_table[n].name))
			return keyword_table[n].code;
	return CPP_UNKNOWN;
}

static int
cpp_fence(
int sdir,
int key)
{
	int count = 1;
	int i, j, this;

	/* patch: this should come from arguments */
	if (key == CPP_ENDIF)
		sdir = REVERSE;
	else
		sdir = FORWARD;

	/* set up for scan */
	if (sdir == REVERSE)
		DOT.l = lback(DOT.l);
	else
		DOT.l = lforw(DOT.l);

	while (count > 0 && !is_header_line(DOT, curbp)) {
		if ((i = firstchar(DOT.l)) >= 0
		 && lgetc(DOT.l,i) == '#'
		 && (j = nextchar(DOT.l, i+1)) >= 0
		 && ((this = cpp_keyword(DOT.l, j)) != CPP_UNKNOWN)) {
			int	done = FALSE;

			switch (this) {
			case CPP_IF:
				if (sdir == FORWARD) {
					count++;
				} else {
					done = ((count-- == 1) && 
						(key != this));
					if (done)
						count = 0;
				}
				break;

			case CPP_ELIF:
			case CPP_ELSE:
				done = ((sdir == FORWARD) && (count == 1));
				if (done)
					count = 0;
				break;

			case CPP_ENDIF:
				if (sdir == FORWARD) {
					done = (--count == 0);
				} else {
					count++;
				}
			}

			if ((count <= 0) || done) {
				DOT.o = i;
				break;
			}
		}

		if (sdir == REVERSE)
			DOT.l = lback(DOT.l);
		else
			DOT.l = lforw(DOT.l);

		if (is_header_line(DOT,curbp) || interrupted())
			return FALSE;
	}
	if (count == 0) {
		curwp->w_flag |= WFMOVE;
		if (doingopcmd)
			regionshape = FULLLINE;
		return TRUE;
	}
	return FALSE;
}

/*	the cursor is moved to a matching fence */
int
matchfence(int f, int n)
{
	int s = getfence(0, (!f || n > 0) ? FORWARD:REVERSE);
	if (s == FALSE)
		kbd_alarm();
	return s;
}

int
matchfenceback(int f, int n)
{
	int s = getfence(0, (!f || n > 0) ? REVERSE:FORWARD);
	if (s == FALSE)
		kbd_alarm();
	return s;
}

#define PAIRED_FENCE_CH -1

int
is_user_fence(int ch, int *sdirp)
{
	char *fences = b_val_ptr(curbp,VAL_FENCES);
	char *chp, och;
	if (!ch)
		return 0;
	chp = strchr(fences, ch);
	if (!chp)
		return 0;
	if ((chp - fences) & 1) { 
		/* look for the left fence */
		och = chp[-1];
		if (sdirp)
			*sdirp = REVERSE;
	} else { 
		/* look for the right fence */
		och = chp[1];
		if (sdirp)
			*sdirp = FORWARD;
	}
	return och;
}

static int
getfence(
int ch, /* fence type to match against */
int sdir) /* direction to scan if we're not on a fence to begin with */
{
	MARK	oldpos; 	/* original pointer */
	register int ofence = 0;	/* open fence */
	int s, i;
	int key = CPP_UNKNOWN;
	char *C_fences, *ptr;
	int fch;

	/* save the original cursor position */
	oldpos = DOT;

	/* ch may have been passed, if being used internally */
	if (!ch) {
		if ((i = firstchar(DOT.l)) < 0)	/* offset of first nonblank */
			return FALSE;		/* line is entirely blank */

		if (DOT.o <= i && (ch = lgetc(DOT.l,i)) == '#') {
			if (llength(DOT.l) < i+3)
				return FALSE;
		} else if ((ch = char_at(DOT)) == '/' || ch == '*') {
			/* EMPTY */;
		} else if (sdir == FORWARD) {
			/* get the current character */
			if (oldpos.o < llength(oldpos.l)) {
				do {
					ch = char_at(oldpos);
				} while(!is_user_fence(ch, (int *)0) &&
					++oldpos.o < llength(oldpos.l));
			}
			if (is_at_end_of_line(oldpos)) {
				return FALSE;
			}
		} else {
			/* get the current character */
			if (oldpos.o >= 0) {
				do {
					ch = char_at(oldpos);
				} while(!is_user_fence(ch, (int *)0) &&
					--oldpos.o >= 0);
			}

			if (oldpos.o < 0) {
				return FALSE;
			}
		}

		/* we've at least found a fence -- move us that far */
		DOT.o = oldpos.o;
	}

	fch = ch;

	/* is it a "special" fence char? */
	C_fences = "/*#";
	ptr = strchr(C_fences, ch);
	
	if (!ptr) {
		ofence = is_user_fence(ch, &sdir);
		if (ofence)
			fch = PAIRED_FENCE_CH;
	}

	/* setup proper matching fence */
	switch (fch) {
		case PAIRED_FENCE_CH:
			/* NOTHING */
			break;
		case '#':
			if ((i = firstchar(DOT.l)) < 0)
				return FALSE;	/* line is entirely blank */
			if ((i = nextchar(DOT.l, i+1)) >= 0
			 && ((key = cpp_keyword(DOT.l, i)) != CPP_UNKNOWN))
			 	break;
			return FALSE;
		case '*':
			ch = '/';
			if (NextCharIs('/')) {
				sdir = REVERSE;
				forwchar(TRUE,1);
				break;
			} else if (PrevCharIs('/')) {
				sdir = FORWARD;
				backchar(TRUE,1);
				if (doingopcmd)
					pre_op_dot = DOT;
				break;
			}
			return FALSE;
		case '/':
			if (NextCharIs('*')) {
				sdir = FORWARD;
				break;
			} else if (PrevCharIs('*')) {
				sdir = REVERSE;
				break;
			}
			/* FALL THROUGH */
		default: 
			return(FALSE);
	}

	/* ops are inclusive of the endpoint */
	if (doingopcmd && sdir == REVERSE) {
		forwchar(TRUE,1);
		pre_op_dot = DOT;
		backchar(TRUE,1);
	}

	if (key != CPP_UNKNOWN) {  /* we're searching for a cpp keyword */
		s = cpp_fence(sdir, key);
	} else if (ch == '/') {
		s = comment_fence(sdir);
	} else {
		s = simple_fence(sdir, ch, ofence);
	}

	if (s == TRUE)
		return TRUE;

	/* restore the current position */
	DOT = oldpos;
	return(FALSE);
}

static int
simple_fence(int sdir, int ch, int ofence)
{
	int count = 1;	/* Assmue that we're sitting at one end of the fence */
	int c;

	/* scan for fence */
	while (InDirection(sdir) && !interrupted()) {
		c = CurrentChar();
		if (c == ch) {
			++count;
		} else if (c == ofence) {
			if (--count <= 0)
				break;
		}
	}

	/* if count is zero, we have a match, move the sucker */
	if (count <= 0) {
		if (!doingopcmd || doingsweep)
			sweephack = TRUE;
		else if (sdir == FORWARD)
			forwchar(TRUE,1);
		curwp->w_flag |= WFMOVE;
		return TRUE;
	}
	return FALSE;
}

static int
comment_fence(int sdir)
{
	MARK comstartpos;
	int found = FALSE;
	int s = FALSE;
	int first = TRUE;

	comstartpos.l = null_ptr;

	while (!found) {
		if (!first && CurrentChar() == '/') {
			/* is it a comment-end? */
			if (PrevCharIs('*')) {
				if (sdir == FORWARD) {
					found = TRUE;
					break;
				} else if (comstartpos.l != null_ptr) {
					DOT = comstartpos;
					found = TRUE;
					break;
				} else {
					return FALSE;
				}
			}
			/* is it a comment start? */
			if (sdir == REVERSE && NextCharIs('*')) {
				/* remember where we are */
				comstartpos = DOT;
			}
		}

		s = InDirection(sdir);

		if (s == FALSE) {
			if (comstartpos.l != null_ptr) {
				DOT = comstartpos;
				found = TRUE;
				break;
			}
			return FALSE;
		}

		if (interrupted())
			return FALSE;
		first = FALSE;
	}

	/* if found, move the sucker */
	if (found && !first) {
		if (!doingopcmd || doingsweep)
			sweephack = TRUE;
		else if (sdir == FORWARD)
			forwchar(TRUE,1);
		curwp->w_flag |= WFMOVE;
		return TRUE;
	}
	return FALSE;
}

/* get the indent of the line containing the matching brace/paren. */
int
fmatchindent(int c)
{
	int ind;
	    
	MK = DOT;
	    
	if (getfence(c,REVERSE) == FALSE) {
		(void)gomark(FALSE,1);
		return previndent((int *)0);
	}

	ind = indentlen(DOT.l);

	(void)gomark(FALSE,1);
	    
	return ind;
}



/*	Close fences are matched against their partners, and if
	on screen the cursor briefly lights there		*/
void
fmatch(int rch)
{
	MARK	oldpos; 		/* original position */
	register LINE *toplp;	/* top line in current window */
	register int count; /* current fence level count */
	register char c;	/* current character in scan */
	int dir, lch;
	int backcharfailed = FALSE;

	/* get the matching left-fence char, if it exists */
	lch = is_user_fence(rch, &dir);
	if (lch == 0 || dir != REVERSE)
		return;

	/* first get the display update out there */
	(void)update(FALSE);

	/* save the original cursor position */
	oldpos = DOT;

	/* find the top line and set up for scan */
	toplp = lback(curwp->w_line.l);
	count = 1;
	backchar(TRUE, 2);

	/* scan back until we find it, or reach past the top of the window */
	while (count > 0 && DOT.l != toplp) {
		c = CurrentChar();
		if (c == rch)
			++count;
		if (c == lch)
			--count;
		if (backchar(FALSE, 1) != TRUE) {
			backcharfailed = TRUE;
			break;
		}
	}

	/* if count is zero, we have a match, display the sucker */
	if (count == 0) {
		if (!backcharfailed)
			forwchar(FALSE, 1);
		if (update(FALSE) == TRUE)
		/* the idea is to leave the cursor there for about a
			quarter of a second */
			catnap(300, FALSE);
	}

	/* restore the current position */
	DOT = oldpos;
}

#endif /* OPT_CFENCE */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.