From decwrl!elroy.jpl.nasa.gov!usc!cs.utexas.edu!uunet!allbery Sat Mar 10 15:34:29 PST 1990
Article 1388 of comp.sources.misc:
Path: decwrl!elroy.jpl.nasa.gov!usc!cs.utexas.edu!uunet!allbery
From: dmt@pegasus.ATT.COM (Dave Tutelman)
Newsgroups: comp.sources.misc
Subject: v11i013: Stevie 3.69a - 5/6
Message-ID: <80853@uunet.UU.NET>
Date: 10 Mar 90 19:43:18 GMT
Sender: allbery@uunet.UU.NET
Organization: AT&T Bell Labs - Lincroft, NJ
Lines: 2587
Approved: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)

Posting-number: Volume 11, Issue 13
Submitted-by: dmt@pegasus.ATT.COM (Dave Tutelman)
Archive-name: stevie3.69a/part05

: This is a shar archive.  Extract with sh, not csh.
: The rest of this file will extract:
: search.c sentence.c tagcmd.c term.c undo.c version.c tos.c
echo extracting - search.c
sed 's/^X//' > search.c << '!EOR!'
X/* $Header: /nw/tony/src/stevie/src/RCS/search.c,v 1.16 89/08/06 09:50:51 tony Exp $
X *
X * This file contains various searching-related routines. These fall into
X * three groups: string searches (for /, ?, n, and N), character searches
X * within a single line (for f, F, t, T, etc), and "other" kinds of searches
X * like the '%' command, and 'word' searches.
X */
X
X#include "stevie.h"
X#include "regexp.h"	/* Henry Spencer's (modified) reg. exp. routines */
X
X/*
X * String searches
X *
X * The actual searches are done using Henry Spencer's regular expression
X * library.
X */
X
X#define	BEGWORD	"([^a-zA-Z0-9_]|^)"	/* replaces "\<" in search strings */
X#define	ENDWORD	"([^a-zA-Z0-9_]|$)"	/* likewise replaces "\>" */
X
X#define	BEGCHAR(c)	(islower(c) || isupper(c) || isdigit(c) || ((c) == '_'))
X
Xbool_t	begword;	/* does the search include a 'begin word' match */
X
X/*
X * mapstring(s) - map special backslash sequences
X */
Xstatic char *
Xmapstring(s)
Xregister char	*s;
X{
X	static	char	ns[80];
X	register char	*p;
X
X	begword = FALSE;
X
X	for (p = ns; *s ;s++) {
X		if (*s != '\\') {	/* not an escape */
X			*p++ = *s;
X			continue;
X		}
X		switch (*++s) {
X		case '/':
X			*p++ = '/';
X			break;
X
X		case '<':
X			strcpy(p, BEGWORD);
X			p += strlen(BEGWORD);
X			begword = TRUE;
X			break;
X
X		case '>':
X			strcpy(p, ENDWORD);
X			p += strlen(ENDWORD);
X			break;
X
X		default:
X			*p++ = '\\';
X			*p++ = *s;
X			break;
X		}
X	}
X	*p++ = NUL;
X
X	return ns;
X}
X
Xstatic char *laststr = NULL;
Xstatic int lastsdir;
X
Xstatic LPTR *
Xssearch(dir,str)
Xint	dir;	/* FORWARD or BACKWARD */
Xchar	*str;
X{
X	LPTR	*bcksearch(), *fwdsearch();
X	LPTR	*pos;
X	char	*old_ls = laststr;
X
X	reg_ic = P(P_IC);	/* tell the regexp routines how to search */
X
X	laststr = strsave(str);
X	lastsdir = dir;
X
X	if (old_ls != NULL)
X		free(old_ls);
X
X	if (dir == BACKWARD) {
X		smsg("?%s", laststr);
X		pos = bcksearch(mapstring(laststr));
X	} else {
X		smsg("/%s", laststr);
X		pos = fwdsearch(mapstring(laststr));
X	}
X
X	/*
X	 * This is kind of a kludge, but its needed to make
X	 * 'beginning of word' searches land on the right place.
X	 */
X	if (pos != NULL && begword) {
X		if (pos->index != 0 || !BEGCHAR(pos->linep->s[0]))
X			pos->index += 1;
X	}
X	return pos;
X}
X
Xbool_t
Xdosearch(dir,str)
Xint	dir;
Xchar	*str;
X{
X	LPTR	*p;
X
X	if (str == NULL)
X		str = laststr;
X
X	got_int = FALSE;
X
X	if ((p = ssearch(dir,str)) == NULL) {
X		if (got_int)
X			msg("Interrupt");
X		else
X			msg("Pattern not found");
X
X		got_int = FALSE;
X		return FALSE;
X	} else {
X		LPTR savep;
X
X		cursupdate();
X		/*
X		 * if we're backing up, we make sure the line we're on
X		 * is on the screen.
X		 */
X		setpcmark();
X		*Curschar = savep = *p;
X		set_want_col = TRUE;
X		cursupdate();
X
X		return TRUE;
X	}
X}
X
X#define	OTHERDIR(x)	(((x) == FORWARD) ? BACKWARD : FORWARD)
X
Xbool_t
Xrepsearch(flag)
Xint	flag;
X{
X	int	dir = lastsdir;
X	bool_t	found;
X
X	if ( laststr == NULL ) {
X		beep();
X		return FALSE;
X	}
X
X	found = dosearch(flag ? OTHERDIR(lastsdir) : lastsdir, laststr);
X
X	/*
X	 * We have to save and restore 'lastsdir' because it gets munged
X	 * by ssearch() and winds up saving the wrong direction from here
X	 * if 'flag' is true.
X	 */
X	lastsdir = dir;
X
X	return found;
X}
X
X/*
X * regerror - called by regexp routines when errors are detected.
X */
Xvoid
Xregerror(s)
Xchar	*s;
X{
X	emsg(s);
X}
X
Xstatic LPTR *
Xfwdsearch(str)
Xregister char	*str;
X{
X	static LPTR	infile;
X	register LPTR	*p;
X	regexp	*prog;
X
X	register char	*s;
X	register int	i;
X
X	if ((prog = regcomp(str)) == NULL) {
X		emsg("Invalid search string");
X		return NULL;
X	}
X
X	p = Curschar;
X	i = Curschar->index + 1;
X	do {
X		s = p->linep->s + i;
X
X		if (regexec(prog, s, i == 0)) {		/* got a match */
X			infile.linep = p->linep;
X			infile.index = (int) (prog->startp[0] - p->linep->s);
X			free((char *)prog);
X			return (&infile);
X		}
X		i = 0;
X
X		if (got_int)
X			goto fwdfail;
X
X	} while ((p = nextline(p)) != NULL);
X
X	/*
X	 * If wrapscan isn't set, then don't scan from the beginning
X	 * of the file. Just return failure here.
X	 */
X	if (!P(P_WS))
X		goto fwdfail;
X
X	/* search from the beginning of the file to Curschar */
X	for (p = Filemem; p != NULL ;p = nextline(p)) {
X		s = p->linep->s;
X
X		if (regexec(prog, s, TRUE)) {		/* got a match */
X			infile.linep = p->linep;
X			infile.index = (int) (prog->startp[0] - s);
X			free((char *)prog);
X			return (&infile);
X		}
X
X		if (p->linep == Curschar->linep)
X			break;
X
X		if (got_int)
X			goto fwdfail;
X	}
X
Xfwdfail:
X	free((char *)prog);
X	return NULL;
X}
X
Xstatic LPTR *
Xbcksearch(str)
Xchar	*str;
X{
X	static LPTR	infile;
X	register LPTR	*p = &infile;
X	register char	*s;
X	register int	i;
X	register char	*match;
X	regexp	*prog;
X
X	/* make sure str isn't empty */
X	if (str == NULL || *str == NUL)
X		return NULL;
X
X	if ((prog = regcomp(str)) == NULL) {
X		emsg("Invalid search string");
X		return NULL;
X	}
X
X	*p = *Curschar;
X	if (dec(p) == -1) {	/* already at start of file? */
X		*p = *Fileend;
X		p->index = strlen(p->linep->s) - 1;
X	}
X
X	if (begword)		/* so we don't get stuck on one match */
X		dec(p);
X
X	i = p->index;
X
X	do {
X		s = p->linep->s;
X
X		if (regexec(prog, s, TRUE)) {	/* match somewhere on line */
X
X			/*
X			 * Now, if there are multiple matches on this line,
X			 * we have to get the last one. Or the last one
X			 * before the cursor, if we're on that line.
X			 */
X			match = prog->startp[0];
X
X			while (regexec(prog, prog->endp[0], FALSE)) {
X				if ((i >= 0) && ((prog->startp[0] - s) > i))
X					break;
X				match = prog->startp[0];
X			}
X
X			if ((i >= 0) && ((match - s) > i)) {
X				i = -1;
X				continue;
X			}
X
X			infile.linep = p->linep;
X			infile.index = (int) (match - s);
X			free((char *)prog);
X			return (&infile);
X		}
X		i = -1;
X
X		if (got_int)
X			goto bckfail;
X
X	} while ((p = prevline(p)) != NULL);
X
X	/*
X	 * If wrapscan isn't set, bag the search now
X	 */
X	if (!P(P_WS))
X		goto bckfail;
X
X	/* search backward from the end of the file */
X	p = prevline(Fileend);
X	do {
X		s = p->linep->s;
X
X		if (regexec(prog, s, TRUE)) {	/* match somewhere on line */
X
X			/*
X			 * Now, if there are multiple matches on this line,
X			 * we have to get the last one.
X			 */
X			match = prog->startp[0];
X
X			while (regexec(prog, prog->endp[0], FALSE))
X				match = prog->startp[0];
X
X			infile.linep = p->linep;
X			infile.index = (int) (match - s);
X			free((char *)prog);
X			return (&infile);
X		}
X
X		if (p->linep == Curschar->linep)
X			break;
X
X		if (got_int)
X			goto bckfail;
X
X	} while ((p = prevline(p)) != NULL);
X
Xbckfail:
X	free((char *)prog);
X	return NULL;
X}
X
X/*
X * dosub(lp, up, cmd)
X *
X * Perform a substitution from line 'lp' to line 'up' using the
X * command pointed to by 'cmd' which should be of the form:
X *
X * /pattern/substitution/g
X *
X * The trailing 'g' is optional and, if present, indicates that multiple
X * substitutions should be performed on each line, if applicable.
X * The usual escapes are supported as described in the regexp docs.
X */
Xvoid
Xdosub(lp, up, cmd)
XLPTR	*lp, *up;
Xchar	*cmd;
X{
X	LINE	*cp;
X	char	*pat, *sub;
X	regexp	*prog;
X	int	nsubs;
X	bool_t	do_all;		/* do multiple substitutions per line */
X
X	/*
X	 * If no range was given, do the current line. If only one line
X	 * was given, just do that one.
X	 */
X	if (lp->linep == NULL)
X		*up = *lp = *Curschar;
X	else {
X		if (up->linep == NULL)
X			*up = *lp;
X	}
X
X	pat = ++cmd;		/* skip the initial '/' */
X
X	while (*cmd) {
X		if (*cmd == '\\')	/* next char is quoted */
X			cmd += 2;
X		else if (*cmd == '/') {	/* delimiter */
X			*cmd++ = NUL;
X			break;
X		} else
X			cmd++;		/* regular character */
X	}
X
X	if (*pat == NUL) {
X		emsg("NULL pattern specified");
X		return;
X	}
X
X	sub = cmd;
X
X	do_all = FALSE;
X
X	while (*cmd) {
X		if (*cmd == '\\')	/* next char is quoted */
X			cmd += 2;
X		else if (*cmd == '/') {	/* delimiter */
X			do_all = (cmd[1] == 'g');
X			*cmd++ = NUL;
X			break;
X		} else
X			cmd++;		/* regular character */
X	}
X
X	reg_ic = P(P_IC);	/* set "ignore case" flag appropriately */
X
X	if ((prog = regcomp(pat)) == NULL) {
X		emsg("Invalid search string");
X		return;
X	}
X
X	nsubs = 0;
X
X	for (cp = lp->linep; cp != NULL ;cp = cp->next) {
X		if (regexec(prog, cp->s, TRUE)) { /* a match on this line */
X			char	*ns, *sns, *p;
X
X			/*
X			 * Get some space for a temporary buffer
X			 * to do the substitution into.
X			 */
X			sns = ns = alloc(2048);
X			if (!sns)  return;
X			*sns = NUL;
X
X			p = cp->s;
X
X			do {
X				for (ns = sns; *ns ;ns++)
X					;
X				/*
X				 * copy up to the part that matched
X				 */
X				while (p < prog->startp[0])
X					*ns++ = *p++;
X
X				regsub(prog, sub, ns);
X
X				/*
X				 * continue searching after the match
X				 */
X				p = prog->endp[0];
X
X			} while (regexec(prog, p, FALSE) && do_all);
X
X			for (ns = sns; *ns ;ns++)
X				;
X
X			/*
X			 * copy the rest of the line, that didn't match
X			 */
X			while (*p)
X				*ns++ = *p++;
X
X			*ns = NUL;
X
X			free(cp->s);		/* free the original line */
X			cp->s = strsave(sns);	/* and save the modified str */
X			cp->size = strlen(cp->s) + 1;
X			free(sns);		/* free the temp buffer */
X			nsubs++;
X			CHANGED;
X		}
X		if (cp == up->linep)
X			break;
X	}
X
X	if (nsubs) {
X		updatescreen();
X		if (nsubs >= P(P_RP))
X			smsg("%d substitution%c", nsubs, (nsubs>1) ? 's' : ' ');
X	} else
X		msg("No match");
X
X	free((char *)prog);
X}
X
X/*
X * doglob(cmd)
X *
X * Execute a global command of the form:
X *
X * g/pattern/X
X *
X * where 'x' is a command character, currently one of the following:
X *
X * d	Delete all matching lines
X * p	Print all matching lines
X *
X * The command character (as well as the trailing slash) is optional, and
X * is assumed to be 'p' if missing.
X */
Xvoid
Xdoglob(lp, up, cmd)
XLPTR	*lp, *up;
Xchar	*cmd;
X{
X	LINE	*cp;
X	char	*pat;
X	regexp	*prog;
X	int	ndone;
X	char	cmdchar = NUL;	/* what to do with matching lines */
X
X	/*
X	 * If no range was given, do every line. If only one line
X	 * was given, just do that one.
X	 */
X	if (lp->linep == NULL) {
X		*lp = *Filemem;
X		*up = *Fileend;
X	} else {
X		if (up->linep == NULL)
X			*up = *lp;
X	}
X
X	pat = ++cmd;		/* skip the initial '/' */
X
X	while (*cmd) {
X		if (*cmd == '\\')	/* next char is quoted */
X			cmd += 2;
X		else if (*cmd == '/') {	/* delimiter */
X			cmdchar = cmd[1];
X			*cmd++ = NUL;
X			break;
X		} else
X			cmd++;		/* regular character */
X	}
X	if (cmdchar == NUL)
X		cmdchar = 'p';
X
X	reg_ic = P(P_IC);	/* set "ignore case" flag appropriately */
X
X	if (cmdchar != 'd' && cmdchar != 'p') {
X		emsg("Invalid command character");
X		return;
X	}
X
X	if ((prog = regcomp(pat)) == NULL) {
X		emsg("Invalid search string");
X		return;
X	}
X
X	msg("");
X	ndone = 0;
X	got_int = FALSE;
X
X	for (cp = lp->linep; cp != NULL && !got_int ;cp = cp->next) {
X		if (regexec(prog, cp->s, TRUE)) { /* a match on this line */
X			switch (cmdchar) {
X
X			case 'd':		/* delete the line */
X				if (Curschar->linep != cp) {
X					LPTR	savep;
X
X					savep = *Curschar;
X					Curschar->linep = cp;
X					Curschar->index = 0;
X					delline(1, FALSE);
X					*Curschar = savep;
X				} else
X					delline(1, FALSE);
X				break;
X
X			case 'p':		/* print the line */
X				prt_line(cp->s);
X				outstr("\r\n");
X				break;
X			}
X			ndone++;
X		}
X		if (cp == up->linep)
X			break;
X	}
X
X	if (ndone) {
X		switch (cmdchar) {
X
X		case 'd':
X			updatescreen();
X			if (ndone >= P(P_RP) || got_int)
X				smsg("%s%d fewer line%c",
X					got_int ? "Interrupt: " : "",
X					ndone,
X					(ndone > 1) ? 's' : ' ');
X			break;
X
X		case 'p':
X			wait_return();
X			break;
X		}
X	} else {
X		if (got_int)
X			msg("Interrupt");
X		else
X			msg("No match");
X	}
X
X	got_int = FALSE;
X	free((char *)prog);
X}
X
X/*
X * Character Searches
X */
X
Xstatic char lastc = NUL;	/* last character searched for */
Xstatic int  lastcdir;		/* last direction of character search */
Xstatic int  lastctype;		/* last type of search ("find" or "to") */
X
X/*
X * searchc(c, dir, type)
X *
X * Search for character 'c', in direction 'dir'. If type is 0, move to
X * the position of the character, otherwise move to just before the char.
X */
Xbool_t
Xsearchc(c, dir, type)
Xchar	c;
Xint	dir;
Xint	type;
X{
X	LPTR	save;
X
X	save = *Curschar;	/* save position in case we fail */
X	lastc = c;
X	lastcdir = dir;
X	lastctype = type;
X
X	/*
X	 * On 'to' searches, skip one to start with so we can repeat
X	 * searches in the same direction and have it work right.
X	 */
X	if (type)
X		(dir == FORWARD) ? oneright() : oneleft();
X
X	while ( (dir == FORWARD) ? oneright() : oneleft() ) {
X		if (gchar(Curschar) == c) {
X			if (type)
X				(dir == FORWARD) ? oneleft() : oneright();
X			return TRUE;
X		}
X	}
X	*Curschar = save;
X	return FALSE;
X}
X
Xbool_t
Xcrepsearch(flag)
Xint	flag;
X{
X	int	dir = lastcdir;
X	int	rval;
X
X	if (lastc == NUL)
X		return FALSE;
X
X	rval = searchc(lastc, flag ? OTHERDIR(lastcdir) : lastcdir, lastctype);
X
X	lastcdir = dir;		/* restore dir., since it may have changed */
X
X	return rval;
X}
X
X/*
X * "Other" Searches
X */
X
X/*
X * showmatch - move the cursor to the matching paren or brace
X */
XLPTR *
Xshowmatch()
X{
X	static	LPTR	pos;
X	int	(*move)(), inc(), dec();
X	char	initc = gchar(Curschar);	/* initial char */
X	char	findc;				/* terminating char */
X	char	c;
X	int	count = 0;
X
X	pos = *Curschar;		/* set starting point */
X
X	switch (initc) {
X
X	case '(':
X		findc = ')';
X		move = inc;
X		break;
X	case ')':
X		findc = '(';
X		move = dec;
X		break;
X	case '{':
X		findc = '}';
X		move = inc;
X		break;
X	case '}':
X		findc = '{';
X		move = dec;
X		break;
X	case '[':
X		findc = ']';
X		move = inc;
X		break;
X	case ']':
X		findc = '[';
X		move = dec;
X		break;
X	default:
X		return (LPTR *) NULL;
X	}
X
X	while ((*move)(&pos) != -1) {		/* until end of file */
X		c = gchar(&pos);
X		if (c == initc)
X			count++;
X		else if (c == findc) {
X			if (count == 0)
X				return &pos;
X			count--;
X		}
X	}
X	return (LPTR *) NULL;			/* never found it */
X}
X
X
X/*
X * The following routines do the word searches performed by the
X * 'w', 'W', 'b', 'B', 'e', and 'E' commands.
X */
X
X/*
X * To perform these searches, characters are placed into one of three
X * classes, and transitions between classes determine word boundaries.
X *
X * The classes are:
X *
X * 0 - white space
X * 1 - letters, digits, and underscore
X * 2 - everything else
X */
X
Xstatic	int	stype;		/* type of the word motion being performed */
X
X#define	C0(c)	(((c) == ' ') || ((c) == '\t') || ((c) == NUL))
X#define	C1(c)	(isalpha(c) || isdigit(c) || ((c) == '_'))
X
X/*
X * cls(c) - returns the class of character 'c'
X *
X * The 'type' of the current search modifies the classes of characters
X * if a 'W', 'B', or 'E' motion is being done. In this case, chars. from
X * class 2 are reported as class 1 since only white space boundaries are
X * of interest.
X */
Xstatic	int
Xcls(c)
Xchar	c;
X{
X	if (C0(c))
X		return 0;
X
X	if (C1(c))
X		return 1;
X
X	/*
X	 * If stype is non-zero, report these as class 1.
X	 */
X	return (stype == 0) ? 2 : 1;
X}
X
X
X/*
X * fwd_word(pos, type) - move forward one word
X *
X * Returns the resulting position, or NULL if EOF was reached.
X */
XLPTR *
Xfwd_word(p, type)
XLPTR	*p;
Xint	type;
X{
X	static	LPTR	pos;
X	int	sclass = cls(gchar(p));		/* starting class */
X
X	pos = *p;
X
X	stype = type;
X
X	/*
X	 * We always move at least one character.
X	 */
X	if (inc(&pos) == -1)
X		return NULL;
X
X	if (sclass != 0) {
X		while (cls(gchar(&pos)) == sclass) {
X			if (inc(&pos) == -1)
X				return NULL;
X		}
X		/*
X		 * If we went from 1 -> 2 or 2 -> 1, return here.
X		 */
X		if (cls(gchar(&pos)) != 0)
X			return &pos;
X	}
X
X	/* We're in white space; go to next non-white */
X
X	while (cls(gchar(&pos)) == 0) {
X		/*
X		 * We'll stop if we land on a blank line
X		 */
X		if (pos.index == 0 && pos.linep->s[0] == NUL)
X			break;
X
X		if (inc(&pos) == -1)
X			return NULL;
X	}
X
X	return &pos;
X}
X
X/*
X * bck_word(pos, type) - move backward one word
X *
X * Returns the resulting position, or NULL if EOF was reached.
X */
XLPTR *
Xbck_word(p, type)
XLPTR	*p;
Xint	type;
X{
X	static	LPTR	pos;
X	int	sclass = cls(gchar(p));		/* starting class */
X
X	pos = *p;
X
X	stype = type;
X
X	if (dec(&pos) == -1)
X		return NULL;
X
X	/*
X	 * If we're in the middle of a word, we just have to
X	 * back up to the start of it.
X	 */
X	if (cls(gchar(&pos)) == sclass && sclass != 0) {
X		/*
X		 * Move backward to start of the current word
X		 */
X		while (cls(gchar(&pos)) == sclass) {
X			if (dec(&pos) == -1)
X				return NULL;
X		}
X		inc(&pos);			/* overshot - forward one */
X		return &pos;
X	}
X
X	/*
X	 * We were at the start of a word. Go back to the start
X	 * of the prior word.
X	 */
X
X	while (cls(gchar(&pos)) == 0) {		/* skip any white space */
X		/*
X		 * We'll stop if we land on a blank line
X		 */
X		if (pos.index == 0 && pos.linep->s[0] == NUL)
X			return &pos;
X
X		if (dec(&pos) == -1)
X			return NULL;
X	}
X
X	sclass = cls(gchar(&pos));
X
X	/*
X	 * Move backward to start of this word.
X	 */
X	while (cls(gchar(&pos)) == sclass) {
X		if (dec(&pos) == -1)
X			return NULL;
X	}
X	inc(&pos);			/* overshot - forward one */
X
X	return &pos;
X}
X
X/*
X * end_word(pos, type, in_change) - move to the end of the word
X *
X * There is an apparent bug in the 'e' motion of the real vi. At least
X * on the System V Release 3 version for the 80386. Unlike 'b' and 'w',
X * the 'e' motion crosses blank lines. When the real vi crosses a blank
X * line in an 'e' motion, the cursor is placed on the FIRST character
X * of the next non-blank line. The 'E' command, however, works correctly.
X * Since this appears to be a bug, I have not duplicated it here.
X *
X * There's a strange special case here that the 'in_change' parameter
X * helps us deal with. Vi effectively turns 'cw' into 'ce'. If we're on
X * a word with only one character, we need to stick at the current
X * position so we don't change two words.
X *
X * Returns the resulting position, or NULL if EOF was reached.
X */
XLPTR *
Xend_word(p, type, in_change)
XLPTR	*p;
Xint	type;
Xbool_t	in_change;
X{
X	static	LPTR	pos;
X	int	sclass = cls(gchar(p));		/* starting class */
X
X	pos = *p;
X
X	stype = type;
X
X	if (inc(&pos) == -1)
X		return NULL;
X
X	/*
X	 * If we're in the middle of a word, we just have to
X	 * move to the end of it.
X	 */
X	if (cls(gchar(&pos)) == sclass && sclass != 0) {
X		/*
X		 * Move forward to end of the current word
X		 */
X		while (cls(gchar(&pos)) == sclass) {
X			if (inc(&pos) == -1)
X				return NULL;
X		}
X		dec(&pos);			/* overshot - forward one */
X		return &pos;
X	}
X
X	/*
X	 * We were at the end of a word. Go to the end of the next
X	 * word, unless we're doing a change. In that case we stick
X	 * at the end of the current word.
X	 */
X	if (in_change)
X		return p;
X
X	while (cls(gchar(&pos)) == 0) {		/* skip any white space */
X		if (inc(&pos) == -1)
X			return NULL;
X	}
X
X	sclass = cls(gchar(&pos));
X
X	/*
X	 * Move forward to end of this word.
X	 */
X	while (cls(gchar(&pos)) == sclass) {
X		if (inc(&pos) == -1)
X			return NULL;
X	}
X	dec(&pos);			/* overshot - forward one */
X
X	return &pos;
X}
!EOR!
echo extracting - sentence.c
sed 's/^X//' > sentence.c << '!EOR!'
X/*	Find the NEXT/PREVIOUS:
X *	- SENTENCE	findsent (dir)
X *	- PARAGRAPH	findpara (dir)
X *	- FUNCTION	findfunc (dir)
X *
X *	I've split these off from SEARCH.C, because they're alike and
X *	SEARCH.C is a big file already.  findfunc() was already there.
X *	I added findsent() and findpara().  -  Dave Tutelman
X */
X
X#include "stevie.h"
X#include "ops.h"
X
X/* We'll be doing some classification of input characters, into: */
X#define	BLANK	0	/* Whitespace */
X#define	DOT	1	/* Period, exclamation, q-mark */
X#define	EOL	2	/* End-of-line */
X#define	OTHER	3	/* Any other non-blank stuff */
X
Xextern	int	operator;	/* From normal.c, is there an operator
X				 * pending?
X				 */
X
Xint
Xinclass (c)
X  char c;
X{
X	switch (c) {
X	  case ' ':
X	  case '\t':
X		return BLANK;
X	  case '.':
X	  case '!':
X	  case '?':
X		return DOT;
X	  case '\n':
X	  case '\r':
X	  case '\0':
X		return EOL;
X	  default:
X		if (c<' ' || c>'~')	return EOL;
X		else			return OTHER;
X	}
X}
X
X/* We'll also need to (1) tell if a line is just blanks, and
X *                    (2) skip to the next OTHER character.
X * Here are a couple of functions to do it.
X */
X
Xbool_t
Xblankline (line)
X  LPTR *line;
X{
X	char	*p;
X	int	class;
X
X	if (! line)	return TRUE;
X	for (p = line->linep->s; (class=inclass(*p))!=EOL; p++)
X		if (class!=BLANK)	return FALSE;
X	return TRUE;
X}
X
X
XLPTR *
Xskiptotext (lp, dir)
X  LPTR *lp;
X  int  dir;
X{
X	LPTR *lpp;
X
X	lpp = lp;
X	while (inclass( CHAR( lpp )) != OTHER) {
X		lpp = (dir==FORWARD) ? nextchar (lpp) : prevchar (lpp);
X		if (!lpp) return (lp);		/* hit the end */
X	}
X	return (lpp);
X}
X
X
X/*
X * findsent (dir) - Find the next sentence in direction 'dir'
X *
X * Return TRUE if a sentence was found.
X *
X * Algorithm: found end of a sentence if:
X *   FWD - current char is BLANK | EOL and last is DOT.
X *   BKWD- current char is DOT and last is BLANK | EOL.
X * In either case, we then have to skip to text at beginning of next sentence.
X *
X */
Xbool_t
Xfindsent (dir)
Xint	dir;
X{
X	LPTR	*curr, *last;	/* LPTR for current & last characters */
X	int	ccurr, clast;	/* class of curr and last characters */
X	int	oldindex;	/* need to keep in case search fails */
X
X	curr  = Curschar;
X	oldindex = curr->index;
X	/* Get INTO most recent sentence sentence. */
X	if (dir==BACKWARD)
X		curr = prevchar (curr);
X	curr = skiptotext (curr, BACKWARD);
X	ccurr = OTHER;
X
X
X	do {
X		/* Take a step */
X		last = curr; clast = ccurr;
X
X		curr = (dir == FORWARD) ? nextchar(curr) : prevchar(curr);
X		ccurr = inclass (CHAR( curr ));
X
X		/* Test halting condition */
X		if (dir==FORWARD &&
X		    (ccurr==BLANK || ccurr==EOL) && clast==DOT) {
X			setpcmark();
X			last = skiptotext (last, FORWARD);
X			*Curschar = *last;
X			return TRUE;
X		}
X		else if (dir==BACKWARD &&
X		     ccurr==DOT && (clast==BLANK || clast==EOL)) {
X			setpcmark();
X			last = skiptotext (last, FORWARD);
X			*Curschar = *last;
X			return TRUE;
X		}
X	} while (curr != NULL);
X
X	Curschar->index = oldindex;	/* restore if search failed */
X	return FALSE;
X}
X
X
X/*
X * findpara(dir) - Find the next paragraph in direction 'dir'
X *
X * Return TRUE if a paragraph was found.
X *
X * Algorithm: found beginning of paragraph if:
X *   FWD - current line is non-blank and last is blank.
X *   BKWD- current line is blank and last is non-blank.
X * Then we skip to the first non-blank, non-dot text.
X *
X */
Xbool_t
Xfindpara(dir)
Xint	dir;
X{
X	LPTR	*curr, *last;	/* current & last lines */
X	LPTR	*marker;	/* end of current para */
X	bool_t	bcurr, blast;	/* "blankness" value for lines */
X
X	curr  = Curschar;
X	bcurr = (dir==FORWARD) ? FALSE : TRUE;	/* keeps us from passing the
X						 * text initially. */
X
X	do {
X		/* Take a step */
X		last = curr; blast = bcurr;
X		curr = (dir == FORWARD) ? nextline(curr) : prevline(curr);
X		bcurr = blankline (curr);
X
X		/* Test halting condition */
X		if (dir==FORWARD && bcurr && !blast) {
X			setpcmark();
X			curr = skiptotext (curr, FORWARD);
X			*Curschar = *curr;
X			return TRUE;
X		}
X		else if (dir==BACKWARD && bcurr && !blast) {
X			setpcmark();
X			last = skiptotext (last, FORWARD);
X			*Curschar = *last;
X			return TRUE;
X		}
X	} while (curr != NULL);
X
X	return FALSE;
X}
X
X
X/*
X * findfunc(dir) - Find the next function in direction 'dir'
X *
X * Return TRUE if a function was found.
X *
X * Algorithm depends on a style of C coding in which the ONLY '{'
X * in the first column occurs at the beginning of a function definition.
X * This is a good and common style, but not syntactically required by C.
X */
Xbool_t
Xfindfunc(dir)
Xint	dir;
X{
X	LPTR	*curr;
X
X	curr = Curschar;
X
X	do {
X		curr = (dir == FORWARD) ? nextline(curr) : prevline(curr);
X
X		if (curr != NULL && curr->linep->s[0] == '{') {
X			setpcmark();
X			*Curschar = *curr;
X			return TRUE;
X		}
X	} while (curr != NULL);
X
X	return FALSE;
X}
X
!EOR!
echo extracting - tagcmd.c
sed 's/^X//' > tagcmd.c << '!EOR!'
X/* #Header: /?????
X *
X * Routines to implement tags, and, especially, tag stacking.
X * Added by Dave Tutelman - 12/89.
X * The dotag() routine is a modification of the posted
X * version by Tony Andrews.
X * The untag() routine is new.
X */
X
X#include "stevie.h"
X
X#define	LSIZE	256	/* max. size of a line in the tags file */
X
X#ifdef TAGSTACK
X/*  We build a stack of file records, on which we push info about
X *  current file when dotag() is called.
X */
X#define	TAGSTACKSIZE	12		/* how many tag calls can we stack? */
Xstatic	struct filerecord {
X		char	*name;		/* file name pointer */
X		int	linenum;	/* line number when we left */
X} tagstack [TAGSTACKSIZE];
Xstatic	int	stackindex = 0;		/* here's how we keep track */
X#endif
X
Xextern	char	**files;
Xextern	int	curfile;
Xstatic	void	pushtags(), poptags();
X
X
X/*
X * dotag(tag, force) - goto tag.  If force=TRUE, dump pending changes.
X */
Xvoid
Xdotag(tag, force)
Xchar	*tag;
Xbool_t	force;
X{
X	FILE	*tp, *fopen();
X	char	lbuf[LSIZE];		/* line buffer */
X	char	pbuf[LSIZE];		/* search pattern buffer */
X	register char	*fname, *str;
X	register char	*p;
X
X	if ((tp = fopen("tags", "r")) == NULL) {
X		emsg("Can't open tags file");
X		return;
X	}
X
X	while (fgets(lbuf, LSIZE, tp) != NULL) {
X	
X		if ((fname = strchr(lbuf, TAB)) == NULL) {
X			emsg("Format error in tags file");
X			return;
X		}
X		*fname++ = '\0';
X		if ((str = strchr(fname, TAB)) == NULL) {
X			emsg("Format error in tags file");
X			return;
X		}
X		*str++ = '\0';
X
X		if (strcmp(lbuf, tag) == 0) {
X
X			/*
X			 * Scan through the search string. If we see a magic
X			 * char, we have to quote it. This lets us use "real"
X			 * implementations of ctags.
X			 */
X			p = pbuf;
X			*p++ = *str++;		/* copy the '/' or '?' */
X			*p++ = *str++;		/* copy the '^' */
X
X			for (; *str != NUL ;str++) {
X				if (*str == '\\') {
X					*p++ = *str++;
X					*p++ = *str;
X				} else if (strchr("/?", *str) != NULL) {
X					if (str[1] != '\n') {
X						*p++ = '\\';
X						*p++ = *str;
X					} else
X						*p++ = *str;
X				} else if (strchr("^()*.", *str) != NULL) {
X					*p++ = '\\';
X					*p++ = *str;
X				} else
X					*p++ = *str;
X			}
X			*p = NUL;
X
X#ifdef TAGSTACK
X			/* Push current position onto stack, unless this
X			 * is a startup call using '-t' option. */
X			if (Filename!=NULL && *Filename!='\0')
X				pushtags ();
X#endif
X
X			/*
X			 * This looks out of order, but by calling stuffin()
X			 * before doecmd() we keep an extra screen update
X			 * from occuring. This stuffins() have no effect
X			 * until we get back to the main loop, anyway.
X			 */
X
X			stuffin(pbuf);		/* str has \n at end */
X			stuffin("\007");	/* CTRL('g') */
X
X			if (doecmd(fname, force)) {
X				fclose(tp);
X				return;
X			} else {
X#ifdef TAGSTACK
X				poptags ();	/* cancel stack entry */
X#endif
X				stuffin(NULL);	/* clear the input */
X			}
X		}
X	}
X	emsg("tag not found");
X	fclose(tp);
X}
X
X/*
X * dountag (spec) - undo the last ':ta' command, popping the tag stack.
X *	spec is the appended character, giving specifics:
X *	  '!'	dump pending changes.
X *	  'e'	came from K_CCIRCM "shortcut".  do :e# if stack empty.
X *	  ' '	do normal untag.
X *	  else	bad command.
X */
X
Xvoid
Xdountag (spec)
X  char spec;
X{
X#ifndef TAGSTACK
X	badcmd();	/* complain & return */
X}
X#else
X
X	char	force=0, shortcut=0;
X	char	*newfile;
X	char	buf [LSIZE];
X
X	switch (spec) {
X	  case '!':
X		force++;
X		break;
X	  case 'e':
X		shortcut++;
X		break;
X	  case ' ':
X	  case '\n':
X	  case '\r':
X	  case '\0':
X		break;
X	  default:
X		badcmd();
X		return;
X	}
X
X	/* Check the stack.  If empty, don't pop */
X	if (!stackindex) {
X		if (shortcut)		/* just edit altfile */
X			stuffin(":e #\n");
X		else
X			emsg("Tags stack empty");
X		return;
X	}
X
X	/* Get the top of the stack, and do the implied edit.
X	 * If it succeeds, switch; if not, back off */
X	newfile = tagstack [stackindex-1].name;
X	if (doecmd (newfile, force)) {
X		sprintf (buf, "%dG:f\n", tagstack [stackindex-1].linenum);
X		stuffin(buf);
X		poptags ();
X		return;
X	}
X	else
X		stuffin(NULL);
X}
X
X/*
X * pushtags () - push the current state onto the tagstack.
X */
X
Xstatic void
Xpushtags ()
X{
X	int	i;
X
X	/* If stack full, throw away oldest and push the rest.
X	 * This is clearly the best and most transparent way to behave,
X	 * much preferable to either complaining or losing new entry.
X	 */
X	if (stackindex >= TAGSTACKSIZE) {
X		for (i=0; i<TAGSTACKSIZE-1; i++) {
X			tagstack[i].name    = tagstack[i+1].name;
X			tagstack[i].linenum = tagstack[i+1].linenum;
X		}
X		stackindex--;
X	}
X
X	/* Get current state, and put it in stack.
X	 * Right now, the state is file name & line number.
X	 * This is less than perfect, in that line numbers may change if
X	 * you edit elsewhere in the file.  Eventually, I'd like to base
X	 * it on "hidden" marks, if I can implement them.  DMT
X	 */
X	tagstack [stackindex].name    = strsave (Filename);
X	tagstack [stackindex].linenum = cntllines (Filemem, Curschar);
X	stackindex++;
X}
X
X/*
X * poptags () - pop the tag stack.
X */
X
Xstatic void
Xpoptags ()
X{
X	if (!stackindex) {
X		emsg("Tags stack empty");
X		return;
X	}
X
X	stackindex--;
X	free (tagstack [stackindex].name);
X}
X
X#endif
X
X
!EOR!
echo extracting - term.c
sed 's/^X//' > term.c << '!EOR!'
X/* $Header: /nw/tony/src/stevie/src/RCS/term.c,v 1.4 89/03/11 22:43:55 tony Exp $
X *
X * Termcap initialization (optional).
X */
X
X#include <stdio.h>
X#include "stevie.h"
X
X#ifdef	TERMCAP
X
Xstatic	char	buf[1024];	/* termcap entry read here */
Xstatic	char	cap[256];	/* capability strings go in here */
X
Xchar	*T_EL;		/* erase the entire current line */
Xchar	*T_IL;		/* insert one line */
Xchar	*T_DL;		/* delete one line */
Xchar	*T_SC;		/* save the cursor position */
Xchar	*T_ED;		/* erase display (may optionally home cursor) */
Xchar	*T_RC;		/* restore the cursor position */
Xchar	*T_CI;		/* invisible cursor (very optional) */
Xchar	*T_CV;		/* visible cursor (very optional) */
X
Xchar	*T_CM;		/* cursor motion string */
X
Xextern	int	tgetent(), tgetnum();
Xextern	char	*tgetstr();
Xextern	char	*getenv();
X
Xint
Xt_init()
X{
X	char	*term;
X	int	n;
X	char	*cp = cap;
X
X	if ((term = getenv("TERM")) == NULL)
X		return 0;
X
X	if (tgetent(buf, term) != 1)
X		return 0;
X
X	if ((n = tgetnum("li")) == -1)
X		return 0;
X	else
X		P(P_LI) = Rows = n;
X
X	if ((n = tgetnum("co")) == -1)
X		return 0;
X	else
X		Columns = n;
X
X	/*
X	 * Get mandatory capability strings.
X	 */
X	if ((T_CM = tgetstr("cm", &cp)) == NULL)
X		return 0;
X
X	if ((T_EL = tgetstr("ce", &cp)) == NULL)
X		return 0;
X
X	if ((T_ED = tgetstr("cl", &cp)) == NULL)
X		return 0;
X
X	/*
X	 * Optional capabilities.
X	 */
X	if ((T_IL = tgetstr("al", &cp)) == NULL)
X		T_IL = "";
X
X	if ((T_DL = tgetstr("dl", &cp)) == NULL)
X		T_DL = "";
X
X	if ((T_SC = tgetstr("sc", &cp)) == NULL)
X		T_SC = "";
X
X	if ((T_RC = tgetstr("rc", &cp)) == NULL)
X		T_RC = "";
X
X	if ((T_CI = tgetstr("vi", &cp)) == NULL)
X		T_CI = "";
X
X	if ((T_CV = tgetstr("ve", &cp)) == NULL)
X		T_CV = "";
X
X	return 1;
X}
X
X#endif
!EOR!
echo extracting - undo.c
sed 's/^X//' > undo.c << '!EOR!'
X/* $Header: /nw/tony/src/stevie/src/RCS/undo.c,v 1.7 89/08/06 09:51:06 tony Exp $
X *
X * Undo facility
X *
X * The routines in this file comprise a general undo facility for use
X * throughout the rest of the editor. The routine u_save() is called
X * before each edit operation to save the current contents of the lines
X * to be editted. Later, u_undo() can be called to return those lines
X * to their original state. The routine u_clear() should be called
X * whenever a new file is going to be editted to clear the undo buffer.
X */
X
X#include "stevie.h"
X
X/*
X * The next two variables mark the boundaries of the changed section
X * of the file. Lines BETWEEN the lower and upper bounds are changed
X * and originally contained the lines pointed to by u_lines. To undo
X * the last change, insert the lines in u_lines between the lower and
X * upper bounds.
X */
Xstatic	LINE	*u_lbound = NULL; /* line just prior to first changed line */
Xstatic	LINE	*u_ubound = NULL; /* line just after the last changed line */
X
Xstatic	LINE	*u_lline  = NULL; /* bounds of the saved lines */
Xstatic	LINE	*u_uline  = NULL;
X
Xstatic	int	u_col;
Xstatic	bool_t	u_valid = FALSE;  /* is the undo buffer valid */
X
X/*
X * Local forward declarations
X */
Xstatic	LINE	*copyline();
Xstatic	void	u_lsave();
Xstatic	void	u_lfree();
X
X/*
X * u_save(l, u) - save the current contents of part of the file
X *
X * The lines between 'l' and 'u' are about to be changed. This routine
X * saves their current contents into the undo buffer. The range l to u
X * is not inclusive because when we do an open, for example, there aren't
X * any lines in between. If no lines are to be saved, then l->next == u.
X */
Xvoid
Xu_save(l, u)
XLINE	*l, *u;
X{
X	LINE	*nl;			/* copy of the current line */
X
X	/*
X	 * If l or u is null, there's an error. We don't return an
X	 * indication to the caller. They should find the problem
X	 * while trying to perform whatever edit is being requested
X	 * (e.g. a join on the last line).
X	 */
X	if (l == NULL || u == NULL)
X		return;
X
X	u_clear();			/* clear the buffer, first */
X
X	u_lsave(l, u);		/* save to the "line undo" buffer, if needed */
X
X	u_lbound = l;
X	u_ubound = u;
X
X	if (l->next != u) {		/* there are lines in the middle */
X		l = l->next;
X		u = u->prev;
X
X		u_lline = nl = copyline(l);	/* copy the first line */
X		while (l != u) {
X			nl->next = copyline(l->next);
X			nl->next->prev = nl;
X			l = l->next;
X			nl = nl->next;
X		}
X		u_uline = nl;
X	} else
X		u_lline = u_uline = NULL;
X
X	u_valid = TRUE;
X	u_col = Cursvcol;
X}
X
X/*
X * u_saveline() - save the current line in the undo buffer
X */
Xvoid
Xu_saveline()
X{
X	u_save(Curschar->linep->prev, Curschar->linep->next);
X}
X
X/*
X * u_undo() - effect an 'undo' operation
X *
X * The last edit is undone by restoring the modified section of the file
X * to its original state. The lines we're going to trash are copied to
X * the undo buffer so that even an 'undo' can be undone. Rings the bell
X * if the undo buffer is empty.
X */
Xvoid
Xu_undo()
X{
X	LINE	*tl, *tu;
X
X	if (!u_valid) {
X		beep();
X		return;
X	}
X
X	/*
X	 * Get the first line of the thing we're undoing on the screen.
X	 */
X	Curschar->linep = u_lbound->next;
X	Curschar->index = 0;			/* for now */
X	if (Curschar->linep == Fileend->linep)
X		Curschar->linep = Curschar->linep->prev;
X	cursupdate();
X
X	/*
X	 * Save pointers to what's in the file now.
X	 */
X	if (u_lbound->next != u_ubound) {	/* there are lines to get */
X		tl = u_lbound->next;
X		tu = u_ubound->prev;
X		tl->prev = NULL;
X		tu->next = NULL;
X	} else
X		tl = tu = NULL;			/* no lines between bounds */
X
X	/*
X	 * Link the undo buffer into the right place in the file.
X	 */
X	if (u_lline != NULL) {		/* there are lines in the undo buf */
X
X		/*
X		 * If the top line of the screen is being undone, we need to
X		 * fix up Topchar to point to the new line that will be there.
X		 */
X		if (u_lbound->next == Topchar->linep)
X			Topchar->linep = u_lline;
X
X		u_lbound->next = u_lline;
X		u_lline->prev  = u_lbound;
X		u_ubound->prev = u_uline;
X		u_uline->next  = u_ubound;
X	} else {			/* no lines... link the bounds */
X		if (u_lbound->next == Topchar->linep)
X			Topchar->linep = u_ubound;
X		if (u_lbound == Filetop->linep)
X			Topchar->linep = u_ubound;
X			
X		u_lbound->next = u_ubound;
X		u_ubound->prev = u_lbound;
X	}
X
X	/*
X	 * If we swapped the top line, patch up Filemem appropriately.
X	 */
X	if (u_lbound == Filetop->linep)
X		Filemem->linep = Filetop->linep->next;
X
X	/*
X	 * Now save the old stuff in the undo buffer.
X	 */
X	u_lline = tl;
X	u_uline = tu;
X
X	renum();		/* have to renumber everything */
X
X	/*
X	 * Put the cursor on the first line of the 'undo' region.
X	 */
X	Curschar->linep = u_lbound->next;
X	Curschar->index = 0;
X	if (Curschar->linep == Fileend->linep)
X		Curschar->linep = Curschar->linep->prev;
X	*Curschar = *coladvance(Curschar, u_col);
X	cursupdate();
X	updatescreen();		/* now show the change */
X
X	u_lfree();		/* clear the "line undo" buffer */
X}
X
X/*
X * u_clear() - clear the undo buffer
X *
X * This routine is called to clear the undo buffer at times when the
X * pointers are about to become invalid, such as when a new file is
X * about to be editted.
X */
Xvoid
Xu_clear()
X{
X	LINE	*l, *nextl;
X
X	if (!u_valid)		/* nothing to do */
X		return;
X
X	for (l = u_lline; l != NULL ;l = nextl) {
X		nextl = l->next;
X		free(l->s);
X		free((char *)l);
X	}
X
X	u_lbound = u_ubound = u_lline = u_uline = NULL;
X	u_valid = FALSE;
X}
X
X/*
X * The following functions and data implement the "line undo" feature
X * performed by the 'U' command.
X */
X
Xstatic	LINE	*u_line;		/* pointer to the line we last saved */
Xstatic	LINE	*u_lcopy = NULL;	/* local copy of the original line */
X
X/*
X * u_lfree() - free the line save buffer
X */
Xstatic	void
Xu_lfree()
X{
X	if (u_lcopy != NULL) {
X		free(u_lcopy->s);
X		free((char *)u_lcopy);
X		u_lcopy = NULL;
X	}
X	u_line = NULL;
X}
X
X/*
X * u_lsave() - save the current line if necessary
X */
Xstatic	void
Xu_lsave(l, u)
XLINE	*l, *u;
X{
X
X	if (l->next != u->prev) {	/* not changing exactly one line */
X		u_lfree();
X		return;
X	}
X
X	if (l->next == u_line)		/* more edits on the same line */
X		return;
X
X	u_lfree();
X	u_line = l->next;
X	u_lcopy = copyline(l->next);
X}
X
X/*
X * u_lundo() - undo the current line (the 'U' command)
X */
Xvoid
Xu_lundo()
X{
X	if (u_lcopy != NULL) {
X		free(Curschar->linep->s);
X		Curschar->linep->s = u_lcopy->s;
X		Curschar->linep->size = u_lcopy->size;
X		free((char *)u_lcopy);
X	} else
X		beep();
X	Curschar->index = 0;
X
X	cursupdate();
X	updatescreen();		/* now show the change */
X
X	u_lcopy = NULL;	/* can't undo this kind of undo */
X	u_line = NULL;
X}
X
X/*
X * u_lcheck() - clear the "line undo" buffer if we've moved to a new line
X */
Xvoid
Xu_lcheck()
X{
X	if (Curschar->linep != u_line)
X		u_lfree();
X}
X
X/*
X * copyline(l) - copy the given line, and return a pointer to the copy
X */
Xstatic LINE *
Xcopyline(l)
XLINE	*l;
X{
X	LINE	*nl;		/* the new line */
X
X	nl = newline(strlen(l->s));
X	strcpy(nl->s, l->s);
X
X	return nl;
X}
!EOR!
echo extracting - version.c
sed 's/^X//' > version.c << '!EOR!'
Xstatic	char	RCSid[] =
X"$Header: /nw/tony/src/stevie/src/RCS/version.c,v 3.69 89/08/13 11:41:58 tony Exp $";
X
X/*
X * Contains the declaration of the global version number variable.
X *
X * $Log:	version.c,v $
X *
X * Revision 3.69a  90/02/04            dave
X * A couple of minor bugfixes, and a whole load of enhancements:
X *   -	Added tag-stacking.
X *   -	Added searches for sentences and paragraphs.
X *   -	Enhancements to "help" command:
X *	 . Navigation of help screens, not just next-screen.
X *	 . Index of help screens.
X *	 . Screen for MSDOS-specific features.
X *	 . Screen for :set commands.
X *	 . A little context-sensitivity.
X *   -	File mode is preserved (it wasn't in previous versions).
X *   -	UNIX-like specifications of environments variables are expanded
X *	in file names.  (E.g.- you can say  :e $HOME/autoexec.bat)
X *   -	PC keys for Insert & Delete now work.
X *   -	Ported to Turbo C for the PC.
X *   -	Integrated Larry Shurr's BIOS option for the PC.
X *   -	Added color and multi-line options for the PC, and hooks for them
X *	in other systems.
X *   -	Made "ctags" a little more general.
X *
X * Revision 3.69  89/08/13  11:41:58  tony
X * Fixed a bug that caused messages from fileinfo() (in misccmds.c) to get
X * screwed up. The routine smsg() which uses the kludge approach to varargs
X * didn't have enough parameters for some of the calls made to it.
X *
X * Revision 3.68  89/08/06  09:51:20  tony
X * Misc. minor changes to make lint happier before posting to USENET.
X * 
X * Revision 3.67  89/08/03  13:08:52  tony
X * There was some code in ops.c that was duplicating the function of the
X * getcmdln() routine in cmdline.c. I modified getcmdln() to be slightly
X * more general, and changed dofilter() in ops.c to use it.
X * 
X * Revision 3.66  89/08/02  20:00:12  tony
X * Fixed some problems with mode lines. There were still extra screen
X * redraws that needed to be avoided. There was also a problem involving
X * nested calls to docmdln() that can occur when mode lines are used.
X * 
X * Revision 3.65  89/08/02  15:50:03  tony
X * Finally got around to providing full support for the "change" operator.
X * Multi-line changes (like "cL" or "3cc") now work correctly. Also fixed
X * a small problem with multi-line character-oriented deletes leaving the
X * cursor in the wrong location (off by one character). This is mainly
X * useful for multi-line changes (such as "c%") so the insert starts in
X * the right place.
X * 
X * Revision 3.64  89/08/02  12:47:04  tony
X * This message intentionally left blank.
X * 
X * Revision 3.63  89/08/02  12:43:44  tony
X * I just noticed that I had used the RCS cookie for log messages in one
X * of my prior version messages. This caused these version update messages
X * to be duplicated in this file. I just removed that string, and the
X * extra message copies that had been generated.
X * 
X * Revision 3.62  89/08/02  12:26:20  tony
X * The ^G command now shows where you are in the file list, if more than one
X * file is being edited. Also, the commands ":e#" and ":e!#" (note the lack
X * of a space between the command and file name) will now work.
X *
X * Revision 3.61  89/08/02  11:03:16  tony
X * Misc. cleanups regarding tags. Also added support for the "terse" option.
X * This is ignored, but improves compatibility with vi, since we no longer
X * complain about an unknown option if "terse" is used.
X * 
X * Revision 3.60  89/08/02  09:26:39  tony
X * Added code to avoid screen redraws when input is being read from the
X * "stuffin" buffer. This avoids extra redraws when switching to the
X * alternate file, or when invoking the editor with one of the "+" options,
X * or when using tags.
X *
X * Revision 3.59  89/08/01  16:28:31  tony
X * Added better support for counts on several cursor motion commands. These
X * include ^F, ^B, f, F, t, T, as well as the repeated character search
X * commands (command and semi-colon).
X *
X * Revision 3.58  89/07/19  08:08:23  tony
X * Added the ability for '~' to be an operator. If enabled (by defined TILDEOP
X * in env.h), the parameter "tildeop" (or "to") may be set to turn tilde into
X * an operator.
X * 
X * Revision 3.57  89/07/13  22:47:05  tony
X * Made some generic speed improvements in screen.c and some TOS-specific
X * improvements in tos.c. The TOS version is now much faster at screen
X * updates than before.
X * 
X * Revision 3.56  89/07/13  14:52:03  tony
X * Minor cleanups in normal.c
X * 
X * Revision 3.55  89/07/13  14:19:12  tony
X * Cleaned up the logic in getcmdln() A LOT. The routine docmdln() needs a
X * similar overhaul.
X *
X * Revision 3.54  89/07/12  21:40:01  tony
X * Lots of misc. cleanup in normal.c and cmdline.c, but nothing much in the
X * way of functional improvements. One change is that things like d/foo<CR>
X * will now work since searches are less of a special case now.
X * 
X * Revision 3.53  89/07/11  16:16:08  tony
X * Added general support for interrupt-handling for those environments that
X * can actually generate them. Basically, long-running operations are now
X * able to terminate early if an error occurs. These operations are: string
X * searches, the global command (":g/.../"), and file reads. File writes
X * should probably be done as well, but this is more dangerous. In all cases,
X * the user is given an indication on the status line that the operation
X * terminated due to an interrupt.
X * 
X * Revision 3.52  89/07/11  12:35:09  tony
X * Improved the code in dosub() and doglob() that detects quoted characters
X * and delimiters in search strings and replacement patterns. The current
X * code didn't allow certain valid strings to be used. The delimiter is still
X * required to be '/', but it can be quoted reliably now with backslash.
X * 
X * Revision 3.51  89/07/10  14:01:58  tony
X * Removed the function addtobuff() since it was rarely used and could be
X * replaced by calls to other library functions. Also removed some other
X * obsolete code that was already ifdef'd out anyway.
X * 
X * Revision 3.50  89/07/10  13:10:32  tony
X * Added a workaround in normal.c to avoid problems with broken versions of
X * strncpy() that don't properly deal with a count of zero.
X *
X * Revision 3.49  89/07/07  16:28:37  tony
X * Fixed a long-standing bug with 'cw' when the cursor is positioned on a
X * word with only one character. Also fixed a problems with zero-length files
X * and reverse searches.
X *
X * Revision 3.48  89/03/22  10:26:58  tony
X * Fixed some outdated uses of the ":p" command (which has been changed to
X * ":N" in os2.c and dos.c. Also added macros (F7 and F8) for dos and os/2
X * to use the "cdecl" program to convert lines to and from a pseudo-english
X * form. Use F7 to "explain" the declaration on the current line, and F8 to
X * convert an english-style declaration to the C form. In both cases, the
X * new form is placed on the next line, leaving the original line intact.
X * 
X * Revision 3.47  89/03/11  22:44:14  tony
X * General cleanup. Removed the static "rcsid" variables and the log
X * strings (except in version.c). Fixed some coding style inconsistencies
X * and added a few register declarations.
X * 
X * Revision 3.46  89/02/14  09:52:07  tony
X * Made a first pass at adding Robert Regn's changes, starting with the
X * more portable ones. Added better support for '#' and '%' in colon
X * commands, support for a configurable temp directory, and made the
X * termcap code less picky about capabilities.
X * 
X * Revision 3.45  88/11/10  09:00:06  tony
X * Added support for mode lines. Strings like "vi:stuff:" or "ex:stuff:"
X * occurring in the first or last 5 lines of a file cause the editor to
X * pretend that "stuff" was types as a colon command. This examination
X * is done only if the parameter "modelines" (or "ml") is set. This is
X * not enabled, by default, because of the security implications involved.
X * 
X * Revision 3.44  88/11/01  21:34:11  tony
X * Fixed a couple of minor points for Minix, and improved the speed of
X * the 'put' command dramatically.
X * 
X * Revision 3.43  88/10/31  13:11:33  tony
X * Added optional support for termcap. Initialization is done in term.c
X * and also affects the system-dependent files. To enable termcap in those
X * environments that support it, define the symbol "TERMCAP" in env.h
X *
X * Revision 3.42  88/10/27  18:30:19  tony
X * Removed support for Megamax. Added '%' as an alias for '1,$'. Made the
X * 'r' command more robust. Now prints the string on repeated searches.
X * The ':=" command now works. Some pointer operations are now safer.
X * The ":!" and ":sh" now work correctly. Re-organized the help screens
X * a little.
X * 
X * Revision 3.41  88/10/06  10:15:00  tony
X * Fixed a bug involving ^Y that occurs when the cursor is on the last
X * line, and the line above the screen is long. Also hacked up fileio.c
X * to pass pathnames off to fixname() for system-dependent processing.
X * Used under DOS & OS/2 to trim parts of the name appropriately.
X * 
X * Revision 3.40  88/09/16  08:37:36  tony
X * No longer beeps when repeated searches fail.
X *
X * Revision 3.39  88/09/06  06:51:07  tony
X * Fixed a bug with shifts that was introduced when replace mode was added.
X * 
X * Revision 3.38  88/08/31  20:48:28  tony
X * Made another fix in search.c related to repeated searches.
X * 
X * Revision 3.37  88/08/30  20:37:16  tony
X * After much prodding from Mark, I finally added support for replace mode.
X * 
X * Revision 3.36  88/08/26  13:46:34  tony
X * Added support for the '!' (filter) operator.
X * 
X * Revision 3.35  88/08/26  08:46:01  tony
X * Misc. changes to make lint happy.
X * 
X * Revision 3.34  88/08/25  15:13:36  tony
X * Fixed a bug where the cursor didn't land on the right place after
X * "beginning-of-word" searches if the word was preceded by the start
X * of the line and a single character.
X * 
X * Revision 3.33  88/08/23  12:53:08  tony
X * Fixed a bug in ssearch() where repeated searches ('n' or 'N') resulted
X * in dynamic memory being referenced after it was freed.
X * 
X * Revision 3.32  88/08/17  07:37:07  tony
X * Fixed a general problem in u_save() by checking both parameters for
X * null values. The specific symptom was that a join on the last line of
X * the file would crash the editor.
X * 
X * Revision 3.31  88/07/09  20:39:38  tony
X * Implemented the "line undo" command (i.e. 'U').
X * 
X * Revision 3.30  88/06/28  07:54:22  tony
X * Fixed a bug involving redo's of the '~' command. The redo would just
X * repeat the replacement last performed instead of switching the case of
X * the current character.
X * 
X * Revision 3.29  88/06/26  14:53:19  tony
X * Added support for a simple form of the "global" command. It supports
X * commands of the form "g/pat/d" or "g/pat/p", to delete or print lines
X * that match the given pattern. A range spec may be used to limit the
X * lines to be searched.
X * 
X * Revision 3.28  88/06/25  21:44:22  tony
X * Fixed a problem in the processing of colon commands that caused
X * substitutions of patterns containing white space to fail.
X * 
X * Revision 3.27  88/06/20  14:52:21  tony
X * Merged in changes for BSD Unix sent in by Michael Lichter.
X * 
X * Revision 3.26  88/06/10  13:44:06  tony
X * Fixed a bug involving writing out files with long pathnames. A small
X * fixed size buffer was being used. The space for the backup file name
X * is now allocated dynamically.
X * 
X * Revision 3.25  88/05/04  08:29:02  tony
X * Fixed a minor incompatibility with vi involving the 'G' command. Also
X * changed the RCS version number of version.c to match the actual version
X * of the editor.
X * 
X * Revision 1.12  88/05/03  14:39:52  tony
X * Changed the screen representation of the ascii character DELETE to be
X * compatible with vi. Also merged in support for DOS.
X * 
X * Revision 1.11  88/05/02  21:38:21  tony
X * The code that reads files now handles boundary/error conditions much
X * better, and generates status/error messages that are compatible with
X * the real vi. Also fixed a bug in repeated reverse searches that got
X * inserted in the recent changes to search.c.
X * 
X * Revision 1.10  88/05/02  07:35:41  tony
X * Fixed a bug in the routine plines() that was introduced during changes
X * made for the last version.
X * 
X * Revision 1.9  88/05/01  20:10:19  tony
X * Fixed some problems with auto-indent, and added support for the "number"
X * parameter.
X * 
X * Revision 1.8  88/04/30  20:00:49  tony
X * Added support for the auto-indent feature.
X * 
X * Revision 1.7  88/04/29  14:50:11  tony
X * Fixed a class of bugs involving commands like "ct)" where the cursor
X * motion part of the operator can fail. If the motion failed, the operator
X * was continued, with the cursor position unchanged. Cases like this were
X * modified to abort the operation if the motion fails.
X * 
X * Revision 1.6  88/04/28  08:19:35  tony
X * Modified Henry Spencer's regular expression library to support new
X * features that couldn't be done easily with the existing interface.
X * This code is now a direct part of the editor source code. The editor
X * now supports the "ignorecase" parameter, and multiple substitutions
X * per line, as in "1,$s/foo/bar/g".
X * 
X * Revision 1.5  88/04/24  21:38:00  tony
X * Added preliminary support for the substitute command. Full range specs.
X * are supported, but only a single substitution is allowed on each line.
X * 
X * Revision 1.4  88/04/23  20:41:01  tony
X * Worked around a problem with adding lines to the end of the buffer when
X * the cursor is at the bottom of the screen (in misccmds.c). Also fixed a
X * bug that caused reverse searches from the start of the file to bomb.
X *
X * Revision 1.3  88/03/24  08:57:00  tony
X * Fixed a bug in cmdline() that had to do with backspacing out of colon
X * commands or searches. Searches were okay, but colon commands backed out
X * one backspace too early.
X * 
X * Revision 1.2  88/03/21  16:47:55  tony
X * Fixed a bug in renum() causing problems with large files (>6400 lines).
X * Also moved system-specific defines out of stevie.h and into a new file
X * named env.h. This keeps volatile information outside the scope of RCS.
X * 
X * Revision 1.1  88/03/20  21:00:39  tony
X * Initial revision
X * 
X */
X
Xchar	*Version = "STEVIE - Version 3.69a";
!EOR!
echo extracting - tos.c
sed 's/^X//' > tos.c << '!EOR!'
X/* $Header: /nw/tony/src/stevie/src/RCS/tos.c,v 1.5 89/07/13 22:45:31 tony Exp $
X *
X * System-dependent routines for the Atari ST.
X */
X
X#include "stevie.h"
X
X#include <osbind.h>
X
X/*
X * inchar() - get a character from the keyboard
X *
X * Certain special keys are mapped to values above 0x80. These
X * mappings are defined in keymap.h. If the key has a non-zero
X * ascii value, it is simply returned. Otherwise it may be a
X * special key we want to map.
X *
X * The ST has a bug involving keyboard input that seems to occur
X * when typing quickly, especially typing capital letters. Sometimes
X * a value of 0x02540000 is read. This doesn't correspond to anything
X * on the keyboard, according to my documentation. My solution is to
X * loop when any unknown key is seen. Normally, the bell is rung to
X * indicate the error. If the "bug" value is seen, we ignore it completely.
X */
Xint
Xinchar()
X{
X	register long	c;
X
X	for (;;) {
X		c = Bconin(2);
X	
X		if ((c & 0xff) != 0)
X			return ((int) c);
X	
X		switch ((int) (c >> 16) & 0xff) {
X	
X		case 0x62: return K_HELP;
X		case 0x61: return K_UNDO;
X		case 0x52: return K_INSERT;
X		case 0x47: return K_HOME;
X		case 0x48: return K_UARROW;
X		case 0x50: return K_DARROW;
X		case 0x4b: return K_LARROW;
X		case 0x4d: return K_RARROW;
X		case 0x29: return K_CCIRCM;	/* control-circumflex */
X		
X		/*
X		 * Occurs due to a bug in TOS.
X		 */
X		case 0x54:
X			break;
X		/*
X		 * Add the function keys here later if we put in support
X		 * for macros.
X		 */
X	
X		default:
X			beep();
X			break;
X	
X		}
X	}
X}
X
Xvoid
Xoutchar(c)
Xchar	c;
X{
X	if (c < ' ')
X		Bconout(2, c);
X	else
X		Bconout(5, c);
X}
X
Xvoid
Xoutstr(s)
Xregister char	*s;
X{
X	while (*s)
X		Bconout(2, *s++);
X}
X
X/*
X * flushbuf() - a no-op for TOS
X */
Xvoid
Xflushbuf()
X{
X}
X
X#define	BGND	0
X#define	TEXT	3
X
X/*
X * vbeep() - visual bell
X */
Xstatic void
Xvbeep()
X{
X	int	text, bgnd;		/* text and background colors */
X	long	l;
X
X	text = Setcolor(TEXT, -1);
X	bgnd = Setcolor(BGND, -1);
X
X	Setcolor(TEXT, bgnd);		/* swap colors */
X	Setcolor(BGND, text);
X
X	for (l=0; l < 5000 ;l++)	/* short pause */
X		;
X
X	Setcolor(TEXT, text);		/* restore colors */
X	Setcolor(BGND, bgnd);
X}
X
Xvoid
Xbeep()
X{
X	if (P(P_VB))
X		vbeep();
X	else
X		outchar('\007');
X}
X
X/*
X * remove(file) - remove a file
X */
Xvoid
Xremove(file)
Xchar	*file;
X{
X	Fdelete(file);
X}
X
X/*
X * rename(of, nf) - rename existing file 'of' to 'nf'
X */
Xvoid
Xrename(of, nf)
Xchar	*of, *nf;
X{
X	Fdelete(nf);		/* if 'nf' exists, remove it */
X	Frename(0, of, nf);
X}
X
Xvoid
Xwindinit()
X{
X	if (Getrez() == 0)
X		Columns = 40;		/* low resolution */
X	else
X		Columns = 80;		/* medium or high */
X
X	P(P_LI) = Rows = 25;
X
X	Cursconf(1,NULL);
X}
X
Xvoid
Xwindexit(r)
Xint	r;
X{
X	exit(r);
X}
X
Xstatic	char	gobuf[5] = { '\033', 'Y', '\0', '\0', '\0' };
X
Xvoid
Xwindgoto(r, c)
Xint	r, c;
X{
X	gobuf[2] = r + 040;
X	gobuf[3] = c + 040;
X	outstr(gobuf);
X}
X
X/*
X * System calls or library routines missing in TOS.
X */
X
Xvoid
Xsleep(n)
Xint	n;
X{
X	int	k;
X
X	k = Tgettime();
X	while ( Tgettime() <= k+n )
X		;
X}
X
Xvoid
Xpause()
X{
X	long	n;
X
X	for (n = 0; n < 8000 ;n++)
X		;
X}
X
Xint
Xsystem(cmd)
Xchar	*cmd;
X{
X	char	arg[1];
X
X	arg[0] = (char) 0;	/* no arguments passed to the shell */
X
X	if (Pexec(0, cmd, arg, 0L) < 0)
X		return -1;
X	else
X		return 0;
X}
X
X#ifdef	SOZOBON
X
XFILE *
Xfopenb(fname, mode)
Xchar	*fname;
Xchar	*mode;
X{
X	char	modestr[10];
X
X	sprintf(modestr, "%sb", mode);
X
X	return fopen(fname, modestr);
X}
X
X#endif
X
X#ifndef	SOZOBON
X/*
X * getenv() - get a string from the environment
X *
X * Both Alcyon and Megamax are missing getenv(). This routine works for
X * both compilers and with the Beckemeyer and Gulam shells. With gulam,
X * the env_style variable should be set to either "mw" or "gu".
X */
Xchar *
Xgetenv(name)
Xchar	*name;
X{
X	extern long	_base;
X	char	*envp, *p;
X
X	envp = *((char **) (_base + 0x2c));
X
X	for (; *envp ;envp += strlen(envp)+1) {
X		if (strncmp(envp, name, strlen(name)) == 0) {
X			p = envp + strlen(name);
X			if (*p++ == '=')
X				return p;
X		}
X	}
X	return (char *) 0;
X}
X#endif
X
X/*
X * mktemp() - quick hack since there isn't one here
X */
Xchar *
Xmktemp(name)
Xchar	*name;
X{
X	int	num;		/* pasted into the string to make it unique */
X	char	cbuf[7];
X	char	*s;		/* where the X's start in name */
X	int	fd;
X
X	if ((s = strchr(name, 'X')) == NULL)	/* needs to be an X */
X		return (char *) NULL;
X
X	if (strlen(s) != 6)			/* should be 6 X's */
X		return (char *) NULL;
X
X	for (num = 0; num < 1000 ;num++) {
X		sprintf(cbuf, "%06d", num);
X		strcpy(s, cbuf);
X		if ((fd = open(name, 0)) < 0)
X			return name;
X		close(fd);
X	}
X	return (char *) NULL;
X}
X
Xvoid
Xdoshell(cmd)
Xchar	*cmd;
X{
X	if (cmd == NULL) {
X		shell();
X		return;
X	}
X	system(cmd);
X	wait_return();
X}
X
X#define	PSIZE	128
X
X/*
X * fixname(s) - fix up a dos name
X *
X * Takes a name like:
X *
X *	\x\y\z\base.ext
X *
X * and trims 'base' to 8 characters, and 'ext' to 3.
X */
Xchar *
Xfixname(s)
Xchar	*s;
X{
X	char	*strchr(), *strrchr();
X	static	char	f[PSIZE];
X	char	base[32];
X	char	ext[32];
X	char	*p;
X	int	i;
X
X	strcpy(f, s);
X
X	for (i=0; i < PSIZE ;i++)
X		if (f[i] == '/')
X			f[i] = '\\';
X
X	/*
X	 * Split the name into directory, base, extension.
X	 */
X	if ((p = strrchr(f, '\\')) != NULL) {
X		strcpy(base, p+1);
X		p[1] = '\0';
X	} else {
X		strcpy(base, f);
X		f[0] = '\0';
X	}
X
X	if ((p = strchr(base, '.')) != NULL) {
X		strcpy(ext, p+1);
X		*p = '\0';
X	} else
X		ext[0] = '\0';
X
X	/*
X	 * Trim the base name if necessary.
X	 */
X	if (strlen(base) > 8)
X		base[8] = '\0';
X	
X	if (strlen(ext) > 3)
X		ext[3] = '\0';
X
X	/*
X	 * Paste it all back together
X	 */
X	strcat(f, base);
X	strcat(f, ".");
X	strcat(f, ext);
X
X	return f;
X}
X
X/*
X *	FILL IT IN, FOR YOUR SYSTEM, AND SHARE IT!
X *
X *	The next couple of functions do system-specific stuff.
X *	They currently do nothing; I'm not familiar enough with
X *	system-specific programming on this system.
X *	If you fill it in for your system, please post the results
X *	and share with the rest of us.
X */
X
X
Xsetcolor (c)
X/*
X * Set the color to c, using the local system convention for numbering
X * colors or video attributes.
X *
X * If you implement this, remember to note the original color in
X * windinit(), before you do any setcolor() commands, and
X * do a setcolor() back to the original as part of windexit().
X */
X  int c:
X{
X}
X
X
Xsetrows (r)
X/*
X * Set the number of lines to r, if possible.  Otherwise
X * "do the right thing".  Return the number of lines actually set.
X *
X * If you implement this, remember to note the original number of rows
X * in windinit(), before you do any setrows() commands, and
X * do a setrows() back to the original as part of windexit().
X */
X  int r;
X{
X	/* Since we do nothing, just return the current number of lines */
X	return ( P(P_LI) );
X}
X
X
Xvbeep ()
X/*
X * Do a "visual bell".  This generally consists of flashing the screen
X * once in inverse video.
X */
X{
X	int	color, revco;
X
X	color = P( P_CO );		/* get current color */
X	revco = reverse_color (color);	/* system-specific */
X	setcolor (revco);
X	flushbuf ();
X	pause ();
X	setcolor (color);
X	windgoto (Cursrow, Curscol);
X	flushbuf ();
X}
X
Xreverse_color (co)
X/*
X * Returns the inverse video attribute or color of co.
X * The existing code below is VERY simple-minded.
X * Replace it with proper code for your system.
X */
X int co;
X{
X	if (co)		return (0);
X	else		return (1);
X}
X
X
X/********** End of do-it-yourself kit **********************/
X
!EOR!


