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