[comp.sources.amiga] v89i132: stevie - vi editor clone v3.6, Part03/06

page%swap@Sun.COM (Bob Page) (05/12/89)

Submitted-by: grwalter@watmath.waterloo.edu (Fred Walter)
Posting-number: Volume 89, Issue 132
Archive-name: editors/stevie36.3

# This is a shell archive.
# Remove anything above and including the cut line.
# Then run the rest of the file through 'sh'.
# Unpacked files will be owned by you and have default permissions.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: SHell ARchive
# Run the following text through 'sh' to create:
#	makefile.amiga.lattice
#	makefile.bsd
#	makefile.dos
#	makefile.mwc
#	makefile.os2
#	makefile.tos
#	makefile.usg
#	mark.c
#	misccmds.c
#	mk.c
#	normal.c
# This is archive 3 of a 6-part kit.
# This archive created: Thu May 11 19:41:26 1989
echo "extracting makefile.amiga.lattice"
sed 's/^X//' << \SHAR_EOF > makefile.amiga.lattice
X#
X# Makefile for Lattice C 5.0 on Amiga
X#
X
X.c.o:
X	lc $(CFLAGS) $<
X
X#CFLAGS = -cu -ma -DAMIGA
XCFLAGS = -cu -ma -O -DAMIGA
XLINKFLAGS = NODEBUG
XLIBS = lib:lc.lib
X
XMACH=	amiga.o
X
XOBJ1=	main.o edit.o linefunc.o normal.o cmdline.o charset.o
XOBJ2=	format_l.o misccmds.o help.o dec.o inc.o search.o alloc.o
XOBJ3=	mk.o regexp.o regsub.o version.o
XOBJ4=	s_io.o mark.o screen.o fileio.o param.o
X
XOBJ= $(OBJ1) $(OBJ2) $(OBJ3) $(OBJ4) $(MACH)
X
Xall: stevie
X	say "done all"
X
Xstevie: $(OBJ) Makefile
X	BLINK TO stevie FROM lib:cres.o $(OBJ) LIBRARY $(LIBS) $(LINKFLAGS)
X
Xclean:
X	delete $(OBJ1)
X	delete $(OBJ2)
X	delete $(OBJ3)
X	delete $(OBJ4)
X	delete $(MACH)
X	delete stevie
SHAR_EOF
echo "extracting makefile.bsd"
sed 's/^X//' << \SHAR_EOF > makefile.bsd
X#
X# Makefile for BSD 4.3 UNIX
X#
X
XCFLAGS = -O -DBSD
X#CFLAGS = -pg -g -DBSD
XLINTFLAGS = -DBSD
X
XMACHOBJ=	bsd.o
XMACHSRC=	bsd.c
X
XSRC=	main.c edit.c linefunc.c normal.c cmdline.c charset.c \
X	misccmds.c help.c dec.c inc.c search.c alloc.c \
X	format_l.c mk.c regexp.c regsub.c version.c \
X	s_io.c mark.c screen.c fileio.c param.c $(MACHSRC)
X
XOBJ=	main.o edit.o linefunc.o normal.o cmdline.o charset.o \
X	misccmds.o help.o dec.o inc.o search.o alloc.o \
X	format_l.o mk.o regexp.o regsub.o version.o \
X	s_io.o mark.o screen.o fileio.o param.o $(MACHOBJ)
X
Xall : stevie
X
Xstevie : $(OBJ)
X	$(CC) $(OBJ) $(CFLAGS) -o stevie
X
Xlint:
X	lint $(LINTFLAGS) $(SRC)
X
Xclean :
X	rm -f *.out *.o core stevie *.BAK
SHAR_EOF
echo "extracting makefile.dos"
sed 's/^X//' << \SHAR_EOF > makefile.dos
X#
X# Makefile for DOS
X#
X# This makefile is set up for Microsoft C 5.1
X#
X
X#
X# Compact model lets us edit large files, but keep small model code
X#
XMODEL= /AC
XCFLAGS = $(MODEL)
X
XMACH=	dos.obj
X
XOBJ=	main.obj edit.obj linefunc.obj normal.obj cmdline.obj charset.obj \
X	mk.obj format_l.obj regexp.obj regsub.obj version.obj \
X	misccmds.obj help.obj dec.obj inc.obj search.obj alloc.obj \
X	s_io.obj mark.obj screen.obj fileio.obj param.obj $(MACH)
X
Xall:	stevie.exe
X
Xmain.obj:	main.c
X	cl -c $(CFLAGS) main.c
X
Xformat_l.obj:	format_l.c
X	cl -c $(CFLAGS) format_l.c
X
Xmk.obj:	mk.c
X	cl -c $(CFLAGS) mk.c
X
Xregexp.obj:	regexp.c
X	cl -c $(CFLAGS) regexp.c
X
Xregsub.obj:	regsub.c
X	cl -c $(CFLAGS) regsub.c
X
Xversion.obj:	version.c
X	cl -c $(CFLAGS) version.c
X
Xalloc.obj : alloc.c
X	cl -c $(CFLAGS) alloc.c
X
Xedit.obj : edit.c
X	cl -c $(CFLAGS) edit.c
X
Xs_io.obj : s_io.c
X	cl -c $(CFLAGS) s_io.c
X
Xlinefunc.obj : linefunc.c
X	cl -c $(CFLAGS) linefunc.c
X
Xnormal.obj : normal.c
X	cl -c $(CFLAGS) normal.c
X
Xcmdline.obj : cmdline.c
X	cl -c $(CFLAGS) cmdline.c
X
Xcharset.obj : charset.c
X	cl -c $(CFLAGS) charset.c
X
Xmisccmds.obj : misccmds.c
X	cl -c $(CFLAGS) misccmds.c
X
Xhelp.obj : help.c
X	cl -c $(CFLAGS) help.c
X
Xdec.obj : dec.c
X	cl -c $(CFLAGS) dec.c
X
Xinc.obj : inc.c
X	cl -c $(CFLAGS) inc.c
X
Xsearch.obj : search.c
X	cl -c $(CFLAGS) search.c
X
Xmark.obj : mark.c
X	cl -c $(CFLAGS) mark.c
X
Xscreen.obj : screen.c
X	cl -c $(CFLAGS) screen.c
X
Xfileio.obj : fileio.c
X	cl -c $(CFLAGS) fileio.c
X
Xparam.obj : param.c
X	cl -c $(CFLAGS) param.c
X
Xdos.obj : dos.c
X	cl -c $(CFLAGS) dos.c
X
Xstevie.exe : $(OBJ)
X	cl $(MODEL) *.obj c:\lib\setargv.obj -o stevie.exe /F 6000 -link /NOE
SHAR_EOF
echo "extracting makefile.mwc"
sed 's/^X//' << \SHAR_EOF > makefile.mwc
X#
X#
X#
X# Makefile for Mark Williams C
X#
XCFLAGS = -O -VPEEP 
XLDFLAGS = -s -x -v
XLINKER=ld
X
XOBJ1=	main.o edit.o linefunc.o normal.o cmdline.o charset.o \
X	format_l.o misccmds.o help.o dec.o inc.o 
XOBJ2=	alloc.o search.o mk.o regexp.o regsub.o version.o s_io.o \
X	mark.o screen.o fileio.o param.o tos.o
X
XSRC=	main.c edit.c linefunc.c normal.c cmdline.c charset.c \
X	format_l.c misccmds.c help.c dec.c inc.c search.c alloc.c \
X	mk.c regexp.c regsub.c version.c \
X	s_io.c mark.c screen.c fileio.c param.c \
X	tos.c
X
X
Xall : stevie.ttp
X
Xstevie.a: $(OBJ1) $(OBJ2)
X	ar rv stevie.a $(OBJ1)
X	ar rv stevie.a $(OBJ2)
X
Xstevie.ttp:	stevie.a
X	cc stevie.a $(LIBS) -o stevie.ttp
SHAR_EOF
echo "extracting makefile.os2"
sed 's/^X//' << \SHAR_EOF > makefile.os2
X#
X# Makefile for OS/2
X#
X# The make command with OS/2 is really stupid.
X#
X
X#
X# Compact model lets us edit large files, but keep small model code
X#
XMODEL= -AC
XCFLAGS = $(MODEL) -I..\regexp -J
X
XMACH=	os2.obj
X
XOBJ=	main.obj edit.obj linefunc.obj normal.obj cmdline.obj charset.obj \
X	mk.obj format_l.obj regexp.obj regsub.obj version.obj \
X	misccmds.obj help.obj dec.obj inc.obj search.obj alloc.obj \
X	s_io.obj mark.obj screen.obj fileio.obj param.obj $(MACH)
X
Xall:	stevie.exe
X
Xmain.obj:	main.c
X	cl -c $(CFLAGS) main.c
X
Xformat_l.obj:	format_l.c
X	cl -c $(CFLAGS) format_l.c
X
Xmk.obj:	mk.c
X	cl -c $(CFLAGS) mk.c
X
Xregexp.obj:	regexp.c
X	cl -c $(CFLAGS) regexp.c
X
Xregsub.obj:	regsub.c
X	cl -c $(CFLAGS) regsub.c
X
Xversion.obj:	version.c
X	cl -c $(CFLAGS) version.c
X
Xalloc.obj : alloc.c
X	cl -c $(CFLAGS) alloc.c
X
Xedit.obj : edit.c
X	cl -c $(CFLAGS) edit.c
X
Xs_io.obj : s_io.c
X	cl -c $(CFLAGS) s_io.c
X
Xlinefunc.obj : linefunc.c
X	cl -c $(CFLAGS) linefunc.c
X
Xnormal.obj : normal.c
X	cl -c $(CFLAGS) normal.c
X
Xcmdline.obj : cmdline.c
X	cl -c $(CFLAGS) cmdline.c
X
Xcharset.obj : charset.c
X	cl -c $(CFLAGS) charset.c
X
Xmisccmds.obj : misccmds.c
X	cl -c $(CFLAGS) misccmds.c
X
Xhelp.obj : help.c
X	cl -c $(CFLAGS) help.c
X
Xdec.obj : dec.c
X	cl -c $(CFLAGS) dec.c
X
Xinc.obj : inc.c
X	cl -c $(CFLAGS) inc.c
X
Xsearch.obj : search.c
X	cl -c $(CFLAGS) search.c
X
Xmark.obj : mark.c
X	cl -c $(CFLAGS) mark.c
X
Xscreen.obj : screen.c
X	cl -c $(CFLAGS) screen.c
X
Xfileio.obj : fileio.c
X	cl -c $(CFLAGS) fileio.c
X
Xparam.obj : param.c
X	cl -c $(CFLAGS) param.c
X
Xos2.obj : os2.c
X	cl -c $(CFLAGS) os2.c
X
Xstevie.exe : $(OBJ)
X	cl $(MODEL) *.obj $(LIBS) -o stevie.exe /Lo # use protected mode lib.
X	copy stevie.exe rstevie.exe
X	bind rstevie.exe \lib\api.lib \lib\doscalls.lib
SHAR_EOF
echo "extracting makefile.tos"
sed 's/^X//' << \SHAR_EOF > makefile.tos
X#
X# Makefile for the Atari ST - Megamax C compiler
X#
X
XCFLAGS = -DMEGAMAX
X
X#	Megamax rule
X.c.o:
X	mmcc $(CFLAGS) $<
X	mmimp $*.o
X	mmlib rv vi.lib $*.o
X
XMACH=	tos.o
X
XOBJ=	main.o edit.o linefunc.o normal.o cmdline.o charset.o \
X	format_l.o misccmds.o help.o dec.o inc.o search.o alloc.o \
X	mk.o regexp.o regsub.o version.o \
X	s_io.o mark.o screen.o fileio.o param.o $(MACH)
X
Xall : stevie.ttp
X
Xstevie.ttp : $(OBJ)
X	$(LINKER) vi.lib $(LIBS) -o stevie.ttp
X
Xclean :
X	$(RM) $(OBJ) vi.lib
SHAR_EOF
echo "extracting makefile.usg"
sed 's/^X//' << \SHAR_EOF > makefile.usg
X#
X# Makefile for UNIX (System V)
X#
X
XCFLAGS = -O -DUNIX
X
XMACH=	unix.o
X
XOBJ=	main.o edit.o linefunc.o normal.o cmdline.o charset.o \
X	format_l.o misccmds.o help.o dec.o inc.o search.o alloc.o \
X	mk.o regexp.o regsub.o version.o \
X	s_io.o mark.o screen.o fileio.o param.o $(MACH)
X
Xall : stevie
X
Xstevie : $(OBJ)
X	$(CC) $(OBJ) $(LIBS) -o stevie
X
Xclean :
X	rm $(OBJ)
SHAR_EOF
echo "extracting mark.c"
sed 's/^X//' << \SHAR_EOF > mark.c
X/*
X * STEVIE - Simply Try this Editor for VI Enthusiasts
X *
X * Code Contributions By : Tim Thompson           twitch!tjt
X *                         Tony Andrews           onecom!wldrdg!tony 
X *                         G. R. (Fred) Walter    watmath!watcgl!grwalter 
X */
X
X#include "stevie.h"
X
X#ifdef  MEGAMAX
Xoverlay "mark"
X#endif
X
X/*
X * This file contains routines to maintain and manipulate marks. 
X */
X
X#define NMARKS  10		/* max. # of marks that can be saved */
X
Xstruct mark {
X    char            name;
X    LPtr            pos;
X};
X
Xstatic struct mark mlist[NMARKS];
Xstatic struct mark pcmark;	/* previous context mark */
Xstatic bool_t   pcvalid = FALSE;/* true if pcmark is valid */
X
X/*
X * setmark(c) - set mark 'c' at current cursor position 
X *
X * Returns TRUE on success, FALSE if no room for mark or bad name given. 
X */
Xbool_t
Xsetmark(c)
X    char            c;
X{
X    int             i;
X
X    if (!isalpha(c))
X	return FALSE;
X
X    /*
X     * If there is already a mark of this name, then just use the existing
X     * mark entry. 
X     */
X    for (i = 0; i < NMARKS; i++) {
X	if (mlist[i].name == c) {
X	    mlist[i].pos = *Curschar;
X	    return TRUE;
X	}
X    }
X
X    /*
X     * There wasn't a mark of the given name, so find a free slot 
X     */
X    for (i = 0; i < NMARKS; i++) {
X	if (mlist[i].name == NUL) {	/* got a free one */
X	    mlist[i].name = c;
X	    mlist[i].pos = *Curschar;
X	    return TRUE;
X	}
X    }
X    return FALSE;
X}
X
X/*
X * setpcmark() - set the previous context mark to the current position 
X */
Xvoid
Xsetpcmark()
X{
X    pcmark.pos = *Curschar;
X    pcvalid = TRUE;
X}
X
X/*
X * getmark(c) - find mark for char 'c' 
X *
X * Return pointer to LPtr or NULL if no such mark. 
X */
XLPtr           *
Xgetmark(c)
X    char            c;
X{
X    int             i;
X
X    if (c == '\'' || c == '`')	/* previous context mark */
X	return pcvalid ? &(pcmark.pos) : (LPtr *) NULL;
X
X    for (i = 0; i < NMARKS; i++) {
X	if (mlist[i].name == c)
X	    return &(mlist[i].pos);
X    }
X    return (LPtr *) NULL;
X}
X
X/*
X * clrall() - clear all marks 
X *
X * Used mainly when trashing the entire buffer during ":e" type commands 
X */
Xvoid
Xclrall()
X{
X    int             i;
X
X    for (i = 0; i < NMARKS; i++)
X	mlist[i].name = NUL;
X    pcvalid = FALSE;
X}
X
X/*
X * clrmark(line) - clear any marks for 'line' 
X *
X * Used any time a line is deleted so we don't have marks pointing to
X * non-existent lines. 
X */
Xvoid
Xclrmark(line)
X    LINE           *line;
X{
X    int             i;
X
X    for (i = 0; i < NMARKS; i++) {
X	if (mlist[i].pos.linep == line)
X	    mlist[i].name = NUL;
X    }
X    if (pcvalid && (pcmark.pos.linep == line))
X	pcvalid = FALSE;
X}
SHAR_EOF
echo "extracting misccmds.c"
sed 's/^X//' << \SHAR_EOF > misccmds.c
X/*
X * STEVIE - Simply Try this Editor for VI Enthusiasts
X *
X * Code Contributions By : Tim Thompson           twitch!tjt
X *                         Tony Andrews           onecom!wldrdg!tony 
X *                         G. R. (Fred) Walter    watmath!watcgl!grwalter 
X */
X
X#include "stevie.h"
X
Xextern int      did_ai;
X
X/*
X * OpenForward 
X *
X * Add a blank line below the current line. 
X */
X
Xbool_t
XOpenForward(can_ai)
X    int             can_ai;
X{
X    LINE           *l;
X    LPtr           *next;
X    char           *s;		/* string to be moved to new line, if any */
X    int             newindex = 0;	/* index of the cursor on the new
X					 * line */
X
X    /*
X     * If we're in insert mode, we need to move the remainder of the current
X     * line onto the new line. Otherwise the new line is left blank. 
X     */
X    if (State == INSERT)
X	s = &Curschar->linep->s[Curschar->index];
X    else
X	s = "";
X
X    if ((next = nextline(Curschar)) == NULL)	/* open on last line */
X	next = Fileend;
X
X    /*
X     * By asking for as much space as the prior line had we make sure that
X     * we'll have enough space for any auto-indenting. 
X     */
X    l = newline(strlen(Curschar->linep->s) + SLOP);
X    if (l == NULL) {
X	emsg("out of memory");
X	beep();
X	sleep(2);
X	return (FALSE);
X    }
X    if (can_ai && P(P_AI)) {
X	char           *p;
X
X	/*
X	 * Copy prior line, and truncate after white space 
X	 */
X	strcpy(l->s, Curschar->linep->s);
X
X	for (p = l->s; *p == ' ' || *p == TAB; p++);
X	*p = NUL;
X	newindex = p - l->s;
X	AppendToInsbuff(l->s);
X	if (*s != NUL)
X	    strcat(l->s, s);
X
X	/*
X	 * If we just did an auto-indent, then we didn't type anything on the
X	 * prior line, and it should be truncated. 
X	 */
X	if (did_ai)
X	    Curschar->linep->s[0] = NUL;
X
X	did_ai = TRUE;
X    } else if (*s != NUL) {
X	strcpy(l->s, s);	/* copy string to new line */
X    }
X    if (State == INSERT)	/* truncate current line at cursor */
X	*s = NUL;
X
X    Curschar->linep->next = l;	/* link neighbors to new line */
X    next->linep->prev = l;
X
X    l->prev = Curschar->linep;	/* link new line to neighbors */
X    l->next = next->linep;
X
X    if (next == Fileend) {	/* new line at end */
X	l->num = Curschar->linep->num + LINEINC;
X    } else if ((l->prev->num) + 1 == l->next->num) {	/* no gap, renumber */
X	renum();
X    } else {			/* stick it in the middle */
X	long            lnum;
X
X	lnum = (l->prev->num + l->next->num) / 2;
X	l->num = lnum;
X    }
X
X    *Curschar = *nextline(Curschar);	/* cursor moves down */
X    Curschar->index = newindex;
X
X    S_NOT_VALID;
X    CHANGED;
X
X    return (TRUE);
X}
X
X/*
X * OpenBackward 
X *
X * Add a blank line above the current line. 
X */
X
Xbool_t
XOpenBackward(can_ai)
X    int             can_ai;
X{
X    LINE           *l;
X    LINE           *prev;
X    int             newindex = 0;	/* index of the cursor on the new
X					 * line */
X
X    prev = Curschar->linep->prev;
X
X    l = newline(strlen(Curschar->linep->s) + SLOP);
X    if (l == NULL) {
X	emsg("out of memory");
X	beep();
X	sleep(2);
X	return (FALSE);
X    }
X    Curschar->linep->prev = l;	/* link neighbors to new line */
X    prev->next = l;
X
X    l->next = Curschar->linep;	/* link new line to neighbors */
X    l->prev = prev;
X
X    if (can_ai && P(P_AI)) {
X	char           *p;
X
X	/*
X	 * Copy current line, and truncate after white space 
X	 */
X	strcpy(l->s, Curschar->linep->s);
X
X	for (p = l->s; *p == ' ' || *p == TAB; p++);
X	*p = NUL;
X	newindex = p - l->s;
X	AppendToInsbuff(l->s);
X
X	did_ai = TRUE;
X    }
X    Curschar->linep = Curschar->linep->prev;
X    Curschar->index = newindex;
X
X    if (prev == Filetop->linep) {	/* new start of file */
X	Filemem->linep = l;
X	renum();
X    } else if ((l->prev->num) + 1 == l->next->num) {	/* no gap, renumber */
X	renum();
X    } else {			/* stick it in the middle */
X	long            lnum;
X
X	lnum = (l->prev->num + l->next->num) / 2;
X	l->num = lnum;
X    }
X
X    S_NOT_VALID;
X    CHANGED;
X
X    return (TRUE);
X}
X
Xint
Xcntllines(pbegin, pend)
X    LPtr           *pbegin, *pend;
X{
X    register LINE  *lp;
X    register int    lnum = 1;
X
X    for (lp = pbegin->linep; lp != pend->linep; lp = lp->next)
X	lnum++;
X
X    return (lnum);
X}
X
X/*
X * plines(s) - return the number of physical screen lines taken by the
X *             line pointed to by 's'
X */
X
Xint
Xplines(s)
X    register char  *s;
X{
X    register int    col = 0;
X
X    if (*s == NUL)		/* empty line */
X	return 1;
X
X    for (; *s != NUL; s++) {
X	if (*s == TAB && !P(P_LS))
X	    col += P(P_TS) - (col % P(P_TS));
X	else
X	    col += chars[*s].ch_size;
X    }
X
X    /*
X     * If list mode is on, then the '$' at the end of the line takes up one
X     * extra column. 
X     */
X    if (P(P_LS))
X	col += 1;
X
X    /*
X     * If 'number' mode is on, add another 8. 
X     */
X    if (P(P_NU))
X	col += 8;
X
X    return ((col + (Columns - 1)) / Columns);
X}
X
Xvoid
Xfileinfo()
X{
X    long            l1, l2;
X    char            buf[MAX_COLUMNS + 1];
X
X    if (bufempty()) {
X	msg("Buffer Empty");
X	return;
X    }
X    l1 = cntllines(Filemem, Curschar);
X    l2 = cntllines(Filemem, Fileend) - 1;
X    sprintf(buf, "\"%s\"%s line %ld of %ld -- %ld %% --",
X	    (Filename != NULL) ? Filename : "No File",
X	    Changed ? " [Modified]" : "",
X	    l1, l2, (l1 * 100) / l2);
X    msg(buf);
X}
X
X/*
X * gotoline(n) - return a pointer to line 'n' 
X *
X * Returns a pointer to the last line of the file if n is zero, or beyond the
X * end of the file. 
X */
XLPtr           *
Xgotoline(n)
X    int             n;
X{
X    static LPtr     l;
X
X    l.index = 0;
X
X    if (n == 0)
X	l = *prevline(Fileend);
X    else {
X	LPtr           *p;
X
X	for (l = *Filemem; --n > 0; l = *p)
X	    if ((p = nextline(&l)) == NULL)
X		break;
X    }
X    return &l;
X}
X
Xvoid
Xinschar(c)
X    char            c;
X{
X    register char  *p;
X    register char  *pend;
X
X    /* make room for the new char. */
X    if (!canincrease(1))
X	return;
X
X    p = &Curschar->linep->s[strlen(Curschar->linep->s) + 1];
X    pend = &Curschar->linep->s[Curschar->index];
X
X    for (; p > pend; p--)
X	*p = *(p - 1);
X
X    *p = c;
X
X    if (RedrawingDisabled) {
X	Curschar->index++;
X	return;
X    }
X    /*
X     * If we're in insert mode and showmatch mode is set, then check for
X     * right parens and braces. If there isn't a match, then beep. If there
X     * is a match AND it's on the screen, then flash to it briefly. If it
X     * isn't on the screen, don't do anything. 
X     */
X    if (P(P_SM) && State == INSERT && (c == ')' || c == '}' || c == ']')) {
X	LPtr           *lpos, csave;
X
X	if ((lpos = showmatch()) == NULL)	/* no match, so beep */
X	    beep();
X	else if (LINEOF(lpos) >= LINEOF(Topchar)) {
X	    /* show the new char first */
X	    s_refresh(VALID_TO_CURSCHAR);
X	    csave = *Curschar;
X	    *Curschar = *lpos;	/* move to matching char */
X	    cursupdate(UPDATE_CURSOR);
X	    windgoto(Cursrow, Curscol);
X	    delay();		/* brief pause */
X	    *Curschar = csave;	/* restore cursor position */
X	    cursupdate(UPDATE_ALL);
X	}
X    }
X    inc(Curschar);
X
X    CHANGED;
X}
X
Xvoid
Xinsstr(s)
X    register char  *s;
X{
X    register char  *p;
X    register char  *pend;
X    register int    n = strlen(s);
X
X    /* Move everything in the file over to make */
X    /* room for the new string. */
X    if (!canincrease(n))
X	return;
X
X    p = &Curschar->linep->s[strlen(Curschar->linep->s) + n];
X    pend = &Curschar->linep->s[Curschar->index];
X
X    for (; p > pend; p--)
X	*p = *(p - n);
X
X    for (; n > 0; n--) {
X	*p++ = *s++;
X	Curschar->index++;
X    }
X    CHANGED;
X}
X
Xbool_t
Xdelchar(fixpos, undo)
X    bool_t          fixpos;	/* if TRUE fix the cursor position when done */
X    bool_t          undo;	/* if TRUE put char deleted into Undo buffer */
X{
X    int             i;
X
X    /* Check for degenerate case; there's nothing in the file. */
X    if (bufempty())
X	return FALSE;
X
X    if (lineempty(Curschar))	/* can't do anything */
X	return FALSE;
X
X    if (undo)
X	AppendToUndobuff(mkstr(gchar(Curschar)));
X
X    /* Delete the char. at Curschar by shifting everything in the line down. */
X    for (i = Curschar->index + 1; i < Curschar->linep->size; i++)
X	Curschar->linep->s[i - 1] = Curschar->linep->s[i];
X
X    /*
X     * If we just took off the last character of a non-blank line, we don't
X     * want to end up positioned at the newline. 
X     */
X    if (fixpos) {
X	if (gchar(Curschar) == NUL && Curschar->index > 0 && State != INSERT)
X	    Curschar->index--;
X    }
X    CHANGED;
X    return TRUE;
X}
X
Xvoid
Xdelline(nlines)
X    int             nlines;
X{
X    register LINE  *p;
X    register LINE  *q;
X
X    while (nlines-- > 0) {
X
X	if (bufempty())		/* nothing to delete */
X	    break;
X
X	if (buf1line()) {	/* just clear the line */
X	    Curschar->linep->s[0] = NUL;
X	    Curschar->index = 0;
X	    break;
X	}
X	p = Curschar->linep->prev;
X	q = Curschar->linep->next;
X
X	if (p == Filetop->linep) {	/* first line of file so... */
X	    Filemem->linep = q;	/* adjust start of file */
X	    Topchar->linep = q;	/* and screen */
X	}
X	p->next = q;
X	q->prev = p;
X
X	clrmark(Curschar->linep);	/* clear marks for the line */
X
X	/*
X	 * If deleting the top line on the screen, adjust Topchar 
X	 */
X	if (Topchar->linep == Curschar->linep)
X	    Topchar->linep = q;
X
X	free(Curschar->linep->s);
X	free((char *) (Curschar->linep));
X
X	Curschar->linep = q;
X	Curschar->index = 0;	/* is this right? */
X
X	S_NOT_VALID;
X	CHANGED;
X
X	/* If we delete the last line in the file, back up */
X	if (Curschar->linep == Fileend->linep) {
X	    Curschar->linep = Curschar->linep->prev;
X	    /* and don't try to delete any more lines */
X	    break;
X	}
X    }
X}
SHAR_EOF
echo "extracting mk.c"
sed 's/^X//' << \SHAR_EOF > mk.c
X/*
X * STEVIE - Simply Try this Editor for VI Enthusiasts
X *
X * Code Contributions By : Tim Thompson           twitch!tjt
X *                         Tony Andrews           onecom!wldrdg!tony 
X *                         G. R. (Fred) Walter    watmath!watcgl!grwalter 
X */
X
X#include "stevie.h"
X
Xchar           *
Xmkline(n)
X    int             n;
X{
X    static char     lbuf[9];
X    int             i = 6;
X
X    strcpy(lbuf, "        ");
X
X    lbuf[i--] = (char) ((n % 10) + '0');
X    n /= 10;
X    if (n != 0) {
X	lbuf[i--] = (char) ((n % 10) + '0');
X	n /= 10;
X    }
X    if (n != 0) {
X	lbuf[i--] = (char) ((n % 10) + '0');
X	n /= 10;
X    }
X    if (n != 0) {
X	lbuf[i--] = (char) ((n % 10) + '0');
X	n /= 10;
X    }
X    if (n != 0)
X	lbuf[i] = (char) ((n % 10) + '0');
X
X    return lbuf;
X}
X
Xchar           *
Xmkstr(c)
X    char            c;
X{
X    static char     s[2];
X
X    s[0] = c;
X    s[1] = NUL;
X
X    return s;
X}
SHAR_EOF
echo "extracting normal.c"
sed 's/^X//' << \SHAR_EOF > normal.c
X/*
X * STEVIE - Simply Try this Editor for VI Enthusiasts
X *
X * Code Contributions By : Tim Thompson           twitch!tjt
X *                         Tony Andrews           onecom!wldrdg!tony 
X *                         G. R. (Fred) Walter    watmath!watcgl!grwalter 
X */
X
X/*
X * This file contains the main routine for processing characters in command
X * mode as well as routines for handling the operators. 
X */
X
X#include "stevie.h"
X
Xstatic void
Xdoshift(), dodelete(), doput(), dochange();
Xstatic void
X                startinsert();
Xstatic          bool_t
X                dojoin();
Xstatic          bool_t
X                doyank();
X
X/*
X * Macro evaluates true if char 'c' is a valid identifier character 
X */
X#define IDCHAR(c)       (isalpha(c) || isdigit(c) || (c) == '_')
X
X/*
X * Operators 
X */
X#define NOP     0		/* no pending operation */
X#define DELETE  1
X#define YANK    2
X#define CHANGE  3
X#define LSHIFT  4
X#define RSHIFT  5
X
X#define CLEAROP (operator = NOP)/* clear any pending operator */
X
Xstatic int      operator = NOP;	/* current pending operator */
X
X/*
X * When a cursor motion command is made, it is marked as being a character or
X * line oriented motion. Then, if an operator is in effect, the operation
X * becomes character or line oriented accordingly. 
X *
X * Character motions are marked as being inclusive or not. Most char. motions
X * are inclusive, but some (e.g. 'w') are not. 
X *
X * Generally speaking, every command in normal() should either clear any pending
X * operator (with CLEAROP), or set the motion type variable. 
X */
X
X/*
X * Motion types 
X */
X#define MBAD    (-1)		/* 'bad' motion type marks unusable yank buf */
X#define MCHAR   0
X#define MLINE   1
X
Xstatic int      mtype;		/* type of the current cursor motion */
Xstatic bool_t   mincl;		/* true if char motion is inclusive */
Xstatic int      ybtype = MBAD;
Xstatic int      ybcrossline = FALSE;
X
Xstatic LPtr     startop;	/* cursor pos. at start of operator */
X
X/*
X * Operators can have counts either before the operator, or between the
X * operator and the following cursor motion as in: 
X *
X * d3w or 3dw 
X *
X * If a count is given before the operator, it is saved in opnum. If normal() is
X * called with a pending operator, the count in opnum (if present) overrides
X * any count that came later. 
X */
Xstatic int      opnum = 0;
X
X#define DEFAULT1(x)     (((x) == 0) ? 1 : (x))
X
X/*
X * normal 
X *
X * Execute a command in normal mode. 
X */
X
Xvoid
Xnormal(c)
X    char            c;
X{
X    char           *p;
X    int             n;
X    int             nn;
X    bool_t          flag = FALSE;
X    int             type = 0;	/* used in some operations to modify type */
X    int             dir = FORWARD;	/* search direction */
X    char            nchar = NUL;
X    bool_t          finish_op;
X    LPtr            temp_Curschar;
X
X    last_command = NUL;
X    /*
X     * If there is an operator pending, then the command we take this time
X     * will terminate it. Finish_op tells us to finish the operation before
X     * returning this time (unless the operation was cancelled). 
X     */
X    finish_op = (operator != NOP);
X
X    /*
X     * If we're in the middle of an operator AND we had a count before the
X     * operator, then that count overrides the current value of Prenum. What
X     * this means effectively, is that commands like "3dw" get turned into
X     * "d3w" which makes things fall into place pretty neatly. 
X     */
X    if (finish_op) {
X	if (opnum != 0)
X	    Prenum = opnum;
X    } else
X	opnum = 0;
X
X    switch (c) {
X
X      case K_HELP:
X	CLEAROP;
X	if (help())
X	    s_clear();
X	break;
X
X      case CTRL('L'):
X	CLEAROP;
X	s_clear();
X	break;
X
X      case CTRL('D'):
X	CLEAROP;
X	if (Prenum)
X	    P(P_SS) = (Prenum > Rows - 1) ? Rows - 1 : Prenum;
X	scrollup((P(P_SS) < Rows) ? P(P_SS) : Rows - 1);
X	onedown((P(P_SS) < Rows) ? P(P_SS) : Rows - 1);
X	break;
X
X      case CTRL('U'):
X	CLEAROP;
X	if (Prenum)
X	    P(P_SS) = (Prenum > Rows - 1) ? Rows - 1 : Prenum;
X	scrolldown((P(P_SS) < Rows) ? P(P_SS) : Rows - 1);
X	oneup((P(P_SS) < Rows) ? P(P_SS) : Rows - 1);
X	break;
X
X      case CTRL('F'):
X	CLEAROP;
X	if (nextline(Topchar) == NULL) {
X	    beep();
X	    break;
X	}
X	Prenum = DEFAULT1(Prenum);
X	while (Prenum > 0) {
X	    *Curschar = *prevline(Botchar);
X	    *Topchar = *Curschar;
X	    Topchar->index = 0;
X	    Update_Botchar();
X	    Prenum--;
X	}
X	beginline(TRUE);
X	s_clear();
X	break;
X
X      case CTRL('B'):
X	CLEAROP;
X	if (prevline(Topchar) == NULL) {
X	    beep();
X	    break;
X	}
X	Prenum = DEFAULT1(Prenum);
X	while (Prenum > 0) {
X	    *Curschar = *Topchar;
X	    n = Rows - 1;
X	    {
X		LPtr           *lp = Curschar;
X		int             l = 0;
X
X		while ((l < n) && (lp != NULL)) {
X		    l += plines(lp->linep->s);
X		    *Topchar = *lp;
X		    lp = prevline(lp);
X		}
X	    }
X	    Topchar->index = 0;
X	    Prenum--;
X	}
X	beginline(TRUE);
X	s_clear();
X	break;
X
X      case CTRL('E'):
X	CLEAROP;
X	scrollup(DEFAULT1(Prenum));
X        if (LINEOF(Curschar) < LINEOF(Topchar))
X	    Curschar->linep = Topchar->linep;
X	break;
X
X      case CTRL('Y'):
X	CLEAROP;
X	scrolldown(DEFAULT1(Prenum));
X	Update_Botchar();
X	if (LINEOF(Curschar) >= LINEOF(Botchar)) {
X	    LPtr           *lp;
X
X	    lp = prevline(Botchar);
X	    if (lp == NULL)
X		lp = Topchar;
X	    Curschar->linep = lp->linep;
X	}
X	break;
X
X      case 'z':
X	CLEAROP;
X	S_CHECK_TOPCHAR_AND_BOTCHAR;
X	switch (vgetc()) {
X	  case NL:		/* put Curschar at top of screen */
X	  case CR:
X	    *Topchar = *Curschar;
X	    Topchar->index = 0;
X	    break;
X
X	  case '.':		/* put Curschar in middle of screen */
X	    n = Rows / 2;
X	    goto dozcmd;
X
X	  case '-':		/* put Curschar at bottom of screen */
X	    n = Rows - 1;
X	    /* FALLTHROUGH */
X
X    dozcmd:
X	    {
X		register LPtr  *lp = Curschar;
X		register int    l = 0;
X
X		while ((l < n) && (lp != NULL)) {
X		    l += plines(lp->linep->s);
X		    *Topchar = *lp;
X		    lp = prevline(lp);
X		}
X	    }
X	    Topchar->index = 0;
X	    break;
X
X	  default:
X	    beep();
X	}
X	break;
X
X      case CTRL('G'):
X	CLEAROP;
X	fileinfo();
X	break;
X
X      case 'G':
X	mtype = MLINE;
X	*Curschar = *gotoline(Prenum);
X	if (!UndoInProgress) {
X	    beginline(TRUE);
X	    S_CHECK_TOPCHAR_AND_BOTCHAR;
X	}
X	break;
X
X      case 'H':
X	mtype = MLINE;
X	*Curschar = *Topchar;
X	for (n = Prenum; n && onedown(1); n--);
X	beginline(TRUE);
X	break;
X
X      case 'M':
X	mtype = MLINE;
X	*Curschar = *Topchar;
X	for (n = 0; n < Rows / 2 && onedown(1); n++);
X	beginline(TRUE);
X	break;
X
X      case 'L':
X	mtype = MLINE;
X	*Curschar = *prevline(Botchar);
X	for (n = Prenum; n && oneup(1); n--);
X	beginline(TRUE);
X	break;
X
X      case 'l':
X      case K_RARROW:
X      case ' ':
X	mtype = MCHAR;
X	mincl = FALSE;
X	n = DEFAULT1(Prenum);
X	while (n--) {
X	    if (!oneright()) {
X		if (operator != DELETE && operator != CHANGE) {
X		    beep();
X		} else {
X		    if (lineempty(Curschar)) {
X			CLEAROP;
X			beep();
X		    } else {
X			mincl = TRUE;
X		    }
X		}
X		break;
X	    }
X	}
X	set_want_col = TRUE;
X	break;
X
X      case 'h':
X      case K_LARROW:
X      case CTRL('H'):
X	mtype = MCHAR;
X	mincl = FALSE;
X	Prenum = DEFAULT1(Prenum);
X	n = Prenum;
X	while (n--) {
X	    if (!oneleft()) {
X		if (operator != DELETE && operator != CHANGE) {
X		    beep();
X		} else if (Prenum == 1) {
X		    CLEAROP;
X		    beep();
X		}
X		break;
X	    }
X	}
X	set_want_col = TRUE;
X	break;
X
X      case '-':
X	flag = TRUE;
X	/* FALLTHROUGH */
X
X      case 'k':
X      case K_UARROW:
X      case CTRL('P'):
X	mtype = MLINE;
X	if (!oneup(DEFAULT1(Prenum))) {
X	    CLEAROP;
X	    beep();
X	} else if (flag)
X	    beginline(TRUE);
X	break;
X
X      case '+':
X      case CR:
X      case NL:
X	flag = TRUE;
X	/* FALLTHROUGH */
X
X      case 'j':
X      case K_DARROW:
X      case CTRL('N'):
X	mtype = MLINE;
X	if (!onedown(DEFAULT1(Prenum))) {
X	    CLEAROP;
X	    beep();
X	} else if (flag)
X	    beginline(TRUE);
X	break;
X
X	/*
X	 * This is a strange motion command that helps make operators more
X	 * logical. It is actually implemented, but not documented in the
X	 * real 'vi'. This motion command actually refers to "the current
X	 * line". Commands like "dd" and "yy" are really an alternate form of
X	 * "d_" and "y_". It does accept a count, so "d3_" works to delete 3
X	 * lines. 
X	 */
X      case '_':
Xlineop:
X	mtype = MLINE;
X	if (!onedown(DEFAULT1(Prenum) - 1)) {
X	    CLEAROP;
X	    beep();
X	} else
X	    beginline(TRUE);
X	break;
X
X      case '|':
X	mtype = MCHAR;
X	mincl = TRUE;
X	beginline(FALSE);
X	if (Prenum > 0)
X	    coladvance(Curschar, Prenum - 1);
X	Curswant = Prenum - 1;
X	break;
X
X      case CTRL(']'):		/* :ta to current identifier */
X	CLEAROP;
X	{
X	    char            ch;
X	    LPtr            save;
X
X	    save = *Curschar;
X	    /*
X	     * First back up to start of identifier. This doesn't match the
X	     * real vi but I like it a little better and it shouldn't bother
X	     * anyone. 
X	     */
X	    ch = gchar(Curschar);
X	    while (IDCHAR(ch)) {
X		if (!oneleft())
X		    break;
X		ch = gchar(Curschar);
X	    }
X	    if (!IDCHAR(ch))
X		oneright();
X
X	    stuffReadbuff(":ta ");
X	    /*
X	     * Now grab the chars in the identifier 
X	     */
X	    ch = gchar(Curschar);
X	    while (IDCHAR(ch)) {
X		stuffReadbuff(mkstr(ch));
X		if (!oneright())
X		    break;
X		ch = gchar(Curschar);
X	    }
X	    stuffReadbuff("\n");
X
X	    *Curschar = save;	/* restore, in case of error */
X	}
X	break;
X
X      case '%':
X	S_CHECK_TOPCHAR_AND_BOTCHAR;
X	mtype = MCHAR;
X	mincl = TRUE;
X	{
X	    LPtr           *pos;
X
X	    if ((pos = showmatch()) == NULL) {
X		CLEAROP;
X		beep();
X	    } else {
X		setpcmark();
X		*Curschar = *pos;
X		set_want_col = TRUE;
X	    }
X	}
X	break;
X
X	/*
X	 * Word Motions 
X	 */
X
X      case 'B':
X	type = 1;
X	/* FALLTHROUGH */
X
X      case 'b':
X	mtype = MCHAR;
X	mincl = FALSE;
X	set_want_col = TRUE;
X	for (n = DEFAULT1(Prenum); n > 0; n--) {
X	    LPtr           *pos;
X
X	    if ((Curschar->linep->prev == Filetop->linep)
X		&& (Curschar->index == 0)) {
X		CLEAROP;
X		beep();
X		break;
X	    }
X	    pos = bck_word(Curschar, type);
X	    if (pos == NULL) {
X		CLEAROP;
X		beep();
X		*Curschar = *gotoline(1);	/* goto top of file */
X	    } else
X		*Curschar = *pos;
X	}
X	break;
X
X      case 'W':
X	type = 1;
X	/* FALLTHROUGH */
X
X      case 'w':
X	/*
X	 * This is a little strange. To match what the real vi does, we
X	 * effectively map 'cw' to 'ce', and 'cW' to 'cE'. This seems
X	 * impolite at first, but it's really more what we mean when we say
X	 * 'cw'. 
X	 */
X	if (operator == CHANGE)
X	    goto do_e_cmd;
X
X	mtype = MCHAR;
X	mincl = FALSE;
X	set_want_col = TRUE;
X	for (n = DEFAULT1(Prenum); n > 0; n--) {
X	    LPtr           *pos;
X
X	    if ((pos = fwd_word(Curschar, type)) == NULL) {
X		CLEAROP;
X		beep();
X		break;
X	    } else
X		*Curschar = *pos;
X	}
X	break;
X
X      case 'E':
X	type = 1;
X	/* FALLTHROUGH */
X
X      case 'e':
Xdo_e_cmd:
X	mtype = MCHAR;
X	mincl = TRUE;
X	set_want_col = TRUE;
X	for (n = DEFAULT1(Prenum); n > 0; n--) {
X	    LPtr           *pos;
X
X	    if ((pos = end_word(Curschar, type)) == NULL) {
X		CLEAROP;
X		beep();
X		break;
X	    } else
X		*Curschar = *pos;
X	}
X	break;
X
X      case '$':
X	mtype = MCHAR;
X	mincl = TRUE;
X	while (oneright());
X	Curswant = 999;		/* so we stay at the end */
X	break;
X
X      case '^':
X	flag = TRUE;
X	/* FALLTHROUGH */
X
X      case '0':
X	mtype = MCHAR;
X	mincl = TRUE;
X	beginline(flag);
X	break;
X
X      case 'A':
X	set_want_col = TRUE;
X	while (oneright());
X	ResetBuffers();
X	AppendToRedobuff("A");
X	goto doAPPENDcmd;
X
X      case 'a':
X	ResetBuffers();
X	AppendToRedobuff("a");
X
XdoAPPENDcmd:
X	CLEAROP;
X	/* Works just like an 'i'nsert on the next character. */
X	n = RowNumber(Curschar);
X	AppendPositionToUndoUndobuff(Curschar->index, n);
X	AppendToUndoUndobuff("a");
X
X	if (!lineempty(Curschar))
X	    inc(Curschar);
X
X	n = RowNumber(Curschar);
X	AppendPositionToUndobuff(Curschar->index, n);
X
X	startinsert(FALSE);
X	break;
X
X      case 'I':
X	beginline(TRUE);
X	ResetBuffers();
X	AppendToRedobuff("I");
X	goto doINSERTcmd;
X	/* FALLTHROUGH */
X
X      case 'i':
X      case K_INSERT:
X	ResetBuffers();
X	AppendToRedobuff("i");
X
XdoINSERTcmd:
X	CLEAROP;
X
X	n = RowNumber(Curschar);
X	AppendPositionToUndobuff(Curschar->index, n);
X	AppendPositionToUndoUndobuff(Curschar->index, n);
X	AppendToUndoUndobuff("i");
X
X	startinsert(FALSE);
X	break;
X
X      case 'o':
X	CLEAROP;
X	ResetBuffers();
X
X	n = RowNumber(Curschar);
X	AppendToRedobuff("o");
X	AppendPositionToUndobuff(Curschar->index, n);
X	AppendPositionToUndoUndobuff(Curschar->index, n);
X	AppendToUndoUndobuff("o");
X
X	if (OpenForward(!RedrawingDisabled))
X	    startinsert(TRUE);
X
X	last_command = 'o';
X	break;
X
X      case 'O':
X	CLEAROP;
X	ResetBuffers();
X
X	n = RowNumber(Curschar);
X	AppendToRedobuff("O");
X	AppendPositionToUndobuff(Curschar->index, n);
X	AppendPositionToUndoUndobuff(Curschar->index, n);
X	AppendToUndoUndobuff("O");
X
X	if (OpenBackward(!RedrawingDisabled))
X	    startinsert(TRUE);
X
X	last_command = 'O';
X	break;
X
X      case 'd':
X	if (operator == DELETE)	/* handle 'dd' */
X	    goto lineop;
X	if (Prenum != 0)
X	    opnum = Prenum;
X	startop = *Curschar;
X	operator = DELETE;
X	break;
X
X	/*
X	 * Some convenient abbreviations... 
X	 */
X
X      case 'x':
X	if (Prenum)
X	    stuffnumReadbuff(Prenum);
X	stuffReadbuff("dl");
X	break;
X
X      case 'X':
X	if (Prenum)
X	    stuffnumReadbuff(Prenum);
X	stuffReadbuff("dh");
X	break;
X
X      case 'D':
X	stuffReadbuff("d$");
X	break;
X
X      case 'Y':
X	if (Prenum)
X	    stuffnumReadbuff(Prenum);
X	stuffReadbuff("yy");
X	break;
X
X      case 'C':
X	stuffReadbuff("c$");
X	break;
X
X      case 'c':
X	if (operator == CHANGE) {	/* handle 'cc' */
X	    CLEAROP;
X	    stuffReadbuff("0c$");
X	    break;
X	}
X	if (Prenum != 0)
X	    opnum = Prenum;
X	startop = *Curschar;
X	operator = CHANGE;
X	break;
X
X      case 'y':
X	if (operator == YANK)	/* handle 'yy' */
X	    goto lineop;
X	if (Prenum != 0)
X	    opnum = Prenum;
X	startop = *Curschar;
X	operator = YANK;
X	break;
X
X      case ENABLE_REDRAWING:
X	RedrawingDisabled = FALSE;
X	S_NOT_VALID;
X	break;
X
X      case 'p':
X	if (Yankbuffptr != NULL) {
X	    doput(FORWARD);
X
X	    stuffReadbuff(ENABLE_REDRAWING_STR);
X	    RedrawingDisabled = TRUE;
X	} else
X	    beep();
X	break;
X
X      case 'P':
X	if (Yankbuffptr != NULL) {
X	    doput(BACKWARD);
X
X	    stuffReadbuff(ENABLE_REDRAWING_STR);
X	    RedrawingDisabled = TRUE;
X	} else
X	    beep();
X	break;
X
X      case '>':
X	if (operator == RSHIFT)	/* handle >> */
X	    goto lineop;
X	if (operator == LSHIFT) {
X	    CLEAROP;
X	    beep();
X	    break;
X	}
X	if (Prenum != 0)
X	    opnum = Prenum;
X	startop = *Curschar;	/* save current position */
X	operator = RSHIFT;
X	break;
X
X      case '<':
X	if (operator == LSHIFT)	/* handle << */
X	    goto lineop;
X	if (operator == RSHIFT) {
X	    CLEAROP;
X	    beep();
X	    break;
X	}
X	if (Prenum != 0)
X	    opnum = Prenum;
X	startop = *Curschar;	/* save current position */
X	operator = LSHIFT;
X	break;
X
X      case 's':		/* substitute characters */
X	if (Prenum)
X	    stuffnumReadbuff(Prenum);
X	stuffReadbuff("cl");
X	break;
X
X      case '?':
X      case '/':
X      case ':':
X	CLEAROP;
X	readcmdline(c, (char *) NULL);
X	break;
X
X      case 'n':
X	mtype = MCHAR;
X	mincl = FALSE;
X	set_want_col = TRUE;
X	if (!repsearch(0)) {
X	    CLEAROP;
X	    beep();
X	}
X	break;
X
X      case 'N':
X	mtype = MCHAR;
X	mincl = FALSE;
X	set_want_col = TRUE;
X	if (!repsearch(1)) {
X	    CLEAROP;
X	    beep();
X	}
X	break;
X
X	/*
X	 * Character searches 
X	 */
X      case 'T':
X	dir = BACKWARD;
X	/* FALLTHROUGH */
X
X      case 't':
X	type = 1;
X	goto docsearch;
X
X      case 'F':
X	dir = BACKWARD;
X	/* FALLTHROUGH */
X
X      case 'f':
Xdocsearch:
X	mtype = MCHAR;
X	mincl = TRUE;
X	set_want_col = TRUE;
X	if ((nchar = vgetc()) == ESC)	/* search char */
X	    break;
X	if (!searchc(nchar, dir, type)) {
X	    CLEAROP;
X	    beep();
X	}
X	break;
X
X      case ',':
X	flag = 1;
X	/* FALLTHROUGH */
X
X      case ';':
X	mtype = MCHAR;
X	mincl = TRUE;
X	set_want_col = TRUE;
X	if (!crepsearch(flag)) {
X	    CLEAROP;
X	    beep();
X	}
X	break;
X
X	/*
X	 * Function searches 
X	 */
X
X      case '[':
X	dir = BACKWARD;
X	/* FALLTHROUGH */
X
X      case ']':
X	mtype = MLINE;
X	set_want_col = TRUE;
X	if (vgetc() != c) {
X	    CLEAROP;
X	    beep();
X	    break;
X	}
X	if (!findfunc(dir)) {
X	    CLEAROP;
X	    beep();
X	}
X	break;
X
X	/*
X	 * Marks 
X	 */
X
X      case 'm':
X	CLEAROP;
X	if (!setmark(vgetc()))
X	    beep();
X	break;
X
X      case '\'':
X	flag = TRUE;
X	/* FALLTHROUGH */
X
X      case '`':
X	S_CHECK_TOPCHAR_AND_BOTCHAR;
X	{
X	    LPtr            mtmp;
X	    LPtr            *mark = getmark(vgetc());
X
X	    if (mark == NULL) {
X		CLEAROP;
X		beep();
X	    } else {
X		mtmp = *mark;
X		setpcmark();
X		*Curschar = mtmp;
X		if (flag)
X		    beginline(TRUE);
X	    }
X	    mtype = flag ? MLINE : MCHAR;
X	    mincl = TRUE;	/* ignored if not MCHAR */
X	    set_want_col = TRUE;
X	}
X	break;
X
X      case 'r':
X	CLEAROP;
X	if (lineempty(Curschar)) {	/* Nothing to replace */
X	    beep();
X	    break;
X	}
X	if ((nchar = vgetc()) == ESC)
X	    break;
X
X	Prenum = DEFAULT1(Prenum);
X	n = strlen(Curschar->linep->s) - Curschar->index;
X	if (n < Prenum) {
X	    beep();
X	    break;
X	}
X	ResetBuffers();
X
X	nn = RowNumber(Curschar);
X	AppendPositionToUndobuff(Curschar->index, nn);
X	AppendPositionToUndoUndobuff(Curschar->index, nn);
X
X	while (Prenum > 0) {
X	    AppendToRedobuff("r");
X	    AppendToRedobuff(mkstr(nchar));
X
X	    AppendToUndobuff("r");
X	    AppendToUndobuff(mkstr(gchar(Curschar)));
X
X	    AppendToUndoUndobuff("r");
X	    AppendToUndoUndobuff(mkstr(nchar));
X
X	    pchar(Curschar, nchar);	/* Change current character. */
X
X	    if (Prenum > 1) {
X		oneright();
X		AppendToRedobuff("l");
X		AppendToUndobuff("l");
X		AppendToUndoUndobuff("l");
X	    }
X	    Prenum--;
X	}
X
X	CHANGED;
X	S_LINE_NOT_VALID;
X	break;
X
X      case '~':		/* swap case */
X	CLEAROP;
X	if (lineempty(Curschar)) {
X	    beep();
X	    break;
X	}
X	ResetBuffers();
X
X	n = RowNumber(Curschar);
X	AppendPositionToUndobuff(Curschar->index, n);
X	AppendPositionToUndoUndobuff(Curschar->index, n);
X
X	Prenum = DEFAULT1(Prenum);
X	if (Prenum > 0) {
X	    AppendNumberToRedobuff(Prenum);
X	    AppendNumberToUndobuff(Prenum);
X	    AppendNumberToUndoUndobuff(Prenum);
X	}
X	AppendToRedobuff("~");
X	AppendToUndobuff("~");
X	AppendToUndoUndobuff("~");
X
X	while (Prenum > 0) {
X	    c = gchar(Curschar);
X	    if (isalpha(c)) {
X		if (islower(c))
X		    pchar(Curschar, toupper(c));
X		else
X		    pchar(Curschar, tolower(c));
X	    }
X	    if (!oneright())
X		break;
X	    Prenum--;
X	}
X
X	CHANGED;
X	S_LINE_NOT_VALID;
X	break;
X
X      case UNDO_SHIFTJ:
X	CLEAROP;
X	if (UndoInProgress) {
X	    (void) dojoin(FALSE, FALSE);
X	    break;
X	}
X	goto doSHIFTJcommand;
X
X      case 'J':
X	CLEAROP;
XdoSHIFTJcommand:
X	if (nextline(Curschar) == NULL) {	/* on last line */
X	    beep();
X	    break;
X	}
X	ResetBuffers();
X
X	temp_Curschar = *Curschar;
X	nn = strlen(Curschar->linep->s);
X	if (nn < 0)
X	    nn = 0;
X	n = RowNumber(&temp_Curschar);
X
X	AppendToRedobuff("J");
X
X	AppendPositionToUndobuff(nn, n);
X
X	AppendPositionToUndoUndobuff(0, n);
X	AppendToUndoUndobuff("J");
X
X	if (linewhite(nextline(Curschar))) {
X	    AppendToUndobuff("a\n");
X	    if (!dojoin(FALSE, TRUE)) {
X		beep();
X		break;
X	    }
X	} else if (lineempty(Curschar)) {
X	    AppendToUndobuff("i\n");
X	    if (!dojoin(FALSE, TRUE)) {
X		beep();
X		break;
X	    }
X	} else {
X	    AppendToUndobuff("dli\n");
X	    if (!dojoin(TRUE, TRUE)) {
X		beep();
X		break;
X	    }
X	}
X
X	AppendToUndobuff(ESC_STR);
X	AppendPositionToUndobuff(nn, n);
X	break;
X
X      case K_CGRAVE:		/* shorthand command */
X	CLEAROP;
X	stuffReadbuff(":e #\n");
X	break;
X
X      case 'Z':		/* write, if changed, and exit */
X	if (vgetc() != 'Z') {
X	    beep();
X	    break;
X	}
X	if (Changed) {
X	    if (Filename != NULL) {
X		if (!writeit(Filename, (LPtr *) NULL, (LPtr *) NULL))
X		    return;
X	    } else {
X		emsg("No output file");
X		return;
X	    }
X	}
X	getout(0);
X	break;
X
X      case '.':
X	CLEAROP;
X	if (Redobuffptr != NULL) {
X	    stuffReadbuff(Redobuff);
X
X	    stuffReadbuff(ENABLE_REDRAWING_STR);
X	    RedrawingDisabled = TRUE;
X	} else
X	    beep();
X	break;
X
X      case 'u':
X      case K_UNDO:
X	CLEAROP;
X	if (UndoInProgress) {
X	    p = UndoUndobuff;
X	    UndoUndobuff = Undobuff;
X	    Undobuff = p;
X	    p = UndoUndobuffptr;
X	    UndoUndobuffptr = Undobuffptr;
X	    Undobuffptr = p;
X
X	    UndoInProgress = FALSE;
X	    RedrawingDisabled = FALSE;
X	    S_NOT_VALID;
X	} else if (Undobuffptr != NULL) {
X	    stuffReadbuff(Undobuff);
X	    stuffReadbuff("u");
X	    UndoInProgress = TRUE;
X	    RedrawingDisabled = TRUE;
X	} else {
X	    beep();
X	}
X	break;
X
X      default:
X	CLEAROP;
X	beep();
X	break;
X    }
X
X    /*
X     * If an operation is pending, handle it... 
X     */
X    if (finish_op) {		/* we just finished an operator */
X	if (operator == NOP)	/* ... but it was cancelled */
X	    return;
X
X	switch (operator) {
X
X	  case LSHIFT:
X	  case RSHIFT:
X	    ResetBuffers();
X
X	    n = RowNumber(&startop);
X	    AppendPositionToUndobuff(startop.index, n);
X	    AppendPositionToUndoUndobuff(startop.index, n);
X	    if (Prenum != 0) {
X		AppendNumberToRedobuff(Prenum);
X		AppendNumberToUndobuff(Prenum);
X		AppendNumberToUndoUndobuff(Prenum);
X	    }
X	    AppendToRedobuff((operator == LSHIFT) ? "<" : ">");
X	    AppendToUndobuff((operator == LSHIFT) ? ">" : "<");
X	    AppendToUndoUndobuff((operator == LSHIFT) ? "<" : ">");
X	    AppendToRedobuff(mkstr(c));
X	    if (c == '>')
X		AppendToUndobuff("<");
X	    else if (c == '<')
X		AppendToUndobuff(">");
X	    else
X		AppendToUndobuff(mkstr(c));
X	    AppendToUndoUndobuff(mkstr(c));
X
X	    doshift(operator);
X	    break;
X
X	  case DELETE:
X	    ResetBuffers();
X
X	    n = RowNumber(&startop);
X	    AppendPositionToUndoUndobuff(startop.index, n);
X
X	    temp_Curschar = (lt(&startop, Curschar) ? startop : *Curschar);
X	    n = RowNumber(&temp_Curschar);
X	    if (Prenum != 0) {
X		AppendNumberToRedobuff(Prenum);
X		AppendNumberToUndoUndobuff(Prenum);
X	    }
X	    AppendToRedobuff("d");
X	    AppendToUndoUndobuff("d");
X	    AppendToRedobuff(mkstr(c));
X	    AppendToUndoUndobuff(mkstr(c));
X	    if (nchar != NUL) {
X		AppendToRedobuff(mkstr(nchar));
X		AppendToUndoUndobuff(mkstr(nchar));
X	    }
X	    AppendPositionToUndobuff(temp_Curschar.index, n);
X
X	    dodelete(!UndoInProgress, !UndoInProgress, !UndoInProgress);
X
X	    AppendPositionToUndobuff(temp_Curschar.index, n);
X	    break;
X
X	  case YANK:
X	    ResetBuffers();	/* no redo/undo/(undo of undo) on yank... */
X	    if (!doyank())
X		msg("yank buffer exceeded");
X	    if (!ybcrossline)
X		*Curschar = startop;
X	    else if (lt(&startop, Curschar))
X		*Curschar = startop;
X	    break;
X
X	  case CHANGE:
X	    ResetBuffers();
X
X	    n = RowNumber(&startop);
X	    AppendPositionToUndoUndobuff(startop.index, n);
X
X	    temp_Curschar = (lt(&startop, Curschar) ? startop : *Curschar);
X	    n = RowNumber(&temp_Curschar);
X	    if (mtype == MLINE)
X		AppendPositionToUndobuff(0, n);
X	    else
X		AppendPositionToUndobuff(temp_Curschar.index, n);
X
X	    if (Prenum != 0) {
X		AppendNumberToRedobuff(Prenum);
X		AppendNumberToUndoUndobuff(Prenum);
X	    }
X	    AppendToRedobuff("c");
X	    AppendToUndoUndobuff("c");
X	    AppendToRedobuff(mkstr(c));
X	    AppendToUndoUndobuff(mkstr(c));
X	    if (nchar != NUL) {
X		AppendToRedobuff(mkstr(nchar));
X		AppendToUndoUndobuff(mkstr(nchar));
X	    }
X	    dochange();
X
X	    last_command = 'c';
X	    break;
X
X	  default:
X	    beep();
X	}
X	operator = NOP;
X    }
X}
X
X/*
X * tabinout(shift_type, num) 
X *
X * If shift_type == RSHIFT, add a tab to the begining of the next num lines;
X * otherwise delete a tab from the beginning of the next num lines. 
X */
Xstatic void
Xtabinout(shift_type, num)
X    int             shift_type;
X    int             num;
X{
X    LPtr           *p;
X
X    beginline(FALSE);
X    while (num-- > 0) {
X	beginline(FALSE);
X	if (shift_type == RSHIFT)
X	    inschar(TAB);
X	else {
X	    if (gchar(Curschar) == TAB)
X		delchar(TRUE, FALSE);
X	}
X	if (num > 0) {
X	    if ((p = nextline(Curschar)) != NULL)
X		*Curschar = *p;
X	    else
X		break;
X	}
X    }
X}
X
X/*
X * doshift - handle a shift operation 
X */
Xstatic void
Xdoshift(op)
X    int             op;
X{
X    LPtr            top, bot;
X    int             nlines;
X
X    top = startop;
X    bot = *Curschar;
X
X    if (lt(&bot, &top))
X	pswap(top, bot);
X
X    nlines = cntllines(&top, &bot);
X    *Curschar = top;
X    tabinout(op, nlines);
X
X    /*
X     * The cursor position afterward is the prior of the two positions. 
X     */
X    *Curschar = top;
X
X    /*
X     * If we were on the last char of a line that got shifted left, then move
X     * left one so we aren't beyond the end of the line 
X     */
X    if (gchar(Curschar) == NUL && Curschar->index > 0)
X	Curschar->index--;
X
X    if (op == RSHIFT)
X	oneright();
X    else
X	oneleft();
X
X    S_NOT_VALID;
X
X    if (nlines > P(P_RP))
X	smsg("%d lines %ced", nlines, (op == RSHIFT) ? '>' : '<');
X}
X
X/*
X * dodelete - handle a delete operation 
X */
Xstatic void
Xdodelete(redraw, setup_for_undo, try_to_yank)
X    bool_t          redraw;
X    bool_t          setup_for_undo;
X    bool_t          try_to_yank;
X{
X    LPtr            top, bot;
X    int             nlines;
X    int             n;
X
X    /*
X     * Do a yank of whatever we're about to delete. If there's too much stuff
X     * to fit in the yank buffer, then get a confirmation before doing the
X     * delete. This is crude, but simple. And it avoids doing a delete of
X     * something we can't put back if we want. 
X     */
X    if (try_to_yank) {
X	if (!doyank()) {
X	    msg("yank buffer exceeded: press <y> to confirm");
X	    if (vgetc() != 'y') {
X		emsg("delete aborted");
X		*Curschar = startop;
X		return;
X	    }
X	}
X    }
X    top = startop;
X    bot = *Curschar;
X
X    if (lt(&bot, &top))
X	pswap(top, bot);
X
X    *Curschar = top;
X    nlines = cntllines(&top, &bot);
X
X    if (mtype == MLINE) {
X	if (operator == CHANGE) {
X	    last_command_char = 'a';
X	    delline(nlines - 1);
X	    Curschar->index = 0;
X	    while (delchar(TRUE, FALSE));
X	} else {
X	    if ((Filetop->linep->next == top.linep) &&
X		(bot.linep->next == Fileend->linep))
X		last_command_char = 'a';
X	    else if (bot.linep->next == Fileend->linep)
X		last_command_char = 'o';
X	    else
X		last_command_char = 'O';
X	    if (setup_for_undo)
X		AppendToUndobuff(mkstr(last_command_char));
X	    delline(nlines);
X	}
X    } else if (top.linep == bot.linep) {	/* del. within line */
X	if (!mincl)
X	    dec(&bot);
X
X	if (endofline(&bot))
X	    last_command_char = 'a';
X	else
X	    last_command_char = 'i';
X	if (setup_for_undo)
X	    AppendToUndobuff(mkstr(last_command_char));
X	n = bot.index - top.index + 1;
X	while (n--)
X	    if (!delchar(TRUE, FALSE))
X		break;
X    } else {			/* del. between lines */
X	if (endofline(&top)) {
X	    if (nextline(&top)) {
X		if (lineempty(nextline(&top)))
X		    last_command_char = 'a';
X		else
X		    last_command_char = 'i';
X	    } else {
X		last_command_char = 'a';
X	    }
X	} else {
X	    last_command_char = 'i';
X	}
X	if (setup_for_undo)
X	    AppendToUndobuff(mkstr(last_command_char));
X
X	n = Curschar->index;
X	while (Curschar->index >= n)
X	    if (!delchar(TRUE, FALSE))
X		break;
X
X	top = *Curschar;
X	*Curschar = *nextline(Curschar);
X	delline(nlines - 2);
X	Curschar->index = 0;
X	n = bot.index;
X	if (!mincl)
X	    n--;
X
X	while (n-- >= 0)
X	    if (!delchar(TRUE, FALSE))
X		break;
X	*Curschar = top;
X	dojoin(FALSE, FALSE);
X    }
X
X    if (mtype == MCHAR && nlines == 1 && redraw && P(P_NU) == FALSE) {
X	S_LINE_NOT_VALID;
X    } else {
X	S_NOT_VALID;
X    }
X
X    if (nlines > P(P_RP))
X	smsg("%d fewer lines", nlines);
X
X    if (setup_for_undo) {
X	AppendToUndobuff(Yankbuff);
X	AppendToUndobuff(ESC_STR);
X    }
X}
X
X/*
X * dochange - handle a change operation 
X */
Xstatic void
Xdochange()
X{
X    LPtr            l;
X
X    if (lt(Curschar, &startop))
X	l = *Curschar;
X    else
X	l = startop;
X
X    dodelete(FALSE, FALSE, !UndoInProgress);
X
X    if ((l.index > Curschar->index) && !lineempty(Curschar))
X	inc(Curschar);
X
X    startinsert(FALSE);
X}
X
Xstatic          bool_t
Xdoyank()
X{
X    LPtr            top, bot;
X    char           *ybend = &Yankbuff[YANKSIZE - 1];
X    int             nlines;
X
X    Yankbuffptr = Yankbuff;
X
X    top = startop;
X    bot = *Curschar;
X
X    if (lt(&bot, &top))
X	pswap(top, bot);
X
X    nlines = cntllines(&top, &bot);
X
X    ybtype = mtype;		/* set the yank buffer type */
X    ybcrossline = FALSE;
X    if (LINEOF(&top) != LINEOF(&bot))
X	ybcrossline = TRUE;
X
X    if (mtype == MLINE) {
X	ybcrossline = TRUE;
X	top.index = 0;
X	bot.index = strlen(bot.linep->s);
X	/*
X	 * The following statement checks for the special case of yanking a
X	 * blank line at the beginning of the file. If not handled right, we
X	 * yank an extra char (a newline). 
X	 */
X	if (dec(&bot) == -1) {
X	    *Yankbuff = NUL;
X	    Yankbuffptr = NULL;
X	    return TRUE;
X	}
X    } else {
X	if (!mincl)
X	    if (!equal(&top, &bot))
X		dec(&bot);
X    }
X
X    for (; ltoreq(&top, &bot); inc(&top)) {
X	*Yankbuffptr = (gchar(&top) != NUL) ? gchar(&top) : NL;
X	Yankbuffptr++;
X	if (Yankbuffptr >= ybend) {
X	    *Yankbuffptr = NUL;
X	    msg("yank too big for buffer");
X	    ybtype = MBAD;
X	    return FALSE;
X	}
X    }
X
X    *Yankbuffptr = NUL;
X
X    if (operator == YANK)
X	if (nlines > P(P_RP))
X	    smsg("%d lines yanked", nlines);
X
X    return TRUE;
X}
X
Xstatic void
Xdoput(dir)
X    int             dir;
X{
X    bool_t          type;
X
X    if (ybtype == MBAD) {
X	beep();
X	return;
X    }
X    type = (ybtype == MCHAR);
X    if (dir == FORWARD)
X	stuffReadbuff(type ? "a" : "o");
X    else
X	stuffReadbuff(type ? "i" : "O");
X
X    stuffReadbuff(Yankbuff);
X    stuffReadbuff(ESC_STR);
X
X    if (ybtype != MCHAR)
X	stuffReadbuff("^");
X}
X
Xstatic void
Xstartinsert(startln)
X    int             startln;	/* if set, insert at start of line */
X{
X    *Insstart = *Curschar;
X    if (startln) {
X	Insstart->index = 0;
X    }
X    *Insbuff = NUL;
X    Insbuffptr = NULL;
X
X    State = INSERT;
X    if (P(P_MO))
X	msg("Insert Mode");
X}
X
Xvoid
XResetBuffers()
X{
X    if (UndoInProgress)
X	return;
X
X    *Redobuff = NUL;
X    Redobuffptr = NULL;
X
X    *Undobuff = NUL;
X    Undobuffptr = NULL;
X
X    *UndoUndobuff = NUL;
X    UndoUndobuffptr = NULL;
X}
X
Xvoid
XAppendToInsbuff(s)
X    char           *s;
X{
X    if (UndoInProgress)
X	return;
X
X    if (Insbuffptr == NULL) {
X	if ((strlen(s) + 1) < INSERT_SIZE) {
X	    strcpy(Insbuff, s);
X	    Insbuffptr = Insbuff;
X	    return;
X	}
X    } else if ((strlen(Insbuff) + strlen(s) + 1) < INSERT_SIZE) {
X	strcat(Insbuff, s);
X	return;
X    }
X    emsg("Couldn't AppendToInsbuff() - clearing Insbuff\n");
X    *Insbuff = NUL;
X    Insbuffptr = NULL;
X}
X
Xvoid
XAppendToRedobuff(s)
X    char           *s;
X{
X    if (UndoInProgress)
X	return;
X
X    if (Redobuffptr == (char *) (-2)) {
X	return;
X    }
X    if (Redobuffptr == (char *) (-1)) {
X	Redobuffptr = (char *) (-2);
X	emsg("Couldn't AppendToRedobuff() - Redobuff corrupt");
X	return;
X    }
X    if (Redobuffptr == NULL) {
X	if ((strlen(s) + 1) < REDO_UNDO_SIZE) {
X	    strcpy(Redobuff, s);
X	    Redobuffptr = Redobuff;
X	    return;
X	}
X    } else if ((strlen(Redobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) {
X	strcat(Redobuff, s);
X	return;
X    }
X    emsg("Couldn't AppendToRedobuff() - clearing Redobuff");
X    *Redobuff = NUL;
X    Redobuffptr = (char *) (-1);
X}
X
Xvoid
XAppendNumberToRedobuff(n)
X    int             n;
X{
X    char            buf[32];
X
X    if (UndoInProgress)
X	return;
X
X    sprintf(buf, "%d", n);
X    AppendToRedobuff(buf);
X}
X
Xvoid
XAppendToUndobuff(s)
X    char           *s;
X{
X    if (UndoInProgress)
X	return;
X
X    if (Undobuffptr == (char *) (-2)) {
X	return;
X    }
X    if (Undobuffptr == (char *) (-1)) {
X	Undobuffptr = (char *) (-2);
X	emsg("Couldn't AppendToUndobuff() - Undobuff corrupt");
X	return;
X    }
X    if (Undobuffptr == NULL) {
X	if ((strlen(s) + 1) < REDO_UNDO_SIZE) {
X	    strcpy(Undobuff, s);
X	    Undobuffptr = Undobuff;
X	    return;
X	}
X    } else if ((strlen(Undobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) {
X	strcat(Undobuff, s);
X	return;
X    }
X    emsg("Couldn't AppendToUndobuff() - clearing Undobuff");
X    *Undobuff = NUL;
X    Undobuffptr = (char *) (-1);
X}
X
Xvoid
XAppendNumberToUndobuff(n)
X    int             n;
X{
X    char            buf[32];
X
X    if (UndoInProgress)
X	return;
X
X    sprintf(buf, "%d", n);
X    AppendToUndobuff(buf);
X}
X
Xvoid
XAppendPositionToUndobuff(column, row)
X    int             column;
X    int             row;
X{
X    if (UndoInProgress)
X	return;
X
X    AppendNumberToUndobuff(row);
X    AppendToUndobuff("G");
X    AppendNumberToUndobuff(column);
X    if (column)
X	AppendToUndobuff("l");
X}
X
Xvoid
XAppendToUndoUndobuff(s)
X    char           *s;
X{
X    if (UndoInProgress)
X	return;
X
X    if (UndoUndobuffptr == (char *) (-2)) {
X	return;
X    }
X    if (UndoUndobuffptr == (char *) (-1)) {
X	UndoUndobuffptr = (char *) (-2);
X	emsg("Couldn't AppendToUndoUndobuff() - UndoUndobuff corrupt");
X	return;
X    }
X    if (UndoUndobuffptr == NULL) {
X	if ((strlen(s) + 1) < REDO_UNDO_SIZE) {
X	    strcpy(UndoUndobuff, s);
X	    UndoUndobuffptr = Undobuff;
X	    return;
X	}
X    } else if ((strlen(UndoUndobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) {
X	strcat(UndoUndobuff, s);
X	return;
X    }
X    emsg("Couldn't AppendToUndoUndobuff() - clearing UndoUndobuff");
X    *UndoUndobuff = NUL;
X    UndoUndobuffptr = (char *) (-1);
X}
X
Xvoid
XAppendNumberToUndoUndobuff(n)
X    int             n;
X{
X    char            buf[32];
X
X    if (UndoInProgress)
X	return;
X
X    sprintf(buf, "%d", n);
X    AppendToUndoUndobuff(buf);
X}
X
Xvoid
XAppendPositionToUndoUndobuff(column, row)
X    int             column;
X    int             row;
X{
X    if (UndoInProgress)
X	return;
X
X    AppendNumberToUndoUndobuff(row);
X    AppendToUndoUndobuff("G");
X    AppendNumberToUndoUndobuff(column);
X    if (column)
X	AppendToUndoUndobuff("l");
X}
X
Xstatic          bool_t
Xdojoin(leading_space, strip_leading_spaces)
X    bool_t          leading_space;
X    bool_t          strip_leading_spaces;
X{
X    int             scol;	/* save cursor column */
X    int             currsize;	/* size of the current line */
X    int             nextsize;	/* size of the next line */
X
X    if (nextline(Curschar) == NULL)	/* on last line */
X	return FALSE;
X
X    nextsize = strlen(Curschar->linep->next->s);
X    if (!canincrease(nextsize))
X	return FALSE;
X
X    currsize = strlen(Curschar->linep->s);
X
X    while (oneright());		/* to end of line */
X
X    strcat(Curschar->linep->s, Curschar->linep->next->s);
X
X    /*
X     * Delete the following line. To do this we move the cursor there
X     * briefly, and then move it back. Don't back up if the delete made us
X     * the last line. 
X     */
X    Curschar->linep = Curschar->linep->next;
X    scol = Curschar->index;
X
X    if (nextline(Curschar) != NULL) {
X	delline(1);
X	Curschar->linep = Curschar->linep->prev;
X    } else
X	delline(1);
X
X    Curschar->index = scol;
X
X    if (currsize)
X	oneright();		/* go to first char. of joined line */
X
X    if (nextsize != 0 && strip_leading_spaces) {
X	/*
X	 * Delete leading white space on the joined line and insert a single
X	 * space. 
X	 */
X	while (gchar(Curschar) == ' ' || gchar(Curschar) == TAB) {
X	    delchar(TRUE, TRUE);
X	}
X	if (leading_space)
X	    inschar(' ');
X    }
X    CHANGED;
X
X    return TRUE;
X}
X
X/*
X * linewhite() - returns TRUE if the line consists only of white space
X */
X
Xbool_t
Xlinewhite(p)
X    LPtr           *p;
X{
X    register int    i;
X    register char   c;
X
X    i = 1;
X    c = p->linep->s[0];
X    while (c != NUL) {
X	if (c != ' ' && c != '\t')
X	    return (FALSE);
X	c = p->linep->s[i++];
X    }
X
X    return (TRUE);
X}
SHAR_EOF
echo "End of archive 3 (of 6)"
# if you want to concatenate archives, remove anything after this line
exit