[comp.sources.misc] v11i010: Stevie 3.69a - 2/6

dmt@pegasus.ATT.COM (Dave Tutelman) (03/11/90)

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

: This is a shar archive.  Extract with sh, not csh.
: The rest of this file will extract:
: alloc.c cmdline.c edit.c enveval.c fileio.c help.c hexchars.c env.h keymap.h ops.h param.h regexp.h regmagic.h
echo extracting - alloc.c
sed 's/^X//' > alloc.c << '!EOR!'
X/* $Header: /nw/tony/src/stevie/src/RCS/alloc.c,v 1.5 89/08/06 09:49:22 tony Exp $
X *
X * Various allocation routines and routines returning information about
X * allocated objects.
X */
X
X#include "stevie.h"
X
Xchar *
Xalloc(size)
Xunsigned size;
X{
X	char	*p;		/* pointer to new storage space */
X
X	p = malloc(size);
X	if ( p == (char *)NULL ) {	/* if there is no more room... */
X		emsg("alloc() is unable to find memory!");
X	}
X	return(p);
X}
X
Xchar *
Xstrsave(string)
Xchar	*string;
X{
X	return(strcpy(alloc((unsigned)(strlen(string)+1)),string));
X}
X
Xscreenalloc()
X{
X	/*
X	 * If we're changing the size of the screen, free the old arrays
X	 */
X	if (Realscreen != NULL)
X		free(Realscreen);
X	if (Nextscreen != NULL)
X		free(Nextscreen);
X
X	Realscreen = malloc((unsigned)(Rows*Columns));
X	Nextscreen = malloc((unsigned)(Rows*Columns));
X	if (!Realscreen || !Nextscreen)
X		return (-1);
X	else	return (0);
X}
X
X/*
X * Allocate and initialize a new line structure with room for
X * 'nchars'+1 characters. We add one to nchars here to allow for
X * null termination because all the callers would just do it otherwise.
X */
XLINE *
Xnewline(nchars)
Xint	nchars;
X{
X	register LINE	*l;
X
X	if ((l = (LINE *) alloc(sizeof(LINE))) == NULL)
X		return (LINE *) NULL;
X
X	l->s = alloc((unsigned) (nchars+1));	/* the line is empty */
X	if (l->s == NULL)	return (LINE *) NULL;
X	l->s[0] = NUL;
X	l->size = nchars + 1;
X
X	l->prev = (LINE *) NULL;	/* should be initialized by caller */
X	l->next = (LINE *) NULL;
X
X	return l;
X}
X
X/*
X * filealloc() - construct an initial empty file buffer
X */
Xvoid
Xfilealloc()
X{
X	if ((Filemem->linep = newline(0)) == NULL) {
X		fprintf(stderr,"Unable to allocate file memory!\n");
X		exit(1);
X	}
X	if ((Filetop->linep = newline(0)) == NULL) {
X		fprintf(stderr,"Unable to allocate file memory!\n");
X		exit(1);
X	}
X	if ((Fileend->linep = newline(0)) == NULL) {
X		fprintf(stderr,"Unable to allocate file memory!\n");
X		exit(1);
X	}
X	Filemem->index = 0;
X	Filetop->index = 0;
X	Fileend->index = 0;
X
X	Filetop->linep->next = Filemem->linep;	/* connect Filetop to Filemem */
X	Filemem->linep->prev = Filetop->linep;
X
X	Filemem->linep->next = Fileend->linep;	/* connect Filemem to Fileend */
X	Fileend->linep->prev = Filemem->linep;
X
X	*Curschar = *Filemem;
X	*Topchar  = *Filemem;
X
X	Filemem->linep->num = 0;
X	Fileend->linep->num = 0xffff;
X
X	clrall();		/* clear all marks */
X	u_clear();		/* clear the undo buffer */
X}
X
X/*
X * freeall() - free the current buffer
X *
X * Free all lines in the current buffer.
X */
Xvoid
Xfreeall()
X{
X	register LINE	*lp, *xlp;
X
X	for (lp = Filetop->linep; lp != NULL ;lp = xlp) {
X		if (lp->s != NULL)
X			free(lp->s);
X		xlp = lp->next;
X		free((char *)lp);
X	}
X
X	Curschar->linep = NULL;		/* clear pointers */
X	Filetop->linep = NULL;
X	Filemem->linep = NULL;
X	Fileend->linep = NULL;
X
X	u_clear();
X}
X
X/*
X * bufempty() - return TRUE if the buffer is empty
X */
Xbool_t
Xbufempty()
X{
X	return (buf1line() && Filemem->linep->s[0] == NUL);
X}
X
X/*
X * buf1line() - return TRUE if there is only one line
X */
Xbool_t
Xbuf1line()
X{
X	return (Filemem->linep->next == Fileend->linep);
X}
X
X/*
X * lineempty() - return TRUE if the current line is empty
X */
Xbool_t
Xlineempty()
X{
X	return (Curschar->linep->s[0] == NUL);
X}
X
X/*
X * endofline() - return TRUE if the given position is at end of line
X *
X * This routine will probably never be called with a position resting
X * on the NUL byte, but handle it correctly in case it happens.
X */
Xbool_t
Xendofline(p)
Xregister LPTR	*p;
X{
X	return (p->linep->s[p->index] == NUL || p->linep->s[p->index+1] == NUL);
X}
X/*
X * canincrease(n) - returns TRUE if the current line can be increased 'n' bytes
X *
X * This routine returns immediately if the requested space is available.
X * If not, it attempts to allocate the space and adjust the data structures
X * accordingly. If everything fails it returns FALSE.
X */
Xbool_t
Xcanincrease(n)
Xregister int	n;
X{
X	register int	nsize;
X	register char	*s;		/* pointer to new space */
X
X	nsize = strlen(Curschar->linep->s) + 1 + n;	/* size required */
X
X	if (nsize <= Curschar->linep->size)
X		return TRUE;
X
X	/*
X	 * Need to allocate more space for the string. Allow some extra
X	 * space on the assumption that we may need it soon. This avoids
X	 * excessive numbers of calls to malloc while entering new text.
X	 */
X	if ((s = alloc((unsigned) (nsize + SLOP))) == NULL) {
X		emsg("Can't add anything, file is too big!");
X		State = NORMAL;
X		return FALSE;
X	}
X
X	Curschar->linep->size = nsize + SLOP;
X	strcpy(s, Curschar->linep->s);
X	free(Curschar->linep->s);
X	Curschar->linep->s = s;
X	
X	return TRUE;
X}
X
Xchar *
Xmkstr(c)
Xchar	c;
X{
X	static	char	s[2];
X
X	s[0] = c;
X	s[1] = NUL;
X
X	return s;
X}
!EOR!
echo extracting - cmdline.c
sed 's/^X//' > cmdline.c << '!EOR!'
X/* $Header: /nw/tony/src/stevie/src/RCS/cmdline.c,v 1.20 89/08/13 11:41:23 tony Exp $
X *
X * Routines to parse and execute "command line" commands, such as searches
X * or colon commands.
X */
X
X#include "stevie.h"
X
Xstatic	char	*altfile = NULL;	/* alternate file */
Xstatic	int	altline;		/* line # in alternate file */
X
Xstatic	char	*nowrtmsg = "No write since last change (use ! to override)";
Xstatic	char	*nooutfile = "No output file";
Xstatic	char	*morefiles = "more files to edit";
X
Xextern	char	**files;		/* used for "n" and "rew" */
Xextern	int	numfiles, curfile;
X
X#define	CMDSZ	100		/* size of the command buffer */
X
Xstatic	void	get_range();
Xstatic	LPTR	*get_line();
X
X/*
X * getcmdln() - read a command line from the terminal
X *
X * Reads a command line started by typing '/', '?', '!', or ':'. Returns a
X * pointer to the string that was read. For searches, an optional trailing
X * '/' or '?' is removed.
X */
Xchar *
Xgetcmdln(firstc)
Xchar	firstc;
X{
X	static	char	buff[CMDSZ];
X	register char	*p = buff;
X	register int	c;
X	register char	*q;
X
X	gotocmd(TRUE, firstc);
X
X	/* collect the command string, handling '\b' and @ */
X	do {
X		switch (c = vgetc()) {
X
X		default:		/* a normal character */
X			outchar(c);
X			*p++ = c;
X			break;
X
X		case BS:
X			if (p > buff) {
X				/*
X				 * this is gross, but it relies
X				 * only on 'gotocmd'
X				 */
X				p--;
X				gotocmd(TRUE, firstc);
X				for (q = buff; q < p ;q++)
X					outchar(*q);
X			} else {
X				msg("");
X				return NULL;		/* back to cmd mode */
X			}
X			break;
X
X		case '@':			/* line kill */
X			p = buff;
X			gotocmd(TRUE, firstc);
X			break;
X
X		case ESC:			/* abandon command */
X			msg("");
X			return  NULL;
X			break;
X
X		case NL:			/* done reading the line */
X		case CR:
X			break;
X		}
X	} while (c != NL && c != CR);
X
X	*p = '\0';
X
X	if (firstc == '/' || firstc == '?') {	/* did we do a search? */
X		/*
X		 * Look for a terminating '/' or '?'. This will be the first
X		 * one that isn't quoted. Truncate the search string there.
X		 */
X		for (p = buff; *p ;) {
X			if (*p == firstc) {	/* we're done */
X				*p = '\0';
X				break;
X			} else if (*p == '\\')	/* next char quoted */
X				p += 2;
X			else
X				p++;		/* normal char */
X		}
X	}
X	return buff;
X}
X
X/*
X * docmdln() - handle a colon command
X *
X * Handles a colon command received interactively by getcmdln() or from
X * the environment variable "EXINIT" (or eventually .virc).
X */
Xvoid
Xdocmdln(cmdline)
Xchar	*cmdline;
X{
X	char	buff[CMDSZ];
X	char	cmdbuf[CMDSZ];
X	char	argbuf[CMDSZ];
X	char	*cmd, *arg;
X	register char	*p;
X	/*
X	 * The next two variables contain the bounds of any range given in a
X	 * command. If no range was given, both contain null line pointers.
X	 * If only a single line was given, u_pos will contain a null line
X	 * pointer.
X	 */
X	LPTR	l_pos, u_pos;
X
X
X	/*
X	 * Clear the range variables.
X	 */
X	l_pos.linep = (struct line *) NULL;
X	u_pos.linep = (struct line *) NULL;
X
X	if (cmdline == NULL)
X		return;
X
X	if (strlen(cmdline) > CMDSZ-2) {
X		msg("Error: command line too long");
X		return;
X	}
X	strcpy(buff, cmdline);
X
X	/* skip any initial white space */
X	for (cmd = buff; *cmd != NUL && isspace(*cmd) ;cmd++)
X		;
X
X	if (*cmd == '%') {		/* change '%' to "1,$" */
X		strcpy(cmdbuf, "1,$");	/* kind of gross... */
X		strcat(cmdbuf, cmd+1);
X		strcpy(cmd, cmdbuf);
X	}
X
X	while ((p=strchr(cmd, '%')) != NULL && *(p-1) != '\\') {
X					/* change '%' to Filename */
X		if (Filename == NULL) {
X			emsg("No filename");
X			return;
X		}
X		*p= NUL;
X		strcpy (cmdbuf, cmd);
X		strcat (cmdbuf, Filename);
X		strcat (cmdbuf, p+1);
X		strcpy(cmd, cmdbuf);
X		msg(cmd);			/*repeat */
X	}
X
X	while ((p=strchr(cmd, '#')) != NULL && *(p-1) != '\\') {
X					/* change '#' to Altname */
X		if (altfile == NULL) {
X			emsg("No alternate file");
X			return;
X		}
X		*p= NUL;
X		strcpy (cmdbuf, cmd);
X		strcat (cmdbuf, altfile);
X		strcat (cmdbuf, p+1);
X		strcpy(cmd, cmdbuf);
X		msg(cmd);			/*repeat */
X	}
X
X	/*
X	 * Parse a range, if present (and update the cmd pointer).
X	 */
X	get_range(&cmd, &l_pos, &u_pos);
X
X	if (l_pos.linep != NULL) {
X		if (LINEOF(&l_pos) > LINEOF(&u_pos)) {
X			emsg("Invalid range");
X			return;
X		}
X	}
X
X	strcpy(cmdbuf, cmd);	/* save the unmodified command */
X
X	/* isolate the command and find any argument */
X	for ( p=cmd; *p != NUL && ! isspace(*p); p++ )
X		;
X	if ( *p == NUL )
X		arg = NULL;
X	else {
X		*p = NUL;
X		for (p++; *p != NUL && isspace(*p) ;p++)
X			;
X		if (*p == NUL)
X			arg = NULL;
X		else {
X			strcpy(argbuf, p);
X			arg = argbuf;
X		}
X	}
X	if (strcmp(cmd,"q!") == 0)
X		getout();
X	if (strcmp(cmd,"q") == 0) {
X		if (Changed)
X			emsg(nowrtmsg);
X		else {
X			if ((curfile + 1) < numfiles)
X				emsg(morefiles);
X			else
X				getout();
X		}
X		return;
X	}
X	if (strcmp(cmd,"w") == 0) {
X		if (arg == NULL) {
X			if (Filename != NULL) {
X				writeit(Filename, &l_pos, &u_pos);
X			} else
X				emsg(nooutfile);
X		}
X		else
X			writeit(arg, &l_pos, &u_pos);
X		return;
X	}
X	if (strcmp(cmd,"wq") == 0) {
X		if (Filename != NULL) {
X			if (writeit(Filename, (LPTR *)NULL, (LPTR *)NULL))
X				getout();
X		} else
X			emsg(nooutfile);
X		return;
X	}
X	if (strcmp(cmd, "x") == 0) {
X		doxit();
X		return;
X	}
X
X	if (strcmp(cmd,"f") == 0 && arg == NULL) {
X		fileinfo();
X		return;
X	}
X	if (*cmd == 'n') {
X		if ((curfile + 1) < numfiles) {
X			/*
X			 * stuff ":e[!] FILE\n"
X			 */
X			stuffin(":e");
X			if (cmd[1] == '!')
X				stuffin("!");
X			stuffin(" ");
X			stuffin(files[++curfile]);
X			stuffin("\n");
X		} else
X			emsg("No more files!");
X		return;
X	}
X	if (*cmd == 'N') {
X		if (curfile > 0) {
X			/*
X			 * stuff ":e[!] FILE\n"
X			 */
X			stuffin(":e");
X			if (cmd[1] == '!')
X				stuffin("!");
X			stuffin(" ");
X			stuffin(files[--curfile]);
X			stuffin("\n");
X		} else
X			emsg("No more files!");
X		return;
X	}
X	if (strncmp(cmd, "rew", 3) == 0) {
X		if (numfiles <= 1)		/* nothing to rewind */
X			return;
X		curfile = 0;
X		/*
X		 * stuff ":e[!] FILE\n"
X		 */
X		stuffin(":e");
X		if (cmd[3] == '!')
X			stuffin("!");
X		stuffin(" ");
X		stuffin(files[0]);
X		stuffin("\n");
X		return;
X	}
X	if (strcmp(cmd,"e") == 0 || strcmp(cmd,"e!") == 0) {
X		(void) doecmd(arg, cmd[1] == '!');
X		return;
X	}
X	/*
X	 * The command ":e#" gets expanded to something like ":efile", so
X	 * detect that case here.
X	 */
X	if (*cmd == 'e' && arg == NULL) {
X		if (cmd[1] == '!')
X			(void) doecmd(&cmd[2], TRUE);
X		else
X			(void) doecmd(&cmd[1], FALSE);
X		return;
X	}
X	if (strcmp(cmd,"f") == 0) {
X		EnvEval (arg, CMDSZ);	/* expand environment vars */
X		Filename = strsave(arg);
X		filemess("");
X		return;
X	}
X	if (strcmp(cmd,"r") == 0) {
X		if (arg == NULL) {
X			badcmd();
X			return;
X		}
X		if (readfile(arg, Curschar, 1)) {
X			emsg("Can't open file");
X			return;
X		}
X		updatescreen();
X		CHANGED;
X		return;
X	}
X	if (strcmp(cmd,"=") == 0) {
X		smsg("%d", cntllines(Filemem, &l_pos));
X		return;
X	}
X	if (strncmp(cmd,"ta", 2) == 0) {
X		dotag(arg, cmd[2] == '!');
X		return;
X	}
X	if (strncmp(cmd,"untag", 5) == 0) {
X		if (P(P_TG))
X			dountag(cmd[5]);
X		else
X			emsg("Tag stacking not enabled");
X		return;
X	}
X	if (strncmp(cmd,"set", 2) == 0) {
X		doset(arg);
X		return;
X	}
X	if (strcmp(cmd,"help") == 0) {
X		if (help()) {
X			screenclear();
X			updatescreen();
X		}
X		return;
X	}
X	if (strncmp(cmd, "ve", 2) == 0) {
X		extern	char	*Version;
X
X		msg(Version);
X		return;
X	}
X	if (strcmp(cmd, "sh") == 0) {
X		doshell(NULL);
X		return;
X	}
X	if (*cmd == '!') {
X		doshell(cmdbuf+1);
X		return;
X	}
X	if (strncmp(cmd, "s/", 2) == 0) {
X		dosub(&l_pos, &u_pos, cmdbuf+1);
X		return;
X	}
X	if (strncmp(cmd, "g/", 2) == 0) {
X		doglob(&l_pos, &u_pos, cmdbuf+1);
X		return;
X	}
X	/*
X	 * If we got a line, but no command, then go to the line.
X	 */
X	if (*cmd == NUL && l_pos.linep != NULL) {
X		*Curschar = l_pos;
X		return;
X	}
X
X	badcmd();
X}
X
X
Xdoxit()
X{
X	if (Changed) {
X		if (Filename != NULL) {
X			if (!writeit(Filename, (LPTR *)NULL, (LPTR *)NULL))
X				return;
X		} else {
X			emsg(nooutfile);
X			return;
X		}
X	}
X	if ((curfile + 1) < numfiles)
X		emsg(morefiles);
X	else
X		getout();
X}
X
X/*
X * get_range - parse a range specifier
X *
X * Ranges are of the form:
X *
X * addr[,addr]
X *
X * where 'addr' is:
X *
X * $  [+- NUM]
X * 'x [+- NUM]	(where x denotes a currently defined mark)
X * .  [+- NUM]
X * NUM
X *
X * The pointer *cp is updated to point to the first character following
X * the range spec. If an initial address is found, but no second, the
X * upper bound is equal to the lower.
X */
Xstatic void
Xget_range(cp, lower, upper)
Xregister char	**cp;
XLPTR	*lower, *upper;
X{
X	register LPTR	*l;
X	register char	*p;
X
X	if ((l = get_line(cp)) == NULL)
X		return;
X
X	*lower = *l;
X
X	for (p = *cp; *p != NUL && isspace(*p) ;p++)
X		;
X
X	*cp = p;
X
X	if (*p != ',') {		/* is there another line spec ? */
X		*upper = *lower;
X		return;
X	}
X
X	*cp = ++p;
X
X	if ((l = get_line(cp)) == NULL) {
X		*upper = *lower;
X		return;
X	}
X
X	*upper = *l;
X}
X
Xstatic LPTR *
Xget_line(cp)
Xchar	**cp;
X{
X	static	LPTR	pos;
X	LPTR	*lp;
X	register char	*p, c;
X	register int	lnum;
X
X	pos.index = 0;		/* shouldn't matter... check back later */
X
X	p = *cp;
X	/*
X	 * Determine the basic form, if present.
X	 */
X	switch (c = *p++) {
X
X	case '$':
X		pos.linep = Fileend->linep->prev;
X		break;
X
X	case '.':
X		pos.linep = Curschar->linep;
X		break;
X
X	case '\'':
X		if ((lp = getmark(*p++)) == NULL) {
X			emsg("Unknown mark");
X			return (LPTR *) NULL;
X		}
X		pos = *lp;
X		break;
X
X	case '0': case '1': case '2': case '3': case '4':
X	case '5': case '6': case '7': case '8': case '9':
X		for (lnum = c - '0'; isdigit(*p) ;p++)
X			lnum = (lnum * 10) + (*p - '0');
X
X		pos = *gotoline(lnum);
X		break;
X
X	default:
X		return (LPTR *) NULL;
X	}
X
X	while (*p != NUL && isspace(*p))
X		p++;
X
X	if (*p == '-' || *p == '+') {
X		bool_t	neg = (*p++ == '-');
X
X		for (lnum = 0; isdigit(*p) ;p++)
X			lnum = (lnum * 10) + (*p - '0');
X
X		if (neg)
X			lnum = -lnum;
X
X		pos = *gotoline( cntllines(Filemem, &pos) + lnum );
X	}
X
X	*cp = p;
X	return &pos;
X}
X
Xvoid
Xbadcmd()
X{
X	emsg("Unrecognized command");
X}
X
Xbool_t
Xdoecmd(arg, force)
Xchar	*arg;
Xbool_t	force;
X{
X	int	line = 1;		/* line # to go to in new file */
X
X	if (!force && Changed) {
X		emsg(nowrtmsg);
X		if (altfile)
X			free(altfile);
X		altfile = strsave(arg);
X		return FALSE;
X	}
X	if (arg != NULL) {
X		/*
X		 * First detect a ":e" on the current file. This is mainly
X		 * for ":ta" commands where the destination is within the
X		 * current file.
X		 */
X		if (Filename != NULL && strcmp(arg, Filename) == 0) {
X			if (!Changed || (Changed && !force))
X				return TRUE;
X		}
X		if (altfile) {
X			if (strcmp (arg, altfile) == 0)
X				line = altline;
X			free(altfile);
X		}
X		altfile = Filename;
X		altline = cntllines(Filemem, Curschar);
X		Filename = strsave(arg);
X	}
X	if (Filename == NULL) {
X		emsg("No filename");
X		return FALSE;
X	}
X
X	/* clear mem and read file */
X	freeall();
X	filealloc();
X	UNCHANGED;
X
X	if (readfile(Filename, Filemem, 0))
X		filemess("[New File]");
X
X	*Topchar = *Curschar;
X	if (line != 1) {
X		stuffnum(line);
X		stuffin("G");
X	}
X	do_mlines();
X	setpcmark();
X	updatescreen();
X	return TRUE;
X}
X
Xvoid
Xgotocmd(clr, firstc)
Xbool_t  clr;
Xchar	firstc;
X{
X	windgoto(Rows-1,0);
X	if (clr)
X		CLEOL;		/* clear the bottom line */
X	if (firstc)
X		outchar(firstc);
X}
X
X/*
X * msg(s) - displays the string 's' on the status line
X */
Xvoid
Xmsg(s)
Xchar	*s;
X{
X	gotocmd(TRUE, 0);
X	outstr(s);
X	flushbuf();
X}
X
X/*VARARGS1*/
Xvoid
Xsmsg(s, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16)
Xchar	*s;
Xint	a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16;
X{
X	char	sbuf[80];
X
X	sprintf(sbuf, s,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16);
X	msg(sbuf);
X}
X
X/*
X * emsg() - display an error message
X *
X * Rings the bell, if appropriate, and calls message() to do the real work
X */
Xvoid
Xemsg(s)
Xchar	*s;
X{
X	if (P(P_EB))
X		beep();
X	msg(s);
X}
X
Xvoid
Xwait_return()
X{
X	register char	c;
X
X	if (got_int)
X		outstr("Interrupt: ");
X
X	outstr("Press RETURN to continue");
X	do {
X		c = vgetc();
X	} while (c != CR && c != NL && c != ' ' && c != ':');
X
X	if (c == ':') {
X		outchar(NL);
X		docmdln(getcmdln(c));
X	} else
X		screenclear();
X
X	updatescreen();
X}
!EOR!
echo extracting - edit.c
sed 's/^X//' > edit.c << '!EOR!'
X/* $Header: /nw/tony/src/stevie/src/RCS/edit.c,v 1.11 89/08/02 19:57:12 tony Exp $
X *
X * The main edit loop as well as some other simple cursor movement routines.
X */
X
X#include "stevie.h"
X
X/*
X * This flag is used to make auto-indent work right on lines where only
X * a <RETURN> or <ESC> is typed. It is set when an auto-indent is done,
X * and reset when any other editting is done on the line. If an <ESC>
X * or <RETURN> is received, and did_ai is TRUE, the line is truncated.
X */
Xbool_t	did_ai = FALSE;
X
Xvoid
Xedit()
X{
X	extern	bool_t	need_redraw;
X	int	c;
X	register char	*p, *q;
X
X	Prenum = 0;
X
X	/* position the display and the cursor at the top of the file. */
X	*Topchar = *Filemem;
X	*Curschar = *Filemem;
X	Cursrow = Curscol = 0;
X
X	do_mlines();		/* check for mode lines before starting */
X
X	updatescreen();
X
X	for ( ;; ) {
X
X	/* Figure out where the cursor is based on Curschar. */
X	cursupdate();
X
X	if (need_redraw && !anyinput()) {
X		updatescreen();
X		need_redraw = FALSE;
X	}
X
X	if (!anyinput())
X		windgoto(Cursrow,Curscol);
X
X
X	c = vgetc();
X
X	if (State == NORMAL) {
X
X		/* We're in the normal (non-insert) mode. */
X
X		/* Pick up any leading digits and compute 'Prenum' */
X		if ( (Prenum>0 && isdigit(c)) || (isdigit(c) && c!='0') ){
X			Prenum = Prenum*10 + (c-'0');
X			continue;
X		}
X		/* execute the command */
X		normal(c);
X		Prenum = 0;
X
X	} else {
X
X		/*
X		 * Insert or Replace mode.
X		 */
X		switch (c) {
X
X		case ESC:	/* an escape ends input mode */
X
X			/*
X			 * If we just did an auto-indent, truncate the
X			 * line, and put the cursor back.
X			 */
X			if (did_ai) {
X				Curschar->linep->s[0] = NUL;
X				Curschar->index = 0;
X				did_ai = FALSE;
X			}
X
X			set_want_col = TRUE;
X
X			/* Don't end up on a '\n' if you can help it. */
X			if (gchar(Curschar) == NUL && Curschar->index != 0)
X				dec(Curschar);
X
X			/*
X			 * The cursor should end up on the last inserted
X			 * character. This is an attempt to match the real
X			 * 'vi', but it may not be quite right yet.
X			 */
X			if (Curschar->index != 0 && !endofline(Curschar))
X				dec(Curschar);
X
X			State = NORMAL;
X			msg("");
X
X			/* construct the Redo buffer */
X			p=Redobuff;
X			q=Insbuff;
X			while ( q < Insptr )
X				*p++ = *q++;
X			*p++ = ESC;
X			*p = NUL;
X			updatescreen();
X			break;
X
X		case CTRL('D'):
X			/*
X			 * Control-D is treated as a backspace in insert
X			 * mode to make auto-indent easier. This isn't
X			 * completely compatible with vi, but it's a lot
X			 * easier than doing it exactly right, and the
X			 * difference isn't very noticeable.
X			 */
X		case BS:
X			/* can't backup past starting point */
X			if (Curschar->linep == Insstart->linep &&
X			    Curschar->index <= Insstart->index) {
X				beep();
X				break;
X			}
X
X			/* can't backup to a previous line */
X			if (Curschar->linep != Insstart->linep &&
X			    Curschar->index <= 0) {
X				beep();
X				break;
X			}
X
X			did_ai = FALSE;
X			dec(Curschar);
X			if (State == INSERT)
X				delchar(TRUE);
X			/*
X			 * It's a little strange to put backspaces into
X			 * the redo buffer, but it makes auto-indent a
X			 * lot easier to deal with.
X			 */
X			*Insptr++ = BS;
X			Ninsert++;
X			cursupdate();
X			updateline();
X			break;
X
X		case CR:
X		case NL:
X			if (State == REPLACE)		/* DMT added, 12/89 */
X				delchar(FALSE);
X			*Insptr++ = NL;
X			Ninsert++;
X			opencmd(FORWARD, TRUE);		/* open a new line */
X			break;
X
X		default:
X			did_ai = FALSE;
X			insertchar(c);
X			break;
X		}
X	}
X	}
X}
X
Xvoid
Xinsertchar(c)
Xint	c;
X{
X	inschar(c);
X	*Insptr++ = c;
X	Ninsert++;
X	/*
X	 * The following kludge avoids overflowing the statically
X	 * allocated insert buffer. Just dump the user back into
X	 * command mode, and print a message.
X	 */
X	if (Insptr+10 >= &Insbuff[1024]) {
X		stuffin(mkstr(ESC));
X		emsg("No buffer space - returning to command mode");
X		sleep(2);
X	}
X	updateline();
X}
X
Xvoid
Xgetout()
X{
X	windgoto(Rows-1,0);
X	putchar('\r');
X	putchar('\n');
X	windexit(0);
X}
X
Xvoid
Xscrolldown(nlines)
Xint	nlines;
X{
X	register LPTR	*p;
X	register int	done = 0;	/* total # of physical lines done */
X
X	/* Scroll up 'nlines' lines. */
X	while (nlines--) {
X		if ((p = prevline(Topchar)) == NULL)
X			break;
X		done += plines(p);
X		*Topchar = *p;
X		/*
X		 * If the cursor is on the bottom line, we need to
X		 * make sure it gets moved up the appropriate number
X		 * of lines so it stays on the screen.
X		 */
X		if (Curschar->linep == Botchar->linep->prev) {
X			int	i = 0;
X			while (i < done) {
X				i += plines(Curschar);
X				*Curschar = *prevline(Curschar);
X			}
X		}
X	}
X	s_ins(0, done);
X}
X
Xvoid
Xscrollup(nlines)
Xint	nlines;
X{
X	register LPTR	*p;
X	register int	done = 0;	/* total # of physical lines done */
X	register int	pl;		/* # of plines for the current line */
X
X	/* Scroll down 'nlines' lines. */
X	while (nlines--) {
X		pl = plines(Topchar);
X		if ((p = nextline(Topchar)) == NULL)
X			break;
X		done += pl;
X		if (Curschar->linep == Topchar->linep)
X			*Curschar = *p;
X		*Topchar = *p;
X
X	}
X	s_del(0, done);
X}
X
X/*
X * oneright
X * oneleft
X * onedown
X * oneup
X *
X * Move one char {right,left,down,up}.  Return TRUE when
X * sucessful, FALSE when we hit a boundary (of a line, or the file).
X */
X
Xbool_t
Xoneright()
X{
X	set_want_col = TRUE;
X
X	switch (inc(Curschar)) {
X
X	case 0:
X		return TRUE;
X
X	case 1:
X		dec(Curschar);		/* crossed a line, so back up */
X		/* fall through */
X	case -1:
X		return FALSE;
X	}
X	/*NOTREACHED*/
X}
X
Xbool_t
Xoneleft()
X{
X	set_want_col = TRUE;
X
X	switch (dec(Curschar)) {
X
X	case 0:
X		return TRUE;
X
X	case 1:
X		inc(Curschar);		/* crossed a line, so back up */
X		/* fall through */
X	case -1:
X		return FALSE;
X	}
X	/*NOTREACHED*/
X}
X
Xvoid
Xbeginline(flag)
Xbool_t	flag;
X{
X	while ( oneleft() )
X		;
X	if (flag) {
X		while (isspace(gchar(Curschar)) && oneright())
X			;
X	}
X	set_want_col = TRUE;
X}
X
Xbool_t
Xoneup(n)
Xint	n;
X{
X	LPTR	p, *np;
X	register int	k;
X
X	p = *Curschar;
X	for ( k=0; k<n; k++ ) {
X		/* Look for the previous line */
X		if ( (np=prevline(&p)) == NULL ) {
X			/* If we've at least backed up a little .. */
X			if ( k > 0 )
X				break;	/* to update the cursor, etc. */
X			else
X				return FALSE;
X		}
X		p = *np;
X	}
X	*Curschar = p;
X	/* This makes sure Topchar gets updated so the complete line */
X	/* is one the screen. */
X	cursupdate();
X	/* try to advance to the column we want to be at */
X	*Curschar = *coladvance(&p, Curswant);
X	return TRUE;
X}
X
Xbool_t
Xonedown(n)
Xint	n;
X{
X	LPTR	p, *np;
X	register int	k;
X
X	p = *Curschar;
X	for ( k=0; k<n; k++ ) {
X		/* Look for the next line */
X		if ( (np=nextline(&p)) == NULL ) {
X			if ( k > 0 )
X				break;
X			else
X				return FALSE;
X		}
X		p = *np;
X	}
X	/* try to advance to the column we want to be at */
X	*Curschar = *coladvance(&p, Curswant);
X	return TRUE;
X}
!EOR!
echo extracting - enveval.c
sed 's/^X//' > enveval.c << '!EOR!'
X/*
X *	Evaluate a string, expanding environment variables
X *	where encountered.
X *	We'll use the UNIX convention for representing environment
X *	variables: $xxx, where xxx is the shortest string that
X *	matches some environment variable.
X */
X
X#include <stdio.h>
X#include <string.h>
X
Xchar	*getenv();
X
Xint
XEnvEval (s, len)
X  char *s;
X  int   len;
X/*------------------------------------------------------------------
X *  s=	Pointer to buffer, currently containing string.  It will be
X *	expanded in-place in the buffer.
X *  len=Maximum allowable length of the buffer.  (In this version, we
X *	use a static buffer of 256 bytes internally.)
X *
X * RETURNS:
X *	 0	on success.
X *	-1	on failure.  In this case, s may contain a partially
X *			converted string, but it won't contain a partial
X *			string.  It will be the FULL string, with as
X *			many substitutions as we could find.
X */
X
X{
X#define	LEN	256
X	char	buf [LEN];
X	char	*s1, *s2;
X	char	*b1;
X	int	done=0;
X
X	if (len > LEN)
X		return (-1);
X
X	s1 = s;
X
X	/*  Check for '$', and expand when we find one.  */
X	while (!done) {
X		if ((s1 = strchr (s1, '$')) == NULL)
X			done = 1;
X		else {
X			/*
X			 *  Here's where the real work gets done.
X			 *  We'll find the env.var., and convert
X			 *  it into buf, then copy back into s
X			 *  and continue.
X			 */
X			char	c;
X			int	need, got;
X
X			/* Test successively longer strings, to see
X			 * if they're env.vars.
X			 */
X			for (s2=++s1+1;	; s2++) {
X				c = *s2;	/* save it */
X				*s2 = '\0';
X				b1 = getenv (s1);
X				*s2 = c;		/* restore it */
X				if (b1) 		/* found it */
X					break;
X				if (!*s2)		/* nothing to try */
X					goto Failed;
X			}
X			--s1;	/* Back to the '$' */
X
X			/* OK, we've found one (between s1 & s2,
X			 * non-inclusive).  Its value is in b1.
X			 * Do the substitution into bufp,
X			 * and copy back into s.
X			 */
X			need = strlen(b1) + strlen(s2) + 1;
X			got  = len - (s1-s);
X			if (need > got)
X				goto Failed;
X			strcpy (buf, b1);
X			strcat (buf, s2);
X			strcpy (s1, buf);
X		}
X	}
X
X	/*  If we get here, the converted value is in s  */
X	return (0);
X
X   Failed:
X	return (-1);
X}
X
X
X/* #define SAMPLE */
X#ifdef SAMPLE  /***************************************************/
X
Xmain (int argc, char **argv)
X{
X	int	i, ret;
X
X	for (i=1; i<argc; i++) {
X		printf ("Convert  %s  to", argv [i]);
X		ret = EnvEval (argv [i], 80);
X		printf ("  %s", argv [i]);
X		if (ret)
X			printf ("  -  Failed");
X		putchar ('\n');
X	}
X}
X
X#endif
!EOR!
echo extracting - fileio.c
sed 's/^X//' > fileio.c << '!EOR!'
X/* $Header: /nw/tony/src/stevie/src/RCS/fileio.c,v 1.12 89/08/06 09:50:01 tony Exp $
X *
X * Basic file I/O routines.
X */
X
X#include <sys/types.h>		/* For stat() and chmod() */
X#include <sys/stat.h>		/* Ditto */
X#include "stevie.h"
X
Xvoid
Xfilemess(s)
Xchar	*s;
X{
X	smsg("\"%s\" %s", (Filename == NULL) ? "" : Filename, s);
X	flushbuf();
X}
X
Xvoid
Xrenum()
X{
X	LPTR	*p;
X	unsigned long l = 0;
X
X	for (p = Filemem; p != NULL ;p = nextline(p), l += LINEINC)
X		p->linep->num = l;
X
X	Fileend->linep->num = 0xffffffff;
X}
X
X#define	MAXLINE	256	/* maximum size of a line */
X
Xbool_t
Xreadfile(fname,fromp,nochangename)
X/*-------------------------------------------------
X * Note that this will try to expand the file name using environment
X * variables.  For this reason, we copy it into an 80-byte buffer,
X * so that there's room to expand it.
X *
X * It uses the environment-variable convention of UNIX, even
X * under systems with other conventions.  That is, your home directory
X * would be called $HOME (even in DOS, where you might want to say %HOME%)
X *-----------------------------------------------------*/
Xchar	*fname;
XLPTR	*fromp;
Xbool_t	nochangename;	/* if TRUE, don't change the Filename */
X{
X	FILE	*f, *fopen();
X	register LINE	*curr;
X	char	buff[MAXLINE], buf2[80];
X	char	namebuf[80];
X	register int	i, c;
X	register long	nchars = 0;
X	int	linecnt = 0;
X	bool_t	wasempty = bufempty();
X	int	nonascii = 0;		/* count garbage characters */
X	int	nulls = 0;		/* count nulls */
X	bool_t	incomplete = FALSE;	/* was the last line incomplete? */
X	bool_t	toolong = FALSE;	/* a line was too long */
X
X	curr = fromp->linep;
X
X	strncpy (namebuf, fname, 80);
X	EnvEval (namebuf, 80);
X
X	if ( ! nochangename )
X		Filename = strsave(namebuf);
X
X	if ( (f=fopen(fixname(namebuf),"r")) == NULL )
X		return TRUE;
X
X	filemess("");
X
X	i = 0;
X	do {
X		c = getc(f);
X
X		if (c == EOF) {
X			if (i == 0)	/* normal loop termination */
X				break;
X
X			/*
X			 * If we get EOF in the middle of a line, note the
X			 * fact and complete the line ourselves.
X			 */
X			incomplete = TRUE;
X			c = NL;
X		}
X
X		/*
X		 * Abort if we get an interrupt, but finished reading the
X		 * current line first.
X		 */
X		if (got_int && i == 0)
X			break;
X
X		if (c >= 0x80) {
X			c -= 0x80;
X			nonascii++;
X		}
X
X		/*
X		 * If we reached the end of the line, OR we ran out of
X		 * space for it, then process the complete line.
X		 */
X		if (c == NL || i == (MAXLINE-1)) {
X			LINE	*lp;
X
X			if (c != NL)
X				toolong = TRUE;
X
X			buff[i] = '\0';
X			if ((lp = newline(strlen(buff))) == NULL)
X				exit(1);
X
X			strcpy(lp->s, buff);
X
X			curr->next->prev = lp;	/* new line to next one */
X			lp->next = curr->next;
X
X			curr->next = lp;	/* new line to prior one */
X			lp->prev = curr;
X
X			curr = lp;		/* new line becomes current */
X			i = 0;
X			linecnt++;
X
X		} else if (c == NUL)
X			nulls++;		/* count and ignore nulls */
X		else {
X			buff[i++] = c;		/* normal character */
X		}
X
X		nchars++;
X
X	} while (!incomplete && !toolong);
X
X	fclose(f);
X
X	/*
X	 * If the buffer was empty when we started, we have to go back
X	 * and remove the "dummy" line at Filemem and patch up the ptrs.
X	 */
X	if (wasempty && nchars != 0) {
X		LINE	*dummy = Filemem->linep;	/* dummy line ptr */
X
X		free(dummy->s);				/* free string space */
X		Filemem->linep = Filemem->linep->next;
X		free((char *)dummy);			/* free LINE struct */
X		Filemem->linep->prev = Filetop->linep;
X		Filetop->linep->next = Filemem->linep;
X
X		Curschar->linep = Filemem->linep;
X		Topchar->linep  = Filemem->linep;
X	}
X
X	renum();
X
X	if (got_int) {
X		smsg("\"%s\" Interrupt", namebuf);
X		got_int = FALSE;
X		return FALSE;		/* an interrupt isn't really an error */
X	}
X
X	if (toolong) {
X		smsg("\"%s\" Line too long", namebuf);
X		return FALSE;
X	}
X
X	sprintf(buff, "\"%s\" %s%d line%s, %ld character%s",
X		namebuf,
X		incomplete ? "[Incomplete last line] " : "",
X		linecnt, (linecnt != 1) ? "s" : "",
X		nchars, (nchars != 1) ? "s" : "");
X
X	buf2[0] = NUL;
X
X	if (nonascii || nulls) {
X		if (nonascii) {
X			if (nulls)
X				sprintf(buf2, " (%d null, %d non-ASCII)",
X					nulls, nonascii);
X			else
X				sprintf(buf2, " (%d non-ASCII)", nonascii);
X		} else
X			sprintf(buf2, " (%d null)", nulls);
X	}
X	strcat(buff, buf2);
X	msg(buff);
X
X	return FALSE;
X}
X
X
X/*
X * writeit - write to file 'fname' lines 'start' through 'end'
X *
X * If either 'start' or 'end' contain null line pointers, the default
X * is to use the start or end of the file respectively.
X */
Xbool_t
Xwriteit(fname, start, end)
Xchar	*fname;
XLPTR	*start, *end;
X{
X	FILE	*f, *fopen();
X	FILE	*fopenb();		/* open in binary mode, where needed */
X	char	*backup;
X	register char	*s;
X	register long	nchars;
X	register int	lines;
X	register LPTR	*p;
X	struct stat statbuf;
X	int	    statres;
X
X	smsg("\"%s\"", fname);
X
X	/* Expand any environment variables left in the name.
X	 * fname better be in a variable big enough to handle the
X	 * expansion (80 bytes).
X	 */
X	EnvEval (fname, 80);
X
X	/* If the file already exists, get what we need to know
X	 * (like current mode).
X	 */
X	statres = stat (fname, &statbuf);
X
X	/*
X	 * Form the backup file name - change foo.* to foo.bak
X	 */
X	backup = alloc((unsigned) (strlen(fname) + 5));
X	if (backup == NULL) {
X		emsg("Can't open file for writing!");
X		return FALSE;
X	}
X
X	strcpy(backup, fname);
X	for (s = backup; *s && *s != '.' ;s++)
X		;
X	*s = NUL;
X	strcat(backup, ".bak");
X
X	/*
X	 * Delete any existing backup and move the current version
X	 * to the backup. For safety, we don't remove the backup
X	 * until the write has finished successfully. And if the
X	 * 'backup' option is set, leave it around.
X	 */
X	rename(fname, backup);
X
X
X	f = P(P_CR) ? fopen(fixname(fname), "w") : fopenb(fixname(fname), "w");
X
X	if (f == NULL) {
X		emsg("Can't open file for writing!");
X		free(backup);
X		return FALSE;
X	}
X
X	/*
X	 * If we were given a bound, start there. Otherwise just
X	 * start at the beginning of the file.
X	 */
X	if (start == NULL || start->linep == NULL)
X		p = Filemem;
X	else
X		p = start;
X
X	lines = nchars = 0;
X	do {
X		fprintf(f, "%s\n", p->linep->s);
X		nchars += strlen(p->linep->s) + 1;
X		lines++;
X
X		/*
X		 * If we were given an upper bound, and we just did that
X		 * line, then bag it now.
X		 */
X		if (end != NULL && end->linep != NULL) {
X			if (end->linep == p->linep)
X				break;
X		}
X
X	} while ((p = nextline(p)) != NULL);
X
X	fclose(f);
X	smsg("\"%s\" %d line%s, %ld character%s", fname,
X		lines, (lines > 1) ? "s" : "",
X		nchars, (nchars > 1) ? "s" : "");
X
X	UNCHANGED;
X
X	/*
X	 * Remove the backup unless they want it left around
X	 */
X	if (!P(P_BK))
X		remove(backup);
X
X	free(backup);
X
X	/*
X	 * Set the mode of the new file to agree with the old.
X	 */
X	if (statres==0)
X		chmod (fname, statbuf.st_mode);
X
X	return TRUE;
X}
!EOR!
echo extracting - help.c
sed 's/^X//' > help.c << '!EOR!'
X/* $Header: /nw/tony/src/stevie/src/RCS/help.c,v 1.9 89/08/06 09:50:09 tony Exp $
X *
X * Routine to display a command summary.
X * (Dave Tutelman note:
X *	I added the ability to page backwards and forwards through help.
X *	In order to minimize the abuse to the existing code, I used
X *	"goto"s and labeled each screen.  It's not the way I would have
X *	done help from scratch, but it's not TOO ugly.
X * )
X */
X
X#include <ctype.h>
X#include "stevie.h"
X#include "ascii.h"
X#include "keymap.h"
X
X/* Macro to show help screen 'n'.
X * If C supported label types, it'd be cleaner to do it that way. */
X#define	SHOWHELP( n )	switch(n) {		\
X			case 0: goto Screen0;   \
X			case 1: goto Screen1;	\
X			case 2: goto Screen2;	\
X			case 3: goto Screen3;	\
X			case 4: goto Screen4;	\
X			case 5: goto Screen5;	\
X			case 6: goto Screen6;	\
X			case 7: goto Screen7;	\
X			case 8: goto Screen8;   \
X			default: return (TRUE);	}
X
Xextern	char	*Version;
X
Xstatic	int	helprow;
Xstatic	int	lastscreen = 0;		/* return to help in previous screen */
X
X#ifdef	HELP
X
Xstatic	void	longline();
X
Xbool_t
Xhelp()
X{
X	int k;
X
X	SHOWHELP( lastscreen );		/* where did we quit help last ? */
X
X/***********************************************************************
X * Zeroth Screen : Index to the help screens.
X ***********************************************************************/
X
XScreen0:
X	CLS;
X	windgoto(helprow = 0, 0);
X
Xlongline("\
X   Index to HELP Screens\n\
X   =====================\n\n");
Xlongline("\
X      0    Help index  (this screen)\n\
X      1    Positioning within file, adjusting the screen\n\
X      2    Character positioning\n\
X      3    Line positioning, marking & returning, undo & redo\n");
Xlongline("\
X      4    Insert & replace, words, sentences, paragraphs\n\
X      5    Operators, miscellaneous operations, yank & put\n\
X      6    \"Ex\" command line operations\n\
X      7    Set parameters\n\
X      8    System-specific features\n");
X
X	windgoto(0, 52);
X	longline(Version);
X
X	SHOWHELP( helpkey (0) );
X
X
X/***********************************************************************
X * First Screen:   Positioning within file, Adjusting the Screen
X ***********************************************************************/
X
XScreen1:
X	CLS;
X	windgoto(helprow = 0, 0);
X
Xlongline("\
X   Positioning within file\n\
X   =======================\n\
X      ^F             Forward screenfull\n\
X      ^B             Backward screenfull\n");
Xlongline("\
X      ^D             scroll down half screen\n\
X      ^U             scroll up half screen\n");
Xlongline("\
X      G              Goto line (end default)\n\
X      ]]             next function\n\
X      [[             previous function\n\
X      /re            next occurence of regular expression 're'\n");
Xlongline("\
X      ?re            prior occurence of regular expression 're'\n\
X      n              repeat last / or ?\n\
X      N              reverse last / or ?\n\
X      %              find matching (, ), {, }, [, or ]\n");
Xlongline("\
X\n\
X   Adjusting the screen\n\
X   ====================\n\
X      ^L             Redraw the screen\n\
X      ^E             scroll window down 1 line\n\
X      ^Y             scroll window up 1 line\n");
Xlongline("\
X      z<RETURN>      redraw, current line at top\n\
X      z-             ... at bottom\n\
X      z.             ... at center\n");
X
X	SHOWHELP( helpkey (1) );
X
X
X/***********************************************************************
X * Second Screen:   Character positioning
X ***********************************************************************/
X
XScreen2:
X	CLS;
X	windgoto(helprow = 0, 0);
X
Xlongline("\
X   Character Positioning\n\
X   =====================\n\
X      ^              first non-white\n\
X      0              beginning of line\n\
X      $              end of line\n\
X      h              backward\n");
Xlongline("\
X      l              forward\n\
X      ^H             same as h\n\
X      space          same as l\n\
X      fx             find 'x' forward\n");
Xlongline("\
X      Fx             find 'x' backward\n\
X      tx             upto 'x' forward\n\
X      Tx             upto 'x' backward\n\
X      ;              Repeat last f, F, t, or T\n");
Xlongline("\
X      ,              inverse of ;\n\
X      |              to specified column\n\
X      %              find matching (, ), {, }, [, or ]\n");
X
X	SHOWHELP( helpkey (2) );
X
X
X/***********************************************************************
X * Third Screen:   Line Positioning, Marking and Returning
X ***********************************************************************/
X
XScreen3:
X	CLS;
X	windgoto(helprow = 0, 0);
X
Xlongline("\
X    Line Positioning\n\
X    ================\n\
X    H           home window line\n\
X    L           last window line\n\
X    M           middle window line\n");
Xlongline("\
X    +           next line, at first non-white\n\
X    -           previous line, at first non-white\n\
X    CR          return, same as +\n\
X    j           next line, same column\n\
X    k           previous line, same column\n");
X
Xlongline("\
X\n\
X    Marking and Returning\n\
X    =====================\n\
X    ``          previous context\n\
X    ''          ... at first non-white in line\n");
Xlongline("\
X    mx          mark position with letter 'x'\n\
X    `x          to mark 'x'\n\
X    'x          ... at first non-white in line\n");
X
Xlongline("\n\
X    Undo  &  Redo\n\
X    =============\n\
X    u           undo last change\n\
X    U           restore current line\n\
X    .           repeat last change\n");
X
X	SHOWHELP( helpkey (3) );
X
X
X/***********************************************************************
X * Fourth Screen:   Insert & Replace,
X ***********************************************************************/
X
XScreen4:
X	CLS;
X	windgoto(helprow = 0, 0);
X
Xlongline("\
X    Insert and Replace\n\
X    ==================\n\
X    a           append after cursor\n\
X    i           insert before cursor\n\
X    A           append at end of line\n\
X    I           insert before first non-blank\n");
Xlongline("\
X    o           open line below\n\
X    O           open line above\n\
X    rx          replace single char with 'x'\n\
X    R           replace characters\n");
Xif (! P(P_TO))
Xlongline("\
X    ~           change case (upper/lower) of single char\n");
X
Xlongline("\
X\n\
X    Words, sentences, paragraphs\n\
X    ============================\n\
X    w           word forward\n\
X    b           back word\n\
X    e           end of word\n\
X    )           to next sentence\n\
X    }           to next paragraph\n");
Xlongline("\
X    (           back sentence\n\
X    {           back paragraph\n\
X    W           blank delimited word\n\
X    B           back W\n\
X    E           to end of W\n");
X
X	SHOWHELP( helpkey (4) );
X
X
X/***********************************************************************
X * Fifth Screen:   Operators, Misc. operations, Yank & Put
X ***********************************************************************/
X
XScreen5:
X	CLS;
X	windgoto(helprow = 0, 0);
X
Xlongline("\
X    Operators (double to affect lines)\n\
X    ==================================\n\
X    d           delete\n\
X    c           change\n");
Xlongline("\
X    <           left shift\n\
X    >           right shift\n\
X    y           yank to buffer\n\
X    !           filter lines (command name follows)\n");
Xif (P(P_TO))
Xlongline("\
X    ~           reverse case (upper/lower)\n");
X
Xlongline("\n\
X    Miscellaneous operations\n\
X    ========================\n\
X    C           change rest of line\n\
X    D           delete rest of line\n\
X    s           substitute chars\n");
Xlongline("\
X    S           substitute lines (not yet)\n\
X    J           join lines\n\
X    x           delete characters\n\
X    X           ... before cursor\n");
X
Xlongline("\n\
X    Yank and Put\n\
X    ============\n\
X    p           put back text\n\
X    P           put before\n\
X    Y           yank lines");
X
X	SHOWHELP( helpkey (5) );
X
X
X/***********************************************************************
X * Sixth Screen:   Command-line operations
X ***********************************************************************/
X
XScreen