summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiquel van Smoorenburg <miquels@cistron.nl>1997-05-20 05:38:24 -0700
committerMiquel van Smoorenburg <miquels@cistron.nl>1997-05-20 05:38:24 -0700
commit4b98f8613ac91192c8dfaf42471fbe895959b6a1 (patch)
tree170d3d1e91cc1ccad6ff19fa486b9f6935bfde35
Import elvis-tiny_1.4.orig.tar.gz
[dgit import orig elvis-tiny_1.4.orig.tar.gz]
-rw-r--r--Elvis.lnk8
-rw-r--r--Elvis.mak123
-rw-r--r--Elvis.prj29
-rw-r--r--KNOWN.BUGS43
-rw-r--r--Makefile.mix472
-rw-r--r--README32
-rw-r--r--alias.c102
-rw-r--r--atari.c94
-rw-r--r--blk.c468
-rw-r--r--cmd1.c1268
-rw-r--r--cmd2.c896
-rw-r--r--config.h351
-rw-r--r--ctags.c350
-rw-r--r--curses.c729
-rw-r--r--curses.h252
-rw-r--r--cut.c668
-rw-r--r--date.c0
-rw-r--r--doc/cflags.doc264
-rw-r--r--doc/ctags.man65
-rw-r--r--doc/cutbufs.doc198
-rw-r--r--doc/differ.doc198
-rw-r--r--doc/elvis.man89
-rw-r--r--doc/environ.doc66
-rw-r--r--doc/ex.doc528
-rw-r--r--doc/index.doc264
-rw-r--r--doc/internal.doc198
-rw-r--r--doc/intro.doc66
-rw-r--r--doc/options.doc594
-rw-r--r--doc/ref.man21
-rw-r--r--doc/refont.man67
-rw-r--r--doc/regexp.doc132
-rw-r--r--doc/termcap.doc132
-rw-r--r--doc/versions.doc264
-rw-r--r--doc/virec.man51
-rw-r--r--doc/visual.doc330
-rw-r--r--ex.c675
-rw-r--r--input.c817
-rw-r--r--main.c422
-rw-r--r--misc.c103
-rw-r--r--modify.c479
-rw-r--r--move1.c623
-rw-r--r--move2.c238
-rw-r--r--move3.c163
-rw-r--r--move4.c213
-rw-r--r--move5.c230
-rw-r--r--opts.c680
-rw-r--r--osk.c205
-rw-r--r--osk.h26
-rw-r--r--pc.c270
-rw-r--r--profile.sh2
-rw-r--r--recycle.c111
-rw-r--r--redraw.c999
-rw-r--r--ref.c140
-rw-r--r--refont.c475
-rw-r--r--regexp.c830
-rw-r--r--regexp.h21
-rw-r--r--regsub.c203
-rw-r--r--shell.c231
-rw-r--r--sysdos.c158
-rw-r--r--system.c434
-rw-r--r--tinytcap.c154
-rw-r--r--tio.c864
-rw-r--r--tmp.c591
-rw-r--r--vars.c93
-rw-r--r--vcmd.c764
-rw-r--r--vi.c767
-rw-r--r--vi.h517
-rw-r--r--virec.c245
-rw-r--r--wildcard.c140
69 files changed, 22295 insertions, 0 deletions
diff --git a/Elvis.lnk b/Elvis.lnk
new file mode 100644
index 0000000..eb40871
--- /dev/null
+++ b/Elvis.lnk
@@ -0,0 +1,8 @@
+blk.obj cmd1.obj cmd2.obj curses.obj cut.obj +
+ex.obj input.obj main.obj misc.obj modify.obj +
+move1.obj move2.obj move3.obj move4.obj move5.obj +
+opts.obj recycle.obj redraw.obj regexp.obj +
+regsub.obj system.obj tio.obj tmp.obj vars.obj +
+vcmd.obj vi.obj pc.obj sysdos.obj tinytcap.obj /co /noi /map +
+/pac /far /stack:0x4000
+elvis.exe;
diff --git a/Elvis.mak b/Elvis.mak
new file mode 100644
index 0000000..704638b
--- /dev/null
+++ b/Elvis.mak
@@ -0,0 +1,123 @@
+# Makefile for MSC - if you don't have NDmake, use this one,
+# but don't expect to be happy.
+# And don't expect to do anything but making the executables, either.
+
+OBJS= blk.obj cmd1.obj cmd2.obj curses.obj cut.obj ex.obj input.obj \
+ main.obj misc.obj modify.obj move1.obj move2.obj move3.obj move4.obj \
+ move5.obj opts.obj recycle.obj redraw.obj regexp.obj regsub.obj \
+ system.obj tio.obj tmp.obj vars.obj vcmd.obj vi.obj \
+ pc.obj sysdos.obj tinytcap.obj
+
+CFLAGS= -DCS_IBMPC -DCS_SPECIAL
+CC= cl -AM
+
+blk.obj: blk.c
+ $(CC) $(CFLAGS) -c blk.c
+
+cmd1.obj: cmd1.c
+ $(CC) $(CFLAGS) -c cmd1.c
+
+cmd2.obj: cmd2.c
+ $(CC) $(CFLAGS) -c cmd2.c
+
+curses.obj: curses.c
+ $(CC) $(CFLAGS) -c curses.c
+
+cut.obj: cut.c
+ $(CC) $(CFLAGS) -c cut.c
+
+ex.obj: ex.c
+ $(CC) $(CFLAGS) -c ex.c
+
+input.obj: input.c
+ $(CC) $(CFLAGS) -c input.c
+
+main.obj: main.c
+ $(CC) $(CFLAGS) -c main.c
+
+misc.obj: misc.c
+ $(CC) $(CFLAGS) -c misc.c
+
+modify.obj: modify.c
+ $(CC) $(CFLAGS) -c modify.c
+
+move1.obj: move1.c
+ $(CC) $(CFLAGS) -c move1.c
+
+move2.obj: move2.c
+ $(CC) $(CFLAGS) -c move2.c
+
+move3.obj: move3.c
+ $(CC) $(CFLAGS) -c move3.c
+
+move4.obj: move4.c
+ $(CC) $(CFLAGS) -c move4.c
+
+move5.obj: move5.c
+ $(CC) $(CFLAGS) -c move5.c
+
+opts.obj: opts.c
+ $(CC) $(CFLAGS) -c opts.c
+
+recycle.obj: recycle.c
+ $(CC) $(CFLAGS) -c recycle.c
+
+redraw.obj: redraw.c
+ $(CC) $(CFLAGS) -c redraw.c
+
+regexp.obj: regexp.c
+ $(CC) $(CFLAGS) -c regexp.c
+
+regsub.obj: regsub.c
+ $(CC) $(CFLAGS) -c regsub.c
+
+system.obj: system.c
+ $(CC) $(CFLAGS) -c system.c
+
+tio.obj: tio.c
+ $(CC) $(CFLAGS) -c tio.c
+
+tmp.obj: tmp.c
+ $(CC) $(CFLAGS) -c tmp.c
+
+vars.obj: vars.c
+ $(CC) $(CFLAGS) -c vars.c
+
+vcmd.obj: vcmd.c
+ $(CC) $(CFLAGS) -c vcmd.c
+
+vi.obj: vi.c
+ $(CC) $(CFLAGS) -c vi.c
+
+pc.obj: pc.c
+ $(CC) $(CFLAGS) -c pc.c
+
+sysdos.obj: sysdos.c
+ $(CC) $(CFLAGS) -c sysdos.c
+
+tinytcap.obj: tinytcap.c
+ $(CC) $(CFLAGS) -c tinytcap.c
+
+elvis.exe: $(OBJS)
+ link @elvis.lnk
+
+ctags.exe: ctags.c wildcard.c
+ $(CC) ctags.c -o ctags.exe
+
+ref.exe: ref.c
+ $(CC) ref.c -o ref.exe
+
+virec.exe: virec.c wildcard.c
+ $(CC) virec.c -o virec.exe
+
+wildcard.exe: wildcard.c
+ $(CC) wildcard.c -o wildcard.exe
+
+ex.exe: alias.c
+ $(CC) alias.c -o ex.exe
+
+vi.exe: ex.exe
+ copy ex.exe vi.exe
+
+view.exe: ex.exe
+ copy ex.exe view.exe
diff --git a/Elvis.prj b/Elvis.prj
new file mode 100644
index 0000000..bcfda29
--- /dev/null
+++ b/Elvis.prj
@@ -0,0 +1,29 @@
+blk
+cmd1
+cmd2
+curses
+cut
+ex
+input
+main
+misc
+modify
+move1
+move2
+move3
+move4
+move5
+opts
+recycle
+redraw
+regexp
+regsub
+system
+tio
+tmp
+vars
+vcmd
+vi
+pc
+sysdos
+tinytcap
diff --git a/KNOWN.BUGS b/KNOWN.BUGS
new file mode 100644
index 0000000..a8e6c93
--- /dev/null
+++ b/KNOWN.BUGS
@@ -0,0 +1,43 @@
+(These are sorted by how irritating they are. The worst bugs are at the top.)
+
+- It is impossible to edit or view the same file more than once. It is
+ also impossible to invoke Elvis twice with no arguments when in the same
+ directory. This is caused by the way the temp file is named based on ".",
+ see function tmpstart() in tmp.c.
+
+- Inserting characters to make a line longer than BLKSIZE-1 (including the
+ newline) causes a crash. BLKSIZE is 1024 by default. The shift-J and :join
+ commands protect against this, but something like "9999a!<Esc>" will make
+ Elvis misbehave. Also, huge files (more than about 500k) cannot be edited.
+
+- autoindent is confusingly different from vi when editing typical
+ indented C code - e.g. after <ESC>o<ESC>xxxxxxxxxxi (x's to column 1)
+ the last i indents again.
+
+ ^U backspaces to the beginning of the line, but it should only backspace to
+ the start of auto-indent. (A second ^U could reasonably delete the indent
+ too)
+
+- The :@ and :source commands share a single buffer. This means that they
+ can't call each other. You can't run :source from within your .exrc file
+ either, for the same reason.
+
+- Commands which delete text before the cursor, such as `dB', don't move the
+ cursor, but they should.
+
+- Using the substitute command, it is not possible to replace with multi-
+ line text using the ^V^M construct in the replacement string. Vi allows
+ this as a special case.
+
+- The Elvis.prj file (used by Turbo-C under MS-DOS) does not force large model
+ and the text segment is > 64K.
+
+- The visual "put" commands can't be repeated by hitting ".".
+
+- "!!ls %" doesn't expand %
+
+- Sideways scrolling is unacceptable for slow terminals.
+
+- The ":set number" option is missing, among other things.
+
+- In DOS, the default colors are not very good.
diff --git a/Makefile.mix b/Makefile.mix
new file mode 100644
index 0000000..eb27397
--- /dev/null
+++ b/Makefile.mix
@@ -0,0 +1,472 @@
+# combined Makefile for ELVIS - a clone of `vi`
+#
+# After editing this Makefile as described below, you should...
+#
+# Use `make` to compile all programs
+# Use `make install` to copy the programs to the BIN directory
+# Use `make clean` to remove all object files
+# Use `make clobber` to remove everything except source & documentation
+# Use `make tags` to build new "tags" and "refs" files
+# Use `make uue` to produce uuencoded compressed tar archives of the source
+# Use `make sh` to produce shar archives of the source
+# Use `make print` to print the Elvis documentation
+#
+# Several groups of Makefile settings are included below. Choose *ONE* group
+# of settings for your particular system, and leave the others commented out.
+# The meanings of these settings are:
+# O the filename extension for unlinked object files -- usually .o
+# E the filename extension for executable files -- usually null
+# EXTRA version-specific object files used in elvis
+# EXTRA2 version-specific object files used in elvis, virec, & refont
+# LIBS any special libraries, such as "-ltermcap"
+# BIN directory where executables should be installed
+# CC the C compiler command, possibly with "memory model" flags
+# CFLAGS compiler flags used to select compile-time options
+# OF link flag to control the output file's name -- usually -o<space>
+# RF flag used to denote "compile but don't link" -- usually -c
+# DATE a "cc" flag that defines "DATE". e.g. DATE=-DDATE=\"7/4/76\"
+# EVAL the word "eval", if DATE requires it
+# PROGS the list of all programs
+# CHMEM any extra commands to be run after ELVIS is linked
+# SORT if the "tags" file must be sorted, then SORT=-DSORT
+# INST installation method: inst.dos, inst.tos, inst.os9, or inst.unix
+# RM the name of a program that deletes files
+# PR1 used to print documentation -- typically "refont -c"
+# PR2 used to send text to printer -- typically "| lpr"
+# DUMMY usually nothing, but OS9 needs "dummy"
+# DOC name of "doc" directory, with a trailing slash
+
+#---- These settings are recommended for System-V UNIX and SCO XENIX-386 ----
+#O= .o
+#E=
+#EXTRA=
+#EXTRA2=
+#LIBS= -ltermcap
+#BIN= /usr/local/bin
+#CFLAGS= -DM_SYSV -O
+#OF= -o
+#RF= -c
+#DATE= -DDATE=\'\"`date`\"\'
+#EVAL= eval
+#PROGS= elvis$E ctags$E ref$E virec$E refont$E
+#CHMEM=
+#SORT= -DSORT
+#INST= inst.unix
+#RM= rm -f
+#PR1= refont -c
+#PR2= | lp
+#DUMMY=
+#DOC= doc/
+
+#---- These settings are recommended for SCO XENIX-286 ----
+#O= .o
+#E=
+#EXTRA=
+#EXTRA2=
+#LIBS= -ltermcap
+#BIN= /usr/local/bin
+#CC= cc -M2s -i
+#CFLAGS= -DM_SYSV -Ox -DCS_IBMPC
+#OF= -o
+#RF= -c
+#DATE= -DDATE=\'\"`date`\"\'
+#EVAL= eval
+#PROGS= elvis$E ctags$E ref$E virec$E refont$E
+#CHMEM=
+#SORT= -DSORT
+#INST= inst.unix
+#RM= rm -f
+#PR1= refont -c
+#PR2= | lp
+#DUMMY=
+#DOC= doc/
+
+#---- These settings are recommended for BSD 4.3 UNIX ----
+#O= .o
+#E=
+#EXTRA=
+#EXTRA2=
+#LIBS= -ltermcap
+#BIN= /usr/local/bin
+#CFLAGS= -Dbsd -O
+#OF= -o
+#RF= -c
+#DATE= -DDATE=\'\"`date`\"\'
+#EVAL= eval
+#PROGS= elvis$E ctags$E ref$E virec$E refont$E
+#CHMEM=
+#SORT= -DSORT
+#INST= inst.unix
+#RM= rm -f
+#PR1= refont -c
+#PR2= | lpr
+#DUMMY=
+#DOC= doc/
+
+#---- These settings are recommended for Coherent ----
+#O=.o
+#E=
+#EXTRA=
+#EXTRA2=
+#LIBS= -lterm
+#BIN= /usr/bin
+#CC= cc
+#CFLAGS= -O -DCOHERENT -DCRUNCH -DNO_CHARATTR -DNO_CURSORSHAPE \
+# -DNO_DIGRAPH -DNO_MKEXRC -VSUVAR
+#OF= -o
+#RF= -c
+#DATE= -DDATE=\'\"`date`\"\'
+#EVAL= eval
+#PROGS= elvis$E ctags$E ref$E virec$E refont$E
+#CHMEM= fixstack 2000 elvis$E
+#SORT=
+#INST= inst.unix
+#RM= rm -f
+#PR1= refont -b
+#PR2= | lpr
+#DUMMY=
+#DOC= doc/
+
+#---- These settings are recommended for Minix-ST ----
+#O= .o
+#E=
+#EXTRA=
+#EXTRA2=
+#LIBS=
+#BIN= /usr/bin
+#CC= cc
+#CFLAGS=
+#OF= -o
+#RF= -c
+#DATE= -DDATE=\'\"`date`\"\'
+#EVAL= eval
+#PROGS= elvis$E ctags$E ref$E virec$E refont$E
+#CHMEM= chmem =18000 elvis
+#SORT=
+#INST= inst.unix
+#RM= rm -f
+#PR1= lpr
+#PR2=
+#DUMMY=
+#DOC= doc/
+
+#---- These settings are recommended for Minix-PC ----
+#O= .s
+#E=
+#EXTRA= tinytcap$O
+#EXTRA2=
+#LIBS=
+#BIN= /usr/bin
+#CC= cc -i
+#CFLAGS= -O -DCRUNCH -DNO_MKEXRC -DNO_CURSORSHAPE -DNO_CHARATTR \
+# -DNO_SHOWMODE -DNO_MODELINE -DNO_OPTCOLS -DNO_DIGRAPH -DNO_ABBR \
+# -DNO_AT -DNO_SENTENCE -DNO_ERRLIST
+#### (all but -DNO_EXTENSIONS, -DNO_RECYCLE, -DNO_MAGIC, and -DNO_CHARSEARCH)
+#OF= -o
+#RF= -c
+#DATE= -DDATE=\'\"`date`\"\'
+#EVAL= eval
+#PROGS= elvis$E ctags$E ref$E virec$E refont$E
+#CHMEM=
+#SORT=
+#INST= inst.unix
+#RM= rm -f
+#PR1= lpr
+#PR2=
+#DUMMY=
+#DOC= doc/
+
+#---- These settings are recommended for MS-DOS + MS-C + NDMAKE ----
+#O= .obj
+#E= .exe
+#EXTRA= pc$O sysdos$O tinytcap$O
+#EXTRA2=
+#LIBS=
+#BIN= c:\dos
+#CC= cl /AM
+#CFLAGS= -O -DCS_IBMPC -DCS_SPECIAL
+#OF= -o
+#RF= -c
+#DATE=
+#EVAL=
+#PROGS= elvis$E ex$E ctags$E ref$E virec$E wildcard$E refont$E
+#CHMEM=
+#SORT=
+#INST= inst.dos
+#RM= del
+#PR1= refont -c
+#PR2= >PRN
+#DUMMY=
+#DOC= doc\
+
+#---- These settings are recommended for Atari TOS + Mark Williams C ----
+#O=.o
+#E=.ttp
+#EXTRA= sysdos$O tinytcap$O
+#EXTRA2= atari$O
+#LIBS=
+#BIN= c:\
+#CC= cc -VPEEP
+#CFLAGS= -O -DCS_IBMPC -DCS_SPECIAL
+#OF= -o
+#RF= -c
+#DATE=
+#EVAL=
+#PROGS= elvis$E ctags$E ref$E virec$E wildcard$E shell$E refont$E
+#CHMEM=
+#SORT=
+#INST= inst.tos
+#RM= rm -f
+#PR1= refont -e
+#PR2= >PRT:
+#DUMMY=
+#DOC= 'doc\'
+
+#---- These settings are recommended for OS-9/68K V2.3 ----
+#O= .r
+#E=
+#EXTRA= date$O
+#EXTRA2= osk$O
+#LIBS= -l=/dd/lib/termlib.l
+#BIN= /dd/usr/cmds
+#CC= cc
+#ODIR= /dd/usr/src/elvis
+#CFLAGS= -gq -m=2
+#OF= -f=$(ODIR)/
+#RF= -r
+#DATE=
+#EVAL=
+#PROGS= elvis$E vi$E view$E input$E ctags$E ref$E virec$E refont$E
+#CHMEM= touch date.r
+#SORT=
+#INST= inst.os9
+#RM= del *.stb *.dbg
+#PR1= refont -b
+#PR2= >/p
+#DUMMY= dummy
+#DOC= doc/
+
+###########################################################################
+###########################################################################
+### ###
+### The rest of this Makefile contains no user-servicable parts ###
+### ###
+###########################################################################
+###########################################################################
+
+OBJS= blk$O cmd1$O cmd2$O curses$O cut$O ex$O input$O main$O misc$O \
+ modify$O move1$O move2$O move3$O move4$O move5$O opts$O recycle$O \
+ redraw$O regexp$O regsub$O system$O tio$O tmp$O vars$O vcmd$O vi$O
+
+ALIAS= alias$O
+
+DOCS= $(DOC)index.doc $(DOC)intro.doc $(DOC)visual.doc $(DOC)ex.doc \
+ $(DOC)regexp.doc $(DOC)options.doc $(DOC)cutbufs.doc $(DOC)differ.doc \
+ $(DOC)internal.doc $(DOC)cflags.doc $(DOC)termcap.doc \
+ $(DOC)environ.doc $(DOC)versions.doc
+
+SRC1= README KNOWN.BUGS $(DOC)intro.doc $(DOC)visual.doc $(DOC)ex.doc \
+ $(DOC)versions.doc $(DOC)cflags.doc $(DOC)differ.doc
+SRC2= $(DOC)cutbufs.doc $(DOC)options.doc $(DOC)environ.doc $(DOC)regexp.doc \
+ $(DOC)internal.doc $(DOC)termcap.doc $(DOC)index.doc $(DOC)ctags.man \
+ $(DOC)elvis.man $(DOC)ref.man $(DOC)refont.man $(DOC)virec.man
+SRC3= Elvis.lnk Elvis.mak Elvis.prj Makefile.mix alias.c atari.c \
+ ctags.c pc.c ref.c shell.c sysdos.c virec.c wildcard.c \
+ profile.sh osk.c osk.h date.c
+SRC4= blk.c cmd1.c cmd2.c config.h curses.c
+SRC5= curses.h cut.c ex.c input.c main.c misc.c
+SRC6= modify.c move1.c move2.c move3.c move4.c move5.c opts.c recycle.c \
+ redraw.c
+SRC7= regexp.c regexp.h regsub.c system.c tinytcap.c tio.c tmp.c
+SRC8= vars.c vcmd.c vi.c vi.h refont.c
+
+###########################################################################
+
+all: $(PROGS)
+ @echo done.
+
+elvis$E: $(OBJS) $(EXTRA) $(EXTRA2)
+ $(CC) $(CFLAGS) $(OF)elvis$E $(OBJS) $(EXTRA) $(EXTRA2) $(LIBS)
+ $(CHMEM)
+
+ctags$E: ctags.c
+ $(CC) $(CFLAGS) $(SORT) $(OF)ctags$E ctags.c
+
+ref$E: ref.c
+ $(CC) $(CFLAGS) $(OF)ref$E ref.c
+
+virec$E: virec.c
+ $(CC) $(CFLAGS) $(OF)virec$E virec.c $(EXTRA2)
+
+view$E: $(ALIAS)
+ $(CC) $(CFLAGS) $(OF)view$E $(ALIAS)
+
+ex$E: $(ALIAS)
+ $(CC) $(CFLAGS) $(OF)ex$E $(ALIAS)
+
+vi$E: $(ALIAS)
+ $(CC) $(CFLAGS) $(OF)vi$E $(ALIAS)
+
+input$E: $(ALIAS)
+ $(CC) $(CFLAGS) $(OF)input$E $(ALIAS)
+
+shell$E: shell.c
+ $(CC) $(CFLAGS) $(OF)shell$E shell.c
+
+wildcard$E: wildcard.c
+ $(CC) $(CFLAGS) $(OF)wildcard$E wildcard.c
+
+refont$E: refont.c
+ $(CC) $(CFLAGS) $(OF)refont$E refont.c $(EXTRA2)
+
+##############################################################################
+
+# The file cmd1.c is compiled with the extra flag -DDATE="today's date".
+cmd1$O: cmd1.c vi.h config.h
+ $(EVAL) $(CC) $(CFLAGS) $(RF) $(DATE) cmd1.c
+
+# "It all depends..."
+$(OBJS): vi.h curses.h config.h
+
+# OS9 must create a custom date.c file, and compile it.
+date$O: $(OBJS)
+ @echo '/* compilation date of elvis */' >-date.c
+ @echo -r 'char date[] = "' >+date.c
+ @echo -r 'echo -r ' >-/dd/tmp/date.c
+ @date >+/dd/tmp/date.c
+ @shell /dd/tmp/date.c >+date.c
+ @echo '";' >+date.c
+ @del /dd/tmp/date.c
+ $(CC) $(CFLAGS) $(RF) date.c
+
+##############################################################################
+install: $(INST)
+ @echo Installation complete.
+
+inst.unix: $(DUMMY)
+ cp $(PROGS) $(BIN)
+ (cd $(BIN); chmod 755 $(PROGS))
+ (cd $(BIN); chown bin $(PROGS))
+ -ln $(BIN)/elvis $(BIN)/vi
+ -ln $(BIN)/elvis $(BIN)/ex
+ -ln $(BIN)/elvis $(BIN)/view
+ -ln $(BIN)/elvis $(BIN)/input
+
+inst.dos: $(DUMMY)
+ copy $(PROGS) $(BIN)
+ copy $(BIN)/ex$E $(BIN)/vi$E
+ copy $(BIN)/ex$E $(BIN)/view$E
+ copy $(BIN)/ex$E $(BIN)/input$E
+
+inst.tos: $(DUMMY)
+ copy $(PROGS) $(BIN)
+
+inst.os9: $(DUMMY)
+ copy $(PROGS) -rw=$(BIN)
+ chd $(BIN); attr -epenprnpw $(PROGS)
+
+##############################################################################
+clean: $(DUMMY)
+ $(RM) *$O $(DOC)*.1 elvis?.uue elvis?.sh core
+
+clobber: clean
+ $(RM) tags refs $(PROGS)
+
+##############################################################################
+print: refont$E
+ $(PR1) $(DOCS) $(PR2)
+
+tags refs: ctags$E
+ ctags -r *.c *.h
+
+##############################################################################
+uue: elvis1.uue elvis2.uue elvis3.uue elvis4.uue elvis5.uue \
+ elvis6.uue elvis7.uue elvis8.uue
+
+elvis1.uue: $(SRC1)
+ tar cf elvis1.tar $(SRC1)
+ compress -b13 elvis1.tar
+ cp README elvis1.uue
+ uue elvis1.tar.Z - >>elvis1.uue
+ $(RM) elvis1.tar*
+
+elvis2.uue: $(SRC2)
+ tar cf elvis2.tar $(SRC2)
+ compress -b13 elvis2.tar
+ uue elvis2.tar.Z
+ $(RM) elvis2.tar*
+
+elvis3.uue: $(SRC3)
+ tar cf elvis3.tar $(SRC3)
+ compress -b13 elvis3.tar
+ uuencode elvis3.tar.Z <elvis3.tar.Z >elvis3.uue
+ $(RM) elvis3.tar*
+
+elvis4.uue: $(SRC4)
+ tar cf elvis4.tar $(SRC4)
+ compress -b13 elvis4.tar
+ uuencode elvis4.tar.Z <elvis4.tar.Z >elvis4.uue
+ $(RM) elvis4.tar*
+
+elvis5.uue: $(SRC5)
+ tar cf elvis5.tar $(SRC5)
+ compress -b13 elvis5.tar
+ uuencode elvis5.tar.Z <elvis5.tar.Z >elvis5.uue
+ $(RM) elvis5.tar*
+
+elvis6.uue: $(SRC6)
+ tar cf elvis6.tar $(SRC6)
+ compress -b13 elvis6.tar
+ uuencode elvis6.tar.Z <elvis6.tar.Z >elvis6.uue
+ $(RM) elvis6.tar*
+
+elvis7.uue: $(SRC7)
+ tar cf elvis7.tar $(SRC7)
+ compress -b13 elvis7.tar
+ uuencode elvis7.tar.Z <elvis7.tar.Z >elvis7.uue
+ $(RM) elvis7.tar*
+
+elvis8.uue: $(SRC8)
+ tar cf elvis8.tar $(SRC8)
+ compress -b13 elvis8.tar
+ uuencode elvis8.tar.Z <elvis8.tar.Z >elvis8.uue
+ $(RM) elvis8.tar*
+
+##############################################################################
+sh: elvis1.sh elvis2.sh elvis3.sh elvis4.sh elvis5.sh elvis6.sh \
+ elvis7.sh elvis8.sh
+
+elvis1.sh: $(SRC1)
+ cat >elvis1.sh README
+ echo >>elvis1.sh ': ------------------------ CUT HERE --------------------'
+ echo >>elvis1.sh 'test -d doc || mkdir doc || exit 2'
+ shar >>elvis1.sh -h $(SRC1)
+
+elvis2.sh: $(SRC2)
+ echo >elvis2.sh ': ------------------------ CUT HERE --------------------'
+ echo >>elvis2.sh 'test -d doc || mkdir doc || exit 2'
+ shar >>elvis2.sh -h $(SRC2)
+
+elvis3.sh: $(SRC3)
+ shar $(SRC3) >elvis3.sh
+
+elvis4.sh: $(SRC4)
+ shar $(SRC4) >elvis4.sh
+
+elvis5.sh: $(SRC5)
+ shar $(SRC5) >elvis5.sh
+
+elvis6.sh: $(SRC6)
+ shar $(SRC6) >elvis6.sh
+
+elvis7.sh: $(SRC7)
+ shar $(SRC7) >elvis7.sh
+
+elvis8.sh: $(SRC8)
+ shar $(SRC8) >elvis8.sh
+
+##############################################################################
+
+# Under XENIX only! This stores all sources on a 3.5" 720k floppy disk.
+floppy: $(SRC1) $(SRC2) $(SRC3) $(SRC4) $(SRC5) $(SRC6) $(SRC7) $(SRC8)
+ tar c5v $(SRC1) $(SRC2) $(SRC3) $(SRC4) $(SRC5) $(SRC6) $(SRC7) $(SRC8)
diff --git a/README b/README
new file mode 100644
index 0000000..9f77d33
--- /dev/null
+++ b/README
@@ -0,0 +1,32 @@
+Elvis is a clone of vi/ex, the standard UNIX editor. Elvis supports nearly
+all of the vi/ex commands, in both visual mode and colon mode.
+
+Elvis runs under BSD UNIX, AT&T SysV UNIX, SCO Xenix, Minix, MS-DOS, Atari TOS,
+OS9/68000, and Coherent. Ports to other operating systems are in progress;
+contact me before you start porting it to some other OS, because somebody else
+may have already done it for you.
+
+Elvis is freely redistributable, in either source form or executable form.
+There are no restrictions on how you may use it.
+
+The documentation will reside in a subdirectory called "doc". On some systems,
+you may need to create this directory before you can extract the documentation.
+The "doc/*.man" files are UNIX-style man pages; they are meant to be processed
+by "nroff -man". The "doc/*.doc" files are all chapters of the manual. They
+have already been formatted, and they contain Epson-compatible escape sequences
+to control type styles. A program called "refont" is included for stripping
+these out, if necessary.
+
+The file named "Makefile.mix" is used for all systems except MS-DOS. You
+should copy "Makefile.mix" to "Makefile", and then edit "Makefile" to select
+the appropriate group of settings for your system.
+
+
+Author: Steve Kirkendall
+
+E-mail: kirkenda@cs.pdx.edu
+
+Snail 14407 SW Teal Blvd. Apt.C
+ Mail: Beaverton, OR 97005
+
+Phone: (503) 643-6980
diff --git a/alias.c b/alias.c
new file mode 100644
index 0000000..0c2ff26
--- /dev/null
+++ b/alias.c
@@ -0,0 +1,102 @@
+/* alias.c */
+
+/* Author:
+ * Peter Reinig
+ * Universitaet Kaiserslautern
+ * Postfach 3049
+ * 7650 Kaiserslautern
+ * W. Germany
+ * reinig@physik.uni-kl.de
+ */
+
+/* This tiny program executes elvis with the flags that are appropriate
+ * for a given command name. This program is used only on systems that
+ * don't allow UNIX-style file links.
+ *
+ * The benefit of this program is: instead of having 5 copies of elvis
+ * on your disk, you only need one copy of elvis and 4 copies of this
+ * little program.
+ */
+
+#include <stdio.h>
+#include "config.h"
+
+#if OSK
+#define ELVIS "/dd/usr/cmds/elvis"
+#else
+#define ELVIS "elvis"
+#endif
+
+extern char **environ;
+extern int errno;
+extern char *malloc();
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int pid, i, j;
+ int letter;
+ char **argblk;
+#if OSK
+ extern int chainc();
+#endif
+
+ /* allocate enough space for a copy of the argument list, plus a
+ * terminating NULL, plus maybe an added flag.
+ */
+ argblk = (char **) malloc((argc + 2) * sizeof(char *));
+ if (!argblk)
+ {
+#if OSK
+ _errmsg(errno, "Can't get enough memory\n");
+#else
+ perror(argv[0]);
+#endif
+ exit(2);
+ }
+
+ /* find the last letter in the invocation name of this program */
+ i = strlen(argv[0]);
+#if MSDOS || TOS
+ /* we almost certainly must bypass ".EXE" or ".TTP" from argv[0] */
+ if (i > 4 && argv[0][i - 4] == '.')
+ i -= 4;
+#endif
+ letter = argv[0][i - 1];
+
+ /* copy argv to argblk, possibly inserting a flag such as "-R" */
+ argblk[0] = ELVIS;
+ i = j = 1;
+ switch (letter)
+ {
+ case 'w': /* "view" */
+ case 'W':
+ argblk[i++] = "-R";
+ break;
+#if !OSK
+ case 'x': /* "ex" */
+ case 'X':
+ argblk[i++] = "-e";
+ break;
+#endif
+ case 't': /* "input" */
+ case 'T':
+ argblk[i++] = "-i";
+ break;
+ }
+ while (j < argc)
+ {
+ argblk[i++] = argv[j++];
+ }
+ argblk[i] = (char *)0;
+
+ /* execute the real ELVIS program */
+#if OSK
+ pid = os9exec(chainc, argblk[0], argblk, environ, 0, 0, 3);
+ fprintf(stderr, "%s: cannot execute\n", argblk[0]);
+#else
+ (void)execvp(argblk[0], argblk);
+ perror(ELVIS);
+#endif
+}
diff --git a/atari.c b/atari.c
new file mode 100644
index 0000000..b426149
--- /dev/null
+++ b/atari.c
@@ -0,0 +1,94 @@
+/* atari.c */
+
+/* Author:
+ * Guntram Blohm
+ * Buchenstrasse 19
+ * 7904 Erbach, West Germany
+ * Tel. ++49-7305-6997
+ * sorry - no regular network connection
+ */
+
+/*
+ * This file contains the 'standard' functions which are not supported
+ * by Atari/Mark Williams, and some other TOS-only requirements.
+ */
+
+#include "config.h"
+#include "vi.h"
+
+#if TOS
+#include <osbind.h>
+
+/* vi uses mode==0 only ... */
+int access(file, mode)
+ char *file;
+{
+ int fd=Fopen(file, 0);
+ if (fd<0)
+ return -1;
+ Fclose(fd);
+ return 0;
+}
+
+char *mktemp(template)
+ char *template;
+{
+ return template;
+}
+
+/* read -- text mode, compress \r\n to \n
+ * warning: might fail when maxlen==1 and at eol
+ */
+
+int tread(fd, buf, maxlen)
+ int fd;
+ char *buf;
+ int maxlen;
+{
+ int i, j, nread=read(fd, buf, (unsigned)maxlen);
+
+ if (nread && buf[nread-1]=='\r')
+ { nread--;
+ lseek(fd, -1l, 1);
+ }
+ for (i=j=0; j<nread; i++,j++)
+ { if (buf[j]=='\r' && buf[j+1]=='\n')
+ j++;
+ buf[i]=buf[j];
+ }
+ return i;
+}
+
+int twrite(fd, buf, maxlen)
+ int fd;
+ char *buf;
+ int maxlen;
+{
+ int i, j, nwritten=0, hadnl=0;
+ char writbuf[BLKSIZE];
+
+ for (i=j=0; j<maxlen; )
+ {
+ if ((writbuf[i++]=buf[j++])=='\n')
+ { writbuf[i-1]='\r';
+ if (i<BLKSIZE)
+ writbuf[i++]='\n';
+ else
+ hadnl=1;
+ }
+ if (i==BLKSIZE)
+ {
+ write(fd, writbuf, (unsigned)i);
+ i=0;
+ }
+ if (hadnl)
+ {
+ writbuf[i++]='\n';
+ hadnl=0;
+ }
+ }
+ if (i)
+ write(fd, writbuf, (unsigned)i);
+ return j;
+}
+#endif
diff --git a/blk.c b/blk.c
new file mode 100644
index 0000000..6ece5dd
--- /dev/null
+++ b/blk.c
@@ -0,0 +1,468 @@
+/* blk.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains the functions that get/put blocks from the temp file.
+ * It also contains the "do" and "undo" functions.
+ */
+
+#include "config.h"
+#include "vi.h"
+
+#ifndef NBUFS
+# define NBUFS 5 /* must be at least 3 -- more is better */
+#endif
+
+extern long lseek();
+
+/*------------------------------------------------------------------------*/
+
+BLK hdr; /* buffer for the header block */
+
+static int b4cnt; /* used to count context of beforedo/afterdo */
+static struct _blkbuf
+{
+ BLK buf; /* contents of a text block */
+ unsigned short logical; /* logical block number */
+ int dirty; /* must the buffer be rewritten? */
+}
+ blk[NBUFS], /* buffers for text[?] blocks */
+ *toonew, /* buffer which shouldn't be recycled yet */
+ *newtoo, /* another buffer which should be recycled */
+ *recycle = blk; /* next block to be recycled */
+
+
+
+
+
+/* This function wipes out all buffers */
+void blkinit()
+{
+ int i;
+
+ for (i = 0; i < NBUFS; i++)
+ {
+ blk[i].logical = 0;
+ blk[i].dirty = FALSE;
+ }
+ for (i = 0; i < MAXBLKS; i++)
+ {
+ hdr.n[i] = 0;
+ }
+}
+
+/* This function allocates a buffer and fills it with a given block's text */
+BLK *blkget(logical)
+ int logical; /* logical block number to fetch */
+{
+ REG struct _blkbuf *this; /* used to step through blk[] */
+ REG int i;
+
+ /* if logical is 0, just return the hdr buffer */
+ if (logical == 0)
+ {
+ return &hdr;
+ }
+
+ /* see if we have that block in mem already */
+ for (this = blk; this < &blk[NBUFS]; this++)
+ {
+ if (this->logical == logical)
+ {
+ newtoo = toonew;
+ toonew = this;
+ return &this->buf;
+ }
+ }
+
+ /* choose a block to be recycled */
+ do
+ {
+ this = recycle++;
+ if (recycle == &blk[NBUFS])
+ {
+ recycle = blk;
+ }
+ } while (this == toonew || this == newtoo);
+
+ /* if it contains a block, flush that block */
+ blkflush(this);
+
+ /* fill this buffer with the desired block */
+ this->logical = logical;
+ if (hdr.n[logical])
+ {
+ /* it has been used before - fill it from tmp file */
+ lseek(tmpfd, (long)hdr.n[logical] * (long)BLKSIZE, 0);
+ if (read(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE)
+ {
+ msg("Error reading back from tmp file!");
+ }
+ }
+ else
+ {
+ /* it is new - zero it */
+ for (i = 0; i < BLKSIZE; i++)
+ {
+ this->buf.c[i] = 0;
+ }
+ }
+
+ /* This isn't really a change, but it does potentially invalidate
+ * the kinds of shortcuts that the "changes" variable is supposed
+ * to protect us from... so count it as a change.
+ */
+ changes++;
+
+ /* mark it as being "not dirty" */
+ this->dirty = 0;
+
+ /* return it */
+ newtoo = toonew;
+ toonew = this;
+ return &this->buf;
+}
+
+
+
+/* This function writes a block out to the temporary file */
+void blkflush(this)
+ REG struct _blkbuf *this; /* the buffer to flush */
+{
+ long seekpos; /* seek position of the new block */
+ unsigned short physical; /* physical block number */
+
+ /* if its empty (an orphan blkadd() maybe?) then make it dirty */
+ if (this->logical && !*this->buf.c)
+ {
+ blkdirty(&this->buf);
+ }
+
+ /* if it's an empty buffer or a clean version is on disk, quit */
+ if (!this->logical || hdr.n[this->logical] && !this->dirty)
+ {
+ return;
+ }
+
+ /* find a free place in the file */
+#ifndef NO_RECYCLE
+ seekpos = allocate();
+ lseek(tmpfd, seekpos, 0);
+#else
+ seekpos = lseek(tmpfd, 0L, 2);
+#endif
+ physical = seekpos / BLKSIZE;
+
+ /* put the block there */
+ if (write(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE)
+ {
+ msg("Trouble writing to tmp file");
+ }
+ this->dirty = FALSE;
+
+ /* update the header so it knows we put it there */
+ hdr.n[this->logical] = physical;
+}
+
+
+/* This function sets a block's "dirty" flag or deletes empty blocks */
+void blkdirty(bp)
+ BLK *bp; /* buffer returned by blkget() */
+{
+ REG int i, j;
+ REG char *scan;
+ REG int k;
+
+ /* find the buffer */
+ for (i = 0; i < NBUFS && bp != &blk[i].buf; i++)
+ {
+ }
+#ifdef DEBUG
+ if (i >= NBUFS)
+ {
+ msg("blkdirty() called with unknown buffer at 0x%lx", bp);
+ return;
+ }
+ if (blk[i].logical == 0)
+ {
+ msg("blkdirty called with freed buffer");
+ return;
+ }
+#endif
+
+ /* if this block ends with line# INFINITY, then it must have been
+ * allocated unnecessarily during tmpstart(). Forget it.
+ */
+ if (lnum[blk[i].logical] == INFINITY)
+ {
+#ifdef DEBUG
+ if (blk[i].buf.c[0])
+ {
+ msg("bkldirty called with non-empty extra BLK");
+ }
+#endif
+ blk[i].logical = 0;
+ blk[i].dirty = FALSE;
+ return;
+ }
+
+ /* count lines in this block */
+ for (j = 0, scan = bp->c; *scan && scan < bp->c + BLKSIZE; scan++)
+ {
+ if (*scan == '\n')
+ {
+ j++;
+ }
+ }
+
+ /* adjust lnum, if necessary */
+ k = blk[i].logical;
+ j += (lnum[k - 1] - lnum[k]);
+ if (j != 0)
+ {
+ nlines += j;
+ while (k < MAXBLKS && lnum[k] != INFINITY)
+ {
+ lnum[k++] += j;
+ }
+ }
+
+ /* if it still has text, mark it as dirty */
+ if (*bp->c)
+ {
+ blk[i].dirty = TRUE;
+ }
+ else /* empty block, so delete it */
+ {
+ /* adjust the cache */
+ k = blk[i].logical;
+ for (j = 0; j < NBUFS; j++)
+ {
+ if (blk[j].logical >= k)
+ {
+ blk[j].logical--;
+ }
+ }
+
+ /* delete it from hdr.n[] and lnum[] */
+ blk[i].logical = 0;
+ blk[i].dirty = FALSE;
+ while (k < MAXBLKS - 1)
+ {
+ hdr.n[k] = hdr.n[k + 1];
+ lnum[k] = lnum[k + 1];
+ k++;
+ }
+ hdr.n[MAXBLKS - 1] = 0;
+ lnum[MAXBLKS - 1] = INFINITY;
+ }
+}
+
+
+/* insert a new block into hdr, and adjust the cache */
+BLK *blkadd(logical)
+ int logical; /* where to insert the new block */
+{
+ REG int i;
+
+ /* adjust hdr and lnum[] */
+ for (i = MAXBLKS - 1; i > logical; i--)
+ {
+ hdr.n[i] = hdr.n[i - 1];
+ lnum[i] = lnum[i - 1];
+ }
+ hdr.n[logical] = 0;
+ lnum[logical] = lnum[logical - 1];
+
+ /* adjust the cache */
+ for (i = 0; i < NBUFS; i++)
+ {
+ if (blk[i].logical >= logical)
+ {
+ blk[i].logical++;
+ }
+ }
+
+ /* return the new block, via blkget() */
+ return blkget(logical);
+}
+
+
+/* This function forces all dirty blocks out to disk */
+void blksync()
+{
+ int i;
+
+ for (i = 0; i < NBUFS; i++)
+ {
+ /* blk[i].dirty = TRUE; */
+ blkflush(&blk[i]);
+ }
+ if (*o_sync)
+ {
+ sync();
+ }
+}
+
+/*------------------------------------------------------------------------*/
+
+static MARK undocurs; /* where the cursor should go if undone */
+static long oldnlines;
+static long oldlnum[MAXBLKS];
+
+
+/* This function should be called before each command that changes the text.
+ * It defines the state that undo() will reset the file to.
+ */
+void beforedo(forundo)
+ int forundo; /* boolean: is this for an undo? */
+{
+ REG int i;
+ REG long l;
+
+ /* if this is a nested call to beforedo, quit! Use larger context */
+ if (b4cnt++ > 0)
+ {
+ return;
+ }
+
+ /* force all block buffers to disk */
+ blksync();
+
+#ifndef NO_RECYCLE
+ /* perform garbage collection on blocks from tmp file */
+ garbage();
+#endif
+
+ /* force the header out to disk */
+ lseek(tmpfd, 0L, 0);
+ if (write(tmpfd, hdr.c, (unsigned)BLKSIZE) != BLKSIZE)
+ {
+ msg("Trouble writing header to tmp file ");
+ }
+
+ /* copy or swap oldnlines <--> nlines, oldlnum <--> lnum */
+ if (forundo)
+ {
+ for (i = 0; i < MAXBLKS; i++)
+ {
+ l = lnum[i];
+ lnum[i] = oldlnum[i];
+ oldlnum[i] = l;
+ }
+ l = nlines;
+ nlines = oldnlines;
+ oldnlines = l;
+ }
+ else
+ {
+ for (i = 0; i < MAXBLKS; i++)
+ {
+ oldlnum[i] = lnum[i];
+ }
+ oldnlines = nlines;
+ }
+
+ /* save the cursor position */
+ undocurs = cursor;
+
+ /* upon return, the calling function continues and makes changes... */
+}
+
+/* This function marks the end of a (nested?) change to the file */
+void afterdo()
+{
+ if (--b4cnt)
+ {
+ /* after abortdo(), b4cnt may decribe nested beforedo/afterdo
+ * pairs incorrectly. If it is decremented to often, then
+ * keep b4cnt sane but don't do anything else.
+ */
+ if (b4cnt < 0)
+ b4cnt = 0;
+
+ return;
+ }
+
+ /* make sure the cursor wasn't left stranded in deleted text */
+ if (markline(cursor) > nlines)
+ {
+ cursor = MARK_LAST;
+ }
+ /* NOTE: it is still possible that markidx(cursor) is after the
+ * end of a line, so the Vi mode will have to take care of that
+ * itself */
+
+ /* if a significant change has been made to this file, then set the
+ * MODIFIED flag.
+ */
+ if (significant)
+ {
+ setflag(file, MODIFIED);
+ }
+}
+
+/* This function cuts short the current set of changes. It is called after
+ * a SIGINT.
+ */
+void abortdo()
+{
+ /* finish the operation immediately. */
+ if (b4cnt > 0)
+ {
+ b4cnt = 1;
+ afterdo();
+ }
+
+ /* in visual mode, the screen is probably screwed up */
+ if (mode == MODE_COLON)
+ {
+ mode = MODE_VI;
+ }
+ if (mode == MODE_VI)
+ {
+ redraw(MARK_UNSET, FALSE);
+ }
+}
+
+/* This function discards all changes made since the last call to beforedo() */
+int undo()
+{
+ BLK oldhdr;
+
+ /* if beforedo() has never been run, fail */
+ if (!tstflag(file, MODIFIED))
+ {
+ msg("You haven't modified this file yet.");
+ return FALSE;
+ }
+
+ /* read the old header form the tmp file */
+ lseek(tmpfd, 0L, 0);
+ if (read(tmpfd, oldhdr.c, (unsigned)BLKSIZE) != BLKSIZE)
+ {
+ msg("Trouble rereading the old header from tmp file");
+ }
+
+ /* "do" the changed version, so we can undo the "undo" */
+ cursor = undocurs;
+ beforedo(TRUE);
+ afterdo();
+
+ /* wipe out the block buffers - we can't assume they're correct */
+ blkinit();
+
+ /* use the old header -- and therefore the old text blocks */
+ hdr = oldhdr;
+
+ /* This is a change */
+ changes++;
+
+ return TRUE;
+}
diff --git a/cmd1.c b/cmd1.c
new file mode 100644
index 0000000..dd7ef77
--- /dev/null
+++ b/cmd1.c
@@ -0,0 +1,1268 @@
+/* cmd1.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains some of the EX commands - mostly ones that deal with
+ * files, options, etc. -- anything except text.
+ */
+
+#include "config.h"
+#include <ctype.h>
+#include "vi.h"
+#include "regexp.h"
+
+#if MSDOS
+#define DATE __DATE__
+#endif
+
+#ifdef DEBUG
+/* print the selected lines with info on the blocks */
+/*ARGSUSED*/
+void cmd_debug(frommark, tomark, cmd, bang, extra)
+ MARK frommark;
+ MARK tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ REG char *scan;
+ REG long l;
+ REG int i;
+ int len;
+
+ /* scan lnum[] to determine which block its in */
+ l = markline(frommark);
+ for (i = 1; l > lnum[i]; i++)
+ {
+ }
+
+ do
+ {
+ /* fetch text of the block containing that line */
+ scan = blkget(i)->c;
+
+ /* calculate its length */
+ if (scan[BLKSIZE - 1])
+ {
+ len = BLKSIZE;
+ }
+ else
+ {
+ len = strlen(scan);
+ }
+
+ /* print block stats */
+ msg("##### hdr[%d]=%d, lnum[%d-1]=%ld, lnum[%d]=%ld (%ld lines)",
+ i, hdr.n[i], i, lnum[i-1], i, lnum[i], lnum[i] - lnum[i - 1]);
+ msg("##### len=%d, buf=0x%lx, %sdirty",
+ len, scan, ((int *)scan)[MAXBLKS + 1] ? "" : "not ");
+ if (bang)
+ {
+ while (--len >= 0)
+ {
+ addch(*scan);
+ scan++;
+ }
+ }
+ exrefresh();
+
+ /* next block */
+ i++;
+ } while (i < MAXBLKS && lnum[i] && lnum[i - 1] < markline(tomark));
+}
+
+
+/* This function checks a lot of conditions to make sure they aren't screwy */
+/*ARGSUSED*/
+void cmd_validate(frommark, tomark, cmd, bang, extra)
+ MARK frommark;
+ MARK tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ char *scan;
+ int i;
+ int nlcnt; /* used to count newlines */
+ int len; /* counts non-NUL characters */
+
+ /* check lnum[0] */
+ if (lnum[0] != 0L)
+ {
+ msg("lnum[0] = %ld", lnum[0]);
+ }
+
+ /* check each block */
+ for (i = 1; lnum[i] <= nlines; i++)
+ {
+ scan = blkget(i)->c;
+ if (scan[BLKSIZE - 1])
+ {
+ msg("block %d has no NUL at the end", i);
+ }
+ else
+ {
+ for (nlcnt = len = 0; *scan; scan++, len++)
+ {
+ if (*scan == '\n')
+ {
+ nlcnt++;
+ }
+ }
+ if (scan[-1] != '\n')
+ {
+ msg("block %d doesn't end with '\\n' (length %d)", i, len);
+ }
+ if (bang || nlcnt != lnum[i] - lnum[i - 1])
+ {
+ msg("block %d (line %ld?) has %d lines, but should have %ld",
+ i, lnum[i - 1] + 1L, nlcnt, lnum[i] - lnum[i - 1]);
+ }
+ }
+ exrefresh();
+ }
+
+ /* check lnum again */
+ if (lnum[i] != INFINITY)
+ {
+ msg("hdr.n[%d] = %d, but lnum[%d] = %ld",
+ i, hdr.n[i], i, lnum[i]);
+ }
+
+ msg("# = \"%s\", %% = \"%s\"", prevorig, origname);
+}
+#endif /* DEBUG */
+
+
+/*ARGSUSED*/
+void cmd_mark(frommark, tomark, cmd, bang, extra)
+ MARK frommark;
+ MARK tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ /* validate the name of the mark */
+ if (!extra || *extra < 'a' || *extra > 'z' || extra[1])
+ {
+ msg("Invalid mark name");
+ return;
+ }
+
+ mark[*extra - 'a'] = tomark;
+}
+
+/*ARGSUSED*/
+void cmd_write(frommark, tomark, cmd, bang, extra)
+ MARK frommark;
+ MARK tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ int fd;
+ int append; /* boolean: write in "append" mode? */
+ REG long l;
+ REG char *scan;
+ REG int i;
+
+ /* if all lines are to be written, use tmpsave() */
+ if (frommark == MARK_FIRST && tomark == MARK_LAST)
+ {
+ tmpsave(extra, bang);
+ return;
+ }
+
+ /* see if we're going to do this in append mode or not */
+ append = FALSE;
+ if (extra[0] == '>' && extra[1] == '>')
+ {
+ extra += 2;
+ append = TRUE;
+ }
+
+ /* either the file must not exist, or we must have a ! or be appending */
+ if (access(extra, 0) == 0 && !bang && !append)
+ {
+ msg("File already exists - Use :w! to overwrite");
+ return;
+ }
+
+ /* else do it line-by-line, like cmd_print() */
+ if (append)
+ {
+#ifdef O_APPEND
+ fd = open(extra, O_WRONLY|O_APPEND);
+#else
+ fd = open(extra, O_WRONLY);
+ if (fd >= 0)
+ {
+ lseek(fd, 0L, 2);
+ }
+#endif
+ }
+ else
+ {
+ fd = -1; /* so we know the file isn't open yet */
+ }
+
+ if (fd < 0)
+ {
+ fd = creat(extra, FILEPERMS);
+ if (fd < 0)
+ {
+ msg("Can't write to \"%s\"", extra);
+ return;
+ }
+ }
+ for (l = markline(frommark); l <= markline(tomark); l++)
+ {
+ /* get the next line */
+ scan = fetchline(l);
+ i = strlen(scan);
+ scan[i++] = '\n';
+
+ /* print the line */
+ twrite(fd, scan, i);
+ }
+ close(fd);
+}
+
+
+/*ARGSUSED*/
+void cmd_shell(frommark, tomark, cmd, bang, extra)
+ MARK frommark, tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ static char prevextra[80];
+
+ /* special case: ":sh" means ":!sh" */
+ if (cmd == CMD_SHELL)
+ {
+ extra = o_shell;
+ frommark = tomark = 0L;
+ }
+
+ /* if extra is "!", substute previous command */
+ if (*extra == '!')
+ {
+ if (!*prevextra)
+ {
+ msg("No previous shell command to substitute for '!'");
+ return;
+ }
+ extra = prevextra;
+ }
+ else if (cmd == CMD_BANG && strlen(extra) < sizeof(prevextra) - 1)
+ {
+ strcpy(prevextra, extra);
+ }
+
+ /* if no lines were specified, just run the command */
+ suspend_curses();
+ if (frommark == 0L)
+ {
+ system(extra);
+ }
+ else /* pipe lines from the file through the command */
+ {
+ filter(frommark, tomark, extra);
+ }
+
+ /* resume curses quietly for MODE_EX, but noisily otherwise */
+ resume_curses(mode == MODE_EX);
+}
+
+
+/*ARGSUSED*/
+void cmd_global(frommark, tomark, cmd, bang, extra)
+ MARK frommark, tomark;
+ CMD cmd;
+ int bang;
+ char *extra; /* rest of the command line */
+{
+ char *cmdptr; /* the command from the command line */
+ char cmdln[100]; /* copy of the command from the command line */
+ char *line; /* a line from the file */
+ long l; /* used as a counter to move through lines */
+ long lqty; /* quantity of lines to be scanned */
+ long nchanged; /* number of lines changed */
+ regexp *re; /* the compiled search expression */
+
+ /* can't nest global commands */
+ if (doingglobal)
+ {
+ msg("Can't nest global commands.");
+ rptlines = -1L;
+ return;
+ }
+
+ /* ":g! ..." is the same as ":v ..." */
+ if (bang)
+ {
+ cmd = CMD_VGLOBAL;
+ }
+
+ /* make sure we got a search pattern */
+ if (*extra != '/' && *extra != '?')
+ {
+ msg("Usage: %c /regular expression/ command", cmd == CMD_GLOBAL ? 'g' : 'v');
+ return;
+ }
+
+ /* parse & compile the search pattern */
+ cmdptr = parseptrn(extra);
+ if (!extra[1])
+ {
+ msg("Can't use empty regular expression with '%c' command", cmd == CMD_GLOBAL ? 'g' : 'v');
+ return;
+ }
+ re = regcomp(extra + 1);
+ if (!re)
+ {
+ /* regcomp found & described an error */
+ return;
+ }
+
+ /* for each line in the range */
+ doingglobal = TRUE;
+ ChangeText
+ {
+ /* NOTE: we have to go through the lines in a forward order,
+ * otherwise "g/re/p" would look funny. *BUT* for "g/re/d"
+ * to work, simply adding 1 to the line# on each loop won't
+ * work. The solution: count lines relative to the end of
+ * the file. Think about it.
+ */
+ for (l = nlines - markline(frommark),
+ lqty = markline(tomark) - markline(frommark) + 1L,
+ nchanged = 0L;
+ lqty > 0 && nlines - l >= 0 && nchanged >= 0L;
+ l--, lqty--)
+ {
+ /* fetch the line */
+ line = fetchline(nlines - l);
+
+ /* if it contains the search pattern... */
+ if ((!regexec(re, line, 1)) == (cmd != CMD_GLOBAL))
+ {
+ /* move the cursor to that line */
+ cursor = MARK_AT_LINE(nlines - l);
+
+ /* do the ex command (without mucking up
+ * the original copy of the command line)
+ */
+ strcpy(cmdln, cmdptr);
+ rptlines = 0L;
+ doexcmd(cmdln);
+ nchanged += rptlines;
+ }
+ }
+ }
+ doingglobal = FALSE;
+
+ /* free the regexp */
+ free(re);
+
+ /* Reporting...*/
+ rptlines = nchanged;
+}
+
+
+/*ARGSUSED*/
+void cmd_file(frommark, tomark, cmd, bang, extra)
+ MARK frommark, tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+#ifndef CRUNCH
+ /* if we're given a new filename, use it as this file's name */
+ if (extra && *extra)
+ {
+ strcpy(origname, extra);
+ }
+#endif
+ if (cmd == CMD_FILE)
+ {
+ msg("\"%s\" %s%s %ld lines, line %ld [%ld%%]",
+ *origname ? origname : "[NO FILE]",
+ tstflag(file, MODIFIED) ? "[MODIFIED]" : "",
+ tstflag(file, READONLY) ? "[READONLY]" : "",
+ nlines,
+ markline(frommark),
+ markline(frommark) * 100 / nlines);
+ }
+ else if (markline(frommark) == markline(tomark))
+ {
+ msg("%ld", markline(frommark));
+ }
+ else
+ {
+ msg("range \"%ld,%ld\" contains %ld lines",
+ markline(frommark),
+ markline(tomark),
+ markline(tomark) - markline(frommark) + 1L);
+ }
+}
+
+
+/*ARGSUSED*/
+void cmd_edit(frommark, tomark, cmd, bang, extra)
+ MARK frommark, tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ long line = 1L; /* might be set to prevline */
+
+ /* Editing previous file? Then start at previous line */
+ if (!strcmp(extra, prevorig))
+ {
+ line = prevline;
+ }
+
+#ifndef CRUNCH
+ /* if we were given an explicit starting line, then start there */
+ if (*extra == '+')
+ {
+ for (extra++, line = 0L; *extra >= '0' && *extra <= '9'; extra++)
+ {
+ line *= 10L;
+ line += (*extra - '0');
+ }
+ while (isascii(*extra) && isspace(*extra))
+ {
+ extra++;
+ }
+ }
+#endif /* not CRUNCH */
+
+ /* switch files */
+ if (tmpabort(bang))
+ {
+ tmpstart(extra);
+ if (line <= nlines && line >= 1L)
+ {
+ cursor = MARK_AT_LINE(line);
+ }
+ }
+ else
+ {
+ msg("Use edit! to abort changes, or w to save changes");
+
+ /* so we can say ":e!#" next time... */
+ strcpy(prevorig, extra);
+ prevline = 1L;
+ }
+}
+
+/* This code is also used for rewind -- GB */
+
+/*ARGSUSED*/
+void cmd_next(frommark, tomark, cmd, bang, extra)
+ MARK frommark, tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ int i, j;
+ char *scan;
+ char *build;
+
+ /* if extra stuff given, use ":args" to define a new args list */
+ if (cmd == CMD_NEXT && extra && *extra)
+ {
+ cmd_args(frommark, tomark, cmd, bang, extra);
+ }
+
+ /* move to the next arg */
+ if (cmd == CMD_NEXT)
+ {
+ i = argno + 1;
+ }
+ else if (cmd == CMD_PREVIOUS)
+ {
+ i = argno - 1;
+ }
+ else /* cmd == CMD_REWIND */
+ {
+ i = 0;
+ }
+ if (i < 0 || i >= nargs)
+ {
+ msg("No %sfiles to edit", cmd == CMD_REWIND ? "" : "more ");
+ return;
+ }
+
+ /* find & isolate the name of the file to edit */
+ for (j = i, scan = args; j > 0; j--)
+ {
+ while(!isascii(*scan) || !isspace(*scan))
+ {
+ scan++;
+ }
+ while (isascii(*scan) && isspace(*scan))
+ {
+ scan++;
+ }
+ }
+ for (build = tmpblk.c; *scan && (!isascii(*scan) || !isspace(*scan)); )
+ {
+ *build++ = *scan++;
+ }
+ *build = '\0';
+
+ /* switch to the next file */
+ if (tmpabort(bang))
+ {
+ tmpstart(tmpblk.c);
+ argno = i;
+ }
+ else
+ {
+ msg("Use :%s! to abort changes, or w to save changes",
+ cmd == CMD_NEXT ? "next" :
+ cmd == CMD_PREVIOUS ? "previous" :
+ "rewind");
+ }
+}
+
+/* also called from :wq -- always writes back in this case */
+
+/*ARGSUSED*/
+void cmd_xit(frommark, tomark, cmd, bang, extra)
+ MARK frommark, tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ static long whenwarned; /* when the user was last warned of extra files */
+ int oldflag;
+
+ /* if there are more files to edit, then warn user */
+ if (argno + 1 < nargs && whenwarned != changes && (!bang || cmd != CMD_QUIT))
+ {
+ msg("More files to edit -- Use \":n\" to go to next file");
+ whenwarned = changes;
+ return;
+ }
+
+ if (cmd == CMD_QUIT)
+ {
+ if (tmpabort(bang))
+ {
+ mode = MODE_QUIT;
+ }
+ else
+ {
+ msg("Use q! to abort changes, or wq to save changes");
+ }
+ }
+ else
+ {
+ /* else try to save this file */
+ oldflag = tstflag(file, MODIFIED);
+ if (cmd == CMD_WQUIT)
+ setflag(file, MODIFIED);
+ if (tmpend(bang))
+ {
+ mode = MODE_QUIT;
+ }
+ else
+ {
+ msg("Could not save file -- use quit! to abort changes, or w filename");
+ }
+ if (!oldflag)
+ clrflag(file, MODIFIED);
+ }
+}
+
+
+/*ARGSUSED*/
+void cmd_args(frommark, tomark, cmd, bang, extra)
+ MARK frommark, tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ char *scan;
+ char *eow;
+ int col;
+ int arg;
+ int addcols;
+ int scrolled = 0;
+
+ /* if no extra names given, or just current name, then report the args
+ * we have now.
+ */
+ if (!extra || !*extra)
+ {
+ for (scan = args, col=arg=0; *scan; )
+ {
+ while (*scan && isascii(*scan) && isspace(*scan))
+ scan++;
+ eow = scan;
+ while (*eow && (!isascii(*++eow) || !isspace(*eow)))
+ ;
+ if (arg == argno)
+ addcols = 2;
+ else
+ addcols = 0;
+ if (col+addcols+(int)(eow-scan)+1>=COLS)
+ {
+ addch('\n');
+ scrolled=1;
+ col=0;
+ }
+ else if (arg)
+ { qaddch(' ');
+ col++;
+ }
+ if (arg == argno)
+ qaddch('[');
+ while (scan < eow)
+ { qaddch(*scan++);
+ col++;
+ }
+ if (arg == argno)
+ qaddch(']');
+ arg++;
+ col+=addcols;
+ }
+ /* write a trailing newline */
+ if ((mode == MODE_EX || mode == MODE_COLON || scrolled) && col)
+ addch('\n');
+ exrefresh();
+ }
+ else /* new args list given */
+ {
+ strcpy(args, extra);
+ argno = -1; /* before the first, so :next will go to first */
+
+ /* count the names */
+ for (nargs = 0, scan = args; *scan; nargs++)
+ {
+ while (*scan && (!isascii(*scan) || !isspace(*scan)))
+ {
+ scan++;
+ }
+ while (isascii(*scan) && isspace(*scan))
+ {
+ scan++;
+ }
+ }
+ msg("%d files to edit", nargs);
+ }
+}
+
+
+/*ARGSUSED*/
+void cmd_cd(frommark, tomark, cmd, bang, extra)
+ MARK frommark, tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ char *getenv();
+
+ /* default directory name is $HOME */
+ if (!*extra)
+ {
+ extra = getenv("HOME");
+ if (!extra)
+ {
+ msg("environment variable $HOME not set");
+ return;
+ }
+ }
+
+ /* go to the directory */
+ if (chdir(extra) < 0)
+ {
+ perror(extra);
+ }
+}
+
+
+/*ARGSUSED*/
+void cmd_map(frommark, tomark, cmd, bang, extra)
+ MARK frommark, tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ char *mapto;
+
+ /* "map" with no extra will dump the map table contents */
+ if (!*extra)
+ {
+ dumpkey(bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD);
+ }
+ else
+ {
+ /* "extra" is key to map, followed my what it maps to */
+ for (mapto = extra; *mapto && *mapto != ' ' && *mapto!= '\t'; mapto++)
+ {
+ }
+ while (*mapto == ' ' || *mapto == '\t')
+ {
+ *mapto++ = '\0';
+ }
+
+ mapkey(extra, mapto, bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, (char *)0);
+ }
+}
+
+
+/*ARGSUSED*/
+void cmd_set(frommark, tomark, cmd, bang, extra)
+ MARK frommark, tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ if (!*extra)
+ {
+ dumpopts(FALSE);/* "FALSE" means "don't dump all" - only set */
+ }
+ else if (!strcmp(extra, "all"))
+ {
+ dumpopts(TRUE); /* "TRUE" means "dump all" - even unset vars */
+ }
+ else
+ {
+ setopts(extra);
+
+ /* That option may have affected the appearence of text */
+ changes++;
+ }
+}
+
+/*ARGSUSED*/
+void cmd_tag(frommark, tomark, cmd, bang, extra)
+ MARK frommark, tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ char *scan; /* used to scan through the tmpblk.c */
+ char *cmp; /* char of tag name we're comparing, or NULL */
+ char *end; /* marks the end of chars in tmpblk.c */
+ int fd; /* file descriptor used to read the file */
+#ifndef NO_MAGIC
+ char wasmagic; /* preserves the original state of o_magic */
+#endif
+ static char prevtag[30];
+
+ /* if no tag is given, use the previous tag */
+ if (!extra || !*extra)
+ {
+ if (!*prevtag)
+ {
+ msg("No previous tag");
+ return;
+ }
+ extra = prevtag;
+ }
+ else
+ {
+ strncpy(prevtag, extra, sizeof prevtag);
+ }
+
+ /* open the tags file */
+ fd = open(TAGS, O_RDONLY);
+ if (fd < 0)
+ {
+ msg("No tags file");
+ return;
+ }
+
+ /* Hmmm... this would have been a lot easier with <stdio.h> */
+
+ /* find the line with our tag in it */
+ for(scan = end = tmpblk.c, cmp = extra; ; scan++)
+ {
+ /* read a block, if necessary */
+ if (scan >= end)
+ {
+ end = tmpblk.c + tread(fd, tmpblk.c, BLKSIZE);
+ scan = tmpblk.c;
+ if (scan >= end)
+ {
+ msg("tag \"%s\" not found", extra);
+ close(fd);
+ return;
+ }
+ }
+
+ /* if we're comparing, compare... */
+ if (cmp)
+ {
+ /* matched??? wow! */
+ if (!*cmp && *scan == '\t')
+ {
+ break;
+ }
+ if (*cmp++ != *scan)
+ {
+ /* failed! skip to newline */
+ cmp = (char *)0;
+ }
+ }
+
+ /* if we're skipping to newline, do it fast! */
+ if (!cmp)
+ {
+ while (scan < end && *scan != '\n')
+ {
+ scan++;
+ }
+ if (scan < end)
+ {
+ cmp = extra;
+ }
+ }
+ }
+
+ /* found it! get the rest of the line into memory */
+ for (cmp = tmpblk.c, scan++; scan < end && *scan != '\n'; )
+ {
+ *cmp++ = *scan++;
+ }
+ if (scan == end)
+ {
+ tread(fd, cmp, BLKSIZE - (cmp - tmpblk.c));
+ }
+
+ /* we can close the tags file now */
+ close(fd);
+
+ /* extract the filename from the line, and edit the file */
+ for (cmp = tmpblk.c; *cmp != '\t'; cmp++)
+ {
+ }
+ *cmp++ = '\0';
+ if (strcmp(origname, tmpblk.c) != 0)
+ {
+ if (!tmpabort(bang))
+ {
+ msg("Use :tag! to abort changes, or :w to save changes");
+ return;
+ }
+ tmpstart(tmpblk.c);
+ }
+
+ /* move to the desired line (or to line 1 if that fails) */
+#ifndef NO_MAGIC
+ wasmagic = *o_magic;
+ *o_magic = FALSE;
+#endif
+ cursor = MARK_FIRST;
+ linespec(cmp, &cursor);
+ if (cursor == MARK_UNSET)
+ {
+ cursor = MARK_FIRST;
+ }
+#ifndef NO_MAGIC
+ *o_magic = wasmagic;
+#endif
+}
+
+
+
+/*ARGSUSED*/
+void cmd_visual(frommark, tomark, cmd, bang, extra)
+ MARK frommark, tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ mode = MODE_VI;
+ msg("");
+}
+
+
+
+
+
+/* describe this version of the program */
+/*ARGSUSED*/
+void cmd_version(frommark, tomark, cmd, bang, extra)
+ MARK frommark;
+ MARK tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+#ifndef DATE
+ msg("%s", VERSION);
+#else
+ msg("%s (%s)", VERSION, DATE);
+#endif
+#ifdef COMPILED_BY
+ msg("Compiled by %s", COMPILED_BY);
+#endif
+#ifdef CREDIT
+ msg("%s", CREDIT);
+#endif
+#ifdef COPYING
+ msg("%s", COPYING);
+#endif
+}
+
+
+#ifndef NO_MKEXRC
+/* make a .exrc file which describes the current configuration */
+/*ARGSUSED*/
+void cmd_mkexrc(frommark, tomark, cmd, bang, extra)
+ MARK frommark;
+ MARK tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ int fd;
+
+ /* the default name for the .exrc file EXRC */
+ if (!*extra)
+ {
+ extra = EXRC;
+ }
+
+ /* create the .exrc file */
+ fd = creat(extra, FILEPERMS);
+ if (fd < 0)
+ {
+ msg("Couldn't create a new \"%s\" file", extra);
+ return;
+ }
+
+ /* save stuff */
+ savekeys(fd);
+ saveopts(fd);
+#ifndef NO_DIGRAPH
+ savedigs(fd);
+#endif
+#ifndef NO_ABBR
+ saveabbr(fd);
+#endif
+
+ /* close the file */
+ close(fd);
+ msg("Created a new \"%s\" file", extra);
+}
+#endif
+
+#ifndef NO_DIGRAPH
+/*ARGSUSED*/
+void cmd_digraph(frommark, tomark, cmd, bang, extra)
+ MARK frommark;
+ MARK tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ do_digraph(bang, extra);
+}
+#endif
+
+
+#ifndef NO_ERRLIST
+static char errfile[256]; /* the name of a file containing an error */
+static long errline; /* the line number for an error */
+
+/* This static function tries to parse an error message.
+ *
+ * For most compilers, the first word is taken to be the name of the erroneous
+ * file, and the first number after that is taken to be the line number where
+ * the error was detected. The description of the error follows, possibly
+ * preceded by an "error ... :" or "warning ... :" label which is skipped.
+ *
+ * For Coherent, error messages look like "line#: filename: message".
+ *
+ * For non-error lines, or unparsable error lines, this function returns NULL.
+ * Normally, though, it alters errfile and errline, and returns a pointer to
+ * the description.
+ */
+static char *parse_errmsg(text)
+ REG char *text;
+{
+ REG char *cpy;
+ long atol();
+# if COHERENT || TOS /* any Mark Williams compiler */
+ /* Get the line number. If no line number, then ignore this line. */
+ errline = atol(text);
+ if (errline == 0L)
+ return (char *)0;
+
+ /* Skip to the start of the filename */
+ while (*text && *text++ != ':')
+ {
+ }
+ if (!*text++)
+ return (char *)0;
+
+ /* copy the filename to errfile */
+ for (cpy = errfile; *text && (*cpy++ = *text++) != ':'; )
+ {
+ }
+ if (!*text++)
+ return (char *)0;
+ cpy[-1] = '\0';
+
+ return text;
+# else /* not a Mark Williams compiler */
+ char *errmsg;
+
+ /* the error message is the whole line, by default */
+ errmsg = text;
+
+ /* skip leading garbage */
+ while (*text && !(isascii(*text) && isalnum(*text)))
+ {
+ text++;
+ }
+
+ /* copy over the filename */
+ cpy = errfile;
+ while(isascii(*text) && isalnum(*text) || *text == '.')
+ {
+ *cpy++ = *text++;
+ }
+ *cpy = '\0';
+
+ /* ignore the name "Error" and filenames that contain a '/' */
+ if (*text == '/' || !strcmp(errfile + 1, "rror") || access(errfile, 0) < 0)
+ {
+ return (char *)0;
+ }
+
+ /* skip garbage between filename and line number */
+ while (*text && !(isascii(*text) && isdigit(*text)))
+ {
+ text++;
+ }
+
+ /* if the number is part of a larger word, then ignore this line */
+ if (*text && isascii(text[-1]) && isalpha(text[-1]))
+ {
+ return (char *)0;
+ }
+
+ /* get the error line */
+ errline = 0L;
+ while (isascii(*text) && isdigit(*text))
+ {
+ errline *= 10;
+ errline += (*text - '0');
+ text++;
+ }
+
+ /* any line which lacks a filename or line number should be ignored */
+ if (!errfile[0] || !errline)
+ {
+ return (char *)0;
+ }
+
+ /* locate the beginning of the error description */
+ while (*text && isascii(*text) && !isspace(*text))
+ {
+ text++;
+ }
+ while (*text)
+ {
+# ifndef CRUNCH
+ /* skip "error #:" and "warning #:" clauses */
+ if (!strncmp(text + 1, "rror ", 5)
+ || !strncmp(text + 1, "arning ", 7)
+ || !strncmp(text + 1, "atal error", 10))
+ {
+ do
+ {
+ text++;
+ } while (*text && *text != ':');
+ continue;
+ }
+# endif
+
+ /* anything other than whitespace or a colon is important */
+ if (!isascii(*text) || (!isspace(*text) && *text != ':'))
+ {
+ errmsg = text;
+ break;
+ }
+
+ /* else keep looking... */
+ text++;
+ }
+
+ return errmsg;
+# endif /* not COHERENT */
+}
+
+/*ARGSUSED*/
+void cmd_errlist(frommark, tomark, cmd, bang, extra)
+ MARK frommark, tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ static long endline;/* original number of lines in this file */
+ static long offset; /* offset of the next line in the errlist file */
+ static int fd = -2;/* fd of the errlist file */
+ int i;
+ char *errmsg;
+
+ /* if a new errlist file is named, open it */
+ if (extra && extra[0])
+ {
+ /* close the old one */
+ if (fd >= 0)
+ {
+ close(fd);
+ }
+
+ fd = open(extra, O_RDONLY);
+ offset = 0L;
+ }
+ else if (fd < 0)
+ {
+ fd = open(ERRLIST, O_RDONLY);
+ offset = 0L;
+ }
+
+ /* do we have an errlist file now? */
+ if (fd < 0)
+ {
+ msg("There is no errlist file");
+ beep();
+ return;
+ }
+
+ /* find the next error message in the file */
+ do
+ {
+ /* read the next line from the errlist */
+ lseek(fd, offset, 0);
+ if (tread(fd, tmpblk.c, (unsigned)BLKSIZE) <= 0)
+ {
+ msg("No more errors");
+ beep();
+ close(fd);
+ return;
+ }
+ for (i = 0; tmpblk.c[i] != '\n'; i++)
+ {
+ }
+ tmpblk.c[i++] = 0;
+
+ /* look for an error message in the line */
+ errmsg = parse_errmsg(tmpblk.c);
+ if (!errmsg)
+ {
+ offset += i;
+ }
+
+ } while (!errmsg);
+
+ /* switch to the file containing the error, if this isn't it */
+ if (strcmp(origname, errfile))
+ {
+ if (!tmpabort(bang))
+ {
+ msg("Use :er! to abort changes, or :w to save changes");
+ beep();
+ return;
+ }
+ tmpstart(errfile);
+ endline = nlines;
+ }
+ else if (endline == 0L)
+ {
+ endline = nlines;
+ }
+
+ /* go to the line where the error was detected */
+ cursor = MARK_AT_LINE(errline + (nlines - endline));
+ if (cursor > MARK_LAST)
+ {
+ cursor = MARK_LAST;
+ }
+ if (mode == MODE_VI)
+ {
+ redraw(cursor, FALSE);
+ }
+
+ /* display the error message */
+ if (nlines > endline)
+ {
+ msg("line %ld(+%ld): %.60s", errline, nlines - endline, errmsg);
+ }
+ else if (nlines < endline)
+ {
+ msg("line %ld(-%ld): %.60s", errline, endline - nlines, errmsg);
+ }
+ else
+ {
+ msg("line %ld: %.65s", errline, errmsg);
+ }
+
+ /* remember where the NEXT error line will start */
+ offset += i;
+}
+
+
+/*ARGSUSED*/
+void cmd_make(frommark, tomark, cmd, bang, extra)
+ MARK frommark, tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ BLK buf;
+
+ /* if the file hasn't been saved, then complain unless ! */
+ if (tstflag(file, MODIFIED) && !bang)
+ {
+ msg("\"%s\" not saved yet", origname);
+ return;
+ }
+
+ /* build the command */
+ sprintf(buf.c, "%s %s %s%s", (cmd == CMD_CC ? o_cc : o_make), extra, REDIRECT, ERRLIST);
+ qaddstr(buf.c);
+ addch('\n');
+
+ /* run the command, with curses temporarily disabled */
+ suspend_curses();
+ system(buf.c);
+ resume_curses(mode == MODE_EX);
+ if (mode == MODE_COLON)
+ mode = MODE_VI;
+
+ /* run the "errlist" command */
+ cmd_errlist(MARK_UNSET, MARK_UNSET, cmd, bang, ERRLIST);
+}
+#endif
+
+
+#ifndef NO_ABBR
+/*ARGSUSED*/
+void cmd_abbr(frommark, tomark, cmd, bang, extra)
+ MARK frommark, tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ do_abbr(extra);
+}
+#endif
diff --git a/cmd2.c b/cmd2.c
new file mode 100644
index 0000000..5f995dd
--- /dev/null
+++ b/cmd2.c
@@ -0,0 +1,896 @@
+/* cmd2.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains some of the commands - mostly ones that change text */
+
+#include <ctype.h>
+#include "config.h"
+#include "vi.h"
+#include "regexp.h"
+#if TOS
+# include <stat.h>
+#else
+# if OSK
+# include "osk.h"
+# else
+# include <sys/stat.h>
+# endif
+#endif
+
+
+/*ARGSUSED*/
+void cmd_substitute(frommark, tomark, cmd, bang, extra)
+ MARK frommark;
+ MARK tomark;
+ CMD cmd;
+ int bang;
+ char *extra; /* rest of the command line */
+{
+ char *line; /* a line from the file */
+ regexp *re; /* the compiled search expression */
+ char *subst; /* the substitution string */
+ char *opt; /* substitution options */
+ long l; /* a line number */
+ char *s, *d; /* used during subtitutions */
+ char *conf; /* used during confirmation */
+ long chline; /* # of lines changed */
+ long chsub; /* # of substitutions made */
+ static optp; /* boolean option: print when done? */
+ static optg; /* boolean option: substitute globally in line? */
+ static optc; /* boolean option: confirm before subst? */
+
+
+ /* for now, assume this will fail */
+ rptlines = -1L;
+
+ if (cmd == CMD_SUBAGAIN)
+ {
+#ifndef NO_MAGIC
+ if (*o_magic)
+ subst = "~";
+ else
+#endif
+ subst = "\\~";
+ re = regcomp("");
+
+ /* if visual "&", then turn off the "p" and "c" options */
+ if (bang)
+ {
+ optp = optc = FALSE;
+ }
+ }
+ else
+ {
+ /* make sure we got a search pattern */
+ if (*extra != '/' && *extra != '?')
+ {
+ msg("Usage: s/regular expression/new text/");
+ return;
+ }
+
+ /* parse & compile the search pattern */
+ subst = parseptrn(extra);
+ re = regcomp(extra + 1);
+ }
+
+ /* abort if RE error -- error message already given by regcomp() */
+ if (!re)
+ {
+ return;
+ }
+
+ if (cmd == CMD_SUBSTITUTE)
+ {
+ /* parse the substitution string & find the option string */
+ for (opt = subst; *opt && *opt != *extra; opt++)
+ {
+ if (*opt == '\\' && opt[1])
+ {
+ opt++;
+ }
+ }
+ if (*opt)
+ {
+ *opt++ = '\0';
+ }
+
+ /* analyse the option string */
+ if (!*o_edcompatible)
+ {
+ optp = optg = optc = FALSE;
+ }
+ while (*opt)
+ {
+ switch (*opt++)
+ {
+ case 'p': optp = !optp; break;
+ case 'g': optg = !optg; break;
+ case 'c': optc = !optc; break;
+ case ' ':
+ case '\t': break;
+ default:
+ msg("Subst options are p, c, and g -- not %c", opt[-1]);
+ return;
+ }
+ }
+ }
+
+ /* if "c" or "p" flag was given, and we're in visual mode, then NEWLINE */
+ if ((optc || optp) && mode == MODE_VI)
+ {
+ addch('\n');
+ exrefresh();
+ }
+
+ ChangeText
+ {
+ /* reset the change counters */
+ chline = chsub = 0L;
+
+ /* for each selected line */
+ for (l = markline(frommark); l <= markline(tomark); l++)
+ {
+ /* fetch the line */
+ line = fetchline(l);
+
+ /* if it contains the search pattern... */
+ if (regexec(re, line, TRUE))
+ {
+ /* increment the line change counter */
+ chline++;
+
+ /* initialize the pointers */
+ s = line;
+ d = tmpblk.c;
+
+ /* do once or globally ... */
+ do
+ {
+#ifndef CRUNCH
+ /* confirm, if necessary */
+ if (optc)
+ {
+ for (conf = line; conf < re->startp[0]; conf++)
+ addch(*conf);
+ standout();
+ for ( ; conf < re->endp[0]; conf++)
+ addch(*conf);
+ standend();
+ for (; *conf; conf++)
+ addch(*conf);
+ addch('\n');
+ exrefresh();
+ if (getkey(0) != 'y')
+ {
+ /* copy accross the original chars */
+ while (s < re->endp[0])
+ *d++ = *s++;
+
+ /* skip to next match on this line, if any */
+ continue;
+ }
+ }
+#endif /* not CRUNCH */
+
+ /* increment the substitution change counter */
+ chsub++;
+
+ /* this may be the first line to redraw */
+ redrawrange(l, l + 1L, l + 1L);
+
+ /* copy stuff from before the match */
+ while (s < re->startp[0])
+ {
+ *d++ = *s++;
+ }
+
+ /* substitute for the matched part */
+ regsub(re, subst, d);
+ s = re->endp[0];
+ d += strlen(d);
+
+ } while (optg && regexec(re, s, FALSE));
+
+ /* copy stuff from after the match */
+ while (*d++ = *s++) /* yes, ASSIGNMENT! */
+ {
+ }
+
+ /* replace the old version of the line with the new */
+ d[-1] = '\n';
+ d[0] = '\0';
+ change(MARK_AT_LINE(l), MARK_AT_LINE(l + 1), tmpblk.c);
+
+ /* if supposed to print it, do so */
+ if (optp)
+ {
+ addstr(tmpblk.c);
+ exrefresh();
+ }
+
+ /* move the cursor to that line */
+ cursor = MARK_AT_LINE(l);
+ }
+ }
+ }
+
+ /* tweak for redrawing */
+ mustredraw = TRUE;
+
+ /* free the regexp */
+ free(re);
+
+ /* if done from within a ":g" command, then finish silently */
+ if (doingglobal)
+ {
+ rptlines = chline;
+ rptlabel = "changed";
+ return;
+ }
+
+ /* Reporting */
+ if (chsub == 0)
+ {
+ msg("Substitution failed");
+ }
+ else if (chline >= *o_report)
+ {
+ msg("%ld substitutions on %ld lines", chsub, chline);
+ }
+}
+
+
+
+
+/*ARGSUSED*/
+void cmd_delete(frommark, tomark, cmd, bang, extra)
+ MARK frommark;
+ MARK tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ MARK curs2; /* an altered form of the cursor */
+
+ /* choose your cut buffer */
+ if (*extra == '"')
+ {
+ extra++;
+ }
+ if (*extra)
+ {
+ cutname(*extra);
+ }
+
+ /* make sure we're talking about whole lines here */
+ frommark = frommark & ~(BLKSIZE - 1);
+ tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
+
+ /* yank the lines */
+ cut(frommark, tomark);
+
+ /* if CMD_DELETE then delete the lines */
+ if (cmd != CMD_YANK)
+ {
+ curs2 = cursor;
+ ChangeText
+ {
+ /* delete the lines */
+ delete(frommark, tomark);
+ }
+ if (curs2 > tomark)
+ {
+ cursor = curs2 - tomark + frommark;
+ }
+ else if (curs2 > frommark)
+ {
+ cursor = frommark;
+ }
+ }
+}
+
+
+/*ARGSUSED*/
+void cmd_append(frommark, tomark, cmd, bang, extra)
+ MARK frommark;
+ MARK tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ long l; /* line counter */
+
+ ChangeText
+ {
+ /* if we're doing a change, delete the old version */
+ if (cmd == CMD_CHANGE)
+ {
+ /* delete 'em */
+ cmd_delete(frommark, tomark, cmd, bang, extra);
+ }
+
+ /* new lines start at the frommark line, or after it */
+ l = markline(frommark);
+ if (cmd == CMD_APPEND)
+ {
+ l++;
+ }
+
+ /* get lines until no more lines, or "." line, and insert them */
+ while (vgets('\0', tmpblk.c, BLKSIZE) >= 0)
+ {
+ addch('\n');
+ if (!strcmp(tmpblk.c, "."))
+ {
+ break;
+ }
+
+ strcat(tmpblk.c, "\n");
+ add(MARK_AT_LINE(l), tmpblk.c);
+ l++;
+ }
+ }
+
+ /* on the odd chance that we're calling this from vi mode ... */
+ redraw(MARK_UNSET, FALSE);
+}
+
+
+/*ARGSUSED*/
+void cmd_put(frommark, tomark, cmd, bang, extra)
+ MARK frommark;
+ MARK tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ /* choose your cut buffer */
+ if (*extra == '"')
+ {
+ extra++;
+ }
+ if (*extra)
+ {
+ cutname(*extra);
+ }
+
+ /* paste it */
+ ChangeText
+ {
+ cursor = paste(frommark, TRUE, FALSE);
+ }
+}
+
+
+/*ARGSUSED*/
+void cmd_join(frommark, tomark, cmd, bang, extra)
+ MARK frommark;
+ MARK tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ long l;
+ char *scan;
+ int len; /* length of the new line */
+
+ /* if only one line is specified, assume the following one joins too */
+ if (markline(frommark) == nlines)
+ {
+ msg("Nothing to join with this line");
+ return;
+ }
+ if (markline(frommark) == markline(tomark))
+ {
+ tomark += BLKSIZE;
+ }
+
+ /* get the first line */
+ l = markline(frommark);
+ strcpy(tmpblk.c, fetchline(l));
+ len = strlen(tmpblk.c);
+
+ /* build the longer line */
+ while (++l <= markline(tomark))
+ {
+ /* get the next line */
+ scan = fetchline(l);
+
+ /* remove any leading whitespace */
+ while (*scan == '\t' || *scan == ' ')
+ {
+ scan++;
+ }
+
+ /* see if the line will fit */
+ if (strlen(scan) + len + 3 > BLKSIZE)
+ {
+ msg("Can't join -- the resulting line would be too long");
+ return;
+ }
+
+ /* catenate it, with a space (or two) in between */
+ if (len >= 1 &&
+ (tmpblk.c[len - 1] == '.'
+ || tmpblk.c[len - 1] == '?'
+ || tmpblk.c[len - 1] == '!'))
+ {
+ tmpblk.c[len++] = ' ';
+ }
+ tmpblk.c[len++] = ' ';
+ strcpy(tmpblk.c + len, scan);
+ len += strlen(scan);
+ }
+ tmpblk.c[len++] = '\n';
+ tmpblk.c[len] = '\0';
+
+ /* make the change */
+ ChangeText
+ {
+ frommark &= ~(BLKSIZE - 1);
+ tomark &= ~(BLKSIZE - 1);
+ tomark += BLKSIZE;
+ change(frommark, tomark, tmpblk.c);
+ }
+
+ /* Reporting... */
+ rptlines = markline(tomark) - markline(frommark) - 1L;
+ rptlabel = "joined";
+}
+
+
+
+/*ARGSUSED*/
+void cmd_shift(frommark, tomark, cmd, bang, extra)
+ MARK frommark;
+ MARK tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ long l; /* line number counter */
+ int oldidx; /* number of chars previously used for indent */
+ int newidx; /* number of chars in the new indent string */
+ int oldcol; /* previous indent amount */
+ int newcol; /* new indent amount */
+ char *text; /* pointer to the old line's text */
+
+ /* figure out how much of the screen we must redraw (for vi mode) */
+ if (markline(frommark) != markline(tomark))
+ {
+ mustredraw = TRUE;
+ redrawrange(markline(frommark), markline(tomark) + 1L, markline(tomark) + 1L);
+ }
+
+ ChangeText
+ {
+ /* for each line to shift... */
+ for (l = markline(frommark); l <= markline(tomark); l++)
+ {
+ /* get the line - ignore empty lines unless ! mode */
+ text = fetchline(l);
+ if (!*text && !bang)
+ continue;
+
+ /* calc oldidx and oldcol */
+ for (oldidx = 0, oldcol = 0;
+ text[oldidx] == ' ' || text[oldidx] == '\t';
+ oldidx++)
+ {
+ if (text[oldidx] == ' ')
+ {
+ oldcol += 1;
+ }
+ else
+ {
+ oldcol += *o_tabstop - (oldcol % *o_tabstop);
+ }
+ }
+
+ /* calc newcol */
+ if (cmd == CMD_SHIFTR)
+ {
+ newcol = oldcol + (*o_shiftwidth & 0xff);
+ }
+ else
+ {
+ newcol = oldcol - (*o_shiftwidth & 0xff);
+ if (newcol < 0)
+ newcol = 0;
+ }
+
+ /* if no change, then skip to next line */
+ if (oldcol == newcol)
+ continue;
+
+ /* build a new indent string */
+ newidx = 0;
+ while (newcol >= *o_tabstop)
+ {
+ tmpblk.c[newidx++] = '\t';
+ newcol -= *o_tabstop;
+ }
+ while (newcol > 0)
+ {
+ tmpblk.c[newidx++] = ' ';
+ newcol--;
+ }
+ tmpblk.c[newidx] = '\0';
+
+ /* change the old indent string into the new */
+ change(MARK_AT_LINE(l), MARK_AT_LINE(l) + oldidx, tmpblk.c);
+ }
+ }
+
+ /* Reporting... */
+ rptlines = markline(tomark) - markline(frommark) + 1L;
+ if (cmd == CMD_SHIFTR)
+ {
+ rptlabel = ">ed";
+ }
+ else
+ {
+ rptlabel = "<ed";
+ }
+}
+
+
+/*ARGSUSED*/
+void cmd_read(frommark, tomark, cmd, bang, extra)
+ MARK frommark;
+ MARK tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ int fd, rc; /* used while reading from the file */
+ char *scan; /* used for finding NUL characters */
+ int hadnul; /* boolean: any NULs found? */
+ int addnl; /* boolean: forced to add newlines? */
+ int len; /* number of chars in current line */
+ long lines; /* number of lines in current block */
+ struct stat statb;
+
+ /* special case: if ":r !cmd" then let the filter() function do it */
+ if (extra[0] == '!')
+ {
+ filter(frommark, MARK_UNSET, extra + 1);
+ return;
+ }
+
+ /* open the file */
+ fd = open(extra, O_RDONLY);
+ if (fd < 0)
+ {
+ msg("Can't open \"%s\"", extra);
+ return;
+ }
+
+#ifndef CRUNCH
+ if (stat(extra, &statb) < 0)
+ {
+ msg("Can't stat \"%s\"", extra);
+ }
+# if TOS
+ if (statb.st_mode & S_IJDIR)
+# else
+# if OSK
+ if (statb.st_mode & S_IFDIR)
+# else
+ if ((statb.st_mode & S_IFMT) != S_IFREG)
+# endif
+# endif
+ {
+ msg("\"%s\" is not a regular file", extra);
+ return;
+ }
+#endif /* not CRUNCH */
+
+ /* get blocks from the file, and add them */
+ ChangeText
+ {
+ /* insertion starts at the line following frommark */
+ tomark = frommark = (frommark | (BLKSIZE - 1L)) + 1L;
+ len = 0;
+ hadnul = addnl = FALSE;
+
+ /* add an extra newline, so partial lines at the end of
+ * the file don't trip us up
+ */
+ add(tomark, "\n");
+
+ /* for each chunk of text... */
+ while ((rc = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
+ {
+ /* count newlines, convert NULs, etc. ... */
+ for (lines = 0, scan = tmpblk.c; rc > 0; rc--, scan++)
+ {
+ /* break up long lines */
+ if (*scan != '\n' && len + 2 > BLKSIZE)
+ {
+ *scan = '\n';
+ addnl = TRUE;
+ }
+
+ /* protect against NUL chars in file */
+ if (!*scan)
+ {
+ *scan = 0x80;
+ hadnul = TRUE;
+ }
+
+ /* starting a new line? */
+ if (*scan == '\n')
+ {
+ /* reset length at newline */
+ len = 0;
+ lines++;
+ }
+ else
+ {
+ len++;
+ }
+ }
+
+ /* add the text */
+ *scan = '\0';
+ add(tomark, tmpblk.c);
+ tomark += MARK_AT_LINE(lines) + len - markidx(tomark);
+ }
+
+ /* if partial last line, then retain that first newline */
+ if (len > 0)
+ {
+ msg("Last line had no newline");
+ tomark += BLKSIZE; /* <- for the rptlines calc */
+ }
+ else /* delete that first newline */
+ {
+ delete(tomark, (tomark | (BLKSIZE - 1L)) + 1L);
+ }
+ }
+
+ /* close the file */
+ close(fd);
+
+ /* Reporting... */
+ rptlines = markline(tomark) - markline(frommark);
+ rptlabel = "read";
+
+ if (addnl)
+ msg("Newlines were added to break up long lines");
+ if (hadnul)
+ msg("NULs were converted to 0x80");
+}
+
+
+
+/*ARGSUSED*/
+void cmd_undo(frommark, tomark, cmd, bang, extra)
+ MARK frommark;
+ MARK tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ undo();
+}
+
+
+/* print the selected lines */
+/*ARGSUSED*/
+void cmd_print(frommark, tomark, cmd, bang, extra)
+ MARK frommark;
+ MARK tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ REG char *scan;
+ REG long l;
+ REG int col;
+
+ for (l = markline(frommark); l <= markline(tomark); l++)
+ {
+ /* display a line number, if CMD_NUMBER */
+ if (cmd == CMD_NUMBER)
+ {
+ sprintf(tmpblk.c, "%6ld ", l);
+ qaddstr(tmpblk.c);
+ col = 8;
+ }
+ else
+ {
+ col = 0;
+ }
+
+ /* get the next line & display it */
+ for (scan = fetchline(l); *scan; scan++)
+ {
+ /* expand tabs to the proper width */
+ if (*scan == '\t' && cmd != CMD_LIST)
+ {
+ do
+ {
+ qaddch(' ');
+ col++;
+ } while (col % *o_tabstop != 0);
+ }
+ else if (*scan >= 0 && *scan < ' ' || *scan == '\177')
+ {
+ qaddch('^');
+ qaddch(*scan ^ 0x40);
+ col += 2;
+ }
+ else if ((*scan & 0x80) && cmd == CMD_LIST)
+ {
+ sprintf(tmpblk.c, "\\%03o", *scan);
+ qaddstr(tmpblk.c);
+ col += 4;
+ }
+ else
+ {
+ qaddch(*scan);
+ col++;
+ }
+
+ /* wrap at the edge of the screen */
+ if (!has_AM && col >= COLS)
+ {
+ addch('\n');
+ col -= COLS;
+ }
+ }
+ if (cmd == CMD_LIST)
+ {
+ qaddch('$');
+ }
+ addch('\n');
+ exrefresh();
+ }
+}
+
+
+/* move or copy selected lines */
+/*ARGSUSED*/
+void cmd_move(frommark, tomark, cmd, bang, extra)
+ MARK frommark;
+ MARK tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ MARK destmark;
+
+ /* parse the destination linespec. No defaults. Line 0 is okay */
+ destmark = cursor;
+ if (!strcmp(extra, "0"))
+ {
+ destmark = 0L;
+ }
+ else if (linespec(extra, &destmark) == extra || !destmark)
+ {
+ msg("invalid destination address");
+ return;
+ }
+
+ /* flesh the marks out to encompass whole lines */
+ frommark &= ~(BLKSIZE - 1);
+ tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
+ destmark = (destmark & ~(BLKSIZE - 1)) + BLKSIZE;
+
+ /* make sure the destination is valid */
+ if (cmd == CMD_MOVE && destmark >= frommark && destmark < tomark)
+ {
+ msg("invalid destination address");
+ }
+
+ /* Do it */
+ ChangeText
+ {
+ /* save the text to a cut buffer */
+ cutname('\0');
+ cut(frommark, tomark);
+
+ /* if we're not copying, delete the old text & adjust destmark */
+ if (cmd != CMD_COPY)
+ {
+ delete(frommark, tomark);
+ if (destmark >= frommark)
+ {
+ destmark -= (tomark - frommark);
+ }
+ }
+
+ /* add the new text */
+ paste(destmark, FALSE, FALSE);
+ }
+
+ /* move the cursor to the last line of the moved text */
+ cursor = destmark + (tomark - frommark) - BLKSIZE;
+ if (cursor < MARK_FIRST || cursor >= MARK_LAST + BLKSIZE)
+ {
+ cursor = MARK_LAST;
+ }
+
+ /* Reporting... */
+ rptlabel = ( (cmd == CMD_COPY) ? "copied" : "moved" );
+}
+
+
+
+/* execute EX commands from a file */
+/*ARGSUSED*/
+void cmd_source(frommark, tomark, cmd, bang, extra)
+ MARK frommark;
+ MARK tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ /* must have a filename */
+ if (!*extra)
+ {
+ msg("\"source\" requires a filename");
+ return;
+ }
+
+ doexrc(extra);
+}
+
+
+#ifndef NO_AT
+/*ARGSUSED*/
+void cmd_at(frommark, tomark, cmd, bang, extra)
+ MARK frommark;
+ MARK tomark;
+ CMD cmd;
+ int bang;
+ char *extra;
+{
+ static nest = FALSE;
+ int result;
+ char buf[MAXRCLEN];
+
+ /* don't allow nested macros */
+ if (nest)
+ {
+ msg("@ macros can't be nested");
+ return;
+ }
+ nest = TRUE;
+
+ /* require a buffer name */
+ if (*extra == '"')
+ extra++;
+ if (!*extra || !isascii(*extra) ||!islower(*extra))
+ {
+ msg("@ requires a cut buffer name (a-z)");
+ }
+
+ /* get the contents of the buffer */
+ result = cb2str(*extra, buf, (unsigned)(sizeof buf));
+ if (result <= 0)
+ {
+ msg("buffer \"%c is empty", *extra);
+ }
+ else if (result >= sizeof buf)
+ {
+ msg("buffer \"%c is too large to execute", *extra);
+ }
+ else
+ {
+ /* execute the contents of the buffer as ex commands */
+ exstring(buf, result);
+ }
+
+ nest = FALSE;
+}
+#endif
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..6096f0b
--- /dev/null
+++ b/config.h
@@ -0,0 +1,351 @@
+/*
+ * vi configuration file
+ * We try to automatically configure to various compilers and operating
+ * systems. Extend the autoconf section as needed.
+ */
+
+/*************************** autoconf section ************************/
+
+/* standard unix V (?) */
+#ifdef M_SYSV
+# define UNIXV 1
+#endif
+
+/* xelos system, University of Ulm */
+#ifdef xelos
+# define UNIXV 1
+#endif
+
+/* BSD UNIX? */
+#ifdef bsd
+# define BSD 1
+#endif
+
+/* Microsoft C: sorry, Watcom does the same thing */
+#ifdef M_I86
+# ifndef M_SYSV
+# define MSDOS 1
+# define MICROSOFT 1
+# define COMPILED_BY "Microsoft C 5.10"
+# endif
+#endif
+
+/* Borlands Turbo C */
+#ifdef __TURBOC__
+# define MSDOS 1
+# define TURBOC 1
+# define COMPILED_BY "Turbo C 2.00"
+#endif
+
+/* Tos Mark-Williams */
+#ifdef M68000
+# define TOS 1
+# define COMPILED_BY "Mark Williams C"
+#endif
+
+/* OS9/68000 */
+#ifdef OSK
+# define COMPILED_BY "Microware C V2.3 Edition 40"
+#endif
+
+/*************************** end of autoconf section ************************/
+
+/* All undefined symbols are defined to zero here, to allow for older */
+/* compilers which dont understand #if defined() or #if UNDEFINED_SYMBOL */
+
+/*************************** operating systems *****************************/
+
+#ifndef BSD
+# define BSD 0 /* UNIX - Berkeley 4.x */
+#endif
+
+#ifndef UNIXV
+# define UNIXV 0 /* UNIX - AT&T SYSV */
+#endif
+
+#ifndef UNIX7
+# define UNIX7 0 /* UNIX - version 7 */
+#endif
+
+#ifndef MSDOS
+# define MSDOS 0 /* PC */
+#endif
+
+#ifndef TOS
+# define TOS 0 /* Atari ST */
+#endif
+
+#ifndef AMIGA
+# define AMIGA 0 /* Commodore Amiga */
+#endif
+
+#ifndef OSK
+# define OSK 0 /* OS-9 / 68k */
+#endif
+
+#ifndef COHERENT
+# define COHERENT 0 /* Coherent */
+#endif
+
+ /* Minix has no predefines */
+#if !BSD && !UNIXV && !UNIX7 && !MSDOS && !TOS && !AMIGA && !OSK && !COHERENT
+# define MINIX 1
+#else
+# define MINIX 0
+#endif
+
+ /* generic combination of Unices */
+#if UNIXV || UNIX7 || BSD || MINIX || COHERENT
+# define ANY_UNIX 1
+#else
+# define ANY_UNIX 0
+#endif
+
+/*************************** compilers **************************************/
+
+#ifndef MICROSOFT
+# define MICROSOFT 0
+#endif
+
+#ifndef TURBOC
+# define TURBOC 0
+#endif
+
+/******************************* Credit ************************************/
+
+#if MSDOS
+# define CREDIT "Ported to MS-DOS by Guntram Blohm & Martin Patzel"
+#endif
+
+#if TOS
+# define CREDIT "Ported to Atari/TOS by Guntram Blohm & Martin Patzel"
+#endif
+
+#if OSK
+# define CREDIT "Ported to Microware OS9/68k by Peter Reinig"
+#endif
+
+#if COHERENT
+# define CREDIT "Ported to Coherent by Esa Ahola"
+#endif
+
+/*************************** functions depending on OS *********************/
+
+/* Only MSDOS, TOS, and OS9 need a special function for reading from the
+ * keyboard. All others just read from file descriptor 0.
+ */
+#if !MSDOS && !TOS && !OSK
+# define ttyread(buf, len) read(0, buf, (unsigned)len) /* raw read */
+#endif
+#if !TOS
+# define ttywrite(buf, len) write(1, buf, (unsigned)(len)) /* raw write */
+#endif
+
+/* The strchr() function is an official standard now, so everybody has it
+ * except Unix version 7 (which is old) and BSD Unix (which is academic).
+ * Those guys use something called index() to do the same thing.
+ */
+#if BSD || UNIX7 || OSK
+# define strchr index
+#endif
+extern char *strchr();
+
+/* BSD uses bcopy() instead of memcpy() */
+#if BSD
+#define memcpy(dest, src, siz) bcopy(src, dest, siz)
+#endif
+
+/* text versa binary mode for read/write */
+#if !TOS
+#define tread(fd,buf,n) read(fd,buf,(unsigned)(n))
+#define twrite(fd,buf,n) write(fd,buf,(unsigned)(n))
+#endif
+
+/**************************** Compiler quirks *********************************/
+
+/* the UNIX version 7 and (some) TOS compilers, don't allow "void" */
+#if UNIX7 || TOS
+# define void int
+#endif
+
+/* as far as I know, all compilers except version 7 support unsigned char */
+/* NEWFLASH: the Minix-ST compiler has subtle problems with unsigned char */
+#if UNIX7 || MINIX
+# define UCHAR(c) ((c) & 0xff)
+# define uchar char
+#else
+# define UCHAR(c) ((unsigned char)(c))
+# define uchar unsigned char
+#endif
+
+/* Some compilers prefer to have malloc declared as returning a (void *) */
+#if BSD
+extern void *malloc();
+#else
+extern char *malloc();
+#endif
+
+/* Most compilers could benefit from using the "register" storage class */
+#if 1
+# define REG register
+#endif
+
+/******************* Names of files and environment vars **********************/
+
+#if ANY_UNIX
+# ifndef TMPDIR
+# if MINIX
+# define TMPDIR "/usr/tmp" /* Keep elvis' temp files off RAM disk! */
+# else
+# define TMPDIR "/tmp" /* directory where temp files live */
+# endif
+# endif
+# define TMPNAME "%s/elv%x%04x%03x" /* temp file */
+# define CUTNAME "%s/elv_%04x%03x" /* cut buffer's temp file */
+# ifndef EXRC
+# define EXRC ".exrc" /* init file in current directory */
+# endif
+# define SCRATCHOUT "%s/soXXXXXX" /* temp file used as input to filter */
+# ifndef EXINIT
+# define EXINIT "EXINIT"
+# endif
+# ifndef SHELL
+# define SHELL "/bin/sh" /* default shell */
+# endif
+# if COHERENT
+# ifndef REDIRECT
+# define REDIRECT ">" /* Coherent CC writes errors to stdout */
+# endif
+# endif
+#endif
+
+#if MSDOS || TOS
+/* do not change TMPNAME, CUTNAME and SCRATCH*: they MUST begin with '%s\\'! */
+# ifndef TMPDIR
+# define TMPDIR "C:\\tmp" /* directory where temp files live */
+# endif
+# define TMPNAME "%s\\elv%x%04x.%03x" /* temp file */
+# define CUTNAME "%s\\elv_%04x.%03x" /* cut buffer's temp file */
+# if MSDOS
+# if MICROSOFT
+# define CC_COMMAND "cl -c" /* C compiler */
+# else /* TURBO_C */
+# define CC_COMMAND "tc" /* C compiler */
+# endif
+# endif
+# define SCRATCHIN "%s\\siXXXXXX" /* DOS ONLY - output of filter program */
+# define SCRATCHOUT "%s\\soXXXXXX" /* temp file used as input to filter */
+# define SLASH '\\'
+# ifndef SHELL
+# if TOS
+# define SHELL "shell.ttp" /* default shell */
+# else
+# define SHELL "command.com" /* default shell */
+# endif
+# endif
+# define NEEDSYNC TRUE /* assume ":se sync" by default */
+# define REDIRECT ">" /* shell's redirection of stderr */
+# ifndef MAXMAPS
+# define MAXMAPS 40
+# endif
+# ifndef EXINIT
+# define EXINIT "EXINIT"
+# endif
+#endif
+
+#if OSK
+# ifndef TMPDIR
+# define TMPDIR "/dd/tmp" /* directory where temp files live */
+# endif
+# define TMPNAME "%s/elv%x%04x%03x" /* temp file */
+# define CUTNAME "%s/elv_%04x%03x" /* cut buffer's temp file */
+# ifndef CC_COMMAND
+# define CC_COMMAND "cc -r" /* name of the compiler */
+# endif
+# ifndef EXRC
+# define EXRC ".exrc" /* init file in current directory */
+# endif
+# define SCRATCHOUT "%s/soXXXXXX" /* temp file used as input to filter */
+# ifndef SHELL
+# define SHELL "shell" /* default shell */
+# endif
+# define FILEPERMS (S_IREAD|S_IWRITE) /* file permissions used for creat() */
+# define REDIRECT ">>-" /* shell's redirection of stderr */
+#endif
+
+#ifndef TAGS
+# define TAGS "tags" /* tags file */
+#endif
+
+#ifndef TMPNAME
+# define TMPNAME "%s/elv%x%04x.%03x" /* temp file */
+#endif
+
+#ifndef CUTNAME
+# define CUTNAME "%s/elv_%04x.%03x" /* cut buffer's temp file */
+#endif
+
+#ifndef EXRC
+# define EXRC "elvis.rc"
+#endif
+
+#ifndef HMEXRC
+# if !MSDOS && !TOS
+# define HMEXRC EXRC
+# endif
+#endif
+
+#ifndef KEYWORDPRG
+# define KEYWORDPRG "ref"
+#endif
+
+#ifndef SCRATCHOUT
+# define SCRATCHIN "%s/SIXXXXXX"
+# define SCRATCHOUT "%s/SOXXXXXX"
+#endif
+
+#ifndef ERRLIST
+# define ERRLIST "errlist"
+#endif
+
+#ifndef SLASH
+# define SLASH '/'
+#endif
+
+#ifndef SHELL
+# define SHELL "shell"
+#endif
+
+#ifndef REG
+# define REG
+#endif
+
+#ifndef NEEDSYNC
+# define NEEDSYNC FALSE
+#endif
+
+#ifndef FILEPERMS
+# define FILEPERMS 0666
+#endif
+
+#ifndef CC_COMMAND
+# define CC_COMMAND "cc -c"
+#endif
+
+#ifndef MAKE_COMMAND
+# define MAKE_COMMAND "make"
+#endif
+
+#ifndef REDIRECT
+# define REDIRECT "2>"
+#endif
+
+#ifndef MAXMAPS
+# define MAXMAPS 20 /* number of :map keys */
+#endif
+#ifndef MAXDIGS
+# define MAXDIGS 30 /* number of :digraph combos */
+#endif
+#ifndef MAXABBR
+# define MAXABBR 20 /* number of :abbr entries */
+#endif
diff --git a/ctags.c b/ctags.c
new file mode 100644
index 0000000..dec74ed
--- /dev/null
+++ b/ctags.c
@@ -0,0 +1,350 @@
+/* ctags.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains the complete source to the ctags program. */
+
+/* Special abilities:
+ * Can also make a "refs" file for use by the "ref" program.
+ */
+
+/* Limitations:
+ * This version of ctags always writes its output to the file "tags".
+ * It assumes that every command-line argument (but "-r") is a C source file.
+ * It does not sort the list of tags, unless CFLAGS=-DSORT.
+ * It does not recognize duplicate definitions.
+ * It does not try to handle "static" functions in a clever way.
+ * It probably won't scan ANSI-C source code very well.
+ */
+
+/* Implementation:
+ * Lines are scanned one-at-a-time.
+ * The context of lines is tracked via a finite state machine.
+ * Contexts are:
+ * EXPECTFN - we're looking for a function name.
+ * ARGS - between function name and its opening {
+ * BODY - we found a function name, skip to end of body.
+ *
+ * Function tags are referenced by a search string, so that lines may be
+ * inserted or deleted without mucking up the tag search.
+ *
+ * Macro tags are referenced by their line number, because 1) they usually
+ * occur near the top of a file, so their line# won't change much; 2) They
+ * often contain characters that are hard to search for; and 3) Their #define
+ * line is likely to be altered.
+ *
+ * Each line of the resulting "tags" file describes one tag. Lines begin with
+ * the tag name, then a tab, then the file name, then a tab, and then either
+ * a line number or a slash-delimited search string.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include "config.h"
+
+#define REFS "refs"
+
+#if OSK
+#define NUMFMT "%%.%ds\t%%s\t%%ld\n"
+#define SRCHFMT "%%.%ds\t%%s\t/^%%s$/\n"
+#define MAINFMT "M%%.%ds\t%%s\t/^%%s$/\n"
+static char fmt[256];
+#else
+#define NUMFMT "%.*s\t%s\t%ld\n"
+#define SRCHFMT "%.*s\t%s\t/^%s$/\n"
+#define MAINFMT "M%.*s\t%s\t/^%s$/\n"
+#endif
+
+#ifdef VERBOSE
+# define SAY(x) fprintf(stderr, "%s\n", x);
+#else
+# define SAY(x)
+#endif
+
+#define EXPECTFN 1
+#define ARGS 2
+#define BODY 3
+
+extern char *fgets();
+
+char *progname; /* argv[0], used for diagnostic output */
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ FILE *fp;
+ int i;
+ FILE *refs; /* used to write to the refs file */
+
+#if MSDOS || TOS
+ char **wildexpand();
+ argv=wildexpand(&argc, argv);
+#endif
+ /* notice the program name */
+ progname = argv[0];
+
+ /* create the "refs" file if first arg is "-r" */
+ if (argc > 1 && !strcmp(argv[1], "-r"))
+ {
+ /* delete the "-r" flag from the args list */
+ argc--;
+ argv++;
+
+ /* open the "refs" file for writing */
+ refs = fopen(REFS, "w");
+ if (!refs)
+ {
+ fprintf(stderr, "%s: could not create \"%s\"\n", progname, REFS);
+ exit(2);
+ }
+ }
+ else
+ {
+ refs = (FILE *)0;
+ }
+
+ /* process each file named on the command line, or complain if none */
+ if (argc > 1)
+ {
+ /* redirect stdout to go to the "tags" file */
+ if (!freopen("tags", "w", stdout))
+ {
+ fprintf(stderr, "%s: could not create \"%s\"\n", progname, TAGS);
+ exit(2);
+ }
+
+ for (i = 1; i < argc; i++)
+ {
+ /* process this named file */
+ fp = fopen(argv[i], "r");
+ if (!fp)
+ {
+ fprintf(stderr, "%s: could not read \"%s\"\n", progname, argv[i]);
+ continue;
+ }
+ ctags(fp, argv[i], refs);
+ fclose(fp);
+ }
+#ifdef SORT
+ /* This is a hack which will sort the tags list. It should
+ * on UNIX and Minix. You may have trouble with csh. Note
+ * that the tags list only has to be sorted if you intend to
+ * use it with the real vi; elvis permits unsorted tags.
+ */
+ fflush(stdout);
+#if OSK
+ fclose(stdout);
+ system("qsort tags >-_tags; -nx; del tags; rename _tags tags");
+#else
+ system("sort tags >_tags$$; mv _tags$$ tags");
+#endif
+#endif
+ exit(0);
+ }
+ else
+ {
+ fprintf(stderr, "usage: %s *.[ch]\n", progname);
+ exit(2);
+ }
+}
+
+
+/* this function finds all tags in a given file */
+ctags(fp, name, refs)
+ FILE *fp; /* stream of the file to scan */
+ char *name; /* name of the file being scanned */
+ FILE *refs; /* NULL, or where to write refs lines */
+{
+ int context; /* context - either EXPECTFN, ARGS, or BODY */
+ long lnum; /* line number */
+ char text[1000]; /* a line of text from the file */
+ char *scan; /* used for searching through text */
+ int len; /* length of the line */
+
+ /* for each line of the file... */
+ for (context = EXPECTFN, lnum = 1; fgets(text, sizeof text, fp); lnum++)
+ {
+#ifdef VERBOSE
+ switch(context)
+ {
+ case EXPECTFN: scan = "EXPECTFN"; break;
+ case ARGS: scan = "ARGS "; break;
+ case BODY: scan = "BODY "; break;
+ default: scan = "context?";
+ }
+ fprintf(stderr, "%s:%s", scan, text);
+#endif
+
+ /* start of body? */
+ if (text[0] == '{')
+ {
+ context = BODY;
+ SAY("Start of BODY");
+ continue;
+ }
+
+ /* argument line, to be written to "refs" ? */
+ if (refs && context == ARGS)
+ {
+ if (text[0] != '\t')
+ {
+ putc('\t', refs);
+ }
+ fputs(text, refs);
+ SAY("Argument line");
+ continue;
+ }
+
+ /* ignore empty or indented lines */
+ if (text[0] <= ' ')
+ {
+ SAY("Empty or indented");
+ continue;
+ }
+
+ /* end of body? */
+ if (text[0] == '}')
+ {
+ context = EXPECTFN;
+ SAY("End of BODY");
+ continue;
+ }
+
+ /* ignore lines in the body of a function */
+ if (context != EXPECTFN)
+ {
+ SAY("BODY or ARGS");
+ continue;
+ }
+
+ /* strip the newline */
+ len = strlen(text);
+ text[--len] = '\0';
+
+ /* a preprocessor line? */
+ if (text[0] == '#')
+ {
+ /* find the preprocessor directive */
+ for (scan = &text[1]; isspace(*scan); scan++)
+ {
+ }
+
+ /* if it's a #define, make a tag out of it */
+ if (!strncmp(scan, "define", 6))
+ {
+ /* find the start of the symbol name */
+ for (scan += 6; isspace(*scan); scan++)
+ {
+ }
+
+ /* find the length of the symbol name */
+ for (len = 1;
+ isalnum(scan[len]) || scan[len] == '_';
+ len++)
+ {
+ }
+#if OSK
+ sprintf(fmt, NUMFMT, len);
+ printf(fmt, scan, name, lnum);
+#else
+ printf(NUMFMT, len, scan, name, lnum);
+#endif
+ }
+ SAY("Preprocessor line");
+ continue;
+ }
+
+ /* an extern or static declaration? */
+ if (text[len - 1] == ';'
+ || !strncmp(text, "extern", 6)
+ || !strncmp(text, "EXTERN", 6)
+ || !strncmp(text, "static", 6)
+ || !strncmp(text, "PRIVATE", 7))
+ {
+ SAY("Extern or static");
+ continue;
+ }
+
+ /* if we get here & the first punctuation other than "*" is
+ * a "(" which is immediately preceded by a name, then
+ * assume the name is that of a function.
+ */
+ for (scan = text; *scan; scan++)
+ {
+ if (ispunct(*scan)
+ && !isspace(*scan) /* in BSD, spaces are punctuation?*/
+ && *scan != '*' && *scan != '_' && *scan != '(')
+ {
+ SAY("Funny punctuation");
+ goto ContinueContinue;
+ }
+
+ if (*scan == '(')
+ {
+ /* permit 0 or 1 spaces between name & '(' */
+ if (scan > text && scan[-1] == ' ')
+ {
+ scan--;
+ }
+
+ /* find the start & length of the name */
+ for (len = 0, scan--;
+ scan >= text && (isalnum(*scan) || *scan == '_');
+ scan--, len++)
+ {
+ }
+ scan++;
+
+ /* did we find a function? */
+ if (len > 0)
+ {
+ /* found a function! */
+ if (len == 4 && !strncmp(scan, "main", 4))
+ {
+#if OSK
+ sprintf(fmt, MAINFMT, strlen(name) - 2);
+ printf(fmt, name, name, text);
+#else
+ printf(MAINFMT, strlen(name) - 2, name, name, text);
+#endif
+ }
+#if OSK
+ sprintf(fmt, SRCHFMT, len);
+ printf(fmt, scan, name, text);
+#else
+ printf(SRCHFMT, len, scan, name, text);
+#endif
+ context = ARGS;
+
+ /* add a line to refs, if needed */
+ if (refs)
+ {
+ fputs(text, refs);
+ putc('\n', refs);
+ }
+
+ goto ContinueContinue;
+ }
+ }
+ else
+ {
+ SAY("No parenthesis");
+ }
+ }
+ SAY("No punctuation");
+
+ContinueContinue:;
+ }
+}
+
+#if MSDOS || TOS
+#define WILDCARD_NO_MAIN
+#include "wildcard.c"
+#endif
diff --git a/curses.c b/curses.c
new file mode 100644
index 0000000..405bb1e
--- /dev/null
+++ b/curses.c
@@ -0,0 +1,729 @@
+/* curses.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains the functions & variables needed for a tiny subset of
+ * curses. The principle advantage of this version of curses is its
+ * extreme speed. Disadvantages are potentially larger code, few supported
+ * functions, limited compatibility with full curses, and only stdscr.
+ */
+
+#include "config.h"
+#include "vi.h"
+
+#if ANY_UNIX
+# if UNIXV
+# include <termio.h>
+# undef TIOCWINSZ /* we can't handle it correctly yet */
+# else
+# include <sgtty.h>
+# endif
+#endif
+
+#if TOS
+# include <osbind.h>
+#endif
+
+#if OSK
+# include <sgstat.h>
+#endif
+
+#include <signal.h>
+
+extern char *getenv();
+static void starttcap();
+
+/* variables, publicly available & used in the macros */
+short ospeed; /* speed of the tty, eg B2400 */
+#if OSK
+char PC_; /* Pad char */
+char *BC; /* backspace character string */
+#else
+char PC; /* Pad char */
+#endif
+WINDOW *stdscr; /* pointer into kbuf[] */
+WINDOW kbuf[KBSIZ]; /* a very large output buffer */
+int LINES; /* :li#: number of rows */
+int COLS; /* :co#: number of columns */
+int AM; /* :am: boolean: auto margins? */
+int PT; /* :pt: boolean: physical tabs? */
+char *VB; /* :vb=: visible bell */
+char *UP; /* :up=: move cursor up */
+char *SO; /* :so=: standout start */
+char *SE; /* :se=: standout end */
+char *US = ""; /* :us=: underline start */
+char *UE = ""; /* :ue=: underline end */
+char *MD = ""; /* :md=: bold start */
+char *ME = ""; /* :me=: bold end */
+char *AS; /* :as=: alternate (italic) start */
+char *AE; /* :ae=: alternate (italic) end */
+char *CM; /* :cm=: cursor movement */
+char *CE; /* :ce=: clear to end of line */
+char *CD; /* :cd=: clear to end of screen */
+char *AL; /* :al=: add a line */
+char *DL; /* :dl=: delete a line */
+#if OSK
+char *SR_; /* :sr=: scroll reverse */
+#else
+char *SR; /* :sr=: scroll reverse */
+#endif
+char *KS; /* :ks=: init string for cursor */
+char *KE; /* :ke=: restore string for cursor */
+char *KU; /* :ku=: key sequence sent by up arrow */
+char *KD; /* :kd=: key sequence sent by down arrow */
+char *KL; /* :kl=: key sequence sent by left arrow */
+char *KR; /* :kr=: key sequence sent by right arrow */
+char *HM; /* :HM=: key sequence sent by the <Home> key */
+char *EN; /* :EN=: key sequence sent by the <End> key */
+char *PU; /* :PU=: key sequence sent by the <PgUp> key */
+char *PD; /* :PD=: key sequence sent by the <PgDn> key */
+char *IM; /* :im=: insert mode start */
+char *IC = ""; /* :ic=: insert the following character */
+char *EI; /* :ei=: insert mode end */
+char *DC; /* :dc=: delete a character */
+char *TI; /* :ti=: terminal init */ /* GB */
+char *TE; /* :te=: terminal exit */ /* GB */
+#ifndef NO_CURSORSHAPE
+char *CQ = (char *)0;/* :cQ=: normal cursor */
+char *CX = (char *)1;/* :cX=: cursor used for EX command/entry */
+char *CV = (char *)2;/* :cV=: cursor used for VI command mode */
+char *CI = (char *)3;/* :cI=: cursor used for VI input mode */
+char *CR = (char *)4;/* :cR=: cursor used for VI replace mode */
+#endif
+char *aend = ""; /* end an attribute -- either UE or ME */
+char ERASEKEY; /* backspace key taken from ioctl structure */
+
+#if ANY_UNIX
+# if UNIXV
+static struct termio oldtermio; /* original tty mode */
+static struct termio newtermio; /* cbreak/noecho tty mode */
+# else
+static struct sgttyb oldsgttyb; /* original tty mode */
+static struct sgttyb newsgttyb; /* cbreak/nl/noecho tty mode */
+static int oldint; /* ^C or DEL, the "intr" character */
+# ifdef TIOCSLTC
+static int oldswitch; /* ^Z, the "suspend" character */
+static int oldquote; /* ^V, the "quote next char" char */
+# endif
+# endif
+#endif
+
+#if OSK
+static struct sgbuf oldsgttyb; /* orginal tty mode */
+static struct sgbuf newsgttyb; /* noecho tty mode */
+#endif
+
+static char *capbuf; /* capability string buffer */
+
+
+void initscr()
+{
+ /* make sure TERM variable is set */
+#if MSDOS
+ char *val;
+ if (! (val = getenv("TERM"))
+ || !strcmp(val, "pcbios"))
+#else
+ if (!getenv("TERM"))
+#endif
+ {
+#if ANY_UNIX
+ write(2, "Environment variable TERM must be set\n", (unsigned)38);
+ exit(1);
+#endif
+#if OSK
+ writeln(2, "Environment variable TERM must be set\n", (unsigned)38);
+ exit(1);
+#endif
+#if MSDOS || TOS
+ getsize(0);
+#endif
+ }
+ else
+ {
+#if MSDOS
+ *o_pcbios=0;
+#endif
+ /* start termcap stuff */
+ starttcap();
+ }
+
+ /* create stdscr and curscr */
+ stdscr = kbuf;
+
+ /* change the terminal mode to cbreak/noecho */
+#if ANY_UNIX
+# if UNIXV
+ ioctl(2, TCGETA, &oldtermio);
+# else
+ ioctl(2, TIOCGETP, &oldsgttyb);
+# endif
+#endif
+
+#if OSK
+ _gs_opt(0, &oldsgttyb);
+#endif
+ resume_curses(TRUE);
+}
+
+
+void endwin()
+{
+ /* change the terminal mode back the way it was */
+ suspend_curses();
+}
+
+
+static int curses_active = FALSE;
+
+void suspend_curses()
+{
+#if ANY_UNIX && !UNIXV
+ struct tchars tbuf;
+# ifdef TIOCSLTC
+ struct ltchars ltbuf;
+# endif
+#endif
+#ifndef NO_CURSORSHAPE
+ if (has_CQ)
+ {
+ do_CQ();
+ }
+#endif
+ if (has_TE) /* GB */
+ {
+ do_TE();
+ }
+ if (has_KE)
+ {
+ do_KE();
+ }
+ refresh();
+
+ /* change the terminal mode back the way it was */
+#if ANY_UNIX
+# if UNIXV
+ ioctl(2, TCSETAW, &oldtermio);
+# else
+ ioctl(2, TIOCSETP, &oldsgttyb);
+
+ ioctl(2, TIOCGETC, &tbuf);
+ tbuf.t_intrc = oldint;
+ ioctl(2, TIOCSETC, &tbuf);
+
+# ifdef TIOCSLTC
+ ioctl(2, TIOCGLTC, &ltbuf);
+ ltbuf.t_suspc = oldswitch;
+ ltbuf.t_lnextc = oldquote;
+ ioctl(2, TIOCSLTC, &ltbuf);
+# endif
+# endif
+#endif
+#if OSK
+ _ss_opt(0, &oldsgttyb);
+#endif
+ curses_active = FALSE;
+}
+
+void resume_curses(quietly)
+ int quietly;
+{
+ if (!curses_active)
+ {
+ /* change the terminal mode to cbreak/noecho */
+#if ANY_UNIX
+# if UNIXV
+ ospeed = (oldtermio.c_cflag & CBAUD);
+ ERASEKEY = oldtermio.c_cc[VERASE];
+ newtermio = oldtermio;
+ newtermio.c_iflag &= (IXON|IXOFF|IXANY|ISTRIP|IGNBRK);
+ newtermio.c_oflag &= ~OPOST;
+ newtermio.c_lflag &= ISIG;
+ newtermio.c_cc[VINTR] = ctrl('C'); /* always use ^C for interrupts */
+ newtermio.c_cc[VMIN] = 1;
+ newtermio.c_cc[VTIME] = 0;
+# ifdef VSWTCH
+ newtermio.c_cc[VSWTCH] = 0;
+# endif
+ ioctl(2, TCSETAW, &newtermio);
+# else /* BSD or V7 or Coherent or Minix */
+ struct tchars tbuf;
+# ifdef TIOCSLTC
+ struct ltchars ltbuf;
+# endif
+
+ ospeed = oldsgttyb.sg_ospeed;
+ ERASEKEY = oldsgttyb.sg_erase;
+ newsgttyb = oldsgttyb;
+ newsgttyb.sg_flags |= CBREAK;
+ newsgttyb.sg_flags &= ~(CRMOD|ECHO|XTABS);
+ ioctl(2, TIOCSETP, &newsgttyb);
+
+ ioctl(2, TIOCGETC, &tbuf);
+ oldint = tbuf.t_intrc;
+ tbuf.t_intrc = ctrl('C'); /* always use ^C for interrupts */
+ ioctl(2, TIOCSETC, &tbuf);
+
+# ifdef TIOCSLTC
+ ioctl(2, TIOCGLTC, &ltbuf);
+ oldswitch = ltbuf.t_suspc;
+ ltbuf.t_suspc = 0; /* disable ^Z for elvis */
+ oldquote = ltbuf.t_lnextc;
+ ltbuf.t_lnextc = 0; /* disable ^V for elvis */
+ ioctl(2, TIOCSLTC, &ltbuf);
+# endif
+
+# endif
+#endif
+#if OSK
+ newsgttyb = oldsgttyb;
+ newsgttyb.sg_echo = 0;
+ newsgttyb.sg_eofch = 0;
+ newsgttyb.sg_kbach = 0;
+ newsgttyb.sg_kbich = ctrl('C');
+ _ss_opt(0, &newsgttyb);
+ ospeed = oldsgttyb.sg_baud;
+ ERASEKEY = oldsgttyb.sg_bspch;
+#endif
+
+ if (has_TI) /* GB */
+ {
+ do_TI();
+ }
+ if (has_KS)
+ {
+ do_KS();
+ }
+
+ curses_active = TRUE;
+ }
+
+ /* If we're supposed to quit quietly, then we're done */
+ if (quietly)
+ {
+ return;
+ }
+
+ signal(SIGINT, SIG_IGN);
+
+ move(LINES - 1, 0);
+ do_SO();
+ qaddstr("[Press <RETURN> to continue]");
+ do_SE();
+ refresh();
+ ttyread(kbuf, 20); /* in RAW mode, so <20 is very likely */
+ if (kbuf[0] == ':')
+ {
+ mode = MODE_COLON;
+ addch('\n');
+ refresh();
+ }
+ else
+ {
+ mode = MODE_VI;
+ redraw(MARK_UNSET, FALSE);
+ }
+ exwrote = FALSE;
+
+#if TURBOC
+ signal(SIGINT, (void(*)()) trapint);
+#else
+ signal(SIGINT, trapint);
+#endif
+}
+
+static void lacking(s)
+ char *s;
+{
+ write(2, "This termcap entry lacks the :", (unsigned)30);
+ write(2, s, (unsigned)2);
+ write(2, "=: capability\n", (unsigned)14);
+#if OSK
+ write(2, "\l", 1);
+#endif
+ exit(1);
+}
+
+static void starttcap()
+{
+ char *str;
+ static char cbmem[800];
+#define MUSTHAVE(T,s) if (!(T = tgetstr(s, &capbuf))) lacking(s)
+#define MAYHAVE(T,s) if (str = tgetstr(s, &capbuf)) T = str
+#define PAIR(T,U,sT,sU) T=tgetstr(sT,&capbuf);U=tgetstr(sU,&capbuf);if (!T||!U)T=U=""
+
+ /* allocate memory for capbuf */
+ capbuf = cbmem;
+
+ /* get the termcap entry */
+ switch (tgetent(kbuf, getenv("TERM")))
+ {
+ case -1:
+ write(2, "Can't read /etc/termcap\n", (unsigned)24);
+#if OSK
+ write(2, "\l", 1);
+#endif
+ exit(2);
+
+ case 0:
+ write(2, "Unrecognized TERM type\n", (unsigned)23);
+#if OSK
+ write(2, "\l", 1);
+#endif
+ exit(3);
+ }
+
+ /* get strings */
+ MUSTHAVE(UP, "up");
+ MAYHAVE(VB, "vb");
+ MUSTHAVE(CM, "cm");
+ PAIR(SO, SE, "so", "se");
+ PAIR(TI, TE, "ti", "te");
+ if (tgetnum("ug") <= 0)
+ {
+ PAIR(US, UE, "us", "ue");
+ PAIR(MD, ME, "md", "me");
+
+ /* get italics, or have it default to underline */
+ PAIR(AS, AE, "as", "ae");
+ if (!*AS)
+ {
+ AS = US;
+ AE = UE;
+ }
+ }
+ MAYHAVE(AL, "al");
+ MAYHAVE(DL, "dl");
+ MUSTHAVE(CE, "ce");
+ MAYHAVE(CD, "cd");
+#if OSK
+ MAYHAVE(SR_, "sr");
+#else
+ MAYHAVE(SR, "sr");
+#endif
+ PAIR(IM, EI, "im", "ei");
+ MAYHAVE(IC, "ic");
+ MAYHAVE(DC, "dc");
+
+ /* other termcap stuff */
+ AM = tgetflag("am");
+ PT = tgetflag("pt");
+ getsize(0);
+
+ /* Key sequences */
+ PAIR(KS, KE, "ks", "ke");
+ MAYHAVE(KU, "ku"); /* up */
+ MAYHAVE(KD, "kd"); /* down */
+ MAYHAVE(KL, "kl"); /* left */
+ MAYHAVE(KR, "kr"); /* right */
+ MAYHAVE(PU, "kP"); /* PgUp */
+ MAYHAVE(PD, "kN"); /* PgDn */
+ MAYHAVE(HM, "kh"); /* Home */
+ MAYHAVE(EN, "kH"); /* End */
+#ifndef CRUNCH
+ if (!PU) MAYHAVE(PU, "K2"); /* "3x3 pad" names for PgUp, etc. */
+ if (!PD) MAYHAVE(PD, "K5");
+ if (!HM) MAYHAVE(HM, "K1");
+ if (!EN) MAYHAVE(EN, "K4");
+
+ MAYHAVE(PU, "PU"); /* old XENIX names for PgUp, etc. */
+ MAYHAVE(PD, "PD"); /* (overrides others, if used.) */
+ MAYHAVE(HM, "HM");
+ MAYHAVE(EN, "EN");
+#endif
+
+#ifndef NO_CURSORSHAPE
+ /* cursor shapes */
+ CQ = tgetstr("cQ", &capbuf);
+ if (has_CQ)
+ {
+ CX = tgetstr("cX", &capbuf);
+ if (!CX) CX = CQ;
+ CV = tgetstr("cV", &capbuf);
+ if (!CV) CV = CQ;
+ CI = tgetstr("cI", &capbuf);
+ if (!CI) CI = CQ;
+ CR = tgetstr("cR", &capbuf);
+ if (!CR) CR = CQ;
+ }
+# ifndef CRUNCH
+ else
+ {
+ PAIR(CQ, CV, "ve", "vs");
+ CX = CI = CR = CQ;
+ }
+# endif /* !CRUNCH */
+#endif /* !NO_CURSORSHAPE */
+
+#undef MUSTHAVE
+#undef MAYHAVE
+#undef PAIR
+}
+
+
+/* This function gets the window size. It uses the TIOCGWINSZ ioctl call if
+ * your system has it, or tgetnum("li") and tgetnum("co") if it doesn't.
+ * This function is called once during initialization, and thereafter it is
+ * called whenever the SIGWINCH signal is sent to this process.
+ */
+int getsize(signo)
+ int signo;
+{
+ int lines;
+ int cols;
+#ifdef TIOCGWINSZ
+ struct winsize size;
+#endif
+
+#ifdef SIGWINCH
+ /* reset the signal vector */
+ signal(SIGWINCH, getsize);
+#endif
+
+ /* get the window size, one way or another. */
+ lines = cols = 0;
+#ifdef TIOCGWINSZ
+ if (ioctl(2, TIOCGWINSZ, &size) >= 0)
+ {
+ lines = size.ws_row;
+ cols = size.ws_col;
+ }
+#endif
+ if ((lines == 0 || cols == 0) && signo == 0)
+ {
+ LINES = CHECKBIOS(v_rows(), tgetnum("li"));
+ COLS = CHECKBIOS(v_cols(), tgetnum("co"));
+ }
+ if (lines >= 2 && cols >= 30)
+ {
+ LINES = lines;
+ COLS = cols;
+ }
+
+ /* Make sure we got values that we can live with */
+ if (LINES < 2 || COLS < 30)
+ {
+ write(2, "Screen too small\n", (unsigned)17);
+#if OSK
+ write(2, "\l", 1);
+#endif
+ endwin();
+ exit(2);
+ }
+
+ /* !!! copy the new values into Elvis' options */
+ {
+ extern char o_columns[], o_lines[];
+
+ *o_columns = COLS;
+ *o_lines = LINES;
+ }
+
+ return 0;
+}
+
+
+/* This is a function version of addch() -- it is used by tputs() */
+int faddch(ch)
+ int ch;
+{
+ addch(ch);
+
+ return 0;
+}
+
+/* These functions are equivelent to the macros of the same names... */
+
+void qaddstr(str)
+ char *str;
+{
+ REG char *s_, *d_;
+
+#if MSDOS
+ if (o_pcbios[0])
+ {
+ while (*str)
+ qaddch(*str++);
+ return;
+ }
+#endif
+ for (s_=(str), d_=stdscr; *d_++ = *s_++; )
+ {
+ }
+ stdscr = d_ - 1;
+}
+
+void attrset(a)
+ int a;
+{
+ do_aend();
+ if (a == A_BOLD)
+ {
+ do_MD();
+ aend = ME;
+ }
+ else if (a == A_UNDERLINE)
+ {
+ do_US();
+ aend = UE;
+ }
+ else if (a == A_ALTCHARSET)
+ {
+ do_AS();
+ aend = AE;
+ }
+ else
+ {
+ aend = "";
+ }
+}
+
+
+void insch(ch)
+ int ch;
+{
+ if (has_IM)
+ do_IM();
+ do_IC();
+ qaddch(ch);
+ if (has_EI)
+ do_EI();
+}
+
+#if MSDOS
+
+static int alarmtime;
+
+/* raw read - #defined to read (0, ...) on non-MSDOS.
+ * With MSDOS, am maximum of 1 byte is read.
+ * If more bytes should be read, just change the loop.
+ * The following code uses the IBM-PC-System-Timer, so probably wont't work
+ * on non-compatibles.
+ */
+/*ARGSUSED*/
+ttyread(buf, len)
+ char *buf;
+ int len;
+{
+ volatile char far *biostimer;
+ char oldtime;
+ int nticks = 0;
+ int pos = 0;
+
+ biostimer = (char far *)0x0040006cl;
+ oldtime = *biostimer;
+
+ while (!pos && (!alarmtime || nticks<alarmtime))
+ { if (kbhit())
+ if ((buf[pos++] = getch()) == 0) /* function key */
+ buf[pos-1] = '#';
+ if (oldtime != *biostimer)
+ { nticks++;
+ oldtime = *biostimer;
+ }
+ }
+ return pos;
+}
+
+alarm(time)
+ int time;
+{
+ alarmtime = 2 * time; /* ticks are 1/18 sec. */
+}
+
+sleep(seconds)
+ unsigned seconds;
+{
+ volatile char far *biostimer = (char far *)0x0040006cl;
+ char stop;
+
+ stop = *biostimer + 18 * seconds;
+ while (*biostimer != stop)
+ {
+ }
+}
+#endif
+
+#if TOS
+
+static int alarmtime;
+static long timer;
+
+static gettime()
+{
+ timer = *(long *)(0x4ba);
+}
+
+/*ARGSUSED*/
+ttyread(buf, len)
+ char *buf;
+ int len;
+{
+ int pos=0;
+ long l;
+ long endtime;
+
+ Supexec(gettime);
+ endtime = timer+alarmtime;
+
+ while (!pos && (!alarmtime || timer<endtime))
+ {
+ if (Bconstat(2))
+ {
+ l = Bconin(2);
+ if ((buf[pos++]=l) == '\0')
+ {
+ buf[pos-1]='#';
+ buf[pos++]=l>>16;
+ }
+ }
+ Supexec(gettime);
+ }
+ return pos;
+}
+
+alarm(time)
+ int time;
+{
+ alarmtime = 50 * time; /* ticks are 1/200 sec. */
+}
+
+ttywrite(buf, len)
+ char *buf;
+ int len;
+{
+ while (len--)
+ Bconout(2, *buf++);
+}
+#endif
+
+#if OSK
+ttyread(buf, len)
+ char *buf;
+ int len;
+{
+ REG int i;
+ if ((i = _gs_rdy(0)) > 0)
+ return read(0, buf, i < len ? i : len);
+ else
+ return read(0, buf, 1);
+}
+
+alarm(time)
+ int time;
+{
+#define TIME(secs) ((secs << 8) | 0x80000000)
+ static int alrmid;
+
+ if (time)
+ alrmid = alm_set(SIGQUIT, TIME(time));
+ else
+ alm_delete(alrmid);
+}
+#endif /* OSK */
diff --git a/curses.h b/curses.h
new file mode 100644
index 0000000..7f4a991
--- /dev/null
+++ b/curses.h
@@ -0,0 +1,252 @@
+/* curses.h */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This is the header file for a small, fast, fake curses package */
+
+/* termcap stuff */
+extern char *tgoto();
+extern char *tgetstr();
+extern void tputs();
+
+#if MSDOS
+/* BIOS interface used instead of termcap for MS-DOS */
+extern int vmode;
+extern void v_up();
+extern void v_cb();
+extern void v_cs();
+extern void v_ce();
+extern void v_cl();
+extern void v_cd();
+extern void v_al();
+extern void v_dl();
+extern void v_sr();
+extern void v_move();
+#endif
+
+/* faddch() is a function. a pointer to it is passed to tputs() */
+extern int faddch();
+
+/* data types */
+#define WINDOW char
+
+/* CONSTANTS & SYMBOLS */
+#define TRUE 1
+#define FALSE 0
+#define A_NORMAL 0
+#define A_STANDOUT 1
+#define A_BOLD 2
+#define A_UNDERLINE 3
+#define A_ALTCHARSET 4
+#if MSDOS
+#define KBSIZ (10*1024)
+#else
+#define KBSIZ (6*1024)
+#endif
+
+/* extern variables, defined in curses.c */
+extern short ospeed; /* tty speed, eg B2400 */
+#if OSK
+extern char PC_; /* Pad char */
+extern char *BC; /* Backspace char string */
+#else
+extern char PC; /* Pad char */
+#endif
+extern WINDOW *stdscr; /* pointer into kbuf[] */
+extern WINDOW kbuf[KBSIZ]; /* a very large output buffer */
+extern int LINES; /* :li#: number of rows */
+extern int COLS; /* :co#: number of columns */
+extern int AM; /* :am: boolean: auto margins? */
+extern int PT; /* :pt: boolean: physical tabs? */
+extern char *VB; /* :vb=: visible bell */
+extern char *UP; /* :up=: move cursor up */
+extern char *SO; /* :so=: standout start */
+extern char *SE; /* :se=: standout end */
+extern char *US; /* :us=: underline start */
+extern char *UE; /* :ue=: underline end */
+extern char *MD; /* :md=: bold start */
+extern char *ME; /* :me=: bold end */
+extern char *AS; /* :as=: alternate (italic) start */
+extern char *AE; /* :ae=: alternate (italic) end */
+extern char *CM; /* :cm=: cursor movement */
+extern char *CE; /* :ce=: clear to end of line */
+extern char *CD; /* :cd=: clear to end of screen */
+extern char *AL; /* :al=: add a line */
+extern char *DL; /* :dl=: delete a line */
+#if OSK
+extern char *SR_; /* :sr=: scroll reverse */
+#else
+extern char *SR; /* :sr=: scroll reverse */
+#endif
+extern char *KS; /* :ks=: init string for cursor */
+extern char *KE; /* :ke=: restore string for cursor */
+extern char *KU; /* :ku=: sequence sent by up key */
+extern char *KD; /* :kd=: sequence sent by down key */
+extern char *KL; /* :kl=: sequence sent by left key */
+extern char *KR; /* :kr=: sequence sent by right key */
+extern char *PU; /* :PU=: key sequence sent by PgUp key */
+extern char *PD; /* :PD=: key sequence sent by PgDn key */
+extern char *HM; /* :HM=: key sequence sent by Home key */
+extern char *EN; /* :EN=: key sequence sent by End key */
+extern char *IM; /* :im=: insert mode start */
+extern char *IC; /* :ic=: insert following char */
+extern char *EI; /* :ei=: insert mode end */
+extern char *DC; /* :dc=: delete a character */
+extern char *TI; /* :ti=: terminal init */ /* GB */
+extern char *TE; /* :te=: terminal exit */ /* GB */
+#ifndef NO_CURSORSHAPE
+extern char *CQ; /* :cQ=: normal cursor */
+extern char *CX; /* :cX=: cursor used for EX command/entry */
+extern char *CV; /* :cV=: cursor used for VI command mode */
+extern char *CI; /* :cI=: cursor used for VI input mode */
+extern char *CR; /* :cR=: cursor used for VI replace mode */
+#endif
+extern char *aend; /* end an attribute -- either UE or ME */
+extern char ERASEKEY; /* taken from the ioctl structure */
+
+/* Msdos-versions may use bios; others always termcap.
+ * Will emit some 'code has no effect' warnings in unix.
+ */
+
+#if MSDOS
+extern char o_pcbios[1]; /* BAH! */
+#define CHECKBIOS(x,y) (*o_pcbios ? (x) : (y))
+#define VOIDBIOS(x,y) {if (*o_pcbios) {x;} else {y;}}
+#else
+#define CHECKBIOS(x,y) (y)
+#define VOIDBIOS(x,y) {y;}
+#endif
+
+#define do_VB() VOIDBIOS(;, tputs(VB, 1, faddch))
+#define do_UP() VOIDBIOS(v_up(), tputs(UP, 1, faddch))
+#define do_SO() VOIDBIOS((vmode=A_STANDOUT), tputs(SO, 1, faddch))
+#define do_SE() VOIDBIOS((vmode=A_NORMAL), tputs(SE, 1, faddch))
+#define do_US() VOIDBIOS((vmode=A_UNDERLINE), tputs(US, 1, faddch))
+#define do_UE() VOIDBIOS((vmode=A_NORMAL), tputs(UE, 1, faddch))
+#define do_MD() VOIDBIOS((vmode=A_BOLD), tputs(MD, 1, faddch))
+#define do_ME() VOIDBIOS((vmode=A_NORMAL), tputs(ME, 1, faddch))
+#define do_AS() VOIDBIOS((vmode=A_ALTCHARSET), tputs(AS, 1, faddch))
+#define do_AE() VOIDBIOS((vmode=A_NORMAL), tputs(AE, 1, faddch))
+#undef do_CM /* move */
+#define do_CE() VOIDBIOS(v_ce(), tputs(CE, 1, faddch))
+#define do_CD() VOIDBIOS(v_cd(), tputs(CD, 1, faddch))
+#define do_AL() VOIDBIOS(v_al(), tputs(AL, LINES, faddch))
+#define do_DL() VOIDBIOS(v_dl(), tputs(DL, LINES, faddch))
+#if OSK
+#define do_SR() VOIDBIOS(v_sr(), tputs(SR_, 1, faddch))
+#else
+#define do_SR() VOIDBIOS(v_sr(), tputs(SR, 1, faddch))
+#endif
+#define do_KS() VOIDBIOS(1, tputs(KS, 1, faddch))
+#define do_KE() VOIDBIOS(1, tputs(KE, 1, faddch))
+#define do_IM() VOIDBIOS(;, tputs(IM, 1, faddch))
+#define do_IC() VOIDBIOS(;, tputs(IC, 1, faddch))
+#define do_EI() VOIDBIOS(;, tputs(EI, 1, faddch))
+#define do_DC() VOIDBIOS(;, tputs(DC, COLS, faddch))
+#define do_TI() VOIDBIOS(;, (void)ttywrite(TI, (unsigned)strlen(TI)))
+#define do_TE() VOIDBIOS(;, (void)ttywrite(TE, (unsigned)strlen(TE)))
+#ifndef NO_CURSORSHAPE
+# define do_CQ() VOIDBIOS(v_cs(), tputs(CQ, 1, faddch))
+# define do_CX() VOIDBIOS(v_cs(), tputs(CX, 1, faddch))
+# define do_CV() VOIDBIOS(v_cs(), tputs(CV, 1, faddch))
+# define do_CI() VOIDBIOS(v_cb(), tputs(CI, 1, faddch))
+# define do_CR() VOIDBIOS(v_cb(), tputs(CR, 1, faddch))
+#endif
+#define do_aend() VOIDBIOS((vmode=A_NORMAL), tputs(aend, 1, faddch))
+
+#define has_AM CHECKBIOS(1, AM)
+#define has_PT CHECKBIOS(0, PT)
+#define has_VB CHECKBIOS((char *)0, VB)
+#define has_UP CHECKBIOS((char *)1, UP)
+#define has_SO CHECKBIOS((char)1, (*SO))
+#define has_SE CHECKBIOS((char)1, (*SE))
+#define has_US CHECKBIOS((char)1, (*US))
+#define has_UE CHECKBIOS((char)1, (*UE))
+#define has_MD CHECKBIOS((char)1, (*MD))
+#define has_ME CHECKBIOS((char)1, (*ME))
+#define has_AS CHECKBIOS((char)1, (*AS))
+#define has_AE CHECKBIOS((char)1, (*AE))
+#undef has_CM /* cursor move: don't need */
+#define has_CB CHECKBIOS(1, 0)
+#define has_CS CHECKBIOS(1, 0)
+#define has_CE CHECKBIOS((char *)1, CE)
+#define has_CD CHECKBIOS((char *)1, CD)
+#define has_AL CHECKBIOS((char *)1, AL)
+#define has_DL CHECKBIOS((char *)1, DL)
+#if OSK
+#define has_SR CHECKBIOS((char *)1, SR_)
+#else
+#define has_SR CHECKBIOS((char *)1, SR)
+#endif
+#define has_KS CHECKBIOS((char)1, (*KS))
+#define has_KE CHECKBIOS((char)1, (*KE))
+#define has_KU CHECKBIOS("#H", KU)
+#define has_KD CHECKBIOS("#P", KD)
+#define has_KL CHECKBIOS("#K", KL)
+#define has_KR CHECKBIOS("#M", KR)
+#define has_HM CHECKBIOS("#G", HM)
+#define has_EN CHECKBIOS("#O", EN)
+#define has_PU CHECKBIOS("#I", PU)
+#define has_PD CHECKBIOS("#Q", PD)
+#define has_IM CHECKBIOS((char)0, (*IM))
+#define has_IC CHECKBIOS((char)0, (*IC))
+#define has_EI CHECKBIOS((char)0, (*EI))
+#define has_DC CHECKBIOS((char *)0, DC)
+#define has_TI CHECKBIOS((char)0, (*TI))
+#define has_TE CHECKBIOS((char)0, (*TE))
+#ifndef NO_CURSORSHAPE
+#define has_CQ CHECKBIOS((char *)1, CQ)
+#endif
+
+/* (pseudo)-Curses-functions */
+
+#ifdef lint
+# define _addCR VOIDBIOS(;, (stdscr[-1] == '\n' ? qaddch('\r') : (stdscr[-1] = '\n')))
+#else
+# if OSK
+# define _addCR VOIDBIOS(;, (stdscr[-1] == '\n' ? qaddch('\l') : (stdscr[-1] = stdscr[-1])))
+# else
+# define _addCR VOIDBIOS(;, (stdscr[-1] == '\n' ? qaddch('\r') : 0))
+#endif
+#endif
+#define qaddch(ch) CHECKBIOS(v_put(ch), (*stdscr++ = (ch)))
+#if OSK
+#define addch(ch) if (qaddch(ch) == '\n') qaddch('\l'); else
+#else
+#define addch(ch) if (qaddch(ch) == '\n') qaddch('\r'); else
+#endif
+
+extern void initscr();
+extern void endwin();
+extern void suspend_curses();
+extern void resume_curses();
+extern void attrset();
+extern void insch();
+extern void qaddstr();
+#define addstr(str) {qaddstr(str); _addCR;}
+#define move(y,x) VOIDBIOS(v_move(x,y), \
+ tputs(tgoto(CM, x, y), 1, faddch))
+#define mvaddch(y,x,ch) {move(y,x); addch(ch);}
+#define refresh() VOIDBIOS(;, wrefresh(stdscr))
+#define wrefresh(w) if ((w) != kbuf) VOIDBIOS((w) = kbuf, {ttywrite(kbuf, (unsigned)((w) - kbuf)); (w) = kbuf;}) else
+#define wqrefresh(w) if ((w) - kbuf > 2000) VOIDBIOS((w) = kbuf, {ttywrite(kbuf, (unsigned)((w) - kbuf)); (w) = kbuf;}) else
+#define standout() do_SO()
+#define standend() do_SE()
+#define clrtoeol() do_CE()
+#define clrtobot() do_CD()
+#define insertln() do_AL()
+#define deleteln() do_DL()
+#define delch() do_DC()
+#define scrollok(w,b)
+#define raw()
+#define echo()
+#define cbreak()
+#define noraw()
+#define noecho()
+#define nocbreak()
diff --git a/cut.c b/cut.c
new file mode 100644
index 0000000..8507f8e
--- /dev/null
+++ b/cut.c
@@ -0,0 +1,668 @@
+/* cut.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains function which manipulate the cut buffers. */
+
+#include "config.h"
+#include "vi.h"
+#if TURBOC
+#include <process.h> /* needed for getpid */
+#endif
+#if TOS
+#include <osbind.h>
+#define rename(a,b) Frename(0,a,b)
+#endif
+
+# define NANNONS 9 /* number of annonymous buffers */
+
+static struct cutbuf
+{
+ short *phys; /* pointer to an array of #s of BLKs containing text */
+ int nblks; /* number of blocks in phys[] array */
+ int start; /* offset into first block of start of cut */
+ int end; /* offset into last block of end of cut */
+ int fd; /* fd of tmp file, or -1 to use tmpfd */
+ char lnmode; /* boolean: line-mode cut? (as opposed to char-mode) */
+}
+ named[27], /* cut buffers "a through "z and ". */
+ annon[NANNONS]; /* annonymous cut buffers */
+
+static char cbname; /* name chosen for next cut/paste operation */
+
+
+#ifndef NO_RECYCLE
+/* This function builds a list of all blocks needed in the current tmp file
+ * for the contents of cut buffers.
+ * !!! WARNING: if you have more than ~450000 bytes of text in all of the
+ * cut buffers, then this will fail disastrously, because buffer overflow
+ * is *not* allowed for.
+ */
+int cutneeds(need)
+ BLK *need; /* this is where we deposit the list */
+{
+ struct cutbuf *cb; /* used to count through cut buffers */
+ int i; /* used to count through blocks of a cut buffer */
+ int n; /* total number of blocks in list */
+
+ n = 0;
+
+ /* first the named buffers... */
+ for (cb = named; cb < &named[27]; cb++)
+ {
+ if (cb->fd > 0)
+ continue;
+
+ for (i = cb->nblks; i-- > 0; )
+ {
+ need->n[n++] = cb->phys[i];
+ }
+ }
+
+ /* then the anonymous buffers */
+ for (cb = annon; cb < &annon[NANNONS]; cb++)
+ {
+ if (cb->fd > 0)
+ continue;
+
+ for (i = cb->nblks; i-- > 0; )
+ {
+ need->n[n++] = cb->phys[i];
+ }
+ }
+
+ return n;
+}
+#endif
+
+/* This function frees a cut buffer */
+static void cutfree(buf)
+ struct cutbuf *buf;
+{
+ char cutfname[50];
+ int i;
+
+ /* return immediately if the buffer is already empty */
+ if (buf->nblks <= 0)
+ {
+ return;
+ }
+
+ /* else free up stuff */
+ buf->nblks = 0;
+#ifdef DEBUG
+ if (!buf->phys)
+ msg("cutfree() tried to free an NULL buf->phys pointer.");
+#endif
+ free((char *)buf->phys);
+
+ /* see if anybody else needs this tmp file */
+ if (buf->fd >= 0)
+ {
+ for (i = 0; i < 27; i++)
+ {
+ if (named[i].nblks > 0 && named[i].fd == buf->fd)
+ {
+ break;
+ }
+ }
+ }
+
+ /* if nobody else needs it, then discard the tmp file */
+ if (buf->fd >= 0 && i == 27)
+ {
+ close(buf->fd);
+#if MSDOS || TOS
+ strcpy(cutfname, o_directory);
+ if ((i = strlen(cutfname)) && !strchr(":/\\", cutfname[i-1]))
+ cutfname[i++]=SLASH;
+ sprintf(cutfname+i, CUTNAME+3, getpid(), buf->fd);
+#else
+ sprintf(cutfname, CUTNAME, o_directory, getpid(), buf->fd);
+#endif
+ unlink(cutfname);
+ }
+}
+
+/* This function is called when we are about to abort a tmp file. If any
+ * cut buffers still need the file, then a copy of the file should be
+ * created for use by the cut buffers.
+ *
+ * To minimize the number of extra files lying around, only named cut buffers
+ * are preserved in a file switch; the annonymous buffers just go away.
+ */
+void cutswitch(tmpname)
+ char *tmpname; /* name of the tmp file */
+{
+ char cutfname[50]; /* used to build a new name for the tmp file */
+ int fd; /* a new fd for the current tmp file */
+ int i;
+#if MSDOS || TOS
+ int j;
+#endif
+
+ /* discard all annonymous cut buffers */
+ for (i = 0; i < NANNONS; i++)
+ {
+ cutfree(&annon[i]);
+ }
+
+ /* find the first named buffer that uses this tmp file */
+ for (i = 0; i < 27; i++)
+ {
+ if (named[i].nblks > 0 && named[i].fd < 0)
+ {
+ break;
+ }
+ }
+
+ /* if none of them use this tmp file, then we're done */
+ if (i == 27)
+ {
+ return;
+ }
+
+ /* else we'll need this file and an fd a little longer */
+#if MSDOS || TOS
+ strcpy(cutfname, o_directory);
+ if ((j = strlen(cutfname)) && !strchr(":/\\", cutfname[j-1]))
+ cutfname[j++]=SLASH;
+ close(tmpfd);
+ fd = open(tmpname, O_RDONLY|O_BINARY);
+ close(fd);
+ sprintf(cutfname+j, CUTNAME+3, getpid(), fd);
+ rename(tmpname, cutfname);
+ fd = open(cutfname, O_RDONLY|O_BINARY);
+ tmpfd = -1; /* we'll try to close this in tmp.c, but who cares? */
+#else
+ fd = dup(tmpfd);
+# if OSK
+ sprintf(cutfname, CUTNAME, "", getpid(), fd);
+ if (!link(tmpname, &cutfname[1])) /* skip slash */
+ unlink(tmpname);
+# else
+ sprintf(cutfname, CUTNAME, o_directory, getpid(), fd);
+ link(tmpname, cutfname) || unlink(tmpname);
+# endif
+#endif
+
+ /* have all cut buffers use the new fd instead */
+ for (; i < 27; i++)
+ {
+ if (named[i].nblks > 0 && named[i].fd < 0)
+ {
+ named[i].fd = fd;
+ }
+ }
+}
+
+/* This function should be called just before termination of vi */
+void cutend()
+{
+ int i;
+
+ /* free all named cut buffers, since they might be forcing an older
+ * tmp file to be retained.
+ */
+ for (i = 0; i < 27; i++)
+ {
+ cutfree(&named[i]);
+ }
+}
+
+
+/* This function is used to select the cut buffer to be used next */
+void cutname(name)
+ int name; /* a single character */
+{
+ cbname = name;
+}
+
+
+
+
+/* This function copies a selected segment of text to a cut buffer */
+void cut(from, to)
+ MARK from; /* start of text to cut */
+ MARK to; /* end of text to cut */
+{
+ int first; /* logical number of first block in cut */
+ int last; /* logical number of last block used in cut */
+ long line; /* a line number */
+ int lnmode; /* boolean: will this be a line-mode cut? */
+ MARK delthru;/* end of text temporarily inserted for apnd */
+ REG struct cutbuf *cb;
+ REG long l;
+ REG int i;
+ REG char *scan;
+ char *blkc;
+
+ /* detect whether this must be a line-mode cut or char-mode cut */
+ if (markidx(from) == 0 && markidx(to) == 0)
+ lnmode = TRUE;
+ else
+ lnmode = FALSE;
+
+ /* by default, we don't "delthru" anything */
+ delthru = MARK_UNSET;
+
+ /* decide which cut buffer to use */
+ if (!cbname)
+ {
+ /* free up the last annonymous cut buffer */
+ cutfree(&annon[NANNONS - 1]);
+
+ /* shift the annonymous cut buffers */
+ for (i = NANNONS - 1; i > 0; i--)
+ {
+ annon[i] = annon[i - 1];
+ }
+
+ /* use the first annonymous cut buffer */
+ cb = annon;
+ cb->nblks = 0;
+ }
+ else if (cbname >= 'a' && cbname <= 'z')
+ {
+ cb = &named[cbname - 'a'];
+ cutfree(cb);
+ }
+#ifndef CRUNCH
+ else if (cbname >= 'A' && cbname <= 'Z')
+ {
+ cb = &named[cbname - 'A'];
+ if (cb->nblks > 0)
+ {
+ /* resolve linemode/charmode differences */
+ if (!lnmode && cb->lnmode)
+ {
+ from &= ~(BLKSIZE - 1);
+ if (markidx(to) != 0 || to == from)
+ {
+ to = to + BLKSIZE - markidx(to);
+ }
+ lnmode = TRUE;
+ }
+
+ /* insert the old cut-buffer before the new text */
+ mark[28] = to;
+ delthru = paste(from, FALSE, TRUE);
+ if (delthru == MARK_UNSET)
+ {
+ return;
+ }
+ delthru++;
+ to = mark[28];
+ }
+ cutfree(cb);
+ }
+#endif /* not CRUNCH */
+ else if (cbname == '.')
+ {
+ cb = &named[26];
+ cutfree(cb);
+ }
+ else
+ {
+ msg("Invalid cut buffer name: \"%c", cbname);
+ cbname = '\0';
+ return;
+ }
+ cbname = '\0';
+ cb->fd = -1;
+
+ /* detect whether we're doing a line mode cut */
+ cb->lnmode = lnmode;
+
+ /* ---------- */
+
+ /* Reporting... */
+ if (markidx(from) == 0 && markidx(to) == 0)
+ {
+ rptlines = markline(to) - markline(from);
+ rptlabel = "yanked";
+ }
+
+ /* ---------- */
+
+ /* make sure each block has a physical disk address */
+ blksync();
+
+ /* find the first block in the cut */
+ line = markline(from);
+ for (first = 1; line > lnum[first]; first++)
+ {
+ }
+
+ /* fetch text of the block containing that line */
+ blkc = scan = blkget(first)->c;
+
+ /* find the mark in the block */
+ for (l = lnum[first - 1]; ++l < line; )
+ {
+ while (*scan++ != '\n')
+ {
+ }
+ }
+ scan += markidx(from);
+
+ /* remember the offset of the start */
+ cb->start = scan - blkc;
+
+ /* ---------- */
+
+ /* find the last block in the cut */
+ line = markline(to);
+ for (last = first; line > lnum[last]; last++)
+ {
+ }
+
+ /* fetch text of the block containing that line */
+ if (last != first)
+ {
+ blkc = scan = blkget(last)->c;
+ }
+ else
+ {
+ scan = blkc;
+ }
+
+ /* find the mark in the block */
+ for (l = lnum[last - 1]; ++l < line; )
+ {
+ while (*scan++ != '\n')
+ {
+ }
+ }
+ if (markline(to) <= nlines)
+ {
+ scan += markidx(to);
+ }
+
+ /* remember the offset of the end */
+ cb->end = scan - blkc;
+
+ /* ------- */
+
+ /* remember the physical block numbers of all included blocks */
+ cb->nblks = last - first;
+ if (cb->end > 0)
+ {
+ cb->nblks++;
+ }
+#ifdef lint
+ cb->phys = (short *)0;
+#else
+ cb->phys = (short *)malloc((unsigned)(cb->nblks * sizeof(short)));
+#endif
+ for (i = 0; i < cb->nblks; i++)
+ {
+ cb->phys[i] = hdr.n[first++];
+ }
+
+#ifndef CRUNCH
+ /* if we temporarily inserted text for appending, then delete that
+ * text now -- before the user sees it.
+ */
+ if (delthru)
+ {
+ line = rptlines;
+ delete(from, delthru);
+ rptlines = line;
+ rptlabel = "yanked";
+ }
+#endif /* not CRUNCH */
+}
+
+
+static void readcutblk(cb, blkno)
+ struct cutbuf *cb;
+ int blkno;
+{
+ int fd; /* either tmpfd or cb->fd */
+
+ /* decide which fd to use */
+ if (cb->fd >= 0)
+ {
+ fd = cb->fd;
+ }
+ else
+ {
+ fd = tmpfd;
+ }
+
+ /* get the block */
+ lseek(fd, (long)cb->phys[blkno] * (long)BLKSIZE, 0);
+ if (read(fd, tmpblk.c, (unsigned)BLKSIZE) != BLKSIZE)
+ {
+ msg("Error reading back from tmp file for pasting!");
+ }
+}
+
+
+/* This function inserts text from a cut buffer, and returns the MARK where
+ * insertion ended. Return MARK_UNSET on errors.
+ */
+MARK paste(at, after, retend)
+ MARK at; /* where to insert the text */
+ int after; /* boolean: insert after mark? (rather than before) */
+ int retend; /* boolean: return end of text? (rather than start) */
+{
+ REG struct cutbuf *cb;
+ REG int i;
+
+ /* decide which cut buffer to use */
+ if (cbname >= 'A' && cbname <= 'Z')
+ {
+ cb = &named[cbname - 'A'];
+ }
+ else if (cbname >= 'a' && cbname <= 'z')
+ {
+ cb = &named[cbname - 'a'];
+ }
+ else if (cbname >= '1' && cbname <= '9')
+ {
+ cb = &annon[cbname - '1'];
+ }
+ else if (cbname == '.')
+ {
+ cb = &named[26];
+ }
+ else if (!cbname)
+ {
+ cb = annon;
+ }
+ else
+ {
+ msg("Invalid cut buffer name: \"%c", cbname);
+ cbname = '\0';
+ return MARK_UNSET;
+ }
+
+ /* make sure it isn't empty */
+ if (cb->nblks == 0)
+ {
+ if (cbname)
+ msg("Cut buffer \"%c is empty", cbname);
+ else
+ msg("Cut buffer is empty");
+ cbname = '\0';
+ return MARK_UNSET;
+ }
+ cbname = '\0';
+
+ /* adjust the insertion MARK for "after" and line-mode cuts */
+ if (cb->lnmode)
+ {
+ at &= ~(BLKSIZE - 1);
+ if (after)
+ {
+ at += BLKSIZE;
+ }
+ }
+ else if (after)
+ {
+ /* careful! if markidx(at) == 0 we might be pasting into an
+ * empty line -- so we can't blindly increment "at".
+ */
+ if (markidx(at) == 0)
+ {
+ pfetch(markline(at));
+ if (plen != 0)
+ {
+ at++;
+ }
+ }
+ else
+ {
+ at++;
+ }
+ }
+
+ /* put a copy of the "at" mark in the mark[] array, so it stays in
+ * sync with changes made via add().
+ */
+ mark[27] = at;
+
+ /* simple one-block paste? */
+ if (cb->nblks == 1)
+ {
+ /* get the block */
+ readcutblk(cb, 0);
+
+ /* isolate the text we need within it */
+ if (cb->end)
+ {
+ tmpblk.c[cb->end] = '\0';
+ }
+
+ /* insert it */
+ ChangeText
+ {
+ add(at, &tmpblk.c[cb->start]);
+ }
+ }
+ else
+ {
+ /* multi-block paste */
+
+ ChangeText
+ {
+ i = cb->nblks - 1;
+
+ /* add text from the last block first */
+ if (cb->end > 0)
+ {
+ readcutblk(cb, i);
+ tmpblk.c[cb->end] = '\0';
+ add(at, tmpblk.c);
+ i--;
+ }
+
+ /* add intervening blocks */
+ while (i > 0)
+ {
+ readcutblk(cb, i);
+ add(at, tmpblk.c);
+ i--;
+ }
+
+ /* add text from the first cut block */
+ readcutblk(cb, 0);
+ add(at, &tmpblk.c[cb->start]);
+ }
+ }
+
+ /* Reporting... */
+ rptlines = markline(mark[27]) - markline(at);
+ rptlabel = "pasted";
+
+ /* return the mark at the beginning/end of inserted text */
+ if (retend)
+ {
+ return mark[27] - 1L;
+ }
+ return at;
+}
+
+
+
+
+#ifndef NO_AT
+
+/* This function copies characters from a cut buffer into a string.
+ * It returns the number of characters in the cut buffer. If the cut
+ * buffer is too large to fit in the string (i.e. if cb2str() returns
+ * a number >= size) then the characters will not have been copied.
+ * It returns 0 if the cut buffer is empty, and -1 for invalid cut buffers.
+ */
+int cb2str(name, buf, size)
+ int name; /* the name of a cut-buffer to get: a-z only! */
+ char *buf; /* where to put the string */
+ unsigned size; /* size of buf */
+{
+ REG struct cutbuf *cb;
+ REG char *src;
+ REG char *dest;
+
+ /* decide which cut buffer to use */
+ if (name >= 'a' && name <= 'z')
+ {
+ cb = &named[name - 'a'];
+ }
+ else
+ {
+ return -1;
+ }
+
+ /* if the buffer is empty, return 0 */
+ if (cb->nblks == 0)
+ {
+ return 0;
+ }
+
+ /* !!! if not a single-block cut, then fail */
+ if (cb->nblks != 1)
+ {
+ return size;
+ }
+
+ /* if too big, return the size now, without doing anything */
+ if (cb->end - cb->start >= size)
+ {
+ return cb->end - cb->start;
+ }
+
+ /* get the block */
+ readcutblk(cb, 0);
+
+ /* isolate the string within that blk */
+ if (cb->start == 0)
+ {
+ tmpblk.c[cb->end] = '\0';
+ }
+ else
+ {
+ for (dest = tmpblk.c, src = dest + cb->start; src < tmpblk.c + cb->end; )
+ {
+ *dest++ = *src++;
+ }
+ *dest = '\0';
+ }
+
+ /* copy the string into the buffer */
+ if (buf != tmpblk.c)
+ {
+ strcpy(buf, tmpblk.c);
+ }
+
+ /* return the length */
+ return cb->end - cb->start;
+}
+#endif
diff --git a/date.c b/date.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/date.c
diff --git a/doc/cflags.doc b/doc/cflags.doc
new file mode 100644
index 0000000..9f2c35f
--- /dev/null
+++ b/doc/cflags.doc
@@ -0,0 +1,264 @@
+
+ Elvis 1.4 CFLAGS Page 9-1
+
+
+E9. CFLAGSF
+
+ Elvis uses many preprocessor symbols to control compilation.
+ Some of these control the sizes of buffers and such. The
+ "-DNO_XXXX" options remove small sets of related features.
+
+ Most Elvis users will probably want to keep all features
+ available. Minix-PC users, though, will have to sacrifice some
+ sets because otherwise Elvis would be too bulky to compile. The
+ "asld" phase of the compiler craps out.
+
+ -DM_SYSV, -DTOS, -DCOHERENT
+ These flags tell the compiler that Elvis is being compiled
+ for System-V UNIX, Atari TOS, or Coherent, respectively.
+ For other systems, the config.h file can generally figure it
+ out automatically.
+
+ -DDATE=4string5
+ DATE should be defined to be a string constant. It is
+ printed by the :version command as the compilation date of
+ the program.
+
+ It is only used in cmd1.c, and even there you may leave it
+ undefined without causing an urp.
+
+ -DNBUFS=4number5
+ Elvis keeps most of your text in a temporary file; only a
+ small amount is actually stored in RAM. This flag allows
+ you to control how much of the file can be in RAM at any
+ time. The default is 5 blocks. (See the -DBLKSIZE flag,
+ below.)
+
+ More RAM allows global changes to happen a little faster.
+ If you're just making many small changes in one section of a
+ file, though, extra RAM won't help much.
+
+ -DBLKSIZE=4number5
+ This controls the size of blocks that Elvis uses
+ internally. The value of BLKSIZE must be a power of two.
+ The default value is 1024, which allows you to edit files up
+ to almost 512K bytes long. Every time you double BLKSIZE,
+ you quadruple the size of a text file that Elvis can handle,
+ but you also cause the temporary file to grow faster.
+
+ -DTMPDIR=4string5
+ This sets the default value of the "directory" option, which
+ specifies where the temporary files should reside. The
+ value of TMPDIR must be a string, so be sure your value
+ includes the quote characters on each end.
+
+ -DEXRC=4str5, -DHMEXRC=4str5, -DSYSEXRC=4str5, -DEXINIT=4str5
+ This lets you control the names of the initialization
+ files. Their values must be strings, so be careful about
+ quoting.
+
+
+
+
+
+
+
+
+
+ Elvis 1.4 CFLAGS Page 9-2
+
+
+ EXRC is the name of the initialization file in the current
+ directory. Its default value is ".exrc" on UNIX systems --
+ the same as the real vi. For other systems, check the
+ config.h file.
+
+ HMEXRC is the name of the initialization file in your home
+ directory. By default, it is the same as EXRC. Elvis will
+ automatically prepend the name of your home directory to
+ HMEXRC at run time, so don't give a full path name.
+
+ SYSEXRC is the name of a system-wide initialization file.
+ It has no default value; if you don't define a value for it,
+ then the code that supports SYSEXRC just isn't compiled.
+ The value of SYSEXRC should be a full pathname, in quotes.
+
+ EXINIT is the name of an environment variable that can
+ contain initialization commands. Normally, its value is
+ "EXINIT".
+
+ -DKEYWORDPRG=4string5
+ This flag determines the default value of the "keywordprg"
+ option. Its value must be a string, so be careful about
+ quoting. The default value of this flag is "ref", which is
+ a C reference program.
+
+ -DCC_COMMAND=4string5 -DMAKE_COMMAND=4string5 -DERRLIST=4string5
+ These control the names of the C compiler, the "make"
+ utility, and the error output file, respectively. They are
+ only used if -DNO_ERRLIST is not given.
+
+ -DMAXMAPS=4number5
+ This controls the capacity of the key map table.
+
+ -DMAXRCLEN=4number5
+ This determines how large a .exrc file can be (measured in
+ bytes). The default is 1000 bytes. If you increase this
+ value significantly, then you may need to allocate extra
+ memory for the stack. See the "CHMEM" setting in the
+ Makefile.
+
+ -DSHELL=4string5
+ This is the default value of the "shell" option, and hence
+ the default shell used from within Elvis. This only
+ controls the default; the value you give here may be
+ overridden at run-time by setting an environment variable
+ named SHELL (or COMSPEC for MS-DOS). Its value must be a
+ string constant, so be careful about quoting.
+
+ -DTAGS=4string5
+ This sets the name of the "tags" file, which is used by the
+ :tag command. Its value must be a string constant, so be
+ careful about quoting.
+
+ -DCS_IBMPC
+ The digraph table and flipcase option will normally start
+ out empty. However, if you add -DCS_IBMPC or -DCS_LATIN1 to
+ your CFLAGS, then they will start out filled with values
+ that are appropriate for the IBM PC character set or the ISO
+
+
+
+
+
+ Elvis 1.4 CFLAGS Page 9-3
+
+
+ Latin-1 character set, respectively.
+
+ -DDEBUG
+ This adds the ":debug" and ":validate" commands, and also
+ adds many internal consistency checks. It increases the
+ size of the ".text" segment by about 6K.
+
+ -DCRUNCH
+ This flag removes some non-critical code, so that Elvis is
+ smaller. For example, it removes a short-cut from the
+ regexp package, so that text searches are slower. Also,
+ screen updates are not as efficient. A couple of obscure
+ features are disabled by this, too.
+
+ -DNO_MKEXRC
+ This removes the ":mkexrc" command, so you have to create
+ any .exrc files manually. The size of the .text segment
+ will be reduced by about 600 bytes.
+
+ -DNO_CHARATTR
+ Permanently disables the charattr option. This reduces the
+ size of your ".text" segment by about 850 bytes.
+
+ -DNO_RECYCLE
+ Normally, Elvis will recycle space (from the tmp file) which
+ contains totally obsolete text. This flag disables this
+ recycling. Without recycling, the ".text" segment is about
+ 1K smaller than it would otherwise be, but the tmp file
+ grows much faster. If you have a lot of free space on your
+ harddisk, but Elvis is too bulky to run with recycling, then
+ try it without recycling.
+
+ When using a version of Elvis that has been compiled with
+ -DNO_RECYCLE, you should be careful to avoid making many
+ small changes to a file because each individual change will
+ cause the tmp file to grow by at least 1k. Hitting "x"
+ thirty times counts as thirty changes, but typing "30x"
+ counts as one change. Also, you should occasionally do a
+ ":w" followed by a ":e" to start with a fresh tmp file.
+
+ -DNO_SENTENCE
+ Leaves out the "(" and ")" visual mode commands. Also, the
+ "[[", "]]", "{", and "}" commands will not recognize *roff
+ macros. The sections and paragraphs options go away. This
+ saves about 650 bytes in the ".text" segment.
+
+ -DNO_CHARSEARCH
+ Leaves out the visual commands which locate a given
+ character in the current line: "f", "t", "F", "T", "," and
+ ";". This saves about 900 bytes.
+
+ -DNO_EXTENSIONS
+ Leaves out the "K" and "#" visual commands. Also, the arrow
+ keys will no longer work in input mode. (Other extensions
+ are either inherent in the design of elvis, or are
+ controlled by more specific flags, or are too tiny to be
+ worth removing.) This saves about 250 bytes.
+
+
+
+
+
+
+ Elvis 1.4 CFLAGS Page 9-4
+
+
+ -DNO_MAGIC
+ Permanently disables the "magic" option, so that most
+ meta-characters in a regular expression are *NOT*
+ recognized. This saves about 3k of space in the ".text"
+ segment, because the complex regular expression code can be
+ replaced by much simpler code.
+
+ -DNO_SHOWMODE
+ Permanently disables the "showmode" option, saving about 250
+ bytes.
+
+ -DNO_CURSORSHAPE
+ Normally, Elvis tries to adjust the shape of the cursor as a
+ reminder of which mode you're in. The -DNO_CURSORSHAPE flag
+ disables this, saving about 150 bytes.
+
+ -DNO_DIGRAPH
+ To allow entry of non-ASCII characters, Elvis supports
+ digraphs. A digraph is a single (non-ASCII) character which
+ is entered as a combination of two other (ASCII)
+ characters. If you don't need to input non-ASCII
+ characters, or if your keyboard supports a better way of
+ entering non-ASCII characters, then you can disable the
+ digraph code and save about 450 bytes.
+
+ -DNO_ERRLIST
+ Elvis adds a ":errlist" command, which is useful to
+ programmers. If you don't need this feature, you can
+ disable it via the -DNO_ERRLIST flag. This will reduce the
+ .text segment by about 900 bytes, and the .bss segment by
+ about 300 bytes.
+
+ -DNO_ABBR
+ The -DNO_ABBR flag disables the ":abbr" command, and reduces
+ the size of Elvis by about 600 bytes.
+
+ -DNO_OPTCOLS
+ When Elvis displays the current options settings via the
+ ":set" command, the options are normally sorted into
+ columns. The -DNO_OPTCOLS flag causes the options to be
+ sorted across the rows, which is much simpler. The
+ -DNO_OPTCOLS flag will reduce the size of your .text segment
+ by about 500 bytes.
+
+ -DNO_MODELINE
+ This removes all support for modelines.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/doc/ctags.man b/doc/ctags.man
new file mode 100644
index 0000000..30615d1
--- /dev/null
+++ b/doc/ctags.man
@@ -0,0 +1,65 @@
+.TH CTAGS 1
+.SH NAME
+ctags - Generates "tags" and (optionally) "refs" files
+.SH SYNOPSIS
+\fBctags\fP [\fB-r\fP] \fIfiles\fP...
+.SH DESCRIPTION
+\fIctags\fP generates the "tags" and "refs" files
+from a group of C source files.
+The "tags" file is used by Elvis' ":tag" command,
+control-] command,
+and -t option.
+The "refs" file is used by the \fIref(1)\fP program.
+.PP
+Each C source file is scanned for #define statements and
+global function definitions.
+The name of the macro or function becomes the name of a tag.
+For each tag, a line is added to the "tags" file which contains:
+.RS
+.nf
+ - the name of the tag
+ - a tab character
+ - the name of the file containing the tag
+ - a tab character
+ - a way to find the particular line within the file.
+.RE
+.fi
+.PP
+The "refs" file is used by the \fIref(1)\fP program, which can be invoked
+via Elvis' K command.
+When ctags finds a global function definition,
+it copies the function header into the "refs" file.
+The first line is flush against the right margin,
+but the argument definitions are indented.
+The ref program can search the "refs" file
+.ul
+much
+faster than it could search all of the C source files.
+.PP
+The filenames list will typically be the names of all C source
+files in the current directory, like this:
+.RS
+.nf
+$ ctags -r *.[ch]
+.RE
+.fi
+.SH OPTIONS
+.IP \fB-r\fP
+This causes \fIctags\fP to generate both "tags" and "refs".
+Without \fB-r\fP, it would only generate "tags".
+.SH FILES
+.IP tags
+The "tags" file.
+.IP refs
+The "refs" file.
+.SH "SEE ALSO"
+elvis(1), refs(1)
+.SH BUGS
+This version of ctags doesn't parse ANSI-C source code very well.
+It has trouble recognizing the new-style function definitions.
+.SH AUTHOR
+.nf
+Steve Kirkendall
+kirkenda@cs.pdx.edu
+\&...uunet!tektronix!psueea!eecs!kirkenda
+.fi
diff --git a/doc/cutbufs.doc b/doc/cutbufs.doc
new file mode 100644
index 0000000..77c1e3d
--- /dev/null
+++ b/doc/cutbufs.doc
@@ -0,0 +1,198 @@
+
+ Elvis 1.4 CUT BUFFERS Page 6-1
+
+
+E6. CUT BUFFERSF
+
+ When elvis deletes text, it stores that text in a cut buffer.
+ This happens in both visual mode and EX mode. There is no
+ practical limit to how much text a cut buffer can hold.
+
+ There are 36 cut buffers: 26 named buffers ("a through "z), 9
+ anonymous buffers ("1 through "9), and 1 extra cut buffer (".).
+
+ In EX mode, the :move and :copy commands use a cut buffer to
+ temporarily hold the text to be moved/copied.
+
+
+ E6.1 FillingF
+
+ In visual mode, text is copied into a cut buffer when you use
+ the d, y, c, C, or s commands.
+
+ By default, the text goes into the "1 buffer. The text that
+ used to be in "1 gets shifted into "2, "2 gets shifted into "3, and
+ so on. The text that used to be in "9 is lost. This way, the last
+ 9 things you deleted are still accessible.
+
+ You can also put the text into a named buffer -- "a through "z.
+ To do this, you should type the buffer's name (two keystrokes: a
+ double-quote and a lowercase letter) before the d/y/c/C/s command.
+ When you do this, "1 through "9 are not affected by the cut.
+
+ You can append text to one of the named buffers. To do this,
+ type the buffer's name in uppercase (a double-quote and an
+ uppercase letter) before the d/y/c/C/s command.
+
+ The ". buffer is special. It isn't affected by the d/y/c/C/s
+ command. Instead, it stores the text that you typed in the last
+ time you were in input mode. It is used to implement the . visual
+ command, and ^A in input mode.
+
+ In EX mode (also known as colon mode), the :delete, :change, and
+ :yank commands all copy text into a cut buffer. Like the visual
+ commands, these EX commands normally use the "1 buffer, but you can
+ use one of the named buffers by giving its name after the command.
+ For example,
+
+ :20,30y a
+
+ will copy lines 20 through 30 into cut buffer "a.
+
+ You can't directly put text into the ". buffer, or the "2
+ through "9 buffers.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Elvis 1.4 CUT BUFFERS Page 6-2
+
+
+ E6.2 Pasting from a Cut BufferF
+
+ There are two styles of pasting: line-mode and character-mode.
+ If a cut buffer contains whole lines (from a command like "dd")
+ then line-mode pasting is used; if it contains partial lines (from
+ a command like "dw") then character-mode pasting is used. The EX
+ commands always cut whole lines.
+
+ Character-mode pasting causes the text to be inserted into the
+ line that the cursor is on.
+
+ Line-mode pasting inserts the text on a new line above or below
+ the line that the cursor is on. It doesn't affect the cursor's
+ line at all.
+
+ In visual mode, the p and P commands insert text from a cut
+ buffer. Uppercase P will insert it before the cursor, and
+ lowercase p will insert it after the cursor. Normally, these
+ commands will paste from the "1 buffer, but you can specify any
+ other buffer to paste from. Just type its name (a double-quote and
+ another character) before you type the P or p.
+
+ In EX mode, the (pu)t command pastes text after a given line.
+ To paste from a buffer other that "1, enter its name after the
+ command.
+
+
+ E6.3 MacrosF
+
+ The contents of a named cut buffer can be executed as a series
+ of ex/vi commands.
+
+ To put the instructions into the cut buffer, you must first
+ insert them into the file, and then delete them into a named cut
+ buffer.
+
+ To execute a cut buffer's contents as EX commands, you should
+ give the EX command "@" and the name of the buffer. For example,
+ :@z will execute "z as a series of EX commands.
+
+ To execute a cut buffer's contents as visual commands, you
+ should give the visual command "@" and the letter of the buffer's
+ name. The visual "@" command is different from the EX "@"
+ command. They interpret the cut buffer's contents differently.
+
+ The visual @ command can be rather finicky. Each character in
+ the buffer is interpretted as a keystroke. If you load the
+ instructions into the cut buffer via a "zdd command, then the
+ newline character at the end of the line will be executed just like
+ any other character, so the cursor would be moved down 1 line. If
+ you don't want the cursor to move down 1 line at the end of each @z
+ command, then you should load the cut buffer by saying 0"zD
+ instead.
+
+ Although cut buffers may hold any amount of text, elvis can only
+ -1execute-0 small buffers. For EX mode, the buffer is limited to about
+ 1k bytes. For visual mode, the buffer is limited to about 80
+ bytes. If a buffer is too large to execute, an error message is
+
+
+
+
+
+ Elvis 1.4 CUT BUFFERS Page 6-3
+
+
+ displayed.
+
+ You can't nest @ commands. You can't run @ commands from your
+ .exrc file, or any other :source file either. Similarly, you can't
+ run a :source command from within an @ command. Hopefully, these
+ restrictions will be lifted in a later version.
+
+
+ E6.4 The Effect of Switching FilesF
+
+ When elvis first starts up, all cut buffers are empty. When you
+ switch to a different file (via the :n or :e commands perhaps) the
+ 9 anonymous cut buffers are emptied again, but the other 27 buffers
+ retain their text.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/doc/differ.doc b/doc/differ.doc
new file mode 100644
index 0000000..207818d
--- /dev/null
+++ b/doc/differ.doc
@@ -0,0 +1,198 @@
+
+ Elvis 1.4 DIFFERENCES BETWEEN ELVIS & BSD VI/EX Page 7-1
+
+
+E7. DIFFERENCES BETWEEN ELVIS & BSD VI/EXF
+
+
+ E7.1 ExtensionsF
+
+ :mkexrc
+ :mk
+
+ This EX command saves the current :set and :map configurations
+ in the ".exrc" file in your current directory.
+
+ :Next
+ :previous
+ :N
+ :pre
+
+ These commands move backwards through the args list.
+
+ zz
+
+ In visual command mode, the (lowercase) "zz" command will center
+ the current line on the screen, like "z=".
+
+ .
+
+ The default count value for . is the same as the previous
+ command which . is meant to repeat. However, you can supply a new
+ count if you wish. For example, after "3dw", "." will delete 3
+ words, but "5." will delete 5 words.
+
+ ".
+
+ The text which was most recently input (via a "cw" command, or
+ something similar) is saved in a cut buffer called ". (which is a
+ pretty hard name to write in an English sentence).
+
+ K
+
+ In visual command mode, you can move the cursor onto a word and
+ press shift-K to have Elvis run a reference program to look that
+ word up. This command alone is worth the price of admission! See
+ the ctags and ref programs.
+
+ #
+
+ In visual command mode, you can move the cursor onto a number
+ and then hit ## or #+ to increment that number by 1. To increment
+ it by a larger amount, type in the increment value before hitting
+ the initial #. The number can also be decremented or set by
+ hitting #- or #=, respectively.
+
+ input
+
+ You can backspace past the beginning of the line.
+
+
+
+
+
+
+
+
+
+ Elvis 1.4 DIFFERENCES BETWEEN ELVIS & BSD VI/EX Page 7-2
+
+
+ The arrow keys work in input mode.
+
+ If you type control-A, then the text that you input last time is
+ inserted. You will remain in input mode, so you can backspace over
+ part of it, or add more to it. (This is sort of like control-@ on
+ the real vi, except that control-A really works.)
+
+ Control-P will insert the contents of the cut buffer.
+
+ Real vi can only remember up to 128 characters of input, but
+ Elvis can remember any amount.
+
+ The ^T and ^D keys can adjust the indent of a line no matter
+ where the cursor happens to be in that line.
+
+ You can save your file and exit Elvis directly from input mode
+ by hitting control-Z twice.
+
+ Elvis supports digraphs as a way to enter non-ASCII characters.
+
+ :set inputmode
+ :se im
+
+ If you set this flag in your .exrc file, then elvis will start
+ up in input mode instead of visual command mode.
+
+ :set charattr
+ :se ca
+
+ Elvis can display "backslash-f" style character attributes on
+ the screen as you edit. The following example shows the recognized
+ atributes:
+
+ normal \fBboldface\fR \fIitalics\fR \fUunderlined\fR normal
+
+ NOTE: you must compile elvis without the -DNO_CHARATTR flag for
+ this to work.
+
+ :set sync
+ :se sy
+
+ After a crash, you can usually recover the altered form of the
+ file from the temporary file that Elvis uses. With the sync option
+ turned on, the odds are shifted a little more in your favor because
+ Elvis will perform a sync() call after each change has been written
+ to the temporary file.
+
+ cursor shape
+
+ Elvis changes the shape of the cursor to indicate which mode
+ you're in, if your terminal's termcap entry includes the necessary
+ capabilities.
+
+ :set hideformat
+ :se hf
+
+
+
+
+
+
+
+
+ Elvis 1.4 DIFFERENCES BETWEEN ELVIS & BSD VI/EX Page 7-3
+
+
+ This option hides format control lines. (They are displayed on
+ the screen as blank lines.)
+
+ :errlist
+ *
+ elvis -m
+
+ Elvis is clever enough to parse the error messages emitted by
+ many compilers. To use this feature, you should collect your
+ compiler's error messages into a file called "errlist"; elvis will
+ read this file, determine which source file caused the error
+ messages, start editing that file, move the cursor to the line
+ where the error was detected, and display the error message on the
+ status line. Nifty!
+
+
+ E7.2 OmissionsF
+
+ The replace mode is a hack. It doesn't save the text that it
+ overwrites.
+
+ Long lines are displayed differently -- where the real vi would
+ wrap a long line onto several rows of the screen, Elvis simply
+ displays part of the line, and allows you to scroll the screen
+ sideways to see the rest of it.
+
+ The ":preserve" and ":recover" commands are missing. So is the
+ -r flag. I've never had a good reason to use ":preserve", and
+ since ":recover" is used so rarely I decided to implement it as a
+ separate program. There's no need to load the recovery code into
+ memory every time you edit a file, I figured.
+
+ LISP support is missing.
+
+ Due to naming conventions used for the temporary files, Elvis
+ can be creating no more that one new file per directory at any
+ given time. Any number of existing files can be edited at the same
+ time on multitasking computer systems, but only one new file can be
+ created simultaneously per directory. To relieve this problem, you
+ would have to edit tmp.c and virec.c This is expected to be done in
+ version 1.5
+
+ Autoindent mode acts a little different from the real vi. It is
+ still quite useful, but if you frequently use both vi and elvis
+ then the differences may be annoying. Autoindent is -1gradually-0
+ improving.
+
+ The visual "put" command cannot be repeated by hitting the .
+ key.
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/doc/elvis.man b/doc/elvis.man
new file mode 100644
index 0000000..dd901a8
--- /dev/null
+++ b/doc/elvis.man
@@ -0,0 +1,89 @@
+.TH ELVIS 1
+.SH NAME
+elvis, ex, vi, view, input - The editor
+.SH SYNOPSIS
+\fBelvis\fP [\fIflags\fP] [\fB+\fP\fIcmd\fP] [\fIfiles\fP...]
+.SH DESCRIPTION
+\fIElvis\fP is a text editor which emulates \fIvi\fP/\fIex\fP.
+.PP
+On systems which pass the program name as an argument, such as Unix and Minix,
+you may also install \fIelvis\fP under the names "ex", "vi", "view", and "input".
+These extra names would normally be links to elvis;
+see the "ln" shell command.
+.PP
+When \fIelvis\fP is invoked as "vi",
+it behaves exactly as though it was invoked as "elvis".
+However, if you invoke \fIelvis\fP as "view",
+then the readonly option is set as though you had given it the "-R" flag.
+If you invoke \fIelvis\fP as "ex",
+then \fIelvis\fP will start up in the colon command mode
+instead of the visual command mode,
+as though you had given it the "-e" flag.
+If you invoke \fIelvis\fP as "input" or "edit",
+then \fIelvis\fP will start up in input mode,
+as though the "-i" flag was given.
+.SH OPTIONS
+.IP \fB-r\fP
+To the real vi, this flag means that a previous edit should be recovered.
+\fIElvis\fP, though, has a separate program, called \fIvirec(1)\fP, for recovering
+files.
+When you invoke \fIelvis\fP with -r, \fIelvis\fP will tell you to run \fIvirec\fP.
+.IP \fB-R\fP
+This sets the "readonly" option,
+so you won't accidentally overwrite a file.
+.IP "\fB-t\fP \fItag\fP"
+This causes \fIelvis\fP to start editing at the given tag.
+.IP "\fB-m\fP [\fIfile\fP]"
+\fIElvis\fP will search through \fIfile\fP for something that looks like
+an error message from a compiler.
+It will then begin editing the source file that caused the error,
+with the cursor sitting on the line where the error was detected.
+If you don't explicitly name a \fIfile\fP, then "errlist" is assumed.
+.IP \fB-e\fP
+\fIElvis\fP will start up in colon command mode.
+.IP \fB-v\fP
+\fIElvis\fP will start up in visual command mode.
+.IP \fB-i\fP
+\fIElvis\fP will start up in input mode.
+.IP \fB+\fP\fIcommand\fP
+If you use the +\fIcommand\fP parameter,
+then after the first file is loaded
+\fIcommand\fP is executed as an EX command.
+A typical example would be "elvis +237 foo",
+which would cause \fIelvis\fP to start editing foo and
+then move directly to line 237.
+.SH FILES
+.IP /tmp/elv*
+During editing,
+\fIelvis\fP stores text in a temporary file.
+For UNIX, this file will usually be stored in the /tmp directory,
+and the first three characters will be "elv".
+For other systems, the temporary files may be stored someplace else;
+see the version-specific section of the documentation.
+.IP tags
+This is the database used by the \fI:tags\fP command and the \fB-t\fP option.
+It is usually created by the \fIctags(1)\fP program.
+.SH "SEE ALSO"
+ctags(1), ref(1), virec(1)
+.PP
+\fIElvis - A Clone of Vi/Ex\fP, the complete \fIelvis\fP documentation.
+.SH BUGS
+There is no LISP support.
+Certain other features are missing, too.
+.PP
+Auto-indent mode is not quite compatible with the real vi.
+Among other things, 0^D and ^^D don't do what you might expect.
+.PP
+Long lines are displayed differently.
+The real vi wraps long lines onto multiple rows of the screen,
+but \fIelvis\fP scrolls sideways.
+.SH AUTHOR
+.nf
+Steve Kirkendall
+kirkenda@cs.pdx.edu
+\&...uunet!tektronix!psueea!eecs!kirkenda
+.fi
+.PP
+Many other people have worked to port \fIelvis\fP to various operating systems.
+To see who deserves credit, run the \fI:version\fP command from within \fIelvis\fP,
+or look in the system-specific section of the complete documentation.
diff --git a/doc/environ.doc b/doc/environ.doc
new file mode 100644
index 0000000..f40d1f1
--- /dev/null
+++ b/doc/environ.doc
@@ -0,0 +1,66 @@
+
+ Elvis 1.4 ENVIRONMENT VARIABLES Page 11-1
+
+
+E11. ENVIRONMENT VARIABLESF
+
+ Elvis examines several environment variables when it starts up.
+ The values of these variables are used internally for a variety of
+ purposes. You don't need to define all of these; on most systems,
+ Elvis only requires TERM to be defined. On MS-DOS systems, even
+ that is optional.
+
+
+ E11.1 TERM, TERMCAPF
+
+ TERM tells Elvis the name of the termcap entry to use. TERMCAP
+ may contain either the entire termcap entry, or the full pathname
+ of the termcap file to search through.
+
+
+ E11.2 TMP, TEMPF
+
+ These only work for MS-DOS and Atari TOS. Either of these
+ variables may be used to set the "directory" option, which controls
+ where temporary files are stored. If you define them both, then
+ TMP is used, and TEMP is ignored.
+
+
+ E11.3 EXINITF
+
+ This variable may contain a colon-mode command, which will be
+ executed after all of the ".exrc" files but before interactive
+ editing begins.
+
+
+ E11.4 SHELL, COMSPECF
+
+ You can use COMSPEC in MS-DOS, or SHELL in any other system, to
+ specify which shell should be used for executing commands and
+ expanding wildcards.
+
+
+ E11.5 HOMEF
+
+ This variable should give the full pathname of your home
+ directory. Elvis needs to know the name of your home directory so
+ it can locate the ".exrc" file there.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/doc/ex.doc b/doc/ex.doc
new file mode 100644
index 0000000..974a295
--- /dev/null
+++ b/doc/ex.doc
@@ -0,0 +1,528 @@
+
+ Elvis 1.4 COLON MODE COMMANDS Page 3-1
+
+
+E3. COLON MODE COMMANDSF
+
+ -1lines command arguments -0
+ [line] -1a-0ppend
+ -1ar-0gs [files]
+ -1cc-0 [files]
+ -1cd-0 [directory]
+ [line][,line] -1c-0hange
+ -1chd-0ir [directory]
+ [line][,line] -1co-0py line
+ [line][,line] -1d-0elete ["x]
+ -1di-0graph[!] [XX [Y]]
+ -1e-0dit[!] [file]
+ -1er-0rlist[!] [errlist]
+ -1e-0x[!] [file]
+ -1f-0ile [file]
+ [line][,line] -1g-0lobal /regexp/ command
+ [line] -1i-0nsert
+ [line][,line] -1j-0oin
+ [line][,line] -1l-0ist
+ -1mak-0e [target]
+ -1ma-0p[!] key mapped_to
+ [line] mar-1k-0 x
+ -1mk-0exrc
+ [line][,line] -1m-0ove line
+ -1n-0ext[!] [files]
+ -1N-0ext[!]
+ [line][,line] -1nu-0mber
+ -1pre-0vious[!]
+ [line][,line] -1p-0rint
+ [line] -1pu-0t ["x]
+ -1q-0uit[!]
+ [line] -1r-0ead file
+ -1rew-0ind[!]
+ -1se-0t [options]
+ -1so-0urce file
+ [line][,line] -1s-0ubstitute /regexp/replacement/[p][g][c]
+ -1ta-0g[!] tagname
+ [line][,line] -1t-0o line
+ -1u-0ndo
+ -1unm-0ap[!] key
+ -1ve-0rsion
+ [line][,line] -1v-0global /regexp/ command
+ -1vi-0sual
+ -1wq-0
+ [line][,line] -1w-0rite[!] [[>>]file]
+ -1x-0it[!]
+ [line][,line] -1y-0ank ["x]
+ [line][,line] -1!-0 command
+ [line][,line] -1<-0
+ [line][,line] -1=-0
+ [line][,line] -1>-0
+ [line][,line] -1&-0
+ -1@-0 "x
+
+
+
+
+
+
+
+
+
+ Elvis 1.4 COLON MODE COMMANDS Page 3-2
+
+
+ To use colon mode commands, you must switch from visual command
+ mode to colon command mode. The visual mode commands to do this
+ are ":" for a single colon command, or "Q" for many colon mode
+ commands.
+
+
+ E3.1 Line SpecifiersF
+
+ Line specifiers are always optional. The first line specifier
+ of most commands usually defaults to the current line. The second
+ line specifier usually defaults to be the same as the first line
+ specifier. Exceptions are :write, :global, and :vglobal, which act
+ on all lines of the file by default, and :!, which acts on no lines
+ by default.
+
+ Line specifiers consist of an absolute part and a relative
+ part. The absolute part of a line specifier may be either an
+ explicit line number, a mark, a dot to denote the current line, a
+ dollar sign to denote the last line of the file, or a forward or
+ backward search.
+
+ An explicit line number is simply a decimal number, expressed as
+ a string of digits.
+
+ A mark is typed in as an apostrophe followed by a letter. Marks
+ must be set before they can be used. You can set a mark in visual
+ command mode by typing "m" and a letter, or you can set it in colon
+ command mode via the "mark" command.
+
+ A forward search is typed in as a regular expression surrounded
+ by slash characters; searching begins at the default line. A
+ backward search is typed in as a regular expression surrounded by
+ question marks; searching begins at the line before the default
+ line.
+
+ If you omit the absolute part, then the default line is used.
+
+ The relative part of a line specifier is typed as a "+" or "-"
+ character followed by a decimal number. The number is added to or
+ subtracted from the absolute part of the line specifier to produce
+ the final line number.
+
+ As a special case, the % character may be used to specify all
+ lines of the file. It is roughly equivelent to saying 1,$. This
+ can be a handy shortcut.
+
+ Some examples:
+
+ :p print the current line
+ :37p print line 37
+ :'gp print the line which contains mark g
+ :/foo/p print the next line that contains "foo"
+ :$p print the last line of the file
+ :20,30p print lines 20 through 30
+ :1,$p print all lines of the file
+ :%p print all lines of the file
+ :/foo/-2,+4p print 5 lines around the next "foo"
+
+
+
+
+
+
+ Elvis 1.4 COLON MODE COMMANDS Page 3-3
+
+
+ E3.2 Text Entry CommandsF
+
+ [line] append
+ [line][,line] change ["x]
+ [line] insert
+
+ The -1a-0ppend command inserts text after the specified line.
+
+ The -1i-0nsert command inserts text before the specified line.
+
+ The -1c-0hange command copies the range of lines into a cut buffer,
+ deletes them, and inserts new text where the old text used to be.
+
+ For all of these commands, you indicate the end of the text
+ you're inserting by hitting ^D or by entering a line which contains
+ only a period.
+
+
+ E3.3 Cut & Paste CommandsF
+
+ [line][,line] delete ["x]
+ [line][,line] yank ["x]
+ [line] put ["x]
+ [line][,line] copy line
+ [line][,line] to line
+ [line][,line] move line
+
+ The -1d-0elete command copies the specified range of lines into a
+ cut buffer, and then deletes them.
+
+ The -1y-0ank command copies the specified range of lines into a cut
+ buffer, but does *not* delete them.
+
+ The -1pu-0t command inserts text from a cut buffer after the
+ specified line.
+
+ The -1co-0py and -1t-0o commands yank the specified range of lines and
+ then immediately paste them after some other line.
+
+ The -1m-0ove command deletes the specified range of lines and then
+ immediately pastes them after some other line. If the destination
+ line comes after the deleted text, then it will be adjusted
+ automatically to account for the deleted lines.
+
+
+ E3.4 Display Text CommandsF
+
+ [line][,line] print
+ [line][,line] list
+ [line][,line] number
+
+ The -1p-0rint command displays the specified range of lines.
+
+ The -1nu-0mber command displays the lines, with line numbers.
+
+
+
+
+
+
+
+
+
+ Elvis 1.4 COLON MODE COMMANDS Page 3-4
+
+
+ The -1l-0ist command also displays them, but it is careful to make
+ control characters visible.
+
+
+ E3.5 Global Operations CommandsF
+
+ [line][,line] global /regexp/ command
+ [line][,line] vglobal /regexp/ command
+
+ The -1g-0lobal command searches through the lines of the specified
+ range (or through the whole file if no range is specified) for
+ lines that contain a given regular expression. It then moves the
+ cursor to each of these lines and runs some other command on them.
+
+ The -1v-0global command is similar, but it searches for lines that
+ -1don't-0 contain the regular expression.
+
+
+ E3.6 Line Editing CommandsF
+
+ [line][,line] join
+ [line][,line] ! program
+ [line][,line] <
+ [line][,line] >
+ [line][,line] substitute /regexp/replacement/[p][g][c]
+ [line][,line] &
+
+ The -1j-0oin command catenates all lines in the specified range
+ together to form one big line. If only a single line is specified,
+ then the following line is catenated onto it.
+
+ The -1!-0 command runs an external filter program, and feeds the
+ specified range of lines to it's stdin. The lines are then
+ replaced by the output of the filter. A typical example would be
+ ":'a,'z!sort" to sort the lines 'a,'z.
+
+ The -1<-0 and -1>-0 commands shift the specified range of lines left or
+ right, normally by the width of 1 tab character. The "shiftwidth"
+ option determines the shifting amount.
+
+ The -1s-0ubstitute command finds the regular expression in each
+ line, and replaces it with the replacement text. The "p" option
+ causes the altered lines to be printed. The "g" option permits all
+ instances of the regular expression to be found & replaced.
+ (Without "g", only the first occurrence in each line is replaced.)
+ The "c" option asks for confirmation before each substitution.
+
+ The -1&-0 command repeats the previous substitution command.
+ Actually, "&" is equivelent to "s//~/" with the same options as
+ last time. It searches for the last regular expression that you
+ specified for any purpose, and replaces it with the the same text
+ that was used in the previous substitution.
+
+
+
+
+
+
+
+
+
+
+
+ Elvis 1.4 COLON MODE COMMANDS Page 3-5
+
+
+ E3.7 Undo CommandF
+
+ undo
+
+ The -1u-0ndo command restores the file to the state it was in before
+ your most recent command which changed text.
+
+
+ E3.8 Configuration & Status CommandsF
+
+ map[!] [key mapped_to]
+ unmap[!] key
+ abbr [word expanded_form_of_word]
+ unabbr word
+ digraph[!] [XX [Y]]
+ set [options]
+ mkexrc
+ [line] mark "x
+ visual
+ version
+ [line][,line] =
+ file [file]
+ source file
+ @ "x
+
+ The -1ma-0p command allows you to configure Elvis to recognize your
+ function keys, and treat them as though they transmitted some other
+ sequence of characters. Normally this mapping is done only when in
+ the visual command mode, but with the [!] present it will map keys
+ under all contexts. When this command is given with no arguments,
+ it prints a table showing all mappings currently in effect. When
+ called with two arguments, the first is the sequence that your
+ function key really sends, and the second is the sequence that you
+ want Elvis to treat it as having sent.
+
+ The -1unm-0ap command removes key definitions that were made via the
+ map command.
+
+ The -1ab-0br command is used to define/list a table of
+ abbreviations. The table contains both the abbreviated form and
+ the fully spelled-out form. When you're in visual input mode, and
+ you type in the abbreviated form, Elvis will replace the
+ abbreviated form with the fully spelled-out form. When this
+ command is called without arguments, it lists the table; with two
+ or more arguments, the first argument is taken as the abbreviated
+ form, and the rest of the command line is the fully-spelled out
+ form.
+
+ The -1una-0bbr command deletes entries from the abbr table.
+
+ The -1di-0graph command allows you to display the set of digraphs
+ that Elvis is using, or add/remove a digraph. To list the set of
+ digraphs, use the digraph command with no arguments. To add a
+ digraph, you should give the digraph command two arguments. The
+ first argument is the two ASCII characters that are to be combined;
+ the second is the non-ASCII character that they represent. The
+ non-ASCII character's most significant bit is automatically set by
+ the digraph command, unless to append a ! to the command name.
+
+
+
+
+
+ Elvis 1.4 COLON MODE COMMANDS Page 3-6
+
+
+ Removal of a digraph is similar to adding a digraph, except that
+ you should leave off the second argument.
+
+ The -1se-0t command allows you examine or set various options. With
+ no arguments, it displays the values of options that have been
+ changed. With the single argument "all" it displays the values of
+ all options, regardless of whether they've been explicitly set or
+ not. Otherwise, the arguments are treated as options to be set.
+
+ The -1mk-0exrc command saves the current configuration to a file
+ called ".exrc" in the current directory.
+
+ The mar-1k-0 command defines a named mark to refer to a specific
+ place in the file. This mark may be used later to specify lines
+ for other commands.
+
+ The -1vi-0sual command puts the editor into visual mode. Instead of
+ emulating ex, Elvis will start emulating vi.
+
+ The -1ve-0rsion command tells you that what version of Elvis this
+ is.
+
+ The -1=-0 command tells you what line you specified, or, if you
+ specified a range of lines, it will tell you both endpoints and the
+ number of lines included in the range.
+
+ The -1f-0ile command tells you the name of the file, whether it has
+ been modified, the number of lines in the file, and the current
+ line number. You can also use it to change the name of the current
+ file.
+
+ The -1so-0urce command reads a sequence of colon mode commands from
+ a file, and interprets them.
+
+ The -1@-0 command executes the contents of a cut-buffer as EX
+ commands.
+
+
+ E3.9 Multiple File CommandsF
+
+ args [files]
+ next[!] [files]
+ Next[!]
+ previous[!]
+ rewind[!]
+
+ When you invoke Elvis from your shell's command line, any
+ filenames that you give to Elvis as arguments are stored in the
+ args list. The -1ar-0gs command will display this list, or define a
+ new one.
+
+ The -1n-0ext command switches from the current file to the next one
+ in the args list. You may specify a new args list here, too.
+
+ The -1N-0ext and -1pre-0vious commands (they're really aliases for the
+ same command) switch from the current file to the preceding file in
+ the args list.
+
+
+
+
+
+
+ Elvis 1.4 COLON MODE COMMANDS Page 3-7
+
+
+ The -1rew-0ind command switches from the current file to the first
+ file in the args list.
+
+
+ E3.10 Switching FilesF
+
+ edit[!] [file]
+ tag[!] tagname
+
+ The -1e-0dit command allows to switch from the current file to some
+ other file. This has nothing to do with the args list, by the
+ way.
+
+ The -1ta-0g command looks up a given tagname in a file called
+ "tags". This tells it which file the tag is in, and how to find it
+ in that file. Elvis then switches to the tag's file and finds the
+ tag.
+
+
+ E3.11 Working with a CompilerF
+
+ cc [files]
+ make [target]
+ errlist[!] [errlist]
+
+ The -1cc-0 and -1mak-0e commands execute your compiler or "make" utility
+ and redirect any error messages into a file called "errlist". By
+ default, cc is run on the current file. (You should write it
+ before running cc.) The contents of the "errlist" file are then
+ scanned for error messages. If an error message is found, then the
+ cursor is moved to the line where the error was detected, and the
+ description of the error is displayed on the status line.
+
+ After you've fixed one error, the -1er-0rlist command will move the
+ cursor to the next error. In visual command mode, hitting `*' will
+ do this, too.
+
+ You can also create an "errlist" file from outside of Elvis, and
+ use "elvis -m" to start elvis and have the cursor moved to the
+ first error. Note that you don't need to supply a filename with
+ "elvis -m" because the error messages always say which source file
+ an error is in.
+
+ Note: When you use errlist repeatedly to fix several errors in a
+ single file, it will attempt to adjust the reported line numbers to
+ allow for lines that you have inserted or deleted. These
+ adjustments are made with the assumption that you will work though
+ the file from the beginning to the end.
+
+
+ E3.12 Exit CommandsF
+
+ quit[!]
+ wq
+ xit
+
+
+
+
+
+
+
+
+ Elvis 1.4 COLON MODE COMMANDS Page 3-8
+
+
+ The -1q-0uit command exits from the editor without saving your
+ file.
+
+ The -1wq-0 command writes your file out, then then exits.
+
+ The -1x-0it command is similar to the -1wq-0 command, except that -1x-0it
+ won't bother to write your file if you haven't modified it.
+
+
+ E3.13 File I/O CommandsF
+
+ [line] read file
+ [line][,line] write[!] [[>>]file]
+
+ The -1r-0ead command gets text from another file and inserts it
+ after the specified line. It can also read the output of a
+ program; simply precede the program name by a '!' and use it in
+ place of the file name.
+
+ The -1w-0rite command writes the whole file, or just part of it, to
+ some other file. The !, if present, will permit the lines to be
+ written even if you've set the readonly option. If you precede the
+ filename by >> then the lines will be appended to the file.
+
+
+ E3.14 Directory CommandsF
+
+ cd [directory]
+ chdir [directory]
+ shell
+
+ The -1cd-0 and -1chd-0ir commands (really two names for one command)
+ switch the current working directory.
+
+ The -1sh-0ell command starts an interactive shell.
+
+
+ E3.15 Debugging CommandsF
+
+ [line][,line] debug[!]
+ validate[!]
+
+ These commands are only available if you compile Elvis with the
+ -DDEBUG flag.
+
+ The de-1b-0ug command lists statistics for the blocks which contain
+ the specified range of lines. If the ! is present, then the
+ contents of those blocks is displayed, too.
+
+ The -1va-0lidate command checks certain variables for internal
+ consistency. Normally it doesn't output anything unless it detects
+ a problem. With the !, though, it will always produce *some*
+ output.
+
+
+
+
+
+
+
+
+
diff --git a/doc/index.doc b/doc/index.doc
new file mode 100644
index 0000000..9dafd99
--- /dev/null
+++ b/doc/index.doc
@@ -0,0 +1,264 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @@@@@@@ @ @ @ @@@ @@@@@
+ @ @ @ @ @ @ @
+ @ @ @ @ @ @
+ @@@@@ @ @ @ @ @@@@@
+ @ @ @ @ @ @
+ @ @ @ @ @ @ @
+ @@@@@@@ @@@@@@@ @ @@@ @@@@@
+
+ - a clone of vi/ex -
+ version 1.4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Author: Steve Kirkendall
+ 14407 SW Teal Blvd., Apt C
+ Beaverton, OR 97005
+
+E-Mail: kirkenda@cs.pdx.edu
+
+Phone: (503) 642-9905
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ---=: C O N T E N T S :=---
+
+
+
+ 1. INTRODUCTION ................................................... 1-1
+ 1.1 Compiling ................................................... 1-1
+ 1.2 Overview of Elvis ........................................... 1-1
+
+ 2. VISUAL MODE COMMANDS ........................................... 2-1
+ 2.1 Input Mode .................................................. 2-4
+ 2.2 Arrow keys in Input Mode .................................... 2-4
+ 2.3 Digraphs .................................................... 2-5
+ 2.4 Abbreviations ............................................... 2-5
+ 2.5 Auto-Indent ................................................. 2-5
+
+ 3. COLON MODE COMMANDS ............................................ 3-1
+ 3.1 Line Specifiers ............................................. 3-2
+ 3.2 Text Entry Commands ......................................... 3-3
+ 3.3 Cut & Paste Commands ........................................ 3-3
+ 3.4 Display Text Commands ....................................... 3-3
+ 3.5 Global Operations Commands .................................. 3-4
+ 3.6 Line Editing Commands ....................................... 3-4
+ 3.7 Undo Command ................................................ 3-5
+ 3.8 Configuration & Status Commands ............................. 3-5
+ 3.9 Multiple File Commands ...................................... 3-6
+ 3.10 Switching Files ............................................ 3-7
+ 3.11 Working with a Compiler .................................... 3-7
+ 3.12 Exit Commands .............................................. 3-7
+ 3.13 File I/O Commands .......................................... 3-8
+ 3.14 Directory Commands ......................................... 3-8
+ 3.15 Debugging Commands ......................................... 3-8
+
+ 4. REGULAR EXPRESSIONS ............................................ 4-1
+ 4.1 Syntax ...................................................... 4-1
+ 4.2 Options ..................................................... 4-1
+ 4.3 Substitutions ............................................... 4-1
+ 4.4 Examples .................................................... 4-2
+
+ 5. OPTIONS ........................................................ 5-1
+ 5.1 AutoIndent .................................................. 5-2
+ 5.2 AutoPrint ................................................... 5-2
+ 5.3 AutoWrite ................................................... 5-2
+ 5.4 CC .......................................................... 5-2
+ 5.5 CharAttr .................................................... 5-2
+ 5.6 COlumns ..................................................... 5-3
+ 5.7 DIGraph ..................................................... 5-3
+ 5.8 DIRectory ................................................... 5-3
+ 5.9 EDcompatible ................................................ 5-3
+ 5.10 ErrorBells ................................................. 5-3
+ 5.11 ExRefresh .................................................. 5-3
+ 5.12 FlipCase ................................................... 5-4
+ 5.13 HideFormat ................................................. 5-4
+ 5.14 IgnoreCase ................................................. 5-4
+ 5.15 InputMode .................................................. 5-4
+ 5.16 KeyTime .................................................... 5-4
+ 5.17 KeywordPrg ................................................. 5-5
+ 5.18 LiNes ...................................................... 5-5
+ 5.19 LIst ....................................................... 5-5
+
+
+
+
+
+
+
+
+ 5.20 MAgic ...................................................... 5-6
+ 5.21 MaKe ....................................................... 5-6
+ 5.22 ModeLine ................................................... 5-6
+ 5.23 PAragraphs ................................................. 5-6
+ 5.24 ReadOnly ................................................... 5-6
+ 5.25 REport ..................................................... 5-7
+ 5.26 SCroll ..................................................... 5-7
+ 5.27 SEctions ................................................... 5-7
+ 5.28 SHell ...................................................... 5-7
+ 5.29 ShiftWidth ................................................. 5-7
+ 5.30 ShowMatch .................................................. 5-8
+ 5.31 ShowMoDe ................................................... 5-8
+ 5.32 SideScroll ................................................. 5-8
+ 5.33 SYnc ....................................................... 5-8
+ 5.34 TabStop .................................................... 5-8
+ 5.35 TErm ....................................................... 5-9
+ 5.36 VBell ...................................................... 5-9
+ 5.37 WArn ....................................................... 5-9
+ 5.38 WrapMargin ................................................. 5-9
+ 5.39 WrapScan ................................................... 5-9
+
+ 6. CUT BUFFERS .................................................... 6-1
+ 6.1 Filling ..................................................... 6-1
+ 6.2 Pasting from a Cut Buffer ................................... 6-2
+ 6.3 Macros ...................................................... 6-2
+ 6.4 The Effect of Switching Files ............................... 6-3
+
+ 7. DIFFERENCES BETWEEN ELVIS & BSD VI/EX .......................... 7-1
+ 7.1 Extensions .................................................. 7-1
+ 7.2 Omissions ................................................... 7-3
+
+ 8. INTERNAL ....................................................... 8-1
+ 8.1 The temporary file .......................................... 8-1
+ 8.2 Implementation of Editing ................................... 8-1
+ 8.3 Marks and the Cursor ........................................ 8-2
+ 8.4 Colon Command Interpretation ................................ 8-2
+ 8.5 Screen Control .............................................. 8-2
+ 8.6 Portability ................................................. 8-3
+
+ 9. CFLAGS ......................................................... 9-1
+
+ 10. TERMCAP ....................................................... 10-1
+ 10.1 Required numeric fields .................................... 10-1
+ 10.2 Required string fields ..................................... 10-1
+ 10.3 Boolean fields ............................................. 10-1
+ 10.4 Optional string fields ..................................... 10-1
+ 10.5 Optional strings received from the keyboard ................ 10-1
+ 10.6 Optional fields that describe character attributes ......... 10-2
+ 10.7 Optional fields that affect the cursor's shape ............. 10-2
+ 10.8 An example ................................................. 10-2
+
+ 11. ENVIRONMENT VARIABLES ......................................... 11-1
+ 11.1 TERM, TERMCAP .............................................. 11-1
+ 11.2 TMP, TEMP .................................................. 11-1
+ 11.3 EXINIT ..................................................... 11-1
+ 11.4 SHELL, COMSPEC ............................................. 11-1
+ 11.5 HOME ....................................................... 11-1
+
+
+
+
+
+
+
+
+
+ 12. VERSIONS ...................................................... 12-1
+ 12.1 BSD UNIX ................................................... 12-1
+ 12.2 System-V UNIX .............................................. 12-1
+ 12.3 SCO Xenix .................................................. 12-2
+ 12.4 Minix ...................................................... 12-2
+ 12.5 Coherent ................................................... 12-2
+ 12.6 MS-DOS ..................................................... 12-3
+ 12.7 Atari TOS .................................................. 12-4
+ 12.8 OS9/68k .................................................... 12-4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/doc/internal.doc b/doc/internal.doc
new file mode 100644
index 0000000..e76e664
--- /dev/null
+++ b/doc/internal.doc
@@ -0,0 +1,198 @@
+
+ Elvis 1.4 INTERNAL Page 8-1
+
+
+E8. INTERNALF
+
+ You don't need to know the material in this section to use
+ elvis. You only need it if you intend to modify elvis.
+
+
+ E8.1 The temporary fileF
+
+ The temporary file is divided into blocks of 1024 bytes each.
+
+ When elvis starts up, the file is copied into the temporary
+ file. Small amounts of extra space are inserted into the temporary
+ file to insure that no text lines cross block boundaries; this
+ speeds up processing and simplifies storage management. The "extra
+ space" is filled with NUL characters; the input file must not
+ contain any NULs, to avoid confusion.
+
+ The first block of the temporary file is an array of shorts
+ which describe the order of the blocks; i.e. header[1] is the
+ block number of the first block, and so on. This limits the
+ temporary file to 512 active blocks, so the largest file you can
+ edit is about 400K bytes long. I hope that's enough!
+
+ When blocks are altered, they are rewritten to a -1different-0 block
+ in the file, and the in-core version of the header block is updated
+ accordingly. The in-core header block will be copied to the temp
+ file immediately before the next change... or, to undo this
+ change, swap the old header (from the temp file) with the new
+ (in-core) header.
+
+ Elvis maintains another in-core array which contains the
+ line-number of the last line in every block. This allows you to go
+ directly to a line, given its line number.
+
+
+ E8.2 Implementation of EditingF
+
+ There are three basic operations which affect text:
+
+ * delete text - delete(from, to)
+ * add text - add(at, text)
+ * yank text - cut(from, to)
+
+ To yank text, all text between two text positions is copied into
+ a cut buffer. The original text is not changed. To copy the text
+ into a cut buffer, you need only remember which physical blocks
+ that contain the cut text, the offset into the first block of the
+ start of the cut, the offset into the last block of the end of the
+ cut, and what kind of cut it was. (Cuts may be either character
+ cuts or line cuts; the kind of a cut affects the way it is later
+ "put".) This is implemented in the function cut().
+
+ To delete text, you must modify the first and last blocks, and
+ remove any reference to the intervening blocks in the header's
+ list. The text to be deleted is specified by two marks. This is
+ implemented in the function delete().
+
+
+
+
+
+
+
+ Elvis 1.4 INTERNAL Page 8-2
+
+
+ To add text, you must specify the text to insert (as a
+ NUL-terminated string) and the place to insert it (as a mark). The
+ block into which the text is to be inserted may need to be split
+ into as many as four blocks, with new intervening blocks needed as
+ well... or it could be as simple as modifying a single block.
+ This is implemented in the function add().
+
+ Other interesting functions are paste() (to copy text from a cut
+ buffer into the file), modify() (for an efficient way to implement
+ a combined delete/add sequence), and input() (to get text from the
+ user & insert it into the file).
+
+ When text is modified, an internal file-revision counter, called
+ "changes", is incremented. This counter is used to detect when
+ certain caches are out of date. (The "changes" counter is also
+ incremented when we switch to a different file, and also in one or
+ two similar situations -- all related to invalidating caches.)
+
+
+ E8.3 Marks and the CursorF
+
+ Marks are places within the text. They are represented
+ internally as a long variable which is split into two bitfields: a
+ line number and a character index. Line numbers start with 1, and
+ character indexes start with 0.
+
+ Since line numbers start with 1, it is impossible for a set mark
+ to have a value of 0L. 0L is therefore used to represent unset
+ marks.
+
+ When you do the "delete text" change, any marks that were part
+ of the deleted text are unset, and any marks that were set to
+ points after it are adjusted. Similarly, marks are adjusted after
+ new text is inserted.
+
+ The cursor is represented as a mark.
+
+
+ E8.4 Colon Command InterpretationF
+
+ Colon commands are parsed, and the command name is looked up in
+ an array of structures which also contain a pointer to the function
+ that implements the command, and a description of the arguments
+ that the command can take. If the command is recognized and its
+ arguments are legal, then the function is called.
+
+ Each function performs its task; this may cause the cursor to be
+ moved to a different line, or whatever.
+
+
+ E8.5 Screen ControlF
+
+ The screen is updated via a package which looks like the
+ "curses" library, but isn't. It is actually much simpler. Most
+ curses operations are implemented as macros which copy characters
+ into a large I/O buffer, which is then written with a single large
+ write() call as part of the refresh() operation.
+
+
+
+
+
+
+ Elvis 1.4 INTERNAL Page 8-3
+
+
+ The functions which modify text (namely add() and delete())
+ remember where text has been modified. They do this by calling the
+ function redrawrange(). The screen redrawing function, redraw(),
+ uses these clues to help it reduce the amount of text that is
+ redrawn each time.
+
+
+ E8.6 PortabilityF
+
+ To improve portability, Elvis collects as much of the
+ system-dependent definitions as possible into the config.h file.
+ This file begins with some preprocessor instructions which attempt
+ to determine which compiler and operating system you have. After
+ that, it conditionally defines some macros and constants for your
+ system.
+
+ One of the more significant macros is ttyread(buf,n). This
+ macro is used to read raw characters from the keyboard. An attempt
+ to read may be cut short by a SIGALRM signal. For UNIX systems,
+ this simply reads bytes from stdin. For MSDOS, TOS, and OS9,
+ ttyread() is a function defined in curses.c. There is also a
+ ttywrite() macro.
+
+ The tread() and twrite() macros are versions of read() and
+ write() that are used for text files. On UNIX systems, these are
+ equivelent to read() and write(). On MS-DOS, these are also
+ equivelent to read() and write(), since DOS libraries are generally
+ clever enough to convert newline characters automatically. For
+ Atari TOS, though, the MWC library is too stupid to do this, so we
+ had to do the conversion explicitly.
+
+ Other macros may substitute index() for strchr(), or bcopy() for
+ memcpy(), or map the "void" data type to "int", or whatever.
+
+ The file "tinytcap.c" contains a set of functions that emulate
+ the termcap library for a small set of terminal types. The
+ terminal-specific info is hard-coded into this file. It is only
+ used for systems that don't support real termcap. Another
+ alternative for screen control can be seen in the "curses.h" and
+ "pc.c" files. Here, macros named VOIDBIOS and CHECKBIOS are used
+ to indirectly call functions which perform low-level screen
+ manipulation via BIOS calls.
+
+ The stat() function must be able to come up with UNIX-style
+ major/minor/inode numbers that uniquely identify a file or
+ directory.
+
+ Please try to keep you changes localized, and wrap them in
+ #if/#endif pairs, so that elvis can still be compiled on other
+ systems. And PLEASE let me know about it, so I can incorporate
+ your changes into my latest-and-greatest version of elvis.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/doc/intro.doc b/doc/intro.doc
new file mode 100644
index 0000000..27b29ab
--- /dev/null
+++ b/doc/intro.doc
@@ -0,0 +1,66 @@
+
+ Elvis 1.4 INTRODUCTION Page 1-1
+
+
+E1. INTRODUCTIONF
+
+ Elvis is a clone of vi/ex, the standard UNIX editor. Elvis
+ supports nearly all of the vi/ex commands, in both visual mode and
+ colon mode.
+
+ Like vi/ex, elvis stores most of the text in a temporary file,
+ instead of RAM. This allows it to edit files that are too large to
+ fit in a single process' data space.
+
+ Elvis runs under BSD UNIX, AT&T SysV UNIX, Minix, MS-DOS, Atari
+ TOS, Coherent, and OS9/68000. The next version is expected to add
+ OS/2, VMS, AmigaDos, and MacOS. Contact me before you start
+ porting it to some other OS, because somebody else may have already
+ done it for you.
+
+ Elvis is freely redistributable, in either source form or
+ executable form. There are no restrictions on how you may use it.
+
+
+ E1.1 CompilingF
+
+ See the "Versions" section of this manual for instructions on
+ how to compile Elvis.
+
+ If you want to port Elvis to another O.S. or compiler, then you
+ should read the "Portability" part of the "Internal" section.
+
+
+ E1.2 Overview of ElvisF
+
+ The user interface of elvis/vi/ex is weird. There are two major
+ command modes in Elvis, and a few text input modes as well. Each
+ command mode has a command which allows you to switch to the other
+ mode.
+
+ You will probably use the 4visual command mode5 most of the time.
+ This is the mode that elvis normally starts up in.
+
+ In visual command mode, the entire screen is filled with lines
+ of text from your file. Each keystroke is interpretted as part of
+ a visual command. If you start typing text, it will -1not-0 be
+ inserted, it will be treated as part of a command. To insert text,
+ you must first give an "insert text" command. This will take some
+ getting used to. (An alternative exists. Lookup the "inputmode"
+ option.)
+
+ The 4colon mode5 is quite different. Elvis displays a ":"
+ character on the bottom line of the screen, as a prompt. You are
+ then expected to type in a command line and hit the <Return> key.
+ The set of commands recognized in the colon mode is different from
+ visual mode's.
+
+
+
+
+
+
+
+
+
+
diff --git a/doc/options.doc b/doc/options.doc
new file mode 100644
index 0000000..10db8ae
--- /dev/null
+++ b/doc/options.doc
@@ -0,0 +1,594 @@
+
+ Elvis 1.4 OPTIONS Page 5-1
+
+
+E5. OPTIONSF
+
+ Options may be set or examined via the colon command "set". The
+ values of options will affect the operation of later commands.
+
+ For convenience, options have both a long descriptive name and a
+ short name which is easy to type. You may use either name
+ interchangably. I like the short names, myself.
+
+-1long name short type default meaning -0
+autoindent ai Bool noai auto-indent during input
+autoprint ap Bool ap in EX, print the current line
+autowrite aw Bool noaw auto-write when switching files
+charattr ca Bool noca interpret \fX sequences?
+cc cc Str cc="cc -c" name of the C compiler
+columns co Num co=80 width of the screen
+digraph dig Bool nodig recognize digraphs?
+directory dir Str dir="/usr/tmp" where tmp files are kept
+edcompatible ed Bool noed remember ":s//" options
+errorbells eb Bool eb ring bell on error
+exrefresh er Bool er write lines indiviually in EX
+flipcase fc Str fc="" non-ASCII chars flipped by ~
+hideformat hf Bool hf hide text formatter commands
+ignorecase ic Bool noic upper/lowercase match in search
+inputmode im Bool noim start vi in insert mode?
+keytime kt Num kt=2 timeout for mapped key entry
+keywordprg kp Str kp="ref" full pathname of shift-K prog
+lines ln Num ln=25 number of lines on the screen
+list li Bool noli display lines in "list" mode
+magic ma Bool ma use regular expression in search
+make mk Str mk="make" name of the "make" program
+modeline ml Bool noml are modelines processed?
+paragraphs pa Str pa="PPppIPLPQP" names of "paragraph" nroff cmd
+readonly ro Bool noro prevent overwriting of orig file
+report re Num re=5 report when 5 or more changes
+scroll sc Num sc=12 scroll amount for ^U and ^D
+sections se Str se="NHSHSSSEse" names of "section" nroff cmd
+shell sh Str sh="/bin/sh" full pathname of the shell
+showmatch sm Bool nosm show matching ()[]{}
+showmode smd Bool nosmd say when we're in input mode
+shiftwidth sw Num sw=8 shift amount for < and >
+sidescroll ss Num ss=8 amount of sideways scrolling
+sync sy Bool nosy call sync() often
+tabstop ts Num ts=8 width of tab characters
+term te Str te="$TERM" name of the termcap entry
+vbell vb Bool vb use visible alternative to bell
+warn wa Bool wa warn for ! if file modified
+wrapmargin wm Num wm=0 wrap long lines in input mode
+wrapscan ws Bool ws at EOF, searches wrap to line 1
+
+ There are three types of options: Bool, string, and numeric.
+ Boolean options are made TRUE by giving the name of the option as
+ an argument to the "set" command; they are made FALSE by prefixing
+ the name with "no". For example, "set autoindent" makes the
+ autoindent option TRUE, and "set noautoindent" makes it FALSE.
+
+
+
+
+
+
+
+
+ Elvis 1.4 OPTIONS Page 5-2
+
+
+ To change the value of a string or numeric option, pass the
+ "set" command the name of the option, followed by an "=" sign and
+ the option's new value. For example, "set tabstop=8" will give the
+ tabstop option a value of 8. For string options, you may enclose
+ the new value in quotes.
+
+
+ E5.1 AutoIndentF
+
+ During input mode, the autoindent option will cause each added
+ line to begin with the same amount of leading whitespace as the
+ line above it. Without autoindent, added lines are initially
+ empty.
+
+
+ E5.2 AutoPrintF
+
+ This option only affects EX mode. If the autoprint option on,
+ and either the cursor has moved to a different line or the previous
+ command modified the file, then Elvis will print the current line.
+
+
+ E5.3 AutoWriteF
+
+ When you're editing one file and decide to switch to another -
+ via the :tag command, or :next command, perhaps - if your current
+ file has been modified, then Elvis will normally print an error
+ message and refuse to switch.
+
+ However, if the autowrite option is on, then Elvis will write
+ the modified version of the current file and successfully switch to
+ the new file.
+
+
+ E5.4 CCF
+
+ The :cc command runs the C compiler. This option should be set
+ to the name of your compiler.
+
+
+ E5.5 CharAttrF
+
+ Many text formatting programs allow you to designate portions of
+ your text to be underlined, italicized, or boldface by embedding
+ the special strings \fU, \fI, and \fB in your text. The special
+ string \fR marks the end of underlined or boldface text.
+
+ Elvis normally treats those special strings just like any other
+ text.
+
+ However, if the charattr option is on, then Elvis will interpret
+ those special strings correctly, to display underlined or boldface
+ text on the screen. (This only works, of course, if your terminal
+ can display underlined and boldface, and if the TERMCAP entry says
+ how to do it.)
+
+
+
+
+
+
+
+
+ Elvis 1.4 OPTIONS Page 5-3
+
+
+ E5.6 COlumnsF
+
+ This is a "read only" option. You can't change its value, but
+ you can have Elvis print it. It shows how wide your screen is.
+
+
+ E5.7 DIGraphF
+
+ This option is used to enable/disable recognition of digraphs.
+ The default value is nodigraph, which means that digraphs will not
+ be recognized.
+
+
+ E5.8 DIRectoryF
+
+ Elvis stores text in temporary files. This option allows you to
+ control which directory those temporary files will appear in. The
+ default is /usr/tmp.
+
+ This option can only be set in a .exrc file; after that, elvis
+ will have already started making temporary files in some other
+ directory, so it would be too late.
+
+
+ E5.9 EDcompatibleF
+
+ This option affects the behaviour of the
+ ":s/regexp/text/options" command. It is normally off (:se noed)
+ which causes all of the substitution options to be off unless
+ explicitly given.
+
+ However, with edcompatible on (:se ed), the substitution command
+ remembers which options you used last time. Those same options
+ will continue to be used until you change them. In edcompatible
+ mode, when you explicitly give the name of a substitution option,
+ you will toggle the state of that option.
+
+ This all seems very strange to me, but its implementation was
+ almost free when I added the ":&" command to repeat the previous
+ substitution, so there it is.
+
+
+ E5.10 ErrorBellsF
+
+ Elvis normally rings a bell when you do something wrong. This
+ option lets you disable the bell.
+
+
+ E5.11 ExRefreshF
+
+ The EX mode of Elvis writes many lines to the screen. You can
+ make Elvis either write each line to the screen separately, or save
+ up many lines and write them all at once.
+
+ The exrefresh option is normally on, so each line is written to
+ the screen separately.
+
+
+
+
+
+
+
+ Elvis 1.4 OPTIONS Page 5-4
+
+
+ You may wish to turn the exrefresh option off (:se noer) if the
+ "write" system call is costly on your machine, or if you're using a
+ windowing environment. (Windowing environments scroll text a lot
+ faster when you write many lines at once.)
+
+ This option has no effect in visual command mode or input mode.
+
+
+ E5.12 FlipCaseF
+
+ The flipcase option allows you to control how the non-ASCII
+ characters are altered by the "~" command.
+
+ The string is divided into pairs of characters. When "~" is
+ applied to a non-ASCII character, Elvis looks up the character in
+ the flipcase string to see which pair it's in, and replaces it by
+ the other character of the pair.
+
+
+ E5.13 HideFormatF
+
+ Many text formatters require you to embed format commands in
+ your text, on lines that start with a "." character. Elvis
+ normally displays these lines like any other text, but if the
+ hideformat option is on, then format lines are displayed as blank
+ lines.
+
+
+ E5.14 IgnoreCaseF
+
+ Normally, when Elvis searches for text, it treats uppercase
+ letters as being different for lowercase letters.
+
+ When the ignorecase option is on, uppercase and lowercase are
+ treated as equal.
+
+
+ E5.15 InputModeF
+
+ This option allows you to have Elvis start up in insert mode.
+ You can still exit insert mode at any time by hitting the ESC key,
+ as usual. Usually, this option would be set in your ".exrc" file.
+
+
+ E5.16 KeyTimeF
+
+ The arrow keys of most terminals send a multi-character
+ sequence. It takes a measurable amount of time for these sequences
+ to be transmitted. The keytime option allows you to control the
+ maximum amount of time to allow for an arrow key (or other mapped
+ key) to be received in full.
+
+ The default keytime value is 2. Because of the way UNIX
+ timekeeping works, the actual amount of time allowed will vary
+ slightly, but it will always be between 1 and 2 seconds.
+
+
+
+
+
+
+
+
+ Elvis 1.4 OPTIONS Page 5-5
+
+
+ If you set keytime to 1, then the actual amount of time allowed
+ will be between 0 and 1 second. This will generally make the
+ keyboard's response be a little faster (mostly for the ESC key),
+ but on those occasions where the time allowed happens to be closer
+ to 0 than 1 second, Elvis may fail to allow enough time for an
+ arrow key's sequence to be received fully. Ugh.
+
+ As a special case, you can set keytime to 0 to disable this time
+ limit stuff altogether. The big problem here is: If your arrow
+ keys' sequences start with an ESC, then every time you hit your ESC
+ key Elvis will wait... and wait... to see if maybe that ESC was
+ part of an arrow key's sequence.
+
+ NOTE: this option is a generalization of the timeout option of
+ the real vi.
+
+
+ E5.17 KeywordPrgF
+
+ Elvis has a special keyword lookup feature. You move the cursor
+ onto a word, and hit shift-K, and Elvis uses another program to
+ look up the word and display information about it.
+
+ This option says which program gets run.
+
+ The default value of this option is "ref", which is a program
+ that looks up the definition of a function in C. It looks up the
+ function name in a file called "refs" which is created by ctags.
+
+ You can subtitute other programs, such as an English dictionary
+ program or the online manual. Elvis runs the program, using the
+ keyword as its only argument. The program should write information
+ to stdout. The program's exit status should be 0, unless you want
+ Elvis to print "<<< failed >>>".
+
+
+ E5.18 LiNesF
+
+ This "read only" option shows how many lines you screen has.
+
+
+ E5.19 LIstF
+
+ In nolist mode (the default), elvis displays text in a "normal"
+ manner -- with tabs expanded to an appropriate number of spaces,
+ etc.
+
+ However, sometimes it is useful to have tab characters displayed
+ differently. In list mode, tabs are displayed as "^I", and a "$"
+ is displayed at the end of each line.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Elvis 1.4 OPTIONS Page 5-6
+
+
+ E5.20 MAgicF
+
+ The search mechanism in Elvis can accept "regular expressions"
+ -- strings in which certain characters have special meaning.
+
+ The magic option is normally on, which causes these characters
+ to be treated specially.
+
+ If you turn the magic option off (:se noma), then all characters
+ except ^ and $ are treated literally. ^ and $ retain their special
+ meanings regardless of the setting of magic.
+
+
+ E5.21 MaKeF
+
+ The :make command runs your "make" program. This option defines
+ the name of your "make" program.
+
+
+ E5.22 ModeLineF
+
+ Elvis supports modelines. Modelines are lines near the
+ beginning or end of your text file which contain "ex:yowza:", where
+ "yowza" is any EX command. A typical "yowza" would be something
+ like "set ts=4 ca kp=spell".
+
+ Normally these lines are ignored, for security reasons, but if
+ you have "set modeline" in your .exrc file then "yowza" is
+ executed.
+
+
+ E5.23 PAragraphsF
+
+ The { and } commands move the cursor forward or backward in
+ increments of one paragraph. Paragraphs may be separated by blank
+ lines, or by a "dot" command of a text formatter. Different text
+ formatters use different "dot" commands. This option allows you to
+ configure Elvis to work with your text formatter.
+
+ It is assumed that your formatter uses commands that start with
+ a "." character at the front of a line, and then have a one- or
+ two-character command name.
+
+ The value of the paragraphs option is a string in which each
+ pair of characters is one possible form of your text formatter's
+ paragraph command.
+
+
+ E5.24 ReadOnlyF
+
+ Normally, Elvis will let you write back any file to which you
+ have write permission. If you don't have write permission, then
+ you can only write the changed version of the file to a -1different-0
+ file.
+
+
+
+
+
+
+
+
+
+ Elvis 1.4 OPTIONS Page 5-7
+
+
+ If you set the readonly option, then Elvis will pretend you
+ don't have write permission to -1any-0 file you edit. It is useful
+ when you really only mean to use Elvis to look at a file, not to
+ change it. This way you can't change it accidentally.
+
+ This option is normally off, unless you use the "view" alias of
+ Elvis. "View" is like "vi" except that the readonly option is on.
+
+
+ E5.25 REportF
+
+ Commands in Elvis may affect many lines. For commands that
+ affect a lot of lines, Elvis will output a message saying what was
+ done and how many lines were affected. This option allows you to
+ define what "a lot of lines" means. The default is 5, so any
+ command which affects 5 or more lines will cause a message to be
+ shown.
+
+
+ E5.26 SCrollF
+
+ The ^U and ^D keys normally scroll backward or forward by half a
+ screenful, but this is adjustable. The value of this option says
+ how many lines those keys should scroll by.
+
+
+ E5.27 SEctionsF
+
+ The [[ and ]] commands move the cursor backward or forward in
+ increments of 1 section. Sections may be delimited by a {
+ character in column 1 (which is useful for C source code) or by
+ means of a text formatter's "dot" commands.
+
+ This option allows you to configure Elvis to work with your text
+ formatter's "section" command, in exectly the same way that the
+ paragraphs option makes it work with the formatter's "paragraphs"
+ command.
+
+
+ E5.28 SHellF
+
+ When Elvis forks a shell (perhaps for the :! or :shell
+ commands) this is the program that is uses as a shell. This is
+ "/bin/sh" by default, unless you have set the SHELL (or COMSPEC,
+ for MS-DOS) environment variable, it which case the default value
+ is copied from the environment.
+
+
+ E5.29 ShiftWidthF
+
+ The < and > commands shift text left or right by some uniform
+ number of columns. The shiftwidth option defines that "uniform
+ number". The default is 8.
+
+
+
+
+
+
+
+
+
+
+ Elvis 1.4 OPTIONS Page 5-8
+
+
+ E5.30 ShowMatchF
+
+ With showmatch set, in input mode every time you hit one of )}],
+ Elvis will momentarily move the cursor to the matching ({[.
+
+
+ E5.31 ShowMoDeF
+
+ In visual mode, it is easy to forget whether you're in the
+ visual command mode or input/replace mode. Normally, the showmode
+ option is off, and you haven't a clue as to which mode you're in.
+ If you turn the showmode option on, though, a little message will
+ appear in the lower right-hand corner of your screen, telling you
+ which mode you're in.
+
+
+ E5.32 SideScrollF
+
+ For long lines, Elvis scrolls sideways. (This is different from
+ the real vi, which wraps a single long line onto several rows of
+ the screen.)
+
+ To minimize the number of scrolls needed, Elvis moves the screen
+ sideways by several characters at a time. The value of this option
+ says how many characters' widths to scroll at a time.
+
+ Generally, the faster your screen can be redrawn, the lower the
+ value you will want in this option.
+
+
+ E5.33 SYncF
+
+ If the system crashes during an edit session, then most of your
+ work can be recovered from the temporary file that elvis uses to
+ store changes. However, sometimes the OS will not copy changes to
+ the hard disk immediately, so recovery might not be possible. The
+ [no]sync option lets you control this.
+
+ In nosync mode (which is the default, for UNIX), elvis lets the
+ operating system control when data is written to the disk. This is
+ generally faster.
+
+ In sync mode (which is the default, for MS-DOS), elvis forces
+ all changes out to disk every time you make a change. This is
+ generally safer, but slower. It can also be a rather rude thing to
+ do on a multi-user system.
+
+
+ E5.34 TabStopF
+
+ Tab characters are normally 8 characters wide, but you can
+ change their widths by means of this option.
+
+
+
+
+
+
+
+
+
+
+
+ Elvis 1.4 OPTIONS Page 5-9
+
+
+ E5.35 TErmF
+
+ This "read only" option shows the name of the termcap entry that
+ Elvis is using for your terminal.
+
+
+ E5.36 VBellF
+
+ If your termcap entry describes a visible alternative to ringing
+ your terminal's bell, then this option will say whether the visible
+ version gets used or not. Normally it will be.
+
+ If your termcap does NOT include a visible bell capability, then
+ the vbell option will be off, and you can't turn it on.
+
+
+ E5.37 WArnF
+
+ If you have modified a file but not yet written it back to disk,
+ then Elvis will normally print a warning before executing a ":!cmd"
+ command. However, in nowarn mode, this warning is not given.
+
+ Elvis also normally prints a message after a successful search
+ that wrapped at EOF. The [no]warn option can also disable this
+ warning.
+
+
+ E5.38 WrapMarginF
+
+ Normally (with wrapmargin=0) Elvis will let you type in
+ extremely long lines, if you wish.
+
+ However, with warpmargin set to something other that 0
+ (wrapmargin=10 is nice), Elvis will automatically cause long lines
+ to be "wrapped" on a word break for lines longer than wrapmargin's
+ setting.
+
+
+ E5.39 WrapScanF
+
+ Normally, when you search for something, Elvis will find it no
+ matter where it is in the file. Elvis starts at the cursor
+ position, and searches forward. If Elvis hits EOF without finding
+ what you're looking for, then it wraps around to continue searching
+ from line 1.
+
+ If you turn off the wrapscan option (:se nows), then when Elvis
+ hits EOF during a search, it will stop and say so.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/doc/ref.man b/doc/ref.man
new file mode 100644
index 0000000..16026a8
--- /dev/null
+++ b/doc/ref.man
@@ -0,0 +1,21 @@
+.TH REF 1
+.SH NAME
+ref - Display a C function header
+.SH SYNOPSIS
+\fBref\fP \fIfunction_name\fP
+.SH DESCRIPTION
+\fIRef\fP is a program which looks up the function header of a
+particular function in any of a series of reference files.
+These reference files are produced by the \fIctags(1)\fP program.
+.PP
+\fIRef\fP is used by Elvis's shift-K command.
+.PP
+The list of files checked includes "refs" in the current directory,
+and possibly others.
+See the source code for an accurate list.
+.SH AUTHOR
+.nf
+Steve Kirkendall
+kirkenda@cs.pdx.edu
+\&...uunet!tektronix!psueea!eecs!kirkenda
+.fi
diff --git a/doc/refont.man b/doc/refont.man
new file mode 100644
index 0000000..d7bbf93
--- /dev/null
+++ b/doc/refont.man
@@ -0,0 +1,67 @@
+.TH REFONT 1
+.SH NAME
+refont - changes the notation used for fonts
+.SH SYNOPSIS
+\fBrefont\fP [\fIflags\fP] \fIfiles\fP...
+.SH DESCRIPTION
+\fIRefont\fP reads a text file which contains font selection codes embedded
+within it,
+and it writes the same text with a different notation for fonts.
+.PP
+For example, the Elvis documentation uses Epson-compatible escape sequences
+to select different fonts.
+You could use the command "refont -b intro.doc >intro.b" to make a file
+that uses overtyping to implement boldface or underlined text.
+.SH OPTIONS
+.IP \fB-b\fP
+Emit text which uses the "backspace" notation for fonts.
+Each underlined character will be preceded by an underscore character
+and a backspace character.
+Bold characters are sent twice, with a backspace in between.
+The UNIX \fImore\fR utility understands this notation.
+.IP \fB-c\fP
+Emit text which uses the "carriage-return" notation for fonts.
+An entire line of text is written,
+followed by a carriage return instead of a newline.
+Then a space is sent for each normal character,
+an underscore is sent for each underlined or italic character,
+and each boldface character is sent a second time.
+Many mainframe line printers accept this notation.
+.IP \fB-d\fP
+Emit text which uses nroff-style "dot" commands for fonts.
+This doesn't work very well.
+.IP \fB-e\fP
+Emit text using Epson-compatible escape sequences for fonts.
+This is useful as a "least common denominator" for font notations,
+because this is the only supported notation to use control-character sequences
+and also distinguish between italics and underlining.
+.IP \fB-f\fP
+Emit text which uses nroff's "\\fX" notation for fonts.
+Underlined text is denoted by "\\fU",
+boldface by "\\fB",
+italics by "\\fI",
+and normal text by "\\fR".
+This is somewhat useful in conjunction with Elvis' "charattr" option.
+.IP \fB-x\fP
+Emit text which has had all font information stripped out.
+.IP \fB-I\fP
+When reading text, \fB-I\fP tells \fIrefont\fP to accept any of the above
+notations for fonts.
+Without \fB-I\fP it will ignore the "dot" command and "\\fX" notations;
+they will be treated as normal text.
+In other words, without \fB-I\fP the only things that could be recognized as
+font changes are control-character sequences.
+.IP \fB-F\fP
+This causes \fIrefont\fP to insert formfeed characters between input files.
+.SH BUGS
+Support for the nroff-style "dot" commands is not very good.
+.PP
+With \fB-b\fP/\fB-c\fP, both underlining and italics are implemented by
+overtyping the underscore character with a text character.
+Since they are represented the same way, the distinction between underlining
+and italics is lost.
+.SH AUTHOR
+.nf
+Steve Kirkendall
+kirkenda@cs.pdx.edu
+\&...uunet!tektronix!psueea!eecs!kirkenda
diff --git a/doc/regexp.doc b/doc/regexp.doc
new file mode 100644
index 0000000..808bc3d
--- /dev/null
+++ b/doc/regexp.doc
@@ -0,0 +1,132 @@
+
+ Elvis 1.4 REGULAR EXPRESSIONS Page 4-1
+
+
+E4. REGULAR EXPRESSIONSF
+
+ Elvis uses regular expressions for searching and substututions.
+
+
+ E4.1 SyntaxF
+
+ Elvis' regexp package treats the following one- or two-character
+ strings (called meta-characters) in special ways:
+
+ \( \) Used to delimit subexpressions
+ ^ Matches the beginning of a line
+ $ Matches the end of a line
+ \< Matches the beginning of a word
+ \> Matches the end of a word
+ [ ] Matches any single character inside the brackets
+ * The preceding may be repeated 0 or more times
+ \+ The preceding may be repeated 1 or more times
+ \? The preceding is optional
+
+ Anything else is treated as a normal character which must match
+ exactly. The special strings may all be preceded by a backslash to
+ force them to be treated normally.
+
+
+ E4.2 OptionsF
+
+ Elvis has two options which affect the way regular expressions
+ are used. These options may be examined or set via the :set
+ command.
+
+ The first option is called "[no]magic". This is a boolean
+ option, and it is "magic" (TRUE) by default. While in magic mode,
+ all of the meta-characters behave as described above. In nomagic
+ mode, only ^ and $ retain their special meaning.
+
+ The second option is called "[no]ignorecase". This is a boolean
+ option, and it is "noignorecase" (FALSE) by default. While in
+ ignorecase mode, the searching mechanism will not distinguish
+ between an uppercase letter and its lowercase form. In
+ noignorecase mode, uppercase and lowercase are treated as being
+ different.
+
+ Also, the "[no]wrapscan" option affects searches.
+
+
+ E4.3 SubstitutionsF
+
+ The :s command has at least two arguments: a regular expression,
+ and a substitution string. The text that matched the regular
+ expression is replaced by text which is derived from the
+ substitution string.
+
+
+
+
+
+
+
+
+
+
+
+ Elvis 1.4 REGULAR EXPRESSIONS Page 4-2
+
+
+ Most characters in the substitution string are copied into the
+ text literally but a few have special meaning:
+
+ & Insert a copy of the original text
+ ~ Insert a copy of the previous replacement text
+ \1 Insert a copy of that portion of the original text which
+ matched the first set of \( \) parentheses.
+ \2 - \9 Does the same for the second (etc.) pair of \( \).
+ \U Convert all chars of any later &, ~, or \# to uppercase
+ \L Convert all chars of any later &, ~, or \# to lowercase
+ \E End the effect of \U or \L
+ \u Convert the first char of the next &, ~ or \# to uppercase
+ \l Convert the first char of the next &, ~ or \# to lowercase
+
+ These may be preceded by a backslash to force them to be treated
+ normally. If "nomagic" mode is in effect, then & and ~ will be
+ treated normally, and you must write them as \& and \~ form them to
+ have special meaning.
+
+
+ E4.4 ExamplesF
+
+ This example changes every occurence of "utilize" to "use":
+
+ :%s/utilize/use/g
+
+ This example deletes all whitespace that occurs at the end of a
+ line anywhere in the file. (The brackets contain a single space
+ and a single tab.):
+
+ :%s/[ ]\+$//
+
+ This example converts the current line to uppercase:
+
+ :s/.*/\U&/
+
+ This example underlines each letter in the current line, by
+ changing it into an "underscore backspace letter" sequence. (The
+ ^H is entered as "control-V backspace".):
+
+ :s/[a-zA-Z]/_^H&/g
+
+ This example locates the last colon in a line, and swaps the
+ text before the colon with the text after the colon. The first \(
+ \) pair is used to delineate the stuff before the colon, and the
+ second pair delineates the stuff after. In the substitution text,
+ \1 and \2 are given in reverse order, to perform the swap:
+
+ :s/\(.*\):\(.*\)/\2:\1/
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/doc/termcap.doc b/doc/termcap.doc
new file mode 100644
index 0000000..a073dea
--- /dev/null
+++ b/doc/termcap.doc
@@ -0,0 +1,132 @@
+
+ Elvis 1.4 TERMCAP Page 10-1
+
+
+E10. TERMCAPF
+
+ Elvis uses fairly standard termcap fields for most things. I
+ invented the cursor shape names but other than that there should be
+ no surprises.
+
+
+ E10.1 Required numeric fieldsF
+
+ :co#: number of columns on the screen (characters per line)
+ :li#: number of lines on the screen
+
+
+ E10.2 Required string fieldsF
+
+ :ce=: clear to end-of-line
+ :cl=: home the cursor & clear the screen
+ :cm=: move the cursor to a given row/column
+ :up=: move the cursor up one line
+
+
+ E10.3 Boolean fieldsF
+
+ :am: auto margins - wrap when a char is written to the last column?
+ :pt: physical tabs?
+
+
+ E10.4 Optional string fieldsF
+
+ :al=: insert a blank row on the screen
+ :dl=: delete a row from the screen
+ :cd=: clear to end of display
+ :ei=: end insert mode
+ :ic=: insert a blank character
+ :im=: start insert mode
+ :dc=: delete a character
+ :sr=: scroll reverse (insert a row at the top of the screen)
+ :vb=: visible bell
+ :ti=: terminal initialization string, to start full-screen mode
+ :te=: terminal termination, to end full-screen mode
+ :ks=: enables the cursor keypad
+ :ke=: disables the cursor keypad
+
+
+ E10.5 Optional strings received from the keyboardF
+
+ :kd=: sequence sent by the <down arrow> key
+ :kl=: sequence sent by the <left arrow> key
+ :kr=: sequence sent by the <right arrow> key
+ :ku=: sequence sent by the <up arrow> key
+ :kP=: sequence sent by the <PgUp> key
+ :kN=: sequence sent by the <PgDn> key
+ :kh=: sequence sent by the <Home> key
+ :kH=: sequence sent by the <End> key
+
+
+
+
+
+
+
+
+
+ Elvis 1.4 TERMCAP Page 10-2
+
+
+ Originally, termcap didn't have any names for the <PgUp>,
+ <PgDn>, <Home>, and <End> keys. Although the capability names
+ shown in the table above are the most common, they are -1not-0
+ universal. SCO Xenix uses :PU=:PD=:HM=:EN=: for those keys. Also,
+ if the four arrow keys happen to be part of a 3x3 keypad, then the
+ five non-arrow keys may be named :K1=: through :K5=:, so an IBM PC
+ keyboard may be described using those names instead. Elvis can any
+ of these names.
+
+
+ E10.6 Optional fields that describe character attributesF
+
+ :so=: :se=: start/end standout mode (We don't care about :sg#:)
+ :us=: :ue=: start/end underlined mode
+ :md=: :me=: start/end boldface mode
+ :as=: :ae=: start/end alternate character set (italics)
+ :ug#: visible gap left by :us=:ue=:md=:me=:as=:ae=:
+
+
+ E10.7 Optional fields that affect the cursor's shapeF
+
+ The :cQ=: string is used by elvis immediately before exiting to
+ undo the effects of the other cursor shape strings. If :cQ=: is
+ not given, then all other cursor shape strings are ignored.
+
+ :cQ=: normal cursor
+ :cX=: cursor shape used for reading EX command -- steady underline
+ :cV=: cursor shape used for reading VI commands -- steady block
+ :cI=: cursor shape used during VI input mode -- blinking underline
+ :cR=: cursor shape used during VI replace mode -- blinking block
+
+ If the capabilities above aren't given, then Elvis will try to
+ use the following values instead.
+
+ :ve=: normal cursor, used as :cQ=:cX=:cI=:cR=:
+ :vs=: gaudy cursor, used as :cV=:
+
+
+ E10.8 An exampleF
+
+ Here's the termcap entry I use on my Minix-ST system. Some of
+ the fields in it have nothing to do with Elvis. Some can only work
+ on my system; I have modified my kernel's screen driver.
+
+
+ mx|minix|minixst|ansi:\
+ :is=\E[0~:co#80:li#25:bs:pt:\
+ :cm=\E[%i%d;%dH:up=\E[A:do=^J:nd=\E[C:sr=\EM:\
+ :cd=\E[J:ce=\E[K:cl=\E[H\E[J:\
+ :al=\E[L:dl=\E[M:ic=\E[@:dc=\E[P:im=:ei=:\
+ :so=\E[7m:se=\E[m:us=\E[4m:ue=\E[m:\
+ :md=\E[1m:me=\E[m:as=\E[1;3m:ae=\E[m:\
+ :ku=\E[A:kd=\E[B:kr=\E[C:kl=\E[D:\
+ :k1=\E[1~:k2=\E[2~:k3=\E[3~:k4=\E[4~:k5=\E[5~:\
+ :k6=\E[6~:k7=\E[17~:k8=\E[18~:k9=\E[19~:k0=\E[20~:\
+ :kU=\E[36~:kQ=\E[32~:kH=\E[28~:\
+ :GV=3:GH=D:G1=?:G2=Z:G3=@:G4=Y:GC=E:GL=4:GR=C:GU=A:GD=B:\
+ :cQ=\E[k:cX=\E[2;0k:cV=\E[16;0k:cI=\E[k:cR=\E[16;20k
+
+
+
+
diff --git a/doc/versions.doc b/doc/versions.doc
new file mode 100644
index 0000000..846175d
--- /dev/null
+++ b/doc/versions.doc
@@ -0,0 +1,264 @@
+
+ Elvis 1.4 VERSIONS Page 12-1
+
+
+E12. VERSIONSF
+
+ Elvis currently works under BSD UNIX, AT&T System-V UNIX, SCO
+ XENIX, Minix, Coherent, MS-DOS, Atari TOS, and OS9/68k. This
+ section of the manual provides special information that applies to
+ each particular version of Elvis.
+
+ For all versions except MS-DOS, the file "Makefile.mix" should
+ be copied to "Makefile", and then edited to select the correct set
+ of options for your system. There is more information about this
+ embedded in the file itself.
+
+
+ E12.1 BSD UNIXF
+
+ Temporary files are stored in /tmp.
+
+ Elvis doesn't have an "expreserve" program yet. Instead, you
+ should modify /etc/rc so that the temp files are not deleted when
+ the system is rebooted. Find a line in /etc/rc which reads
+
+ rm -rf /tmp/*
+
+ or something like that, and change it to read
+
+ rm -rf /tmp/[^e]* /tmp/e[^l]* /tmp/el[^v]* /tmp/elv_*
+
+ If you do not have permission to modify /etc/rc, don't fret.
+ The above modification is only needed to allow you to recover your
+ changes after a system crash. You can still run Elvis without that
+ modification, and you can still recover your changes when Elvis
+ crashes or when your dialup modem looses the carrier signal, or
+ something like that. A system crash is the only thing that could
+ hurt you.
+
+ Both Elvis and the real Vi read initialization commands from a
+ file called ".exrc", but the commands in that file might work on
+ one but not the other. For example, "set keywordprg=man" will work
+ for Elvis, but Vi will complain because it doesn't have a
+ "keywordprg" option. If the warning messages annoy you, then you
+ can edit the config.h file to change the name of the initialization
+ file ".exrc" to something else, such as ".elvisrc".
+
+ If you use X windows, you may wish to add "-DCS_LATIN1" to
+ CFLAGS. This will cause the digraph table and the flipcase option
+ to have default values that are appropriate for the LATIN-1
+ character set. That's the standard character set for X.
+
+
+ E12.2 System-V UNIXF
+
+ If your system uses terminfo instead of termcap, then you will
+ have to edit the LIBS setting in the Makefile. Currently it says
+ "LIBS=-ltermcap", but you may have to change it to
+ "LIBS=-lterminfo" or "LIBS=-lterm" or something like that.
+
+
+
+
+
+
+
+
+ Elvis 1.4 VERSIONS Page 12-2
+
+
+ The /etc/rc file should be modified as described for BSD
+ systems, above. The potential trouble with ".exrc" described above
+ for BSD UNIX applies to System-V UNIX as well.
+
+ Elvis uses control-C as the interrupt key, not Delete.
+
+
+ E12.3 SCO XenixF
+
+ For Xenix-386, you can use the generic System-V settings. You
+ may wish to add "-DCS_IBMPC" to CFLAGS, to have the digraph table
+ and flipcase option start up in a mode that is appropriate for the
+ console.
+ There is a separate group of settings for use with Xenix-286. It
+ already has "-DCS_IBMPC" in CFLAGS.
+
+ Because Xenix is so similar to System-V, everything I said
+ earlier about System-V applies to the Xenix version too.
+
+
+ E12.4 MinixF
+
+ There are separate settings in Makefile.mix for Minix-PC and
+ Minix-ST. The differences between these two are that the ST
+ version uses ".o" for the object file extension where the PC
+ version uses ".s", and the PC version has some extra flags in
+ CFLAGS to reduce the size of Elvis. The PC version also uses
+ tinytcap (instead of the full termcap) to make it smaller.
+
+ Minix-PC users should read the CFLAGS section of this manual
+ very carefully. You have some choices to make...
+
+ The temporary files are stored in /usr/tmp. The /usr/tmp
+ directory must exist before you run Elvis, and it must be
+ readable/writable by everybody. We use /usr/tmp instead of /tmp
+ because after a system crash or power failure, you can recover the
+ altered version of a file from the temporary file in /usr/tmp. If
+ it was stored in /tmp, though, then it would be lost because /tmp
+ is probably located on the RAM disk.
+
+ Elvis uses control-C as the interrupt key, not Delete.
+
+
+ E12.5 CoherentF
+
+ Elvis was ported to Coherent by Esa Ahola.
+
+ Elvis is too large to run under Coherent unless you eliminate
+ some features via the CFLAGS setting. The recommended settings, in
+ Makefile.mix, produce a working version of elvis which emulates Vi
+ faithfully, but lacks most of the extensions. You should read the
+ CFLAGS section of this manual carefully.
+
+ You can probably reduce the size of Elvis by using tinytcap.c
+ instead of -lterm. This would allow you to keep most features of
+ Elvis, at the expense of terminal independence. (Tinytcap.c has
+ ANSI escape sequences hard-coded into it.) To use tinytcap, just
+ add "tinytcap.o" to the "EXTRA=" line in the Makefile, and remove
+
+
+
+
+
+ Elvis 1.4 VERSIONS Page 12-3
+
+
+ "-lterm" from the "LIBS=" line.
+
+ The temporary files are stored in /tmp. You should modify your
+ /etc/rc file as described for BSD earlier.
+
+
+ E12.6 MS-DOSF
+
+ Elvis was ported to MS-DOS by Guntram Blohm and Martin Patzel.
+ Dave Lord also deserves a big "thank you" for exploring a
+ compatibility glitch between DOS 4.01 and Elvis.
+
+ Ideally, Elvis should be compiled with Microsoft C 5.1 and the
+ standard Microsoft Make utility, via the command "make elvis.mak".
+ This will compile Elvis and all related utilities.
+
+ If you have Turbo-C, then you can 4almost5 use the "Elvis.prj"
+ file to compile Elvis. EYou must explicitly force Turbo-C to
+ compile it with the "medium" memoryF Emodel, and you must increase
+ the stack size to 16k.F Most of the related programs (ctags, ref,
+ virec, refont, and wildcard) are only one file long, so you should
+ have no trouble compiling them. The "alias.c" file is meant to be
+ compiled once into an executable named "ex.exe". You should then
+ copy "ex.exe" to "vi.exe", and "view.exe".
+
+ Elvis stores its temporary files in C:\tmp. If this is not
+ satisfactory, then you should edit the CFLAGS line of your Makefile
+ to change TMPDIR to something else before compiling. You can also
+ control the name of the temp directory via an environment variable
+ named TMP or TEMP. The directory must exist before you can run
+ Elvis.
+
+ Normally, the TERM environment variable should not be set, or
+ else it should be set to "pcbios". This way, Elvis will make calls
+ to BIOS to update the screen. (If you don't like the colors that
+ the BIOS interface uses, then edit the attr[] table in pc.c. A
+ ":color" command will be added eventually.)
+
+ You may prefer to use a device driver such as ANSI.SYS or
+ NNANSI.SYS, for speed; or you may NEED to use a device driver for
+ compatibility. If so, you should install one of these drivers by
+ adding "driver = ansi.sys" (or whatever) to your CONFIG.SYS file,
+ and then you should define TERM to be either "ansi" or "nansi" by
+ adding a line such as "set TERM=ansi" to your AUTOEXEC.BAT file.
+ You must then reboot for these changes to take effect. After that,
+ Elvis will notice the "TERM" setting and use the driver.
+
+ Under MS-DOS, Elvis has an extra ":set" option called "pcbios"
+ which indicates whether the BIOS is being used directly. This is a
+ "read only" option; you can't use it to switch your interface style
+ in the middle of an edit session.
+
+ An extra program, called "wildcard", is needed for MS-DOS. It
+ expands wildcard characters in file names.
+
+
+
+
+
+
+
+
+
+ Elvis 1.4 VERSIONS Page 12-4
+
+
+ E12.7 Atari TOSF
+
+ Elvis was ported to Atari TOS by Guntram Blohm and Martin
+ Patzel. It is very similar to the MS-DOS version. It has only
+ been tested with the Mark Williams C compiler.
+
+ The TERM environment variable is ignored; the ST port always
+ assumes that TERM=vt52. The SHELL (not COMSPEC!) variable should
+ be set to the name of a line-oriented shell.
+
+ A simple shell in included with elvis. Its source is in
+ "shell.c", and the name of the executable is "shell.ttp". This was
+ necessary because the standard Atari software doesn't offer any way
+ to set environment variables. The file "profile.sh" should contain
+ a set of instructions to be executed when the shell first starts
+ up. An example of this file is included, but you will almost
+ certainly want to edit it right away to match your configuration.
+
+ If you already have a command-line shell, then you'll probably
+ want to continue using it. The shell that comes with Elvis is very
+ limited.
+
+ Currently, character attributes cannot be displayed on the
+ screen. In other words, the "charattr" option doesn't work very
+ well. Its ironic -- the only system that always has a bitmapped
+ display is the only system that doesn't support multiple fonts!
+
+
+ E12.8 OS9/68kF
+
+ Elvis was ported to OS9/68k by Peter Reinig.
+
+ The Makefile is currently configured to install elvis and the
+ related programs in /dd/usr/cmds If this this is unacceptable, then
+ you should change the BIN setting to some other directory.
+ Similarly, it expects the source code to reside in
+ /dd/usr/src/elvis; the ODIR setting is used to control this.
+
+ Temporary files are stored in the /dd/tmp directory. Your
+ /dd/startup file may need to be modified to prevent it from
+ deleting Elvis' temporary files.
+
+ The program in alias.c is linked repeatedly to produce the "vi",
+ "view", and "input" aliases for elvis. Sadly, the "ex" alias is
+ impossible to implement under OS9, because the shell has a built-in
+ command by that name.
+
+ For some purposes, you must give `make' the "-b" option.
+ Specifically, you need this for "make -b clean" and "make -b
+ install".
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/doc/virec.man b/doc/virec.man
new file mode 100644
index 0000000..aeda675
--- /dev/null
+++ b/doc/virec.man
@@ -0,0 +1,51 @@
+.TH VIREC 1
+.SH NAME
+virec - Recover the modified version of a file after a crash
+.SH SYNOPSIS
+.nf
+\fBvirec\fP [\fB-d\fP \fItmpdir\fP] \fItextfilename...\fP
+\fBvirec\fP </tmp/elv\fIXXX\fP
+.fi
+.SH DESCRIPTION
+\fIVirec\fP is a program which extracts the most recent version of a
+text file from a temporary file in /tmp.
+.PP
+When you edit a file with Elvis,
+only about 5K bytes of the file are stored in RAM;
+the rest is stored in a file in /tmp.
+This allows you to edit files larger than a process' data space.
+It also plays a big part in the way that "undo" and "paste" were implemented.
+.PP
+The \fIvirec\fP program basically extracts the "undo" version from the file.
+This is most useful when the system (or Elvis) crashes in
+the middle of a long edit session,
+since the "undo" version of the file contains everything
+except your last change.
+Nearly all of your work can be salvaged.
+.PP
+The most common way to invoke virec is this:
+You just give virec the name of the file you were editing,
+and it finds the matching file in /tmp and writes
+the newest available version of the file over the existing version.
+It then deletes the /tmp file.
+.PP
+The other way, where you redirect its stdin to come from
+a particular /tmp file, is used when you have
+either forgotten which file that is and you want to see its contents,
+or when you want to recover the file
+without losing either the /tmp file or the current version of the text file.
+.SH OPTIONS
+.IP "\fB-d\fP \fItmpdir\fP"
+If your \fI.exrc\fP or \fI$EXINIT\fP causes Elvis to store temp files in
+a non-standard directory
+(usually, anyplace other than /tmp),
+then you must tell \fIvirec\fP which directory to look in.
+.SH FILES
+.IP /tmp/elv*
+The temporary file left over from when Elvis was editing a text file.
+.SH AUTHOR
+.nf
+Steve Kirkendall
+kirkenda@cs.pdx.edu
+\&...uunet!tektronix!psueea!eecs!kirkenda
+.fi
diff --git a/doc/visual.doc b/doc/visual.doc
new file mode 100644
index 0000000..ee1c28b
--- /dev/null
+++ b/doc/visual.doc
@@ -0,0 +1,330 @@
+
+ Elvis 1.4 VISUAL MODE COMMANDS Page 2-1
+
+
+E2. VISUAL MODE COMMANDSF
+
+ Most visual mode commands are one keystroke long. The following
+ table lists the operation performed by each keystroke, and also
+ denotes any options or arguments that it accepts. Notes at the end
+ of the table describe the notation used in this table.
+
+ In addition to the keys listed here, your keyboard's "arrow"
+ keys will be interpretted as the appropriate cursor movement
+ commands. The same goes for <PgUp> and <PgDn>, if your keyboard
+ has them. There is a colon mode command (to be described later)
+ which will allow you to define other keys, such as function keys.
+
+ A tip: visual command mode looks a lot like text input mode. If
+ you forget which mode you're in, just hit the <Esc> key. If elvis
+ beeps, then you're in visual command mode. If elvis does not beep,
+ then you were in input mode, but by hitting <Esc> you will have
+ switched to visual command mode. So, one way or another, after
+ <Esc> elvis will be ready for a command.
+
+-1command description type-0
+ ^A ---
+ ^B Move toward the top of the file by 1 screenful
+ ^C ---
+count ^D scroll down <count> lines (default 1/2 screen)
+count ^E scroll up <count> lines
+ ^F move toward the bottom of the file by 1 screenful
+ ^G show file status, and the current line #
+count ^H move left, like h MOVE
+ ^I ---
+count ^J move down MOVE
+ ^K ---
+ ^L redraw the screen
+count ^M move to the front of the next line MOVE
+count ^N move down MOVE
+ ^O ---
+count ^P move up MOVE
+ ^Q ---
+ ^R redraw the screen
+ ^S ---
+ ^T ---
+count ^U scroll up <count> lines (default 1/2 screen)
+ ^V ---
+ ^W ---
+ ^X ---
+count ^Y scroll down <count> lines
+ ^Z ---
+ ESC ---
+ ^\ ---
+ ^] if the cursor is on a tag name, go to that tag
+ ^^ switch to the previous file, like ":e #"
+ ^_ ---
+count SPC move right,like l MOVE
+ ! mv run the selected lines thru an external filter program
+ " key select which cut buffer to use next
+count # + increment a number EDIT
+ $ move to the rear of the current line MOVE
+ % move to the matching (){}[] character MOVE
+
+
+
+
+
+ Elvis 1.4 VISUAL MODE COMMANDS Page 2-2
+
+
+count & repeat the previous ":s//" command here EDIT
+ ' key move to a marked line MOVE
+count ( move backward <count> sentences MOVE
+count ) move forward <count> sentences MOVE
+ * go to the next error in the errlist
+count + move to the front of the next line MOVE
+count , repeat the previous [fFtT] but in the other direction MOVE
+count - move to the front of the preceding line MOVE
+count . repeat the previous "edit" command
+ / text search forward for a given regular expression MOVE
+ 0 if not part of count, move to 1st char of this line MOVE
+ 1 part of count
+ 2 part of count
+ 3 part of count
+ 4 part of count
+ 5 part of count
+ 6 part of count
+ 7 part of count
+ 8 part of count
+ 9 part of count
+ : text run single EX cmd
+count ; repeat the previous [fFtT] cmd MOVE
+ < mv shift text left EDIT
+ = ---
+ > mv shift text right EDIT
+ ? text search backward for a given regular expression MOVE
+ @ key execute the contents of a cut-buffer as VI commands
+count A inp append at end of the line EDIT
+count B move back Word MOVE
+ C inp change text from the cursor through the end of the line EDIT
+ D delete text from the cursor through the end of the line EDIT
+count E move end of Word MOVE
+count F key move leftward to a given character MOVE
+count G move to line #<count> (default is the bottom line) MOVE
+count H move to home row (the line at the top of the screen)
+count I inp insert at the front of the line (after indents) EDIT
+count J join lines, to form one big line EDIT
+ K look up keyword
+count L move to last row (the line at the bottom of the screen)
+ M move to middle row
+ N repeat previous search, but in the opposite direction MOVE
+count O inp open up a new line above the current line EDIT
+ P paste text before the cursor
+ Q quit to EX mode
+ R inp overtype EDIT
+count S inp change lines, like <count>cc
+count T key move leftward *almost* to a given character MOVE
+ U Undo all recent changes to the current line
+ V ---
+count W move forward <count> Words MOVE
+count X delete the character(s) to the left of the cursor EDIT
+count Y yank text line(s) (copy them into a cut buffer)
+ Z Z save the file & exit
+ [ [ move back 1 section MOVE
+ \ ---
+ ] ] move forward 1 section MOVE
+ ^ move to the front of the current line (after indent) MOVE
+ _ ---
+
+
+
+
+
+ Elvis 1.4 VISUAL MODE COMMANDS Page 2-3
+
+
+ ` key move to a marked character MOVE
+count a inp insert text after the cursor EDIT
+count b move back <count> words MOVE
+ c mv change text EDIT
+ d mv delete text EDIT
+count e move forward to the end of the current word MOVE
+count f key move rightward to a given character MOVE
+ g ---
+count h move left MOVE
+count i inp insert text at the cursor EDIT
+count j move down MOVE
+count k move up MOVE
+count l move right MOVE
+ m key mark a line or character
+ n repeat the previous search MOVE
+count o inp open a new line below the current line EDIT
+ p paste text after the cursor
+ q ---
+count r key replace <count> chars by a given character EDIT
+count s inp replace <count> chars with text from the user EDIT
+count t key move rightward *almost* to a given character MOVE
+ u undo the previous edit command
+ v ---
+count w move forward <count> words MOVE
+count x delete the character that the cursor's on EDIT
+ y mv yank text (copy it into a cut buffer)
+ z key scroll current line to the screen's +=top -=bottom .=middle
+count { move back <count> paragraphs MOVE
+count | move to column <count> (the leftmost column is 1)
+count } move forward <count> paragraphs MOVE
+count ~ switch a character between uppercase & lowercase EDIT
+ DEL ---
+--------------------------------------------------------------------------------
+
+count Many commands may be preceded by a count. This is a sequence of digits
+ representing a decimal number. For most commands that use a count,
+ the command is repeated <count> times. The count is always optional,
+ and usually defaults to 1.
+
+key Some commands require two keystrokes. The first key always determines
+ which command is to be executed. The second key is used as a parameter
+ to the command.
+
+mv Six commands (! < > c d y) operate on text between the cursor and some
+ other position. To specify that other position, you are expected to
+ follow the command keystroke with a movement command. Also, you may
+ have the command operate on the whole line that the cursor is on by
+ typing the command key a second time.
+
+inp Many commands allow the user to interactively enter text.
+
+EDIT These commands affect text, and may be repeated by the "." command.
+
+MOVE These commands move the cursor, and may be used to specify the extent
+ of a member of the "mv" class of commands.
+
+
+
+
+
+
+
+
+ Elvis 1.4 VISUAL MODE COMMANDS Page 2-4
+
+
+ E2.1 Input ModeF
+
+ You can't type text into your file directly from visual command
+ mode. Instead, you must first give a command which will put you
+ into input mode. The commands to do this are A/C/I/O/R/S/a/i/o/s.
+
+ The S/s/C/c commands temporarily place a $ at the end of the
+ text that they are going to change.
+
+ In input mode, all keystrokes are inserted into the text at the
+ cursor's position, except for the following:
+
+ ^A insert a copy of the last input text
+ ^D delete one indent character
+ ^H (backspace) erase the character before the cursor
+ ^L redraw the screen
+ ^M (carriage return) insert a newline (^J, linefeed)
+ ^P insert the contents of the cut buffer
+ ^R redraw the screen, like ^L
+ ^T insert an indent character
+ ^U backspace to the beginning of the line
+ ^V insert the following keystroke, even if special
+ ^W backspace to the beginning of the current word
+ ^Z^Z write the file & exit elvis
+ ^[ (ESCape) exit from input mode, back to command mode
+
+ Also, on some systems, ^S may stop output, ^Q may restart
+ output, and ^C may interupt execution. ^@ (the NUL character)
+ cannot be inserted.
+
+ The R visual command puts you in overtype mode, which is a
+ slightly different form of input mode. In overtype mode, each time
+ you insert a character, one of the old characters is deleted from
+ the file.
+
+
+ E2.2 Arrow keys in Input ModeF
+
+ The arrow keys can be used to move the cursor in input mode.
+ (This is an extension; the real Vi doesn't support arrow keys in
+ input mode.) The <PgUp>, <PgDn>, <Home>, and <End> keys work in
+ input mode, too. The <Delete> key deletes a single character in
+ input mode.
+
+ The best thing about allowing arrow keys to work in input mode
+ is that as long as you're in input mode, Elvis seems to have a
+ fairly ordinary user interface. With most other text editors, you
+ are always in either insert mode or replace mode, and you can use
+ the arrow keys at any time to move the cursor. Now, Elvis can act
+ like that, too. In fact, with the new "inputmode" option and the
+ "control-Z control-Z" input command, you may never have to go into
+ visual command mode for simple edit sessions.
+
+
+
+
+
+
+
+
+
+
+
+ Elvis 1.4 VISUAL MODE COMMANDS Page 2-5
+
+
+ E2.3 DigraphsF
+
+ Elvis supports digraphs as a way to enter non-ASCII characters.
+ A digraph is a character which is composed of two other
+ characters. For example, an apostrophe and the letter i could be
+ defined as a digraph which is to be stored & displayed as an
+ accented i.
+
+ There is no single standard for extended ASCII character sets.
+ Elvis can be compiled to fill the digraph with values appropriate
+ for either the IBM PC character set, or the LATIN-1 character set
+ used by X windows, or neither. (See the discussions of -DCS_IBMPC
+ and -DCS_LATIN1 in the CFLAGS section of this manual.) You can view
+ or edit the digraph table via the ":digraph" colon command.
+
+ Digraphs wil not be recognized until you've entered ":set
+ digraph".
+
+ To actually use a digraph type the first character, then hit
+ <Backspace>, and then type the second character. Elvis will then
+ substitute the non-ASCII character in their place.
+
+
+ E2.4 AbbreviationsF
+
+ Elvis can expand abbreviations for you. You define an
+ abbreviation with the :abbr command, and then whenever you type in
+ the abbreviated form while in input mode, elvis will immediately
+ the long form. COBOL programmers should find this useful. :-)
+
+ Elvis doesn't perform the substitution until you type a
+ non-alphanumeric character to mark the end of the word. If you
+ type a control-V before that non-alphanumeric character, then Elvis
+ will not perform the substitution.
+
+
+ E2.5 Auto-IndentF
+
+ With the ":set autoindent" option turned on, Elvis will
+ automatically insert leading whitespace at the beginning of each
+ new line that you type in. The leading whitespace is copied from
+ the preceding line.
+
+ To add more leading whitespace, type control-T. To remove some
+ whitespace, type control-D.
+
+ Elvis' autoindent mode isn't 100% compatible with vi's. In
+ Elvis, 0^D and ^^D don't work, ^U can wipeout all indentation, and
+ sometimes Elvis will use a different amount of indentation than vi
+ would.
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ex.c b/ex.c
new file mode 100644
index 0000000..45d9ad4
--- /dev/null
+++ b/ex.c
@@ -0,0 +1,675 @@
+/* ex.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains the code for reading ex commands. */
+
+#include "config.h"
+#include <ctype.h>
+#include "vi.h"
+
+#ifndef isascii
+# define isascii(c) !((c)&~0x7f)
+#endif
+
+/* This data type is used to describe the possible argument combinations */
+typedef short ARGT;
+#define FROM 1 /* allow a linespec */
+#define TO 2 /* allow a second linespec */
+#define BANG 4 /* allow a ! after the command name */
+#define EXTRA 8 /* allow extra args after command name */
+#define XFILE 16 /* expand wildcards in extra part */
+#define NOSPC 32 /* no spaces allowed in the extra part */
+#define DFLALL 64 /* default file range is 1,$ */
+#define DFLNONE 128 /* no default file range */
+#define NODFL 256 /* do not default to the current file name */
+#define EXRCOK 512 /* can be in a .exrc file */
+#define NL 1024 /* if mode!=MODE_EX, then write a newline first */
+#define PLUS 2048 /* allow a line number, as in ":e +32 foo" */
+#define ZERO 4096 /* allow 0 to be given as a line number */
+#define FILES (XFILE + EXTRA) /* multiple extra files allowed */
+#define WORD1 (EXTRA + NOSPC) /* one extra word allowed */
+#define FILE1 (FILES + NOSPC) /* 1 file allowed, defaults to current file */
+#define NAMEDF (FILE1 + NODFL) /* 1 file allowed, defaults to "" */
+#define NAMEDFS (FILES + NODFL) /* multiple files allowed, default is "" */
+#define RANGE (FROM + TO) /* range of linespecs allowed */
+#define NONE 0 /* no args allowed at all */
+
+/* This array maps ex command names to command codes. The order in which
+ * command names are listed below is significant -- ambiguous abbreviations
+ * are always resolved to be the first possible match. (e.g. "r" is taken
+ * to mean "read", not "rewind", because "read" comes before "rewind")
+ */
+static struct
+{
+ char *name; /* name of the command */
+ CMD code; /* enum code of the command */
+ void (*fn)();/* function which executes the command */
+ ARGT argt; /* command line arguments permitted/needed/used */
+}
+ cmdnames[] =
+{ /* cmd name cmd code function arguments */
+ {"append", CMD_APPEND, cmd_append, FROM+ZERO },
+#ifdef DEBUG
+ {"bug", CMD_DEBUG, cmd_debug, RANGE+BANG+EXTRA+NL},
+#endif
+ {"change", CMD_CHANGE, cmd_append, RANGE },
+ {"delete", CMD_DELETE, cmd_delete, RANGE+WORD1 },
+ {"edit", CMD_EDIT, cmd_edit, BANG+FILE1+PLUS },
+ {"file", CMD_FILE, cmd_file, NAMEDF },
+ {"global", CMD_GLOBAL, cmd_global, RANGE+BANG+EXTRA+DFLALL},
+ {"insert", CMD_INSERT, cmd_append, FROM },
+ {"join", CMD_INSERT, cmd_join, RANGE },
+ {"k", CMD_MARK, cmd_mark, FROM+WORD1 },
+ {"list", CMD_LIST, cmd_print, RANGE+NL },
+ {"move", CMD_MOVE, cmd_move, RANGE+EXTRA },
+ {"next", CMD_NEXT, cmd_next, BANG+NAMEDFS },
+ {"Next", CMD_PREVIOUS, cmd_next, BANG },
+ {"print", CMD_PRINT, cmd_print, RANGE+NL },
+ {"quit", CMD_QUIT, cmd_xit, BANG },
+ {"read", CMD_READ, cmd_read, FROM+ZERO+NAMEDF},
+ {"substitute", CMD_SUBSTITUTE, cmd_substitute, RANGE+EXTRA },
+ {"to", CMD_COPY, cmd_move, RANGE+EXTRA },
+ {"undo", CMD_UNDO, cmd_undo, NONE },
+ {"vglobal", CMD_VGLOBAL, cmd_global, RANGE+EXTRA+DFLALL},
+ {"write", CMD_WRITE, cmd_write, RANGE+BANG+FILE1+DFLALL},
+ {"xit", CMD_XIT, cmd_xit, BANG+NL },
+ {"yank", CMD_YANK, cmd_delete, RANGE+WORD1 },
+
+ {"!", CMD_BANG, cmd_shell, EXRCOK+RANGE+NAMEDFS+DFLNONE+NL},
+ {"<", CMD_SHIFTL, cmd_shift, RANGE },
+ {">", CMD_SHIFTR, cmd_shift, RANGE },
+ {"=", CMD_EQUAL, cmd_file, RANGE },
+ {"&", CMD_SUBAGAIN, cmd_substitute, RANGE },
+#ifndef NO_AT
+ {"@", CMD_AT, cmd_at, EXTRA },
+#endif
+
+#ifndef NO_ABBR
+ {"abbreviate", CMD_ABBR, cmd_abbr, EXRCOK+EXTRA },
+#endif
+ {"args", CMD_ARGS, cmd_args, EXRCOK+NAMEDFS },
+#ifndef NO_ERRLIST
+ {"cc", CMD_CC, cmd_make, BANG+FILES },
+#endif
+ {"cd", CMD_CD, cmd_cd, EXRCOK+NAMEDF },
+ {"copy", CMD_COPY, cmd_move, RANGE+EXTRA },
+#ifndef NO_DIGRAPH
+ {"digraph", CMD_DIGRAPH, cmd_digraph, EXRCOK+BANG+EXTRA},
+#endif
+#ifndef NO_ERRLIST
+ {"errlist", CMD_ERRLIST, cmd_errlist, BANG+NAMEDF },
+#endif
+ {"ex", CMD_EDIT, cmd_edit, BANG+FILE1 },
+ {"map", CMD_MAP, cmd_map, EXRCOK+BANG+EXTRA},
+#ifndef NO_MKEXRC
+ {"mkexrc", CMD_MKEXRC, cmd_mkexrc, NAMEDF },
+#endif
+ {"number", CMD_NUMBER, cmd_print, RANGE+NL },
+ {"put", CMD_PUT, cmd_put, FROM+ZERO+WORD1 },
+ {"set", CMD_SET, cmd_set, EXRCOK+EXTRA },
+ {"shell", CMD_SHELL, cmd_shell, NL },
+ {"source", CMD_SOURCE, cmd_source, EXRCOK+NAMEDF },
+ {"tag", CMD_TAG, cmd_tag, BANG+WORD1 },
+ {"version", CMD_VERSION, cmd_version, EXRCOK+NONE },
+ {"visual", CMD_VISUAL, cmd_visual, NONE },
+ {"wq", CMD_WQUIT, cmd_xit, NL },
+
+#ifdef DEBUG
+ {"debug", CMD_DEBUG, cmd_debug, RANGE+BANG+EXTRA+NL},
+ {"validate", CMD_VALIDATE, cmd_validate, BANG+NL },
+#endif
+ {"chdir", CMD_CD, cmd_cd, EXRCOK+NAMEDF },
+#ifndef NO_ERRLIST
+ {"make", CMD_MAKE, cmd_make, BANG+NAMEDFS },
+#endif
+ {"mark", CMD_MARK, cmd_mark, FROM+WORD1 },
+ {"previous", CMD_PREVIOUS, cmd_next, BANG },
+ {"rewind", CMD_REWIND, cmd_next, BANG },
+ {"unmap", CMD_UNMAP, cmd_map, EXRCOK+BANG+EXTRA},
+#ifndef NO_ABBR
+ {"unabbreviate",CMD_UNABBR, cmd_abbr, EXRCOK+WORD1 },
+#endif
+
+ {(char *)0}
+};
+
+
+/* This function parses a search pattern - given a pointer to a / or ?,
+ * it replaces the ending / or ? with a \0, and returns a pointer to the
+ * stuff that came after the pattern.
+ */
+char *parseptrn(ptrn)
+ REG char *ptrn;
+{
+ REG char *scan;
+
+ for (scan = ptrn + 1;
+ *scan && *scan != *ptrn;
+ scan++)
+ {
+ /* allow backslashed versions of / and ? in the pattern */
+ if (*scan == '\\' && scan[1] != '\0')
+ {
+ scan++;
+ }
+ }
+ if (*scan)
+ {
+ *scan++ = '\0';
+ }
+
+ return scan;
+}
+
+
+/* This function parses a line specifier for ex commands */
+char *linespec(s, markptr)
+ REG char *s; /* start of the line specifier */
+ MARK *markptr; /* where to store the mark's value */
+{
+ long num;
+ REG char *t;
+
+ /* parse each ;-delimited clause of this linespec */
+ do
+ {
+ /* skip an initial ';', if any */
+ if (*s == ';')
+ {
+ s++;
+ }
+
+ /* skip leading spaces */
+ while (isascii(*s) && isspace(*s))
+ {
+ s++;
+ }
+
+ /* dot means current position */
+ if (*s == '.')
+ {
+ s++;
+ *markptr = cursor;
+ }
+ /* '$' means the last line */
+ else if (*s == '$')
+ {
+ s++;
+ *markptr = MARK_LAST;
+ }
+ /* digit means an absolute line number */
+ else if (isascii(*s) && isdigit(*s))
+ {
+ for (num = 0; isascii(*s) && isdigit(*s); s++)
+ {
+ num = num * 10 + *s - '0';
+ }
+ *markptr = MARK_AT_LINE(num);
+ }
+ /* appostrophe means go to a set mark */
+ else if (*s == '\'')
+ {
+ s++;
+ *markptr = m_tomark(cursor, 1L, (int)*s);
+ s++;
+ }
+ /* slash means do a search */
+ else if (*s == '/' || *s == '?')
+ {
+ /* put a '\0' at the end of the search pattern */
+ t = parseptrn(s);
+
+ /* search for the pattern */
+ *markptr &= ~(BLKSIZE - 1);
+ if (*s == '/')
+ {
+ pfetch(markline(*markptr));
+ if (plen > 0)
+ *markptr += plen - 1;
+ *markptr = m_fsrch(*markptr, s);
+ }
+ else
+ {
+ *markptr = m_bsrch(*markptr, s);
+ }
+
+ /* adjust command string pointer */
+ s = t;
+ }
+
+ /* if linespec was faulty, quit now */
+ if (!*markptr)
+ {
+ return s;
+ }
+
+ /* maybe add an offset */
+ t = s;
+ if (*t == '-' || *t == '+')
+ {
+ s++;
+ for (num = 0; *s >= '0' && *s <= '9'; s++)
+ {
+ num = num * 10 + *s - '0';
+ }
+ if (num == 0)
+ {
+ num = 1;
+ }
+ *markptr = m_updnto(*markptr, num, *t);
+ }
+ } while (*s == ';' || *s == '+' || *s == '-');
+
+ return s;
+}
+
+
+
+/* This function reads an ex command and executes it. */
+void ex()
+{
+ char cmdbuf[80];
+ REG int cmdlen;
+ static long oldline;
+
+ significant = FALSE;
+ oldline = markline(cursor);
+
+ while (mode == MODE_EX)
+ {
+ /* read a line */
+ cmdlen = vgets(':', cmdbuf, sizeof cmdbuf);
+ if (cmdlen < 0)
+ {
+ return;
+ }
+
+ /* if empty line, assume ".+1" */
+ if (cmdlen == 0)
+ {
+ strcpy(cmdbuf, ".+1");
+ qaddch('\r');
+ clrtoeol();
+ }
+ else
+ {
+ addch('\n');
+ }
+ refresh();
+
+ /* parse & execute the command */
+ doexcmd(cmdbuf);
+
+ /* handle autoprint */
+ if (significant || markline(cursor) != oldline)
+ {
+ significant = FALSE;
+ oldline = markline(cursor);
+ if (*o_autoprint && mode == MODE_EX)
+ {
+ cmd_print(cursor, cursor, CMD_PRINT, FALSE, "");
+ }
+ }
+ }
+}
+
+void doexcmd(cmdbuf)
+ char *cmdbuf; /* string containing an ex command */
+{
+ REG char *scan; /* used to scan thru cmdbuf */
+ MARK frommark; /* first linespec */
+ MARK tomark; /* second linespec */
+ REG int cmdlen; /* length of the command name given */
+ CMD cmd; /* what command is this? */
+ ARGT argt; /* argument types for this command */
+ short forceit; /* bang version of a command? */
+ REG int cmdidx; /* index of command */
+ REG char *build; /* used while copying filenames */
+ int iswild; /* boolean: filenames use wildcards? */
+ int isdfl; /* using default line ranges? */
+ int didsub; /* did we substitute file names for % or # */
+
+
+ /* ex commands can't be undone via the shift-U command */
+ U_line = 0L;
+
+ /* ignore command lines that start with a double-quote */
+ if (*cmdbuf == '"')
+ {
+ return;
+ }
+
+ /* permit extra colons at the start of the line */
+ while (*cmdbuf == ':')
+ {
+ cmdbuf++;
+ }
+
+ /* parse the line specifier */
+ scan = cmdbuf;
+ if (nlines < 1)
+ {
+ /* no file, so don't allow addresses */
+ }
+ else if (*scan == '%')
+ {
+ /* '%' means all lines */
+ frommark = MARK_FIRST;
+ tomark = MARK_LAST;
+ scan++;
+ }
+ else if (*scan == '0')
+ {
+ frommark = tomark = MARK_UNSET;
+ scan++;
+ }
+ else
+ {
+ frommark = cursor;
+ scan = linespec(scan, &frommark);
+ tomark = frommark;
+ if (frommark && *scan == ',')
+ {
+ scan++;
+ scan = linespec(scan, &tomark);
+ }
+ if (!tomark)
+ {
+ /* faulty line spec -- fault already described */
+ return;
+ }
+ if (frommark > tomark)
+ {
+ msg("first address exceeds the second");
+ return;
+ }
+ }
+ isdfl = (scan == cmdbuf);
+
+ /* skip whitespace */
+ while (isascii(*scan) && isspace(*scan))
+ {
+ scan++;
+ }
+
+ /* if no command, then just move the cursor to the mark */
+ if (!*scan)
+ {
+ cursor = tomark;
+ return;
+ }
+
+ /* figure out how long the command name is */
+ if (isascii(*scan) && !isalpha(*scan))
+ {
+ cmdlen = 1;
+ }
+ else
+ {
+ for (cmdlen = 1;
+ !isascii(scan[cmdlen]) || isalpha(scan[cmdlen]);
+ cmdlen++)
+ {
+ }
+ }
+
+ /* lookup the command code */
+ for (cmdidx = 0;
+ cmdnames[cmdidx].name && strncmp(scan, cmdnames[cmdidx].name, cmdlen);
+ cmdidx++)
+ {
+ }
+ argt = cmdnames[cmdidx].argt;
+ cmd = cmdnames[cmdidx].code;
+ if (cmd == CMD_NULL)
+ {
+#if OSK
+ msg("Unknown command \"%s\"", scan);
+#else
+ msg("Unknown command \"%.*s\"", cmdlen, scan);
+#endif
+ return;
+ }
+
+ /* if the command ended with a bang, set the forceit flag */
+ scan += cmdlen;
+ if ((argt & BANG) && *scan == '!')
+ {
+ scan++;
+ forceit = 1;
+ }
+ else
+ {
+ forceit = 0;
+ }
+
+ /* skip any more whitespace, to leave scan pointing to arguments */
+ while (isascii(*scan) && isspace(*scan))
+ {
+ scan++;
+ }
+
+ /* a couple of special cases for filenames */
+ if (argt & XFILE)
+ {
+ /* if names were given, process them */
+ if (*scan)
+ {
+ for (build = tmpblk.c, iswild = didsub = FALSE; *scan; scan++)
+ {
+ switch (*scan)
+ {
+ case '%':
+ if (!*origname)
+ {
+ msg("No filename to substitute for %%");
+ return;
+ }
+ strcpy(build, origname);
+ while (*build)
+ {
+ build++;
+ }
+ didsub = TRUE;
+ break;
+
+ case '#':
+ if (!*prevorig)
+ {
+ msg("No filename to substitute for #");
+ return;
+ }
+ strcpy(build, prevorig);
+ while (*build)
+ {
+ build++;
+ }
+ didsub = TRUE;
+ break;
+
+ case '*':
+ case '?':
+#if !(MSDOS || TOS)
+ case '[':
+ case '`':
+ case '{': /* } */
+ case '$':
+ case '~':
+#endif
+ *build++ = *scan;
+ iswild = TRUE;
+ break;
+
+ default:
+ *build++ = *scan;
+ }
+ }
+ *build = '\0';
+
+ if (cmd == CMD_BANG
+ || cmd == CMD_READ && tmpblk.c[0] == '!'
+ || cmd == CMD_WRITE && tmpblk.c[0] == '!')
+ {
+ if (didsub)
+ {
+ if (mode != MODE_EX)
+ {
+ addch('\n');
+ }
+ addstr(tmpblk.c);
+ addch('\n');
+ exrefresh();
+ }
+ }
+ else
+ {
+ if (iswild && tmpblk.c[0] != '>')
+ {
+ scan = wildcard(tmpblk.c);
+ }
+ }
+ }
+ else /* no names given, maybe assume origname */
+ {
+ if (!(argt & NODFL))
+ {
+ strcpy(tmpblk.c, origname);
+ }
+ else
+ {
+ *tmpblk.c = '\0';
+ }
+ }
+
+ scan = tmpblk.c;
+ }
+
+ /* bad arguments? */
+ if (!(argt & EXRCOK) && nlines < 1L)
+ {
+ msg("Can't use the \"%s\" command in a %s file", cmdnames[cmdidx].name, EXRC);
+ return;
+ }
+ if (!(argt & (ZERO | EXRCOK)) && frommark == MARK_UNSET)
+ {
+ msg("Can't use address 0 with \"%s\" command.", cmdnames[cmdidx].name);
+ return;
+ }
+ if (!(argt & FROM) && frommark != cursor && nlines >= 1L)
+ {
+ msg("Can't use address with \"%s\" command.", cmdnames[cmdidx].name);
+ return;
+ }
+ if (!(argt & TO) && tomark != frommark && nlines >= 1L)
+ {
+ msg("Can't use a range with \"%s\" command.", cmdnames[cmdidx].name);
+ return;
+ }
+ if (!(argt & EXTRA) && *scan)
+ {
+ msg("Extra characters after \"%s\" command.", cmdnames[cmdidx].name);
+ return;
+ }
+ if ((argt & NOSPC) && !(cmd == CMD_READ && (forceit || *scan == '!')))
+ {
+ build = scan;
+#ifndef CRUNCH
+ if ((argt & PLUS) && *build == '+')
+ {
+ while (*build && !(isascii(*build) && isspace(*build)))
+ {
+ build++;
+ }
+ while (*build && isascii(*build) && isspace(*build))
+ {
+ build++;
+ }
+ }
+#endif /* not CRUNCH */
+ for (; *build; build++)
+ {
+ if (isspace(*build))
+ {
+ msg("Too many %s to \"%s\" command.",
+ (argt & XFILE) ? "filenames" : "arguments",
+ cmdnames[cmdidx].name);
+ return;
+ }
+ }
+ }
+
+ /* some commands have special default ranges */
+ if (isdfl && (argt & DFLALL))
+ {
+ frommark = MARK_FIRST;
+ tomark = MARK_LAST;
+ }
+ else if (isdfl && (argt & DFLNONE))
+ {
+ frommark = tomark = 0L;
+ }
+
+ /* write a newline if called from visual mode */
+ if ((argt & NL) && mode != MODE_EX && !exwrote)
+ {
+ addch('\n');
+ exrefresh();
+ }
+
+ /* act on the command */
+ (*cmdnames[cmdidx].fn)(frommark, tomark, cmd, forceit, scan);
+}
+
+
+/* This function executes EX commands from a file. It returns 1 normally, or
+ * 0 if the file could not be opened for reading.
+ */
+int doexrc(filename)
+ char *filename; /* name of a ".exrc" file */
+{
+ int fd; /* file descriptor */
+ int len; /* length of the ".exrc" file */
+ char buf[MAXRCLEN]; /* buffer, holds the entire .exrc file */
+
+ /* open the file, read it, and close */
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ {
+ return 0;
+ }
+ len = tread(fd, buf, MAXRCLEN);
+ close(fd);
+
+ /* execute the string */
+ exstring(buf, len);
+
+ return 1;
+}
+
+void exstring(buf, len)
+ char *buf; /* the commands to execute */
+ int len; /* the length of the string */
+{
+ char *cmd; /* start of a command */
+ char *end; /* used to search for the end of cmd */
+
+ /* find & do each command */
+ for (cmd = buf; cmd < &buf[len]; cmd = end + 1)
+ {
+ /* find the end of the command */
+ for (end = cmd; end < &buf[len] && *end != '\n' && *end != '|'; end++)
+ {
+ }
+ *end = '\0';
+
+ /* do it */
+ doexcmd(cmd);
+ }
+}
diff --git a/input.c b/input.c
new file mode 100644
index 0000000..36524a2
--- /dev/null
+++ b/input.c
@@ -0,0 +1,817 @@
+/* input.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains the input() function, which implements vi's INPUT mode.
+ * It also contains the code that supports digraphs.
+ */
+
+#include <ctype.h>
+#include "config.h"
+#include "vi.h"
+
+
+#ifndef NO_DIGRAPH
+static struct _DIG
+{
+ struct _DIG *next;
+ char key1;
+ char key2;
+ char dig;
+ char save;
+} *digs;
+
+char digraph(key1, key2)
+ char key1; /* the underlying character */
+ char key2; /* the second character */
+{
+ int newkey;
+ REG struct _DIG *dp;
+
+ /* if digraphs are disabled, then just return the new char */
+ if (!*o_digraph)
+ {
+ return key2;
+ }
+
+ /* remember the new key, so we can return it if this isn't a digraph */
+ newkey = key2;
+
+ /* sort key1 and key2, so that their original order won't matter */
+ if (key1 > key2)
+ {
+ key2 = key1;
+ key1 = newkey;
+ }
+
+ /* scan through the digraph chart */
+ for (dp = digs;
+ dp && (dp->key1 != key1 || dp->key2 != key2);
+ dp = dp->next)
+ {
+ }
+
+ /* if this combination isn't in there, just use the new key */
+ if (!dp)
+ {
+ return newkey;
+ }
+
+ /* else use the digraph key */
+ return dp->dig;
+}
+
+/* this function lists or defines digraphs */
+void do_digraph(bang, extra)
+ int bang;
+ char extra[];
+{
+ int dig;
+ REG struct _DIG *dp;
+ struct _DIG *prev;
+ static int user_defined = FALSE; /* boolean: are all later digraphs user-defined? */
+ char listbuf[8];
+
+ /* if "extra" is NULL, then we've reached the end of the built-ins */
+ if (!extra)
+ {
+ user_defined = TRUE;
+ return;
+ }
+
+ /* if no args, then display the existing digraphs */
+ if (*extra < ' ')
+ {
+ listbuf[0] = listbuf[1] = listbuf[2] = listbuf[5] = ' ';
+ listbuf[7] = '\0';
+ for (dig = 0, dp = digs; dp; dp = dp->next)
+ {
+ if (dp->save || bang)
+ {
+ dig += 7;
+ if (dig >= COLS)
+ {
+ addch('\n');
+ exrefresh();
+ dig = 7;
+ }
+ listbuf[3] = dp->key1;
+ listbuf[4] = dp->key2;
+ listbuf[6] = dp->dig;
+ qaddstr(listbuf);
+ }
+ }
+ addch('\n');
+ exrefresh();
+ return;
+ }
+
+ /* make sure we have at least two characters */
+ if (!extra[1])
+ {
+ msg("Digraphs must be composed of two characters");
+ return;
+ }
+
+ /* sort key1 and key2, so that their original order won't matter */
+ if (extra[0] > extra[1])
+ {
+ dig = extra[0];
+ extra[0] = extra[1];
+ extra[1] = dig;
+ }
+
+ /* locate the new digraph character */
+ for (dig = 2; extra[dig] == ' ' || extra[dig] == '\t'; dig++)
+ {
+ }
+ dig = extra[dig];
+ if (!bang && dig)
+ {
+ dig |= 0x80;
+ }
+
+ /* search for the digraph */
+ for (prev = (struct _DIG *)0, dp = digs;
+ dp && (dp->key1 != extra[0] || dp->key2 != extra[1]);
+ prev = dp, dp = dp->next)
+ {
+ }
+
+ /* deleting the digraph? */
+ if (!dig)
+ {
+ if (!dp)
+ {
+#ifndef CRUNCH
+ msg("%c%c not a digraph", extra[0], extra[1]);
+#endif
+ return;
+ }
+ if (prev)
+ prev->next = dp->next;
+ else
+ digs = dp->next;
+ free(dp);
+ return;
+ }
+
+ /* if necessary, create a new digraph struct for the new digraph */
+ if (dig && !dp)
+ {
+ dp = (struct _DIG *)malloc(sizeof *dp);
+ if (!dp)
+ {
+ msg("Out of space in the digraph table");
+ return;
+ }
+ if (prev)
+ prev->next = dp;
+ else
+ digs = dp;
+ dp->next = (struct _DIG *)0;
+ }
+
+ /* assign it the new digraph value */
+ dp->key1 = extra[0];
+ dp->key2 = extra[1];
+ dp->dig = dig;
+ dp->save = user_defined;
+}
+
+# ifndef NO_MKEXRC
+void savedigs(fd)
+ int fd;
+{
+ static char buf[] = "digraph! XX Y\n";
+ REG struct _DIG *dp;
+
+ for (dp = digs; dp; dp = dp->next)
+ {
+ if (dp->save)
+ {
+ buf[9] = dp->key1;
+ buf[10] = dp->key2;
+ buf[12] = dp->dig;
+ write(fd, buf, (unsigned)14);
+ }
+ }
+}
+# endif
+#endif
+
+
+#ifndef NO_ABBR
+static struct _AB
+{
+ struct _AB *next;
+ char *large; /* the expanded form */
+ char small[1]; /* the abbreviated form (appended to struct) */
+}
+ *abbrev;
+
+/* This functions lists or defines abbreviations */
+void do_abbr(extra)
+ char *extra;
+{
+ int smlen; /* length of the small form */
+ int lrg; /* index of the start of the large form */
+ REG struct _AB *ab; /* used to move through the abbrev list */
+ struct _AB *prev;
+
+ /* no arguments? */
+ if (!*extra)
+ {
+ /* list all current abbreviations */
+ for (ab = abbrev; ab; ab = ab->next)
+ {
+ qaddstr("abbr ");
+ qaddstr(ab->small);
+ qaddch(' ');
+ qaddstr(ab->large);
+ addch('\n');
+ exrefresh();
+ }
+ return;
+ }
+
+ /* else one or more arguments. Parse the first & look up in abbrev[] */
+ for (smlen = 0; extra[smlen] && isalnum(extra[smlen]); smlen++)
+ {
+ }
+ for (prev = (struct _AB *)0, ab = abbrev; ab; prev = ab, ab = ab->next)
+ {
+ if (!strncmp(extra, ab->small, smlen) && !ab->small[smlen])
+ {
+ break;
+ }
+ }
+
+ /* locate the start of the large form, if any */
+ for (lrg = smlen; extra[lrg] && isascii(extra[lrg]) && isspace(extra[lrg]); lrg++)
+ {
+ }
+
+ /* only one arg? */
+ if (!extra[lrg])
+ {
+ /* trying to undo an abbreviation which doesn't exist? */
+ if (!ab)
+ {
+#ifndef CRUNCH
+ msg("\"%s\" not an abbreviation", extra);
+#endif
+ return;
+ }
+
+ /* undo the abbreviation */
+ if (prev)
+ prev->next = ab->next;
+ else
+ abbrev = ab->next;
+ free(ab->large);
+ free(ab);
+
+ return;
+ }
+
+ /* multiple args - [re]define an abbreviation */
+ if (ab)
+ {
+ /* redefining - free the old large form */
+ free(ab->large);
+ }
+ else
+ {
+ /* adding a new definition - make a new struct */
+ ab = (struct _AB *)malloc((unsigned)(smlen + sizeof *ab));
+#ifndef CRUNCH
+ if (!ab)
+ {
+ msg("Out of memory -- Sorry");
+ return;
+ }
+#endif
+ strncpy(ab->small, extra, smlen);
+ ab->small[smlen] = '\0';
+ ab->next = (struct _AB *)0;
+ if (prev)
+ prev->next = ab;
+ else
+ abbrev = ab;
+ }
+
+ /* store the new form */
+ ab->large = (char *)malloc((unsigned)(strlen(&extra[lrg]) + 1));
+ strcpy(ab->large, &extra[lrg]);
+}
+
+
+# ifndef NO_MKEXRC
+/* This function is called from cmd_mkexrc() to save the abbreviations */
+void saveabbr(fd)
+ int fd; /* fd to which the :abbr commands should be written */
+{
+ REG struct _AB *ab;
+
+ for (ab = abbrev; ab; ab = ab->next)
+ {
+ twrite(fd, "abbr ", 5);
+ twrite(fd, ab->small, strlen(ab->small));
+ twrite(fd, " ", 1);
+ twrite(fd, ab->large, strlen(ab->large));
+ twrite(fd, "\n", 1);
+ }
+}
+# endif
+
+/* This function should be called before each char is inserted. If the next
+ * char is non-alphanumeric and we're at the end of a word, then that word
+ * is checked against the abbrev[] array and expanded, if appropriate. Upon
+ * returning from this function, the new char still must be inserted.
+ */
+static MARK expandabbr(m, ch)
+ MARK m; /* the cursor position */
+ int ch; /* the character to insert */
+{
+ char *word; /* where the word starts */
+ int len; /* length of the word */
+ REG struct _AB *ab;
+
+ /* if no abbreviations are in effect, or ch is aphanumeric, then
+ * don't do anything
+ */
+ if (!abbrev || !isascii(ch) || isalnum(ch))
+ {
+ return m;
+ }
+
+ /* see where the preceding word starts */
+ pfetch(markline(m));
+ for (word = ptext + markidx(m), len = 0;
+ --word >= ptext && (!isascii(*word) || isalnum(*word));
+ len++)
+ {
+ }
+ word++;
+
+ /* if zero-length, then it isn't a word, really -- so nothing */
+ if (len == 0)
+ {
+ return m;
+ }
+
+ /* look it up in the abbrev list */
+ for (ab = abbrev; ab; ab = ab->next)
+ {
+ if (!strncmp(ab->small, word, len) && !ab->small[len])
+ {
+ break;
+ }
+ }
+
+ /* not an abbreviation? then do nothing */
+ if (!ab)
+ {
+ return m;
+ }
+
+ /* else replace the small form with the large form */
+ add(m, ab->large);
+ delete(m - len, m);
+
+ /* return with the cursor after the end of the large form */
+ return m - len + strlen(ab->large);
+}
+#endif
+
+
+/* This function allows the user to replace an existing (possibly zero-length)
+ * chunk of text with typed-in text. It returns the MARK of the last character
+ * that the user typed in.
+ */
+MARK input(from, to, when)
+ MARK from; /* where to start inserting text */
+ MARK to; /* extent of text to delete */
+ int when; /* either WHEN_VIINP or WHEN_VIREP */
+{
+ char key[2]; /* key char followed by '\0' char */
+ char *build; /* used in building a newline+indent string */
+ char *scan; /* used while looking at the indent chars of a line */
+ MARK m; /* some place in the text */
+#ifndef NO_EXTENSIONS
+ int quit = FALSE; /* boolean: are we exiting after this? */
+#endif
+
+#ifdef DEBUG
+ /* if "from" and "to" are reversed, complain */
+ if (from > to)
+ {
+ msg("ERROR: input(%ld:%d, %ld:%d)",
+ markline(from), markidx(from),
+ markline(to), markidx(to));
+ return MARK_UNSET;
+ }
+#endif
+
+ key[1] = 0;
+
+ /* if we're replacing text with new text, save the old stuff */
+ /* (Alas, there is no easy way to save text for replace mode) */
+ if (from != to)
+ {
+ cut(from, to);
+ }
+
+ ChangeText
+ {
+ /* if doing a dot command, then reuse the previous text */
+ if (doingdot)
+ {
+ /* delete the text that's there now */
+ if (from != to)
+ {
+ delete(from, to);
+ }
+
+ /* insert the previous text */
+ cutname('.');
+ cursor = paste(from, FALSE, TRUE) + 1L;
+ }
+ else /* interactive version */
+ {
+ /* if doing a change within the line... */
+ if (from != to && markline(from) == markline(to))
+ {
+ /* mark the end of the text with a "$" */
+ change(to - 1, to, "$");
+ }
+ else
+ {
+ /* delete the old text right off */
+ if (from != to)
+ {
+ delete(from, to);
+ }
+ to = from;
+ }
+
+ /* handle autoindent of the first line, maybe */
+ cursor = from;
+ if (*o_autoindent && markline(cursor) > 1L && markidx(cursor) == 0)
+ {
+ /* Only autoindent blank lines. */
+ pfetch(markline(cursor));
+ if (plen == 0)
+ {
+ /* Okay, we really want to autoindent */
+ pfetch(markline(cursor) - 1L);
+ for (scan = ptext, build = tmpblk.c;
+ *scan == ' ' || *scan == '\t';
+ )
+ {
+ *build++ = *scan++;
+ }
+ if (build > tmpblk.c)
+ {
+ *build = '\0';
+ add(cursor, tmpblk.c);
+ cursor += (build - tmpblk.c);
+ }
+ }
+ }
+
+ /* repeatedly add characters from the user */
+ for (;;)
+ {
+ /* Get a character */
+ redraw(cursor, TRUE);
+#ifdef DEBUG
+ msg("cursor=%ld.%d, to=%ld.%d",
+ markline(cursor), markidx(cursor),
+ markline(to), markidx(to));
+#endif
+ key[0] = getkey(when);
+
+ /* if whitespace & wrapmargin is set & we're
+ * past the warpmargin, then change the
+ * whitespace character into a newline
+ */
+ if ((*key == ' ' || *key == '\t')
+ && *o_wrapmargin != 0)
+ {
+ pfetch(markline(cursor));
+ if (idx2col(cursor, ptext, TRUE) > COLS - (*o_wrapmargin & 0xff))
+ {
+ *key = '\n';
+ }
+ }
+
+ /* process it */
+ switch (*key)
+ {
+#ifndef NO_EXTENSIONS
+ case 0: /* special movement mapped keys */
+ *key = getkey(0);
+ switch (*key)
+ {
+ case 'h': m = m_left(cursor, 0L); break;
+ case 'j':
+ case 'k': m = m_updnto(cursor, 0L, *key); break;
+ case 'l': m = cursor + 1; break;
+ case 'b': m = m_bword(cursor, 0L); break;
+ case 'w': m = m_fword(cursor, 0L); break;
+ case '^': m = m_front(cursor, 0L); break;
+ case '$': m = m_rear(cursor, 0L); break;
+ case ctrl('B'):
+ case ctrl('F'):
+ m = m_scroll(cursor, 0L, *key); break;
+ case 'x': m = v_xchar(cursor, 0L); break;
+ case 'i': m = to = from = cursor; break;
+ default: m = MARK_UNSET; break;
+ }
+ /* adjust the moved cursor */
+ m = adjmove(cursor, m, (*key == 'j' || *key == 'k' ? 0x20 : 0));
+ if (*key == '$' || (*key == 'l' && m <= cursor))
+ {
+ m++;
+ }
+ /* if the cursor is reasonable, use it */
+ if (m == MARK_UNSET)
+ {
+ beep();
+ }
+ else
+ {
+ if (to > cursor)
+ {
+ delete(cursor, to);
+ redraw(cursor, TRUE);
+ }
+ from = to = cursor = m;
+ }
+ break;
+
+ case ctrl('Z'):
+ if (getkey(0) == ctrl('Z'))
+ {
+ quit = TRUE;
+ goto BreakBreak;
+ }
+ break;
+#endif
+
+ case ctrl('['):
+#ifndef NO_ABBR
+ cursor = expandabbr(cursor, ctrl('['));
+#endif
+ goto BreakBreak;
+
+ case ctrl('U'):
+ if (markline(cursor) == markline(from))
+ {
+ cursor = from;
+ }
+ else
+ {
+ cursor &= ~(BLKSIZE - 1);
+ }
+ break;
+
+ case ctrl('D'):
+ case ctrl('T'):
+ if (to > cursor)
+ {
+ delete(cursor, to);
+ }
+ mark[27] = cursor;
+ cmd_shift(cursor, cursor, *key == ctrl('D') ? CMD_SHIFTL : CMD_SHIFTR, TRUE, "");
+ if (mark[27])
+ {
+ cursor = mark[27];
+ }
+ else
+ {
+ cursor = m_front(cursor, 0L);
+ }
+ to = cursor;
+ break;
+
+ case '\b':
+ if (cursor <= from)
+ {
+ beep();
+ }
+ else if (markidx(cursor) == 0)
+ {
+ cursor -= BLKSIZE;
+ pfetch(markline(cursor));
+ cursor += plen;
+ }
+ else
+ {
+ cursor--;
+ }
+ break;
+
+ case ctrl('W'):
+ m = m_bword(cursor, 1L);
+ if (markline(m) == markline(cursor) && m >= from)
+ {
+ cursor = m;
+ if (from > cursor)
+ {
+ from = cursor;
+ }
+ }
+ else
+ {
+ beep();
+ }
+ break;
+
+ case '\n':
+#if OSK
+ case '\l':
+#else
+ case '\r':
+#endif
+#ifndef NO_ABBR
+ cursor = expandabbr(cursor, '\n');
+#endif
+ build = tmpblk.c;
+ *build++ = '\n';
+ if (*o_autoindent)
+ {
+ /* figure out indent for next line */
+ pfetch(markline(cursor));
+ for (scan = ptext; *scan == ' ' || *scan == '\t'; )
+ {
+ *build++ = *scan++;
+ }
+
+ /* remove indent from this line, if blank */
+ if (!*scan && plen > 0)
+ {
+ to = cursor &= ~(BLKSIZE - 1);
+ delete(cursor, cursor + plen);
+ }
+ }
+ *build = 0;
+ if (cursor >= to && when != WHEN_VIREP)
+ {
+ add(cursor, tmpblk.c);
+ }
+ else
+ {
+ change(cursor, to, tmpblk.c);
+ }
+ redraw(cursor, TRUE);
+ to = cursor = (cursor & ~(BLKSIZE - 1))
+ + BLKSIZE
+ + (int)(build - tmpblk.c) - 1;
+ break;
+
+ case ctrl('A'):
+ case ctrl('P'):
+ if (cursor < to)
+ {
+ delete(cursor, to);
+ }
+ if (*key == ctrl('A'))
+ {
+ cutname('.');
+ }
+ to = cursor = paste(cursor, FALSE, TRUE) + 1L;
+ break;
+
+ case ctrl('V'):
+ if (cursor >= to && when != WHEN_VIREP)
+ {
+ add(cursor, "^");
+ }
+ else
+ {
+ change(cursor, to, "^");
+ to = cursor + 1;
+ }
+ redraw(cursor, TRUE);
+ *key = getkey(0);
+ if (*key == '\n')
+ {
+ /* '\n' too hard to handle */
+#if OSK
+ *key = '\l';
+#else
+ *key = '\r';
+#endif
+ }
+ change(cursor, cursor + 1, key);
+ cursor++;
+ if (cursor > to)
+ {
+ to = cursor;
+ }
+ break;
+
+ case ctrl('L'):
+ case ctrl('R'):
+ redraw(MARK_UNSET, FALSE);
+ break;
+
+ default:
+ if (cursor >= to && when != WHEN_VIREP)
+ {
+#ifndef NO_ABBR
+ cursor = expandabbr(cursor, *key);
+#endif
+ add(cursor, key);
+ cursor++;
+ to = cursor;
+ }
+ else
+ {
+ pfetch(markline(cursor));
+ if (markidx(cursor) == plen)
+ {
+#ifndef NO_ABBR
+ cursor = expandabbr(cursor, *key);
+#endif
+ add(cursor, key);
+ }
+ else
+ {
+#ifndef NO_DIGRAPH
+ *key = digraph(ptext[markidx(cursor)], *key);
+#endif
+#ifndef NO_ABBR
+ cursor = expandabbr(cursor, *key);
+#endif
+ change(cursor, cursor + 1, key);
+ }
+ cursor++;
+ }
+#ifndef NO_SHOWMATCH
+ /* show matching "({[" if neceesary */
+ if (*o_showmatch && strchr(")}]", *key))
+ {
+ redraw(cursor, TRUE);
+ m = m_match(cursor - 1, 0L);
+ if (markline(m) >= topline
+ && markline(m) <= botline)
+ {
+ redraw(m, TRUE);
+ refresh();
+ sleep(1);
+ }
+ }
+#endif
+ } /* end switch(*key) */
+ } /* end for(;;) */
+BreakBreak:;
+
+ /* delete any excess characters */
+ if (cursor < to)
+ {
+ delete(cursor, to);
+ }
+
+ } /* end if doingdot else */
+
+ } /* end ChangeText */
+
+ /* put the new text into a cut buffer for possible reuse */
+ if (!doingdot)
+ {
+ blksync();
+ cutname('.');
+ cut(from, cursor);
+ }
+
+ /* move to last char that we inputted, unless it was newline */
+ if (markidx(cursor) != 0)
+ {
+ cursor--;
+ }
+ redraw(cursor, FALSE);
+
+#ifndef NO_EXTENSIONS
+ if (quit)
+ {
+ /* if this is a nested "do", then cut it short */
+ abortdo();
+
+ /* exit, unless we can't write out the file */
+ cursor = v_xit(cursor, 0L, 'Z');
+ }
+#endif
+
+ rptlines = 0L;
+ return cursor;
+}
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..2872a2a
--- /dev/null
+++ b/main.c
@@ -0,0 +1,422 @@
+/* main.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains the main() function of vi */
+
+#include "config.h"
+#include <signal.h>
+#include <setjmp.h>
+#include "vi.h"
+
+extern trapint(); /* defined below */
+extern char *getenv();
+jmp_buf jmpenv;
+
+#ifndef NO_DIGRAPH
+static init_digraphs();
+#endif
+
+/*---------------------------------------------------------------------*/
+
+void main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i;
+ char *cmd = (char *)0;
+ char *tag = (char *)0;
+ char *err = (char *)0;
+ char *str;
+#if MSDOS || TOS
+ char firstarg[256];
+#else
+ char *firstarg;
+#endif
+
+ /* set mode to MODE_VI or MODE_EX depending on program name */
+ switch (argv[0][strlen(argv[0]) - 1])
+ {
+ case 'x': /* "ex" */
+ mode = MODE_EX;
+ break;
+
+ case 'w': /* "view" */
+ mode = MODE_VI;
+ *o_readonly = TRUE;
+ break;
+#ifndef NO_EXTENSIONS
+ case 't': /* "edit" or "input" */
+ mode = MODE_VI;
+ *o_inputmode = TRUE;
+ break;
+#endif
+ default: /* "vi" or "elvis" */
+ mode = MODE_VI;
+ }
+
+#ifndef DEBUG
+# ifdef SIGQUIT
+ /* normally, we ignore SIGQUIT. SIGINT is trapped later */
+ signal(SIGQUIT, SIG_IGN);
+# endif
+#endif
+
+ /* temporarily ignore SIGINT */
+ signal(SIGINT, SIG_IGN);
+
+ /* start curses */
+ initscr();
+ cbreak();
+ noecho();
+ scrollok(stdscr, TRUE);
+
+ /* initialize the options */
+ initopts();
+
+ /* map the arrow keys. The KU,KD,KL,and KR variables correspond to
+ * the :ku=: (etc.) termcap capabilities. The variables are defined
+ * as part of the curses package.
+ */
+ if (has_KU) mapkey(has_KU, "k", WHEN_VICMD|WHEN_INMV, "<Up>");
+ if (has_KD) mapkey(has_KD, "j", WHEN_VICMD|WHEN_INMV, "<Down>");
+ if (has_KL) mapkey(has_KL, "h", WHEN_VICMD|WHEN_INMV, "<Left>");
+ if (has_KR) mapkey(has_KR, "l", WHEN_VICMD|WHEN_INMV, "<Right>");
+ if (has_HM) mapkey(has_HM, "^", WHEN_VICMD|WHEN_INMV, "<Home>");
+ if (has_EN) mapkey(has_EN, "$", WHEN_VICMD|WHEN_INMV, "<End>");
+ if (has_PU) mapkey(has_PU, "\002", WHEN_VICMD|WHEN_INMV, "<PgUp>");
+ if (has_PD) mapkey(has_PD, "\006", WHEN_VICMD|WHEN_INMV, "<PgDn>");
+#if MSDOS
+ if (*o_pcbios)
+ {
+ mapkey("#R", "i", WHEN_VICMD|WHEN_INMV, "<Insrt>");
+ mapkey("#S", "x", WHEN_VICMD|WHEN_INMV, "<Del>");
+ mapkey("#s", "B", WHEN_VICMD|WHEN_INMV, "^<left>");
+ mapkey("#t", "W", WHEN_VICMD|WHEN_INMV, "^<right>");
+ }
+#else
+ if (ERASEKEY != '\177')
+ {
+ mapkey("\177", "x", WHEN_VICMD|WHEN_INMV, "<Del>");
+ }
+#endif
+
+#ifndef NO_DIGRAPH
+ init_digraphs();
+#endif /* NO_DIGRAPH */
+
+ /* process any flags */
+ for (i = 1; i < argc && *argv[i] == '-'; i++)
+ {
+ switch (argv[i][1])
+ {
+ case 'R': /* readonly */
+ *o_readonly = TRUE;
+ break;
+
+ case 'r': /* recover */
+ msg("Use the `virec` program to recover lost files");
+ endmsgs();
+ refresh();
+ endwin();
+ exit(0);
+ break;
+
+ case 't': /* tag */
+ if (argv[i][2])
+ {
+ tag = argv[i] + 2;
+ }
+ else
+ {
+ i++;
+ tag = argv[i];
+ }
+ break;
+
+ case 'v': /* vi mode */
+ mode = MODE_VI;
+ break;
+
+ case 'e': /* ex mode */
+ mode = MODE_EX;
+ break;
+#ifndef NO_EXTENSIONS
+ case 'i': /* input mode */
+ *o_inputmode = TRUE;
+ break;
+#endif
+#ifndef NO_ERRLIST
+ case 'm': /* use "errlist" as the errlist */
+ if (argv[i][2])
+ {
+ err = argv[i] + 2;
+ }
+ else if (i + 1 < argc)
+ {
+ i++;
+ err = argv[i];
+ }
+ else
+ {
+ err = "";
+ }
+ break;
+#endif
+ default:
+ msg("Ignoring unknown flag \"%s\"", argv[i]);
+ }
+ }
+
+ /* if we were given an initial ex command, save it... */
+ if (i < argc && *argv[i] == '+')
+ {
+ if (argv[i][1])
+ {
+ cmd = argv[i++] + 1;
+ }
+ else
+ {
+ cmd = "$"; /* "vi + file" means start at EOF */
+ i++;
+ }
+ }
+
+ /* the remaining args are file names. */
+ nargs = argc - i;
+ if (nargs > 0)
+ {
+#if ! ( MSDOS || TOS )
+ firstarg = argv[i];
+#endif
+ strcpy(args, argv[i]);
+ while (++i < argc && strlen(args) + 1 + strlen(argv[i]) < sizeof args)
+ {
+ strcat(args, " ");
+ strcat(args, argv[i]);
+ }
+ }
+#if ! ( MSDOS || TOS )
+ else
+ {
+ firstarg = "";
+ }
+#endif
+ argno = 0;
+
+#if MSDOS || TOS
+ if (nargs > 0)
+ {
+ strcpy(args, wildcard(args));
+ nargs = 1;
+ for (i = 0; args[i]; i++)
+ {
+ if (args[i] == ' ')
+ {
+ nargs++;
+ }
+ }
+ for (i = 0; args[i] && args[i] != ' '; i++)
+ {
+ firstarg[i] = args[i];
+ }
+ firstarg[i] = '\0';
+ }
+ else
+ {
+ firstarg[0] = '\0';
+ }
+#endif
+
+ /* perform the .exrc files and EXINIT environment variable */
+#ifdef SYSEXRC
+ doexrc(SYSEXRC);
+#endif
+#ifdef HMEXRC
+ str = getenv("HOME");
+ if (str)
+ {
+ sprintf(tmpblk.c, "%s%c%s", str, SLASH, HMEXRC);
+ doexrc(tmpblk.c);
+ }
+#endif
+ doexrc(EXRC);
+#ifdef EXINIT
+ str = getenv(EXINIT);
+ if (str)
+ {
+ exstring(str, strlen(str));
+ }
+#endif
+
+ /* search for a tag (or an error) now, if desired */
+ blkinit();
+ if (tag)
+ {
+ cmd_tag(MARK_FIRST, MARK_FIRST, CMD_TAG, 0, tag);
+ }
+#ifndef NO_ERRLIST
+ else if (err)
+ {
+ cmd_errlist(MARK_FIRST, MARK_FIRST, CMD_ERRLIST, 0, err);
+ }
+#endif
+
+ /* if no tag/err, or tag failed, then start with first arg */
+ if (tmpfd < 0 && tmpstart(firstarg) == 0 && *origname)
+ {
+ ChangeText
+ {
+ }
+ clrflag(file, MODIFIED);
+ }
+
+ /* now we do the immediate ex command that we noticed before */
+ if (cmd)
+ {
+ doexcmd(cmd);
+ }
+
+ /* repeatedly call ex() or vi() (depending on the mode) until the
+ * mode is set to MODE_QUIT
+ */
+ while (mode != MODE_QUIT)
+ {
+ if (setjmp(jmpenv))
+ {
+ /* Maybe we just aborted a change? */
+ abortdo();
+ }
+#if TURBOC
+ signal(SIGINT, (void(*)()) trapint);
+#else
+ signal(SIGINT, trapint);
+#endif
+
+ switch (mode)
+ {
+ case MODE_VI:
+ vi();
+ break;
+
+ case MODE_EX:
+ ex();
+ break;
+#ifdef DEBUG
+ default:
+ msg("mode = %d?", mode);
+ mode = MODE_QUIT;
+#endif
+ }
+ }
+
+ /* free up the cut buffers */
+ cutend();
+
+ /* end curses */
+#ifndef NO_CURSORSHAPE
+ if (has_CQ)
+ do_CQ();
+#endif
+ endmsgs();
+ move(LINES - 1, 0);
+ clrtoeol();
+ refresh();
+ endwin();
+
+ exit(0);
+ /*NOTREACHED*/
+}
+
+
+/*ARGSUSED*/
+int trapint(signo)
+ int signo;
+{
+ resume_curses(FALSE);
+ abortdo();
+#if OSK
+ sigmask(-1);
+#endif
+#if TURBO_C
+ signal(signo, (void (*)())trapint);
+#else
+ signal(signo, trapint);
+#endif
+ longjmp(jmpenv, 1);
+
+ return 0;
+}
+
+
+#ifndef NO_DIGRAPH
+
+/* This stuff us used to build the default digraphs table. */
+static char digtable[][4] =
+{
+# if CS_IBMPC
+ "C,\200", "u\"\1", "e'\2", "a^\3",
+ "a\"\4", "a`\5", "a@\6", "c,\7",
+ "e^\10", "e\"\211", "e`\12", "i\"\13",
+ "i^\14", "i`\15", "A\"\16", "A@\17",
+ "E'\20", "ae\21", "AE\22", "o^\23",
+ "o\"\24", "o`\25", "u^\26", "u`\27",
+ "y\"\30", "O\"\31", "U\"\32", "a'\240",
+ "i'!", "o'\"", "u'#", "n~$",
+ "N~%", "a-&", "o-'", "~?(",
+ "~!-", "\"<.", "\">/",
+# if CS_SPECIAL
+ "2/+", "4/,", "^+;", "^q<",
+ "^c=", "^r>", "^t?", "pp]",
+ "^^^", "oo_", "*a`", "*ba",
+ "*pc", "*Sd", "*se", "*uf",
+ "*tg", "*Ph", "*Ti", "*Oj",
+ "*dk", "*Hl", "*hm", "*En",
+ "*No", "eqp", "pmq", "ger",
+ "les", "*It", "*iu", "*/v",
+ "*=w", "sq{", "^n|", "^2}",
+ "^3~", "^_\377",
+# endif /* CS_SPECIAL */
+# endif /* CS_IBMPC */
+# if CS_LATIN1
+ "~!!", "a-*", "\">+", "o-:",
+ "\"<>", "~??",
+
+ "A`@", "A'A", "A^B", "A~C",
+ "A\"D", "A@E", "AEF", "C,G",
+ "E`H", "E'I", "E^J", "E\"K",
+ "I`L", "I'M", "I^N", "I\"O",
+ "-DP", "N~Q", "O`R", "O'S",
+ "O^T", "O~U", "O\"V", "O/X",
+ "U`Y", "U'Z", "U^[", "U\"\\",
+ "Y'_",
+
+ "a``", "a'a", "a^b", "a~c",
+ "a\"d", "a@e", "aef", "c,g",
+ "e`h", "e'i", "e^j", "e\"k",
+ "i`l", "i'm", "i^n", "i\"o",
+ "-dp", "n~q", "o`r", "o's",
+ "o^t", "o~u", "o\"v", "o/x",
+ "u`y", "u'z", "u^{", "u\"|",
+ "y'~",
+# endif /* CS_LATIN1 */
+ ""
+};
+
+static init_digraphs()
+{
+ int i;
+
+ for (i = 0; *digtable[i]; i++)
+ {
+ do_digraph(FALSE, digtable[i]);
+ }
+ do_digraph(FALSE, (char *)0);
+}
+#endif /* NO_DIGRAPH */
diff --git a/misc.c b/misc.c
new file mode 100644
index 0000000..c9e0f68
--- /dev/null
+++ b/misc.c
@@ -0,0 +1,103 @@
+/* misc.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains functions which didn't seem happy anywhere else */
+
+#include "config.h"
+#include "vi.h"
+
+
+/* find a particular line & return a pointer to a copy of its text */
+char *fetchline(line)
+ long line; /* line number of the line to fetch */
+{
+ int i;
+ REG char *scan; /* used to search for the line in a BLK */
+ long l; /* line number counter */
+ static BLK buf; /* holds ONLY the selected line (as string) */
+ REG char *cpy; /* used while copying the line */
+ static long nextline; /* } These four variables are used */
+ static long chglevel; /* } to implement a shortcut when */
+ static char *nextscan; /* } consecutive lines are fetched */
+ static long nextlnum; /* } */
+
+ /* can we do a shortcut? */
+ if (changes == chglevel && line == nextline)
+ {
+ scan = nextscan;
+ }
+ else
+ {
+ /* scan lnum[] to determine which block its in */
+ for (i = 1; line > lnum[i]; i++)
+ {
+ }
+ nextlnum = lnum[i];
+
+ /* fetch text of the block containing that line */
+ scan = blkget(i)->c;
+
+ /* find the line in the block */
+ for (l = lnum[i - 1]; ++l < line; )
+ {
+ while (*scan++ != '\n')
+ {
+ }
+ }
+ }
+
+ /* copy it into a block by itself, with no newline */
+ for (cpy = buf.c; *scan != '\n'; )
+ {
+ *cpy++ = *scan++;
+ }
+ *cpy = '\0';
+
+ /* maybe speed up the next call to fetchline() ? */
+ if (line < nextlnum)
+ {
+ nextline = line + 1;
+ chglevel = changes;
+ nextscan = scan + 1;
+ }
+ else
+ {
+ nextline = 0;
+ }
+
+ /* Calls to fetchline() interfere with calls to pfetch(). Make sure
+ * that pfetch() resets itself on its next invocation.
+ */
+ pchgs = 0L;
+
+ /* Return a pointer to the line's text */
+ return buf.c;
+}
+
+
+/* error message from the regexp code */
+void regerror(txt)
+ char *txt; /* an error message */
+{
+ msg("RE error: %s", txt);
+}
+
+/* This function is equivelent to the pfetch() macro */
+void pfetch(l)
+ long l; /* line number of line to fetch */
+{
+ if(l != pline || changes != pchgs)
+ {
+ pline = (l);
+ ptext = fetchline(pline);
+ plen = strlen(ptext);
+ pchgs = changes;
+ }
+}
diff --git a/modify.c b/modify.c
new file mode 100644
index 0000000..a3f9806
--- /dev/null
+++ b/modify.c
@@ -0,0 +1,479 @@
+/* modify.c */
+
+/* This file contains the low-level file modification functions:
+ * delete(frommark, tomark) - removes line or portions of lines
+ * add(frommark, text) - inserts new text
+ * change(frommark, tomark, text) - delete, then add
+ */
+
+#include "config.h"
+#include "vi.h"
+
+#ifdef DEBUG
+# include <stdio.h>
+static FILE *dbg;
+
+/*VARARGS1*/
+debout(msg, arg1, arg2, arg3, arg4, arg5)
+ char *msg, *arg1, *arg2, *arg3, *arg4, *arg5;
+{
+ if (!dbg)
+ {
+ dbg = fopen("debug.out", "w");
+ setbuf(dbg, (FILE *)0);
+ }
+ fprintf(dbg, msg, arg1, arg2, arg3, arg4, arg5);
+}
+#endif /* DEBUG */
+
+/* delete a range of text from the file */
+void delete(frommark, tomark)
+ MARK frommark; /* first char to be deleted */
+ MARK tomark; /* AFTER last char to be deleted */
+{
+ int i; /* used to move thru logical blocks */
+ REG char *scan; /* used to scan thru text of the blk */
+ REG char *cpy; /* used when copying chars */
+ BLK *blk; /* a text block */
+ long l; /* a line number */
+ MARK m; /* a traveling version of frommark */
+
+#ifdef DEBUG
+ debout("delete(%ld.%d, %ld.%d)\n", markline(frommark), markidx(frommark), markline(tomark), markidx(tomark));
+#endif
+
+ /* if not deleting anything, quit now */
+ if (frommark == tomark)
+ {
+ return;
+ }
+
+ /* This is a change */
+ changes++;
+ significant = TRUE;
+
+ /* if this is a multi-line change, then we'll have to redraw */
+ if (markline(frommark) != markline(tomark))
+ {
+ mustredraw = TRUE;
+ redrawrange(markline(frommark), markline(tomark), markline(frommark));
+ }
+
+ /* adjust marks 'a through 'z and '' as needed */
+ l = markline(tomark);
+ for (i = 0; i < NMARKS; i++)
+ {
+ if (mark[i] < frommark)
+ {
+ continue;
+ }
+ else if (mark[i] < tomark)
+ {
+ mark[i] = MARK_UNSET;
+ }
+ else if (markline(mark[i]) == l)
+ {
+ if (markline(frommark) == l)
+ {
+ mark[i] -= markidx(tomark) - markidx(frommark);
+ }
+ else
+ {
+ mark[i] -= markidx(tomark);
+ }
+ }
+ else
+ {
+ mark[i] -= MARK_AT_LINE(l - markline(frommark));
+ }
+ }
+
+ /* Reporting... */
+ if (markidx(frommark) == 0 && markidx(tomark) == 0)
+ {
+ rptlines = markline(tomark) - markline(frommark);
+ rptlabel = "deleted";
+ }
+
+ /* find the block containing frommark */
+ l = markline(frommark);
+ for (i = 1; lnum[i] < l; i++)
+ {
+ }
+
+ /* process each affected block... */
+ for (m = frommark;
+ m < tomark && lnum[i] < INFINITY;
+ m = MARK_AT_LINE(lnum[i - 1] + 1))
+ {
+ /* fetch the block */
+ blk = blkget(i);
+
+ /* find the mark in the block */
+ scan = blk->c;
+ for (l = markline(m) - lnum[i - 1] - 1; l > 0; l--)
+ {
+ while (*scan++ != '\n')
+ {
+ }
+ }
+ scan += markidx(m);
+
+ /* figure out where the changes to this block end */
+ if (markline(tomark) > lnum[i])
+ {
+ cpy = blk->c + BLKSIZE;
+ }
+ else if (markline(tomark) == markline(m))
+ {
+ cpy = scan - markidx(m) + markidx(tomark);
+ }
+ else
+ {
+ cpy = scan;
+ for (l = markline(tomark) - markline(m);
+ l > 0;
+ l--)
+ {
+ while (*cpy++ != '\n')
+ {
+ }
+ }
+ cpy += markidx(tomark);
+ }
+
+ /* delete the stuff by moving chars within this block */
+ while (cpy < blk->c + BLKSIZE)
+ {
+ *scan++ = *cpy++;
+ }
+ while (scan < blk->c + BLKSIZE)
+ {
+ *scan++ = '\0';
+ }
+
+ /* adjust tomark to allow for lines deleted from this block */
+ tomark -= MARK_AT_LINE(lnum[i] + 1 - markline(m));
+
+ /* if this block isn't empty now, then advance i */
+ if (*blk->c)
+ {
+ i++;
+ }
+
+ /* the buffer has changed. Update hdr and lnum. */
+ blkdirty(blk);
+ }
+
+ /* must have at least 1 line */
+ if (nlines == 0)
+ {
+ blk = blkadd(1);
+ blk->c[0] = '\n';
+ blkdirty(blk);
+ cursor = MARK_FIRST;
+ }
+}
+
+
+/* add some text at a specific place in the file */
+void add(atmark, newtext)
+ MARK atmark; /* where to insert the new text */
+ char *newtext; /* NUL-terminated string to insert */
+{
+ REG char *scan; /* used to move through string */
+ REG char *build; /* used while copying chars */
+ int addlines; /* number of lines we're adding */
+ int lastpart; /* size of last partial line */
+ BLK *blk; /* the block to be modified */
+ int blkno; /* the logical block# of (*blk) */
+ REG char *newptr; /* where new text starts in blk */
+ BLK buf; /* holds chars from orig blk */
+ BLK linebuf; /* holds part of line that didn't fit */
+ BLK *following; /* the BLK following the last BLK */
+ int i;
+ long l;
+
+#ifdef DEBUG
+ debout("add(%ld.%d, \"%s\")\n", markline(atmark), markidx(atmark), newtext);
+#endif
+#ifdef lint
+ buf.c[0] = 0;
+#endif
+ /* if not adding anything, return now */
+ if (!*newtext)
+ {
+ return;
+ }
+
+ /* This is a change */
+ changes++;
+ significant = TRUE;
+
+ /* count the number of lines in the new text */
+ for (scan = newtext, lastpart = addlines = 0; *scan; )
+ {
+ if (*scan++ == '\n')
+ {
+ addlines++;
+ lastpart = 0;
+ }
+ else
+ {
+ lastpart++;
+ }
+ }
+
+ /* Reporting... */
+ if (lastpart == 0 && markidx(atmark) == 0)
+ {
+ rptlines = addlines;
+ rptlabel = "added";
+ }
+
+ /* extract the line# from atmark */
+ l = markline(atmark);
+
+ /* if more than 0 lines, then we'll have to redraw the screen */
+ if (addlines > 0)
+ {
+ mustredraw = TRUE;
+ if (markidx(atmark) == 0 && lastpart == 0)
+ {
+ redrawrange(l, l, l + addlines);
+ }
+ else
+ {
+ /* make sure the last line gets redrawn -- it was
+ * split, so its appearance has changed
+ */
+ redrawrange(l, l + 1L, l + addlines + 1L);
+ }
+ }
+
+ /* adjust marks 'a through 'z and '' as needed */
+ for (i = 0; i < NMARKS; i++)
+ {
+ if (mark[i] < atmark)
+ {
+ /* earlier line, or earlier in same line: no change */
+ continue;
+ }
+ else if (markline(mark[i]) > l)
+ {
+ /* later line: move down a whole number of lines */
+ mark[i] += MARK_AT_LINE(addlines);
+ }
+ else
+ {
+ /* later in same line */
+ if (addlines > 0)
+ {
+ /* multi-line add, which split this line:
+ * move down, and possibly left or right,
+ * depending on where the split was and how
+ * much text was inserted after the last \n
+ */
+ mark[i] += MARK_AT_LINE(addlines) + lastpart - markidx(atmark);
+ }
+ else
+ {
+ /* totally within this line: move right */
+ mark[i] += lastpart;
+ }
+ }
+ }
+
+ /* get the block to be modified */
+ for (blkno = 1; lnum[blkno] < l && lnum[blkno + 1] < INFINITY; blkno++)
+ {
+ }
+ blk = blkget(blkno);
+ buf = *blk;
+
+ /* figure out where the new text starts */
+ for (newptr = buf.c, l = markline(atmark) - lnum[blkno - 1] - 1;
+ l > 0;
+ l--)
+ {
+ while (*newptr++ != '\n')
+ {
+ }
+ }
+ newptr += markidx(atmark);
+
+ /* keep start of old block */
+ build = blk->c + (newptr - buf.c);
+
+ /* fill this block (or blocks) from the newtext string */
+ while (*newtext)
+ {
+ while (*newtext && build < blk->c + BLKSIZE - 1)
+ {
+ *build++ = *newtext++;
+ }
+ if (*newtext)
+ {
+ /* save the excess */
+ for (scan = linebuf.c + BLKSIZE;
+ build > blk->c && build[-1] != '\n';
+ )
+ {
+ *--scan = *--build;
+ }
+
+ /* write the block */
+ while (build < blk->c + BLKSIZE)
+ {
+ *build++ = '\0';
+ }
+ blkdirty(blk);
+
+ /* add another block */
+ blkno++;
+ blk = blkadd(blkno);
+
+ /* copy in the excess from last time */
+ for (build = blk->c; scan < linebuf.c + BLKSIZE; )
+ {
+ *build++ = *scan++;
+ }
+ }
+ }
+
+ /* fill this block(s) from remainder of orig block */
+ while (newptr < buf.c + BLKSIZE && *newptr)
+ {
+ while (newptr < buf.c + BLKSIZE
+ && *newptr
+ && build < blk->c + BLKSIZE - 1)
+ {
+ *build++ = *newptr++;
+ }
+ if (newptr < buf.c + BLKSIZE && *newptr)
+ {
+ /* save the excess */
+ for (scan = linebuf.c + BLKSIZE;
+ build > blk->c && build[-1] != '\n';
+ )
+ {
+ *--scan = *--build;
+ }
+
+ /* write the block */
+ while (build < blk->c + BLKSIZE)
+ {
+ *build++ = '\0';
+ }
+ blkdirty(blk);
+
+ /* add another block */
+ blkno++;
+ blk = blkadd(blkno);
+
+ /* copy in the excess from last time */
+ for (build = blk->c; scan < linebuf.c + BLKSIZE; )
+ {
+ *build++ = *scan++;
+ }
+ }
+ }
+
+ /* see if we can combine our last block with the following block */
+ if (lnum[blkno] < nlines && lnum[blkno + 1] - lnum[blkno] < (BLKSIZE >> 6))
+ {
+ /* hey, we probably can! Get the following block & see... */
+ following = blkget(blkno + 1);
+ if (strlen(following->c) + (build - blk->c) < BLKSIZE - 1)
+ {
+ /* we can! Copy text from following to blk */
+ for (scan = following->c; *scan; )
+ {
+ *build++ = *scan++;
+ }
+ while (build < blk->c + BLKSIZE)
+ {
+ *build++ = '\0';
+ }
+ blkdirty(blk);
+
+ /* pretend the following was the last blk */
+ blk = following;
+ build = blk->c;
+ }
+ }
+
+ /* that last block is dirty by now */
+ while (build < blk->c + BLKSIZE)
+ {
+ *build++ = '\0';
+ }
+ blkdirty(blk);
+}
+
+
+/* change the text of a file */
+void change(frommark, tomark, newtext)
+ MARK frommark, tomark;
+ char *newtext;
+{
+ int i;
+ long l;
+ char *text;
+ BLK *blk;
+
+#ifdef DEBUG
+ debout("change(%ld.%d, %ld.%d, \"%s\")\n", markline(frommark), markidx(frommark), markline(tomark), markidx(tomark), newtext);
+#endif
+
+ /* optimize for single-character replacement */
+ if (frommark + 1 == tomark && newtext[0] && !newtext[1] && newtext[0] != '\n')
+ {
+ /* find the block containing frommark */
+ l = markline(frommark);
+ for (i = 1; lnum[i] < l; i++)
+ {
+ }
+
+ /* get the block */
+ blk = blkget(i);
+
+ /* find the line within the block */
+ for (text = blk->c, i = l - lnum[i - 1] - 1; i > 0; text++)
+ {
+ if (*text == '\n')
+ {
+ i--;
+ }
+ }
+
+ /* replace the char */
+ text += markidx(frommark);
+ if (*text == newtext[0])
+ {
+ /* no change was needed - same char */
+ return;
+ }
+ else if (*text != '\n')
+ {
+ /* This is a change */
+ changes++;
+ significant = TRUE;
+ ChangeText
+ {
+ *text = newtext[0];
+ blkdirty(blk);
+ }
+ return;
+ }
+ /* else it is a complex change involving newline... */
+ }
+
+ /* couldn't optimize, so do delete & add */
+ ChangeText
+ {
+ delete(frommark, tomark);
+ add(frommark, newtext);
+ rptlabel = "changed";
+ }
+}
diff --git a/move1.c b/move1.c
new file mode 100644
index 0000000..63c98be
--- /dev/null
+++ b/move1.c
@@ -0,0 +1,623 @@
+/* move1.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains most movement functions */
+
+#include "config.h"
+#include <ctype.h>
+#include "vi.h"
+
+#ifndef isascii
+# define isascii(c) !((c) & ~0x7f)
+#endif
+
+MARK m_updnto(m, cnt, cmd)
+ MARK m; /* movement is relative to this mark */
+ long cnt; /* a numeric argument */
+{
+ DEFAULT(cmd == 'G' ? nlines : 1L);
+
+ /* move up or down 'cnt' lines */
+ switch (cmd)
+ {
+ case ('P'&0x1f):
+ case '-':
+ case 'k':
+ m -= MARK_AT_LINE(cnt);
+ break;
+
+ case 'G':
+ if (cnt < 1L || cnt > nlines)
+ {
+ msg("Only %ld lines", nlines);
+ return MARK_UNSET;
+ }
+ m = MARK_AT_LINE(cnt);
+ break;
+
+ default:
+ m += MARK_AT_LINE(cnt);
+ }
+
+ /* if that left us screwed up, then fail */
+ if (m < MARK_FIRST || markline(m) > nlines)
+ {
+ return MARK_UNSET;
+ }
+
+ return m;
+}
+
+/*ARGSUSED*/
+MARK m_right(m, cnt)
+ MARK m; /* movement is relative to this mark */
+ long cnt; /* a numeric argument */
+{
+ int idx; /* index of the new cursor position */
+
+ DEFAULT(1);
+
+ /* move to right, if that's OK */
+ pfetch(markline(m));
+ idx = markidx(m) + cnt;
+ if (idx < plen)
+ {
+ m += cnt;
+ }
+ else
+ {
+ return MARK_UNSET;
+ }
+
+ return m;
+}
+
+/*ARGSUSED*/
+MARK m_left(m, cnt)
+ MARK m; /* movement is relative to this mark */
+ long cnt; /* a numeric argument */
+{
+ DEFAULT(1);
+
+ /* move to the left, if that's OK */
+ if (markidx(m) >= cnt)
+ {
+ m -= cnt;
+ }
+ else
+ {
+ return MARK_UNSET;
+ }
+
+ return m;
+}
+
+/*ARGSUSED*/
+MARK m_tocol(m, cnt)
+ MARK m; /* movement is relative to this mark */
+ long cnt; /* a numeric argument */
+{
+ char *text; /* text of the line */
+ int col; /* column number */
+ int idx; /* index into the line */
+
+ DEFAULT(1);
+
+ /* internally, columns are numbered 0..COLS-1, not 1..COLS */
+ cnt--;
+
+ /* if 0, that's easy */
+ if (cnt == 0)
+ {
+ m &= ~(BLKSIZE - 1);
+ return m;
+ }
+
+ /* find that column within the line */
+ pfetch(markline(m));
+ text = ptext;
+ for (col = idx = 0; col < cnt && *text; text++, idx++)
+ {
+ if (*text == '\t' && !*o_list)
+ {
+ col += *o_tabstop;
+ col -= col % *o_tabstop;
+ }
+ else if (UCHAR(*text) < ' ' || *text == '\177')
+ {
+ col += 2;
+ }
+#ifndef NO_CHARATTR
+ else if (text[0] == '\\' && text[1] == 'f' && text[2] && *o_charattr)
+ {
+ text += 2; /* plus one more as part of for loop */
+ }
+#endif
+ else
+ {
+ col++;
+ }
+ }
+ if (!*text)
+ {
+ return MARK_UNSET;
+ }
+ else
+ {
+ m = (m & ~(BLKSIZE - 1)) + idx;
+ }
+ return m;
+}
+
+/*ARGSUSED*/
+MARK m_front(m, cnt)
+ MARK m; /* movement is relative to this mark */
+ long cnt; /* a numeric argument (ignored) */
+{
+ char *scan;
+
+ /* move to the first non-whitespace character */
+ pfetch(markline(m));
+ scan = ptext;
+ m &= ~(BLKSIZE - 1);
+ while (*scan == ' ' || *scan == '\t')
+ {
+ scan++;
+ m++;
+ }
+
+ return m;
+}
+
+/*ARGSUSED*/
+MARK m_rear(m, cnt)
+ MARK m; /* movement is relative to this mark */
+ long cnt; /* a numeric argument (ignored) */
+{
+ /* Try to move *EXTREMELY* far to the right. It is fervently hoped
+ * that other code will convert this to a more reasonable MARK before
+ * anything tries to actually use it. (See adjmove() in vi.c)
+ */
+ return m | (BLKSIZE - 1);
+}
+
+#ifndef NO_SENTENCE
+/*ARGSUSED*/
+MARK m_fsentence(m, cnt)
+ MARK m; /* movement is relative to this mark */
+ long cnt; /* a numeric argument */
+{
+ REG char *text;
+ REG long l;
+
+ DEFAULT(1);
+
+ /* get the current line */
+ l = markline(m);
+ pfetch(l);
+ text = ptext + markidx(m);
+
+ /* for each requested sentence... */
+ while (cnt-- > 0)
+ {
+ /* search forward for one of [.?!] followed by spaces or EOL */
+ do
+ {
+ /* wrap at end of line */
+ if (!text[0])
+ {
+ if (l >= nlines)
+ {
+ return MARK_UNSET;
+ }
+ l++;
+ pfetch(l);
+ text = ptext;
+ }
+ else
+ {
+ text++;
+ }
+ } while (text[0] != '.' && text[0] != '?' && text[0] != '!'
+ || text[1] && (text[1] != ' ' || text[2] && text[2] != ' '));
+ }
+
+ /* construct a mark for this location */
+ m = buildmark(text);
+
+ /* move forward to the first word of the next sentence */
+ m = m_fword(m, 1L);
+
+ return m;
+}
+
+/*ARGSUSED*/
+MARK m_bsentence(m, cnt)
+ MARK m; /* movement is relative to this mark */
+ long cnt; /* a numeric argument */
+{
+ REG char *text; /* used to scan thru text */
+ REG long l; /* current line number */
+ int flag; /* have we passed at least one word? */
+
+ DEFAULT(1);
+
+ /* get the current line */
+ l = markline(m);
+ pfetch(l);
+ text = ptext + markidx(m);
+
+ /* for each requested sentence... */
+ flag = TRUE;
+ while (cnt-- > 0)
+ {
+ /* search backward for one of [.?!] followed by spaces or EOL */
+ do
+ {
+ /* wrap at beginning of line */
+ if (text == ptext)
+ {
+ do
+ {
+ if (l <= 1)
+ {
+ return MARK_UNSET;
+ }
+ l--;
+ pfetch(l);
+ } while (!*ptext);
+ text = ptext + plen - 1;
+ }
+ else
+ {
+ text--;
+ }
+
+ /* are we moving past a "word"? */
+ if (text[0] >= '0')
+ {
+ flag = FALSE;
+ }
+ } while (flag || text[0] != '.' && text[0] != '?' && text[0] != '!'
+ || text[1] && (text[1] != ' ' || text[2] && text[2] != ' '));
+ }
+
+ /* construct a mark for this location */
+ m = buildmark(text);
+
+ /* move to the front of the following sentence */
+ m = m_fword(m, 1L);
+
+ return m;
+}
+#endif
+
+/*ARGSUSED*/
+MARK m_fparagraph(m, cnt)
+ MARK m; /* movement is relative to this mark */
+ long cnt; /* a numeric argument */
+{
+ char *text;
+ char *pscn; /* used to scan thru value of "paragraphs" option */
+ long l;
+
+ DEFAULT(1);
+
+ for (l = markline(m); cnt > 0 && l++ < nlines; )
+ {
+ text = fetchline(l);
+ if (!*text)
+ {
+ cnt--;
+ }
+#ifndef NO_SENTENCE
+ else if (*text == '.')
+ {
+ for (pscn = o_paragraphs; pscn[0] && pscn[1]; pscn += 2)
+ {
+ if (pscn[0] == text[1] && pscn[1] == text[2])
+ {
+ cnt--;
+ break;
+ }
+ }
+ }
+#endif
+ }
+ if (l <= nlines)
+ {
+ m = MARK_AT_LINE(l);
+ }
+ else
+ {
+ m = MARK_LAST;
+ }
+ return m;
+}
+
+/*ARGSUSED*/
+MARK m_bparagraph(m, cnt)
+ MARK m; /* movement is relative to this mark */
+ long cnt; /* a numeric argument */
+{
+ char *text;
+ char *pscn; /* used to scan thru value of "paragraph" option */
+ long l;
+
+ DEFAULT(1);
+
+ for (l = markline(m); cnt > 0 && l-- > 1; )
+ {
+ text = fetchline(l);
+ if (!*text)
+ {
+ cnt--;
+ }
+#ifndef NO_SENTENCE
+ else if (*text == '.')
+ {
+ for (pscn = o_paragraphs; pscn[0] && pscn[1]; pscn += 2)
+ {
+ if (pscn[0] == text[1] && pscn[1] == text[2])
+ {
+ cnt--;
+ break;
+ }
+ }
+ }
+#endif
+ }
+ if (l >= 1)
+ {
+ m = MARK_AT_LINE(l);
+ }
+ else
+ {
+ m = MARK_FIRST;
+ }
+ return m;
+}
+
+/*ARGSUSED*/
+MARK m_fsection(m, cnt, key)
+ MARK m; /* movement is relative to this mark */
+ long cnt; /* (ignored) */
+ int key; /* second key stroke - must be ']' */
+{
+ char *text;
+ char *sscn; /* used to scan thru value of "sections" option */
+ long l;
+
+ /* make sure second key was ']' */
+ if (key != ']')
+ {
+ return MARK_UNSET;
+ }
+
+ for (l = markline(m); l++ < nlines; )
+ {
+ text = fetchline(l);
+ if (*text == '{')
+ {
+ break;
+ }
+#ifndef NO_SENTENCE
+ else if (*text == '.')
+ {
+ for (sscn = o_sections; sscn[0] && sscn[1]; sscn += 2)
+ {
+ if (sscn[0] == text[1] && sscn[1] == text[2])
+ {
+ goto BreakBreak;
+ }
+ }
+ }
+#endif
+ }
+BreakBreak:
+ if (l <= nlines)
+ {
+ m = MARK_AT_LINE(l);
+ }
+ else
+ {
+ m = MARK_LAST;
+ }
+ return m;
+}
+
+/*ARGSUSED*/
+MARK m_bsection(m, cnt, key)
+ MARK m; /* movement is relative to this mark */
+ long cnt; /* (ignored) */
+ int key; /* second key stroke - must be '[' */
+{
+ char *text;
+ char *sscn; /* used to scan thru value of "sections" option */
+ long l;
+
+ /* make sure second key was '[' */
+ if (key != '[')
+ {
+ return MARK_UNSET;
+ }
+
+ for (l = markline(m); l-- > 1; )
+ {
+ text = fetchline(l);
+ if (*text == '{')
+ {
+ break;
+ }
+#ifndef NO_SENTENCE
+ else if (*text == '.')
+ {
+ for (sscn = o_sections; sscn[0] && sscn[1]; sscn += 2)
+ {
+ if (sscn[0] == text[1] && sscn[1] == text[2])
+ {
+ goto BreakBreak;
+ }
+ }
+ }
+#endif
+ }
+BreakBreak:
+ if (l >= 1)
+ {
+ m = MARK_AT_LINE(l);
+ }
+ else
+ {
+ m = MARK_FIRST;
+ }
+ return m;
+}
+
+
+/*ARGSUSED*/
+MARK m_match(m, cnt)
+ MARK m; /* movement is relative to this mark */
+ long cnt; /* a numeric argument (ignored) */
+{
+ long l;
+ REG char *text;
+ REG char match;
+ REG char nest;
+ REG int count;
+
+ /* get the current line */
+ l = markline(m);
+ pfetch(l);
+ text = ptext + markidx(m);
+
+ /* search forward within line for one of "[](){}" */
+ for (match = '\0'; !match && *text; text++)
+ {
+ /* tricky way to recognize 'em in ASCII */
+ nest = *text;
+ if ((nest & 0xdf) == ']' || (nest & 0xdf) == '[')
+ {
+ match = nest ^ ('[' ^ ']');
+ }
+ else if ((nest & 0xfe) == '(')
+ {
+ match = nest ^ ('(' ^ ')');
+ }
+ else
+ {
+ match = 0;
+ }
+ }
+ if (!match)
+ {
+ return MARK_UNSET;
+ }
+ text--;
+
+ /* search forward or backward for match */
+ if (match == '(' || match == '[' || match == '{')
+ {
+ /* search backward */
+ for (count = 1; count > 0; )
+ {
+ /* wrap at beginning of line */
+ if (text == ptext)
+ {
+ do
+ {
+ if (l <= 1L)
+ {
+ return MARK_UNSET;
+ }
+ l--;
+ pfetch(l);
+ } while (!*ptext);
+ text = ptext + plen - 1;
+ }
+ else
+ {
+ text--;
+ }
+
+ /* check the char */
+ if (*text == match)
+ count--;
+ else if (*text == nest)
+ count++;
+ }
+ }
+ else
+ {
+ /* search forward */
+ for (count = 1; count > 0; )
+ {
+ /* wrap at end of line */
+ if (!*text)
+ {
+ if (l >= nlines)
+ {
+ return MARK_UNSET;
+ }
+ l++;
+ pfetch(l);
+ text = ptext;
+ }
+ else
+ {
+ text++;
+ }
+
+ /* check the char */
+ if (*text == match)
+ count--;
+ else if (*text == nest)
+ count++;
+ }
+ }
+
+ /* construct a mark for this place */
+ m = buildmark(text);
+ return m;
+}
+
+/*ARGSUSED*/
+MARK m_tomark(m, cnt, key)
+ MARK m; /* movement is relative to this mark */
+ long cnt; /* (ignored) */
+ int key; /* keystroke - the mark to move to */
+{
+ /* mark '' is a special case */
+ if (key == '\'' || key == '`')
+ {
+ if (mark[26] == MARK_UNSET)
+ {
+ return MARK_FIRST;
+ }
+ else
+ {
+ return mark[26];
+ }
+ }
+
+ /* if not a valid mark number, don't move */
+ if (key < 'a' || key > 'z')
+ {
+ return MARK_UNSET;
+ }
+
+ /* return the selected mark -- may be MARK_UNSET */
+ if (!mark[key - 'a'])
+ {
+ msg("mark '%c is unset", key);
+ }
+ return mark[key - 'a'];
+}
+
diff --git a/move2.c b/move2.c
new file mode 100644
index 0000000..c69dbb4
--- /dev/null
+++ b/move2.c
@@ -0,0 +1,238 @@
+/* move2.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This function contains the movement functions that perform RE searching */
+
+#include "config.h"
+#include "vi.h"
+#include "regexp.h"
+
+extern long atol();
+
+static regexp *re; /* compiled version of the pattern to search for */
+static prevsf; /* boolean: previous search direction was forward? */
+
+MARK m_nsrch(m)
+ MARK m; /* where to start searching */
+{
+ if (prevsf)
+ {
+ m = m_fsrch(m, (char *)0);
+ prevsf = TRUE;
+ }
+ else
+ {
+ m = m_bsrch(m, (char *)0);
+ prevsf = FALSE;
+ }
+ return m;
+}
+
+MARK m_Nsrch(m)
+ MARK m; /* where to start searching */
+{
+ if (prevsf)
+ {
+ m = m_bsrch(m, (char *)0);
+ prevsf = TRUE;
+ }
+ else
+ {
+ m = m_fsrch(m, (char *)0);
+ prevsf = FALSE;
+ }
+ return m;
+}
+
+MARK m_fsrch(m, ptrn)
+ MARK m; /* where to start searching */
+ char *ptrn; /* pattern to search for */
+{
+ long l; /* line# of line to be searched */
+ char *line; /* text of line to be searched */
+ int wrapped;/* boolean: has our search wrapped yet? */
+ int pos; /* where we are in the line */
+ long delta; /* line offset, for things like "/foo/+1" */
+
+ /* remember: "previous search was forward" */
+ prevsf = TRUE;
+
+ delta = 0L;
+ if (ptrn && *ptrn)
+ {
+ /* locate the closing '/', if any */
+ line = parseptrn(ptrn);
+ if (*line)
+ {
+ delta = atol(line);
+ }
+ ptrn++;
+
+ /* free the previous pattern */
+ if (re) free(re);
+
+ /* compile the pattern */
+ re = regcomp(ptrn);
+ if (!re)
+ {
+ return MARK_UNSET;
+ }
+ }
+ else if (!re)
+ {
+ msg("No previous expression");
+ return MARK_UNSET;
+ }
+
+ /* search forward for the pattern */
+ pos = markidx(m) + 1;
+ pfetch(markline(m));
+ if (pos >= plen)
+ {
+ pos = 0;
+ m = (m | (BLKSIZE - 1)) + 1;
+ }
+ wrapped = FALSE;
+ for (l = markline(m); l != markline(m) + 1 || !wrapped; l++)
+ {
+ /* wrap search */
+ if (l > nlines)
+ {
+ /* if we wrapped once already, then the search failed */
+ if (wrapped)
+ {
+ break;
+ }
+
+ /* else maybe we should wrap now? */
+ if (*o_wrapscan)
+ {
+ l = 0;
+ wrapped = TRUE;
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ /* get this line */
+ line = fetchline(l);
+
+ /* check this line */
+ if (regexec(re, &line[pos], (pos == 0)))
+ {
+ /* match! */
+ if (wrapped && *o_warn)
+ msg("(wrapped)");
+ if (delta != 0L)
+ {
+ l += delta;
+ if (l < 1 || l > nlines)
+ {
+ msg("search offset too big");
+ return MARK_UNSET;
+ }
+ return m_front(MARK_AT_LINE(l), 0L);
+ }
+ return MARK_AT_LINE(l) + (int)(re->startp[0] - line);
+ }
+ pos = 0;
+ }
+
+ /* not found */
+ msg(*o_wrapscan ? "Not found" : "Hit bottom without finding RE");
+ return MARK_UNSET;
+}
+
+MARK m_bsrch(m, ptrn)
+ MARK m; /* where to start searching */
+ char *ptrn; /* pattern to search for */
+{
+ long l; /* line# of line to be searched */
+ char *line; /* text of line to be searched */
+ int wrapped;/* boolean: has our search wrapped yet? */
+ int pos; /* last acceptable idx for a match on this line */
+ int last; /* remembered idx of the last acceptable match on this line */
+ int try; /* an idx at which we strat searching for another match */
+
+ /* remember: "previous search was not forward" */
+ prevsf = FALSE;
+
+ if (ptrn && *ptrn)
+ {
+ /* locate the closing '?', if any */
+ line = parseptrn(ptrn);
+ ptrn++;
+
+ /* free the previous pattern, if any */
+ if (re) free(re);
+
+ /* compile the pattern */
+ re = regcomp(ptrn);
+ if (!re)
+ {
+ return MARK_UNSET;
+ }
+ }
+ else if (!re)
+ {
+ msg("No previous expression");
+ return MARK_UNSET;
+ }
+
+ /* search backward for the pattern */
+ pos = markidx(m);
+ wrapped = FALSE;
+ for (l = markline(m); l != markline(m) - 1 || !wrapped; l--)
+ {
+ /* wrap search */
+ if (l < 1)
+ {
+ if (*o_wrapscan)
+ {
+ l = nlines + 1;
+ wrapped = TRUE;
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ /* get this line */
+ line = fetchline(l);
+
+ /* check this line */
+ if (regexec(re, line, 1) && (int)(re->startp[0] - line) < pos)
+ {
+ /* match! now find the last acceptable one in this line */
+ do
+ {
+ last = (int)(re->startp[0] - line);
+ try = (int)(re->endp[0] - line);
+ } while (try > 0
+ && regexec(re, &line[try], FALSE)
+ && (int)(re->startp[0] - line) < pos);
+
+ if (wrapped && *o_warn)
+ msg("(wrapped)");
+ return MARK_AT_LINE(l) + last;
+ }
+ pos = BLKSIZE;
+ }
+
+ /* not found */
+ msg(*o_wrapscan ? "Not found" : "Hit top without finding RE");
+ return MARK_UNSET;
+}
+
diff --git a/move3.c b/move3.c
new file mode 100644
index 0000000..a9e46ea
--- /dev/null
+++ b/move3.c
@@ -0,0 +1,163 @@
+/* move3.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains movement functions that perform character searches */
+
+#include "config.h"
+#include "vi.h"
+
+#ifndef NO_CHARSEARCH
+static MARK (*prevfwdfn)(); /* function to search in same direction */
+static MARK (*prevrevfn)(); /* function to search in opposite direction */
+static char prev_key; /* sought cvhar from previous [fFtT] */
+
+MARK m__ch(m, cnt, cmd)
+ MARK m; /* current position */
+ long cnt;
+ char cmd; /* command: either ',' or ';' */
+{
+ MARK (*tmp)();
+
+ if (!prevfwdfn)
+ {
+ msg("No previous f, F, t, or T command");
+ return MARK_UNSET;
+ }
+
+ if (cmd == ',')
+ {
+ m = (*prevrevfn)(m, cnt, prev_key);
+
+ /* Oops! we didn't want to change the prev*fn vars! */
+ tmp = prevfwdfn;
+ prevfwdfn = prevrevfn;
+ prevrevfn = tmp;
+
+ return m;
+ }
+ else
+ {
+ return (*prevfwdfn)(m, cnt, prev_key);
+ }
+}
+
+/* move forward within this line to next occurrence of key */
+MARK m_fch(m, cnt, key)
+ MARK m; /* where to search from */
+ long cnt;
+ char key; /* what to search for */
+{
+ REG char *text;
+
+ DEFAULT(1);
+
+ prevfwdfn = m_fch;
+ prevrevfn = m_Fch;
+ prev_key = key;
+
+ pfetch(markline(m));
+ text = ptext + markidx(m);
+ while (cnt-- > 0)
+ {
+ do
+ {
+ m++;
+ text++;
+ } while (*text && *text != key);
+ }
+ if (!*text)
+ {
+ return MARK_UNSET;
+ }
+ return m;
+}
+
+/* move backward within this line to previous occurrence of key */
+MARK m_Fch(m, cnt, key)
+ MARK m; /* where to search from */
+ long cnt;
+ char key; /* what to search for */
+{
+ REG char *text;
+
+ DEFAULT(1);
+
+ prevfwdfn = m_Fch;
+ prevrevfn = m_fch;
+ prev_key = key;
+
+ pfetch(markline(m));
+ text = ptext + markidx(m);
+ while (cnt-- > 0)
+ {
+ do
+ {
+ m--;
+ text--;
+ } while (text >= ptext && *text != key);
+ }
+ if (text < ptext)
+ {
+ return MARK_UNSET;
+ }
+ return m;
+}
+
+/* move forward within this line almost to next occurrence of key */
+MARK m_tch(m, cnt, key)
+ MARK m; /* where to search from */
+ long cnt;
+ char key; /* what to search for */
+{
+ /* skip the adjacent char */
+ pfetch(markline(m));
+ if (plen <= markidx(m))
+ {
+ return MARK_UNSET;
+ }
+ m++;
+
+ m = m_fch(m, cnt, key);
+ if (m == MARK_UNSET)
+ {
+ return MARK_UNSET;
+ }
+
+ prevfwdfn = m_tch;
+ prevrevfn = m_Tch;
+
+ return m - 1;
+}
+
+/* move backward within this line almost to previous occurrence of key */
+MARK m_Tch(m, cnt, key)
+ MARK m; /* where to search from */
+ long cnt;
+ char key; /* what to search for */
+{
+ /* skip the adjacent char */
+ if (markidx(m) == 0)
+ {
+ return MARK_UNSET;
+ }
+ m--;
+
+ m = m_Fch(m, cnt, key);
+ if (m == MARK_UNSET)
+ {
+ return MARK_UNSET;
+ }
+
+ prevfwdfn = m_Tch;
+ prevrevfn = m_tch;
+
+ return m + 1;
+}
+#endif
diff --git a/move4.c b/move4.c
new file mode 100644
index 0000000..963c96b
--- /dev/null
+++ b/move4.c
@@ -0,0 +1,213 @@
+/* move4.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains movement functions which are screen-relative */
+
+#include "config.h"
+#include "vi.h"
+
+/* This moves the cursor to a particular row on the screen */
+/*ARGSUSED*/
+MARK m_row(m, cnt, key)
+ MARK m; /* the cursor position */
+ long cnt; /* the row we'll move to */
+ int key; /* the keystroke of this move - H/L/M */
+{
+ DEFAULT(1);
+
+ /* calculate destination line based on key */
+ cnt--;
+ switch (key)
+ {
+ case 'H':
+ cnt = topline + cnt;
+ break;
+
+ case 'M':
+ cnt = topline + (LINES - 1) / 2;
+ break;
+
+ case 'L':
+ cnt = botline - cnt;
+ break;
+ }
+
+ /* return the mark of the destination line */
+ return MARK_AT_LINE(cnt);
+}
+
+
+/* This function repositions the current line to show on a given row */
+/*ARGSUSED*/
+MARK m_z(m, cnt, key)
+ MARK m; /* the cursor */
+ long cnt; /* the line number we're repositioning */
+ int key; /* key struck after the z */
+{
+ long newtop;
+
+ /* Which line are we talking about? */
+ if (cnt < 0 || cnt > nlines)
+ {
+ return MARK_UNSET;
+ }
+ if (cnt)
+ {
+ m = MARK_AT_LINE(cnt);
+ newtop = cnt;
+ }
+ else
+ {
+ newtop = markline(m);
+ }
+
+ /* allow a "window size" number to be entered, but ignore it */
+ while (key >= '0' && key <= '9')
+ {
+ key = getkey(0);
+ }
+
+ /* figure out which line will have to be at the top of the screen */
+ switch (key)
+ {
+ case '\n':
+#if OSK
+ case '\l':
+#else
+ case '\r':
+#endif
+ case '+':
+ break;
+
+ case '.':
+ case 'z':
+ newtop -= LINES / 2;
+ break;
+
+ case '-':
+ newtop -= LINES - 1;
+ break;
+
+ default:
+ return MARK_UNSET;
+ }
+
+ /* make the new topline take effect */
+ if (newtop >= 1)
+ {
+ topline = newtop;
+ }
+ else
+ {
+ topline = 1L;
+ }
+ mustredraw = TRUE;
+
+ /* The cursor doesn't move */
+ return m;
+}
+
+
+/* This function scrolls the screen. It does this by calling redraw() with
+ * an off-screen line as the argument. It will move the cursor if necessary
+ * so that the cursor is on the new screen.
+ */
+/*ARGSUSED*/
+MARK m_scroll(m, cnt, key)
+ MARK m; /* the cursor position */
+ long cnt; /* for some keys: the number of lines to scroll */
+ int key; /* keystroke that causes this movement */
+{
+ MARK tmp; /* a temporary mark, used as arg to redraw() */
+
+ /* adjust cnt, and maybe *o_scroll, depending of key */
+ switch (key)
+ {
+ case ctrl('F'):
+ case ctrl('B'):
+ DEFAULT(1);
+ mustredraw = TRUE;
+ cnt = cnt * (LINES - 1) - 1; /* keeps one old line on screen */
+ break;
+
+ case ctrl('E'):
+ case ctrl('Y'):
+ DEFAULT(1);
+ break;
+
+ case ctrl('U'):
+ case ctrl('D'):
+ if (cnt == 0) /* default */
+ {
+ cnt = *o_scroll;
+ }
+ else
+ {
+ if (cnt > LINES - 1)
+ {
+ cnt = LINES - 1;
+ }
+ *o_scroll = cnt;
+ }
+ break;
+ }
+
+ /* scroll up or down, depending on key */
+ switch (key)
+ {
+ case ctrl('B'):
+ case ctrl('Y'):
+ case ctrl('U'):
+ cnt = topline - cnt;
+ if (cnt < 1L)
+ {
+ cnt = 1L;
+ m = MARK_FIRST;
+ }
+ tmp = MARK_AT_LINE(cnt) + markidx(m);
+ redraw(tmp, FALSE);
+ if (markline(m) > botline)
+ {
+ m = MARK_AT_LINE(botline);
+ }
+ break;
+
+ case ctrl('F'):
+ case ctrl('E'):
+ case ctrl('D'):
+ cnt = botline + cnt;
+ if (cnt > nlines)
+ {
+ cnt = nlines;
+ m = MARK_LAST;
+ }
+ tmp = MARK_AT_LINE(cnt) + markidx(m);
+ redraw(tmp, FALSE);
+ if (markline(m) < topline)
+ {
+ m = MARK_AT_LINE(topline);
+ }
+ break;
+ }
+
+ /* arrange for ctrl-B and ctrl-F to redraw the smart line */
+ if (key == ctrl('B') || key == ctrl('F'))
+ {
+ changes++;
+
+ /* also, erase the statusline. This happens naturally for
+ * the scrolling commands, but the paging commands need to
+ * explicitly clear the statusline.
+ */
+ msg("");
+ }
+
+ return m;
+}
diff --git a/move5.c b/move5.c
new file mode 100644
index 0000000..c563ca6
--- /dev/null
+++ b/move5.c
@@ -0,0 +1,230 @@
+/* move5.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains the word-oriented movement functions */
+
+#include <ctype.h>
+#include "config.h"
+#include "vi.h"
+
+#ifndef isascii
+# define isascii(c) !((c) & ~0x7f)
+#endif
+
+
+MARK m_fword(m, cnt, cmd)
+ MARK m; /* movement is relative to this mark */
+ long cnt; /* a numeric argument */
+ int cmd; /* either 'w' or 'W' */
+{
+ REG long l;
+ REG char *text;
+ REG int i;
+
+ DEFAULT(1);
+
+ l = markline(m);
+ pfetch(l);
+ text = ptext + markidx(m);
+ while (cnt-- > 0) /* yes, ASSIGNMENT! */
+ {
+ i = *text++;
+
+ if (cmd == 'W')
+ {
+ /* include any non-whitespace */
+ while (i && (!isascii(i) || !isspace(i)))
+ {
+ i = *text++;
+ }
+ }
+ else if (!isascii(i) || isalnum(i) || i == '_')
+ {
+ /* include an alphanumeric word */
+ while (i && (!isascii(i) || isalnum(i) || i == '_'))
+ {
+ i = *text++;
+ }
+ }
+ else
+ {
+ /* include contiguous punctuation */
+ while (i && isascii(i) && !isalnum(i) && !isspace(i))
+ {
+ i = *text++;
+ }
+ }
+
+ /* include trailing whitespace */
+ while (!i || isascii(i) && isspace(i))
+ {
+ /* did we hit the end of this line? */
+ if (!i)
+ {
+ /* move to next line, if there is one */
+ l++;
+ if (l > nlines)
+ {
+ return MARK_UNSET;
+ }
+ pfetch(l);
+ text = ptext;
+ }
+
+ i = *text++;
+ }
+ text--;
+ }
+
+ /* construct a MARK for this place */
+ m = buildmark(text);
+ return m;
+}
+
+
+MARK m_bword(m, cnt, cmd)
+ MARK m; /* movement is relative to this mark */
+ long cnt; /* a numeric argument */
+ int cmd; /* either 'b' or 'B' */
+{
+ REG long l;
+ REG char *text;
+
+ DEFAULT(1);
+
+ l = markline(m);
+ pfetch(l);
+ text = ptext + markidx(m);
+ while (cnt-- > 0) /* yes, ASSIGNMENT! */
+ {
+ text--;
+
+ /* include preceding whitespace */
+ while (text < ptext || isascii(*text) && isspace(*text))
+ {
+ /* did we hit the end of this line? */
+ if (text < ptext)
+ {
+ /* move to preceding line, if there is one */
+ l--;
+ if (l <= 0)
+ {
+ return MARK_UNSET;
+ }
+ pfetch(l);
+ text = ptext + plen - 1;
+ }
+ else
+ {
+ text--;
+ }
+ }
+
+ if (cmd == 'B')
+ {
+ /* include any non-whitespace */
+ while (text >= ptext && (!isascii(*text) || !isspace(*text)))
+ {
+ text--;
+ }
+ }
+ else if (!isascii(*text) || isalnum(*text) || *text == '_')
+ {
+ /* include an alphanumeric word */
+ while (text >= ptext && (!isascii(*text) || isalnum(*text) || *text == '_'))
+ {
+ text--;
+ }
+ }
+ else
+ {
+ /* include contiguous punctuation */
+ while (text >= ptext && isascii(*text) && !isalnum(*text) && !isspace(*text))
+ {
+ text--;
+ }
+ }
+ text++;
+ }
+
+ /* construct a MARK for this place */
+ m = buildmark(text);
+ return m;
+}
+
+MARK m_eword(m, cnt, cmd)
+ MARK m; /* movement is relative to this mark */
+ long cnt; /* a numeric argument */
+ int cmd; /* either 'e' or 'E' */
+{
+ REG long l;
+ REG char *text;
+ REG int i;
+
+ DEFAULT(1);
+
+ l = markline(m);
+ pfetch(l);
+ text = ptext + markidx(m);
+ while (cnt-- > 0) /* yes, ASSIGNMENT! */
+ {
+ text++;
+ i = *text++;
+
+ /* include preceding whitespace */
+ while (!i || isascii(i) && isspace(i))
+ {
+ /* did we hit the end of this line? */
+ if (!i)
+ {
+ /* move to next line, if there is one */
+ l++;
+ if (l > nlines)
+ {
+ return MARK_UNSET;
+ }
+ pfetch(l);
+ text = ptext;
+ }
+
+ i = *text++;
+ }
+
+ if (cmd == 'E')
+ {
+ /* include any non-whitespace */
+ while (i && (!isascii(i) || !isspace(i)))
+ {
+ i = *text++;
+ }
+ }
+ else if (!isascii(i) || isalnum(i) || i == '_')
+ {
+ /* include an alphanumeric word */
+ while (i && (!isascii(i) || isalnum(i) || i == '_'))
+ {
+ i = *text++;
+ }
+ }
+ else
+ {
+ /* include contiguous punctuation */
+ while (i && isascii(i) && !isalnum(i) && !isspace(i))
+ {
+ i = *text++;
+ }
+ }
+ text -= 2;
+ }
+
+ /* construct a MARK for this place */
+ m = buildmark(text);
+ return m;
+}
diff --git a/opts.c b/opts.c
new file mode 100644
index 0000000..8153fe6
--- /dev/null
+++ b/opts.c
@@ -0,0 +1,680 @@
+/* opts.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains the code that manages the run-time options -- The
+ * values that can be modified via the "set" command.
+ */
+
+#include "config.h"
+#include "vi.h"
+#ifndef NULL
+#define NULL (char *)0
+#endif
+extern char *getenv();
+
+/* maximum width to permit for strings, including ="" */
+#define MAXWIDTH 20
+
+/* These are the default values of all options */
+char o_autoindent[1] = {FALSE};
+char o_autoprint[1] = {TRUE};
+char o_autowrite[1] = {FALSE};
+#ifndef NO_ERRLIST
+char o_cc[30] = {CC_COMMAND};
+#endif
+#ifndef NO_CHARATTR
+char o_charattr[1] = {FALSE};
+#endif
+char o_columns[3] = {80, 32, 255};
+#ifndef NO_DIGRAPH
+char o_digraph[1] = {FALSE};
+#endif
+char o_directory[30] = TMPDIR;
+char o_edcompatible[1] = {FALSE};
+char o_errorbells[1] = {TRUE};
+char o_exrefresh[1] = {TRUE};
+#ifndef NO_DIGRAPH
+char o_flipcase[80]
+# if CS_IBMPC
+ = {"\207\200\201\232\202\220\204\216\206\217\221\222\224\231\244\245"}
+# endif
+# if CS_LATIN1
+ /* initialized by initopts() */
+# endif
+ ;
+#endif
+#ifndef NO_SENTENCE
+char o_hideformat[1] = {FALSE};
+#endif
+char o_ignorecase[1] = {FALSE};
+#ifndef NO_EXTENSIONS
+char o_inputmode[1] = {FALSE};
+#endif
+char o_keytime[3] = {2, 0, 5};
+char o_keywordprg[80] = {KEYWORDPRG};
+char o_lines[3] = {25, 2, 50}; /* More lines? Enlarge kbuf */
+char o_list[1] = {FALSE};
+#ifndef NO_MAGIC
+char o_magic[1] = {TRUE};
+#endif
+#ifndef NO_ERRLIST
+char o_make[30] = {MAKE_COMMAND};
+#endif
+#ifndef NO_MODELINE
+char o_modeline[1] = {FALSE};
+#endif
+#ifndef NO_SENTENCE
+char o_paragraphs[30] = "PPppIPLPQP";
+#endif
+#if MSDOS
+char o_pcbios[1] = {TRUE};
+#endif
+char o_readonly[1] = {FALSE};
+char o_report[3] = {5, 1, 127};
+char o_scroll[3] = {12, 1, 127};
+#ifndef NO_SENTENCE
+char o_sections[30] = "NHSHSSSEse";
+#endif
+char o_shell[60] = SHELL;
+char o_shiftwidth[3] = {8, 1, 255};
+#ifndef NO_SHOWMATCH
+char o_showmatch[1] = {FALSE};
+#endif
+#ifndef NO_SHOWMODE
+char o_smd[1] = {FALSE};
+#endif
+char o_sidescroll[3] = {8, 1, 40};
+char o_sync[1] = {NEEDSYNC};
+char o_tabstop[3] = {8, 1, 40};
+char o_term[30] = "?";
+char o_vbell[1] = {TRUE};
+char o_warn[1] = {TRUE};
+char o_wrapmargin[3] = {0, 0, 255};
+char o_wrapscan[1] = {TRUE};
+
+
+/* The following describes the names & types of all options */
+#define BOOL 0
+#define NUM 1
+#define STR 2
+#define SET 0x01 /* this option has had its value altered */
+#define CANSET 0x02 /* this option can be set at any time */
+#define RCSET 0x06 /* this option can be set in a .exrc file only */
+#define MR 0x40 /* does this option affect the way text is displayed? */
+struct
+{
+ char *name; /* name of an option */
+ char *nm; /* short name of an option */
+ char type; /* type of an option */
+ char flags; /* boolean: has this option been set? */
+ char *value; /* value */
+}
+ opts[] =
+{
+ /* name type flags redraw value */
+ { "autoindent", "ai", BOOL, CANSET , o_autoindent },
+ { "autoprint", "ap", BOOL, CANSET , o_autoprint },
+ { "autowrite", "aw", BOOL, CANSET , o_autowrite },
+#ifndef NO_ERRLIST
+ { "cc", "cc", STR, CANSET , o_cc },
+#endif
+#ifndef NO_CHARATTR
+ { "charattr", "ca", BOOL, CANSET | MR, o_charattr },
+#endif
+ { "columns", "co", NUM, SET , o_columns },
+#ifndef NO_DIGRAPH
+ { "digraph", "dig", BOOL, CANSET , o_digraph },
+#endif
+ { "directory", "dir", STR, RCSET , o_directory },
+ { "edcompatible","ed", BOOL, CANSET , o_edcompatible },
+ { "errorbells", "eb", BOOL, CANSET , o_errorbells },
+ { "exrefresh", "er", BOOL, CANSET , o_exrefresh },
+#ifndef NO_DIGRAPH
+ { "flipcase", "fc", STR, CANSET , o_flipcase },
+#endif
+#ifndef NO_SENTENCE
+ { "hideformat", "hf", BOOL, CANSET | MR, o_hideformat },
+#endif
+ { "ignorecase", "ic", BOOL, CANSET , o_ignorecase },
+#ifndef NO_EXTENSIONS
+ { "inputmode", "im", BOOL, CANSET , o_inputmode },
+#endif
+ { "keytime", "kt", NUM, CANSET , o_keytime },
+ { "keywordprg", "kp", STR, CANSET , o_keywordprg },
+ { "lines", "ls", NUM, SET , o_lines },
+ { "list", "li", BOOL, CANSET | MR, o_list },
+#ifndef NO_MAGIC
+ { "magic", "ma", BOOL, CANSET , o_magic },
+#endif
+#ifndef NO_ERRLIST
+ { "make", "mk", STR, CANSET , o_make },
+#endif
+#ifndef NO_MODELINE
+ { "modeline", "ml", BOOL, CANSET , o_modeline },
+#endif
+#ifndef NO_SENTENCE
+ { "paragraphs", "pa", STR, CANSET , o_paragraphs },
+#endif
+#if MSDOS
+ { "pcbios", "pc", BOOL, SET , o_pcbios },
+#endif
+ { "readonly", "ro", BOOL, CANSET , o_readonly },
+ { "report", "re", NUM, CANSET , o_report },
+ { "scroll", "sc", NUM, CANSET , o_scroll },
+#ifndef NO_SENTENCE
+ { "sections", "se", STR, CANSET , o_sections },
+#endif
+ { "shell", "sh", STR, CANSET , o_shell },
+#ifndef NO_SHOWMATCH
+ { "showmatch", "sm", BOOL, CANSET , o_showmatch },
+#endif
+#ifndef NO_SHOWMODE
+ { "showmode", "smd", BOOL, CANSET , o_smd },
+#endif
+ { "shiftwidth", "sw", NUM, CANSET , o_shiftwidth },
+ { "sidescroll", "ss", NUM, CANSET , o_sidescroll },
+ { "sync", "sy", BOOL, CANSET , o_sync },
+ { "tabstop", "ts", NUM, CANSET | MR, o_tabstop },
+ { "term", "te", STR, SET , o_term },
+ { "vbell", "vb", BOOL, CANSET , o_vbell },
+ { "warn", "wa", BOOL, CANSET , o_warn },
+ { "wrapmargin", "wm", NUM, CANSET , o_wrapmargin },
+ { "wrapscan", "ws", BOOL, CANSET , o_wrapscan },
+ { NULL, NULL, 0, CANSET, NULL }
+};
+
+
+/* This function initializes certain options from environment variables, etc. */
+void initopts()
+{
+ char *val;
+ int i;
+
+ /* set some stuff from environment variables */
+#if MSDOS
+ if (val = getenv("COMSPEC")) /* yes, ASSIGNMENT! */
+#else
+ if (val = getenv("SHELL")) /* yes, ASSIGNMENT! */
+#endif
+ {
+ strcpy(o_shell, val);
+ }
+
+#if ANY_UNIX
+ if (val = getenv("TERM")) /* yes, ASSIGNMENT! */
+ {
+ strcpy(o_term, val);
+ }
+#endif
+#if TOS
+ val = "vt52";
+ strcpy(o_term, val);
+#endif
+#if MSDOS
+ if ((val = getenv("TERM")) /* yes, ASSIGNMENT! */
+ && strcmp(val, "pcbios"))
+ {
+ strcpy(o_term, val);
+ o_pcbios[0] = 0;
+ }
+ else
+ {
+ strcpy(o_term, "pcbios");
+ o_pcbios[0] = 1;
+ }
+#endif
+#if MSDOS || TOS
+ if ((val = getenv("TMP")) /* yes, ASSIGNMENT! */
+ || (val = getenv("TEMP")))
+ strcpy(o_directory, val);
+#endif
+
+ *o_scroll = LINES / 2 - 1;
+
+ /* disable the vbell option if we don't know how to do a vbell */
+ if (!has_VB)
+ {
+ for (i = 0; opts[i].value != o_vbell; i++)
+ {
+ }
+ opts[i].flags &= ~CANSET;
+ *o_vbell = FALSE;
+ }
+
+#ifndef NO_DIGRAPH
+# ifdef CS_LATIN1
+ for (i = 0, val = o_flipcase; i < 32; i++)
+ {
+ /* leave out the multiply/divide symbols */
+ if (i == 23)
+ continue;
+
+ /* add upper/lowercase pair */
+ *val++ = i + 0xc0;
+ *val++ = i + 0xe0;
+ }
+ *val = '\0';
+# endif /* CS_LATIN1 */
+#endif /* not NO_DIGRAPH */
+}
+
+/* This function lists the current values of all options */
+void dumpopts(all)
+ int all; /* boolean: dump all options, or just set ones? */
+{
+#ifndef NO_OPTCOLS
+ int i, j, k;
+ char nbuf[4]; /* used for converting numbers to ASCII */
+ int widths[5]; /* width of each column, including gap */
+ int ncols; /* number of columns */
+ int nrows; /* number of options per column */
+ int nset; /* number of options to be output */
+ int width; /* width of a particular option */
+ int todump[50]; /* indicies of options to be dumped */
+
+ /* step 1: count the number of set options */
+ for (nset = i = 0; opts[i].name; i++)
+ {
+ if (all || (opts[i].flags & SET))
+ {
+ todump[nset++] = i;
+ }
+ }
+
+ /* step two: try to use as many columns as possible */
+ for (ncols = (nset > 5 ? 5 : nset); ncols > 1; ncols--)
+ {
+ /* how many would go in this column? */
+ nrows = (nset + ncols - 1) / ncols;
+
+ /* figure out the width of each column */
+ for (i = 0; i < ncols; i++)
+ {
+ widths[i] = 0;
+ for (j = 0, k = nrows * i; j < nrows && k < nset; j++, k++)
+ {
+ /* figure out the width of a particular option */
+ switch (opts[todump[k]].type)
+ {
+ case BOOL:
+ if (!*opts[todump[k]].value)
+ width = 2;
+ else
+ width = 0;
+ break;
+
+ case STR:
+ width = 3 + strlen(opts[todump[k]].value);
+ if (width > MAXWIDTH)
+ width = MAXWIDTH;
+ break;
+
+ case NUM:
+ width = 4;
+ break;
+ }
+ width += strlen(opts[todump[k]].name);
+
+ /* if this is the widest so far, widen col */
+ if (width > widths[i])
+ {
+ widths[i] = width;
+ }
+ }
+
+ }
+
+ /* if the total width is narrow enough, then use it */
+ for (width = -2, i = 0; i < ncols; i++)
+ {
+ width += widths[i] + 2;
+ }
+ if (width < COLS - 1)
+ {
+ break;
+ }
+ }
+
+ /* step 3: output the columns */
+ nrows = (nset + ncols - 1) / ncols;
+ for (i = 0; i < nrows; i++)
+ {
+ for (j = 0; j < ncols; j++)
+ {
+ /* if we hit the end of the options, quit */
+ k = i + j * nrows;
+ if (k >= nset)
+ {
+ break;
+ }
+
+ /* output this option's value */
+ width = 0;
+ switch (opts[todump[k]].type)
+ {
+ case BOOL:
+ if (!*opts[todump[k]].value)
+ {
+ qaddch('n');
+ qaddch('o');
+ width = 2;
+ }
+ qaddstr(opts[todump[k]].name);
+ width += strlen(opts[todump[k]].name);
+ break;
+
+ case NUM:
+ sprintf(nbuf, "%-3d", UCHAR(*opts[todump[k]].value));
+ qaddstr(opts[todump[k]].name);
+ qaddch('=');
+ qaddstr(nbuf);
+ width = 4 + strlen(opts[todump[k]].name);
+ break;
+
+ case STR:
+ qaddstr(opts[todump[k]].name);
+ qaddch('=');
+ qaddch('"');
+ strcpy(tmpblk.c, opts[todump[k]].value);
+ width = 3 + strlen(tmpblk.c);
+ if (width > MAXWIDTH)
+ {
+ width = MAXWIDTH;
+ strcpy(tmpblk.c + MAXWIDTH - 6, "...");
+ }
+ qaddstr(tmpblk.c);
+ qaddch('"');
+ width += strlen(opts[todump[k]].name);
+ break;
+ }
+
+ /* pad the field to the correct size */
+ if (k + nrows <= nset)
+ {
+ while (width < widths[j] + 2)
+ {
+ qaddch(' ');
+ width++;
+ }
+ }
+ }
+ addch('\n');
+ exrefresh();
+ }
+#else
+ int i;
+ int col;
+ char nbuf[4];
+
+ for (i = col = 0; opts[i].name; i++)
+ {
+ /* if not set and not all, ignore this option */
+ if (!all && !(opts[i].flags & SET))
+ {
+ continue;
+ }
+
+ /* align this option in one of the columns */
+ if (col > 52)
+ {
+ addch('\n');
+ col = 0;
+ }
+ else if (col > 26)
+ {
+ while (col < 52)
+ {
+ qaddch(' ');
+ col++;
+ }
+ }
+ else if (col > 0)
+ {
+ while (col < 26)
+ {
+ qaddch(' ');
+ col++;
+ }
+ }
+
+ switch (opts[i].type)
+ {
+ case BOOL:
+ if (!*opts[i].value)
+ {
+ qaddch('n');
+ qaddch('o');
+ col += 2;
+ }
+ qaddstr(opts[i].name);
+ col += strlen(opts[i].name);
+ break;
+
+ case NUM:
+ sprintf(nbuf, "%-3d", UCHAR(*opts[i].value));
+ qaddstr(opts[i].name);
+ qaddch('=');
+ qaddstr(nbuf);
+ col += 4 + strlen(opts[i].name);
+ break;
+
+ case STR:
+ qaddstr(opts[i].name);
+ qaddch('=');
+ qaddch('"');
+ qaddstr(opts[i].value);
+ qaddch('"');
+ col += 3 + strlen(opts[i].name) + strlen(opts[i].value);
+ break;
+ }
+ exrefresh();
+ }
+ if (col > 0)
+ {
+ addch('\n');
+ exrefresh();
+ }
+#endif
+}
+
+#ifndef NO_MKEXRC
+/* This function saves the current configuarion of options to a file */
+void saveopts(fd)
+ int fd; /* file descriptor to write to */
+{
+ int i;
+ char buf[256], *pos;
+
+ /* write each set options */
+ for (i = 0; opts[i].name; i++)
+ {
+ /* if unset or unsettable, ignore this option */
+ if (!(opts[i].flags & SET) || !(opts[i].flags & CANSET))
+ {
+ continue;
+ }
+
+ strcpy(buf, "set ");
+ pos = &buf[4];
+ switch (opts[i].type)
+ {
+ case BOOL:
+ if (!*opts[i].value)
+ {
+ *pos++='n';
+ *pos++='o';
+ }
+ strcpy(pos, opts[i].name);
+ strcat(pos, "\n");
+ break;
+
+ case NUM:
+ sprintf(pos, "%s=%-3d\n", opts[i].name, *opts[i].value & 0xff);
+ break;
+
+ case STR:
+ sprintf(pos, "%s=\"%s\"\n", opts[i].name, opts[i].value);
+ break;
+ }
+ twrite(fd, buf, strlen(buf));
+ }
+}
+#endif
+
+
+/* This function changes the values of one or more options. */
+void setopts(assignments)
+ char *assignments; /* a string containing option assignments */
+{
+ char *name; /* name of variable in assignments */
+ char *value; /* value of the variable */
+ char *scan; /* used for moving through strings */
+ int i, j;
+
+ /* for each assignment... */
+ for (name = assignments; *name; )
+ {
+ /* skip whitespace */
+ if (*name == ' ' || *name == '\t')
+ {
+ name++;
+ continue;
+ }
+
+ /* find the value, if any */
+ for (scan = name; *scan >= 'a' && *scan <= 'z'; scan++)
+ {
+ }
+ if (*scan == '=')
+ {
+ *scan++ = '\0';
+ if (*scan == '"')
+ {
+ value = ++scan;
+ while (*scan && *scan != '"')
+ {
+ scan++;
+ }
+ if (*scan)
+ {
+ *scan++ = '\0';
+ }
+ }
+ else
+ {
+ value = scan;
+ while (*scan && *scan != ' ' && *scan != '\t')
+ {
+ scan++;
+ }
+ if (*scan)
+ {
+ *scan++ = '\0';
+ }
+ }
+ }
+ else
+ {
+ if (*scan)
+ {
+ *scan++ = '\0';
+ }
+ value = NULL;
+ if (name[0] == 'n' && name[1] == 'o')
+ {
+ name += 2;
+ }
+ }
+
+ /* find the variable */
+ for (i = 0;
+ opts[i].name && strcmp(opts[i].name, name) && strcmp(opts[i].nm, name);
+ i++)
+ {
+ }
+
+ /* change the variable */
+ if (!opts[i].name)
+ {
+ msg("invalid option name \"%s\"", name);
+ }
+ else if ((opts[i].flags & CANSET) != CANSET)
+ {
+ msg("option \"%s\" can't be altered", name);
+ }
+ else if ((opts[i].flags & RCSET) != CANSET && nlines >= 1L)
+ {
+ msg("option \"%s\" can only be set in a %s file", name, EXRC);
+ }
+ else if (value)
+ {
+ switch (opts[i].type)
+ {
+ case BOOL:
+ msg("option \"[no]%s\" is boolean", name);
+ break;
+
+ case NUM:
+ j = atoi(value);
+ if (j == 0 && *value != '0')
+ {
+ msg("option \"%s\" must have a numeric value", name);
+ }
+ else if (j < opts[i].value[1] || j > (opts[i].value[2] & 0xff))
+ {
+ msg("option \"%s\" must have a value between %d and %d",
+ name, opts[i].value[1], opts[i].value[2] & 0xff);
+ }
+ else
+ {
+ *opts[i].value = atoi(value);
+ opts[i].flags |= SET;
+ }
+ break;
+
+ case STR:
+ strcpy(opts[i].value, value);
+ opts[i].flags |= SET;
+ break;
+ }
+ if (opts[i].flags & MR)
+ {
+ mustredraw = TRUE;
+ }
+ }
+ else /* valid option, no value */
+ {
+ if (opts[i].type == BOOL)
+ {
+ *opts[i].value = (name[-1] != 'o');
+ opts[i].flags |= SET;
+ if (opts[i].flags & MR)
+ {
+ mustredraw = TRUE;
+ }
+ }
+ else
+ {
+ msg("option \"%s\" must be given a value", name);
+ }
+ }
+
+ /* move on to the next option */
+ name = scan;
+ }
+
+ /* special processing ... */
+
+ /* if "readonly" then set the READONLY flag for this file */
+ if (*o_readonly)
+ {
+ setflag(file, READONLY);
+ }
+}
diff --git a/osk.c b/osk.c
new file mode 100644
index 0000000..732d85a
--- /dev/null
+++ b/osk.c
@@ -0,0 +1,205 @@
+/* osk.c */
+
+/* ------------------------------------------------------------------- *
+ |
+ | OS9Lib: stat(), fstat()
+ |
+ |
+ | Copyright (c) 1988 by Wolfgang Ocker, Puchheim,
+ | Ulli Dessauer, Germering and
+ | Reimer Mellin, Muenchen
+ | (W-Germany)
+ |
+ | This programm can be copied and distributed freely for any
+ | non-commercial purposes. It can only be incorporated into
+ | commercial software with the written permission of the authors.
+ |
+ | If you should modify this program, the authors would appreciate
+ | a notice about the changes. Please send a (context) diff or the
+ | complete source to:
+ |
+ | address: Wolfgang Ocker
+ | Lochhauserstrasse 35a
+ | D-8039 Puchheim
+ | West Germany
+ |
+ | e-mail: weo@altger.UUCP, ud@altger.UUCP, ram@altger.UUCP
+ | pyramid!tmpmbx!recco!weo
+ | pyramid!tmpmbx!nitmar!ud
+ | pyramid!tmpmbx!ramsys!ram
+ |
+ * ----------------------------------------------------------------- */
+
+#ifdef OSK
+
+#define PATCHLEVEL 1
+
+#ifndef VIREC
+#include <stdio.h>
+#include "osk.h"
+#include <modes.h>
+#include <errno.h>
+#endif
+#include <signal.h>
+#include <sgstat.h>
+#include <sg_codes.h>
+#include <direct.h>
+
+/*
+ * f s t a t
+ */
+int fstat(fd, buff)
+ int fd;
+ struct stat *buff;
+{
+ struct fildes ftmp;
+ struct tm ttmp;
+ struct _sgr fopt;
+
+ if (_gs_gfd(fd, &ftmp, 16) < 0) /* 16 insteat of sizeof(struct fildes) */
+ return(-1); /* used due to a bug in stupid os9net */
+
+ if (_gs_opt(fd, &fopt) < 0)
+ return(-1);
+
+ ttmp.tm_year = (int) ftmp.fd_date[0];
+ ttmp.tm_mon = (int) ftmp.fd_date[1] - 1;
+ ttmp.tm_mday = (int) ftmp.fd_date[2];
+ ttmp.tm_hour = (int) ftmp.fd_date[3];
+ ttmp.tm_min = (int) ftmp.fd_date[4];
+ ttmp.tm_sec = 0;
+ ttmp.tm_isdst = -1;
+
+ buff->st_atime = buff->st_mtime = mktime(&ttmp);
+
+ ttmp.tm_year = (int) ftmp.fd_dcr[0];
+ ttmp.tm_mon = (int) ftmp.fd_dcr[1] - 1;
+ ttmp.tm_mday = (int) ftmp.fd_dcr[2];
+ ttmp.tm_hour = ttmp.tm_min = ttmp.tm_sec = 0;
+ ttmp.tm_isdst = -1;
+
+ buff->st_ctime = mktime(&ttmp);
+
+ memcpy(&(buff->st_size), ftmp.fd_fsize, sizeof(long)); /* misalignment! */
+ buff->st_uid = ftmp.fd_own[1];
+ buff->st_gid = ftmp.fd_own[0];
+ buff->st_mode = ftmp.fd_att;
+ buff->st_nlink = ftmp.fd_link;
+
+ buff->st_ino = fopt._sgr_fdpsn;
+ buff->st_dev = fopt._sgr_dvt;
+
+ return(0);
+}
+
+/*
+ * s t a t
+ */
+int stat(filename, buff)
+ char *filename;
+ struct stat *buff;
+{
+ register int i, ret;
+
+ if ((i = open(filename, S_IREAD)) < 0)
+ if ((i = open(filename, S_IFDIR | S_IREAD)) < 0)
+ return(-1);
+
+ ret = fstat(i, buff);
+ close(i);
+
+ return(ret);
+}
+
+/*
+ unix library functions mist in OSK
+ Author: Peter Reinig
+*/
+
+/* NOTE: this version of link() is only capable of renaming files, not true
+ * UNIX-style linking. That's okay, though, because elvis only uses it for
+ * renaming.
+ */
+link(from,to)
+char *from,*to;
+{
+ char *buffer;
+ int status;
+ char *malloc();
+
+ if ((buffer = malloc(strlen(from) + strlen(to) + 12)) == NULL)
+ return -1;
+ sprintf(buffer,"rename %s %s\n",from,to);
+ status = system(buffer);
+ free(buffer);
+ return status;
+}
+
+typedef (*procref)();
+#define MAX_SIGNAL 10
+
+extern exit();
+
+static int (*sig_table[MAX_SIGNAL])();
+static int _sig_install = 0;
+
+sig_handler(sig)
+int sig;
+{
+ if ((int) sig_table[sig] > MAX_SIGNAL)
+ sig_table[sig](sig);
+}
+
+procref signal(sig,func)
+int sig;
+int (*func)();
+{
+ int i, (*sav)();
+
+ if (!_sig_install) {
+ for (i=0; i < MAX_SIGNAL; i++)
+ sig_table[i] = exit;
+ _sig_install = 1;
+ intercept(sig_handler);
+ }
+ sav = sig_table[sig];
+ switch ((int) func) {
+ case SIG_DFL : sig_table[sig] = exit;
+ break;
+ case SIG_IGN : sig_table[sig] = 0;
+ break;
+ default : sig_table[sig] = func;
+ break;
+ }
+ return sav;
+}
+
+perror(str)
+char *str;
+{
+ static int path = 0;
+ if (!path && (path = open("/dd/sys/Errmsg", S_IREAD)) == -1) {
+ fprintf(stderr,"Can\'t open error message file\n");
+ path = 0;
+ }
+ if (str && *str) {
+ fprintf(stderr,"%s: ",str);
+ fflush(stderr);
+ }
+ prerr(path,(short) errno);
+}
+
+isatty(fd)
+int fd;
+{
+ struct sgbuf buffer;
+ char type;
+
+ _gs_opt(fd,&buffer);
+ type = buffer.sg_class;
+ if (type == DT_SCF)
+ return 1;
+ else
+ return 0;
+}
+#endif /* OSK */
diff --git a/osk.h b/osk.h
new file mode 100644
index 0000000..598895c
--- /dev/null
+++ b/osk.h
@@ -0,0 +1,26 @@
+/*
+ * OS9 stat : @(#)stat.h 1.2 87/19/12
+ */
+/* @(#)stat.h 6.1 */
+/*
+ * Structure of the result of stat
+ */
+
+#ifndef CLK_TCK
+#include <time.h>
+#endif
+
+struct stat
+{
+ int st_dev;
+ long st_ino;
+ unsigned short st_mode;
+ unsigned short st_nlink;
+ unsigned short st_uid;
+ unsigned short st_gid;
+ int st_rdev;
+ long st_size;
+ time_t st_atime;
+ time_t st_mtime;
+ time_t st_ctime;
+};
diff --git a/pc.c b/pc.c
new file mode 100644
index 0000000..058fae3
--- /dev/null
+++ b/pc.c
@@ -0,0 +1,270 @@
+/* pc.c */
+
+/* Author:
+ * Guntram Blohm
+ * Buchenstrasse 19
+ * 7904 Erbach, West Germany
+ * Tel. ++49-7305-6997
+ * sorry - no regular network connection
+ */
+
+/* This file implements the ibm pc bios interface. See IBM documentation
+ * for details.
+ * If TERM is set upon invocation of elvis, this code is ignored completely,
+ * and the standard termcap functions are used, thus, even not-so-close
+ * compatibles can run elvis. For close compatibles however, bios output
+ * is much faster (and permits reverse scrolling, adding and deleting lines,
+ * and much more ansi.sys isn't capable of). GB.
+ */
+
+#include "config.h"
+#include "vi.h"
+
+#if MSDOS
+
+#include <dos.h>
+
+static void video();
+
+/* vmode contains the screen attribute index and is set by attrset.*/
+
+int vmode;
+
+/* The following array contains attribute definitions for
+ * color/monochrome attributes. Screen selects one of the sets.
+ * Maybe i'll put them into elvis options one day.
+ */
+
+static int screen;
+static char attr[2][5] =
+{
+ /* :se: :so: :VB: :ul: :as: */
+ { 0x1f, 0x1d, 0x1e, 0x1a, 0x1c, }, /* color */
+ { 0x07, 0x70, 0x0f, 0x01, 0x0f, }, /* monochrome */
+};
+
+/*
+ * bios interface functions for elvis - pc version
+ */
+
+/* cursor up: determine current position, decrement row, set position */
+
+void v_up()
+{
+ int dx;
+ video(0x300,(int *)0,&dx);
+ dx-=0x100;
+ video(0x200,(int *)0,&dx);
+}
+
+#ifndef NO_CURSORSHAPE
+/* cursor big: set begin scan to end scan - 4 */
+void v_cb()
+{
+ int cx;
+ video(0x300, &cx, (int *)0);
+ cx=((cx&0xff)|(((cx&0xff)-4)<<8));
+ video(0x100, &cx, (int *)0);
+}
+
+/* cursor small: set begin scan to end scan - 1 */
+void v_cs()
+{
+ int cx;
+ video(0x300, &cx, (int *)0);
+ cx=((cx&0xff)|(((cx&0xff)-1)<<8));
+ video(0x100, &cx, (int *)0);
+}
+#endif
+
+/* clear to end: get cursor position and emit the aproppriate number
+ * of spaces, without moving cursor.
+ */
+
+void v_ce()
+{
+ int cx, dx;
+ video(0x300,(int *)0,&dx);
+ cx=COLS-(dx&0xff);
+ video(0x920,&cx,(int *)0);
+}
+
+/* clear screen: clear all and set cursor home */
+
+void v_cl()
+{
+ int cx=0, dx=((LINES-1)<<8)+COLS-1;
+ video(0x0600,&cx,&dx);
+ dx=0;
+ video(0x0200,&cx,&dx);
+}
+
+/* clear to bottom: get position, clear to eol, clear next line to end */
+
+void v_cd()
+{
+ int cx, dx, dxtmp;
+ video(0x0300,(int *)0,&dx);
+ dxtmp=(dx&0xff00)|(COLS-1);
+ cx=dx;
+ video(0x0600,&cx,&dxtmp);
+ cx=(dx&0xff00)+0x100;
+ dx=((LINES-1)<<8)+COLS-1;
+ video(0x600,&cx,&dx);
+}
+
+/* add line: scroll rest of screen down */
+
+void v_al()
+{
+ int cx,dx;
+ video(0x0300,(int *)0,&dx);
+ cx=(dx&0xff00);
+ dx=((LINES-1)<<8)+COLS-1;
+ video(0x701,&cx,&dx);
+}
+
+/* delete line: scroll rest up */
+
+void v_dl()
+{
+ int cx,dx;
+ video(0x0300,(int *)0,&dx);
+ cx=(dx&0xff00)/*+0x100*/;
+ dx=((LINES-1)<<8)+COLS-1;
+ video(0x601,&cx,&dx);
+}
+
+/* scroll reverse: scroll whole screen */
+
+void v_sr()
+{
+ int cx=0, dx=((LINES-1)<<8)+COLS-1;
+ video(0x0701,&cx,&dx);
+}
+
+/* set cursor */
+
+void v_move(x,y)
+ int x, y;
+{
+ int dx=(y<<8)+x;
+ video(0x200,(int *)0,&dx);
+}
+
+/* put character: set attribute first, then execute char.
+ * Also remember if current line has changed.
+ */
+
+int v_put(ch)
+ int ch;
+{
+ int cx=1;
+ ch&=0xff;
+ if (ch>=' ')
+ video(0x900|ch,&cx,(int *)0);
+ video(0xe00|ch,(int *)0, (int *)0);
+ if (ch=='\n')
+ { exwrote = TRUE;
+ video(0xe0d, (int *)0, (int *)0);
+ }
+ return ch;
+}
+
+/* determine number of screen columns. Also set attrset according
+ * to monochrome/color screen.
+ */
+
+int v_cols()
+{
+ union REGS regs;
+ regs.h.ah=0x0f;
+ int86(0x10, &regs, &regs);
+ if (regs.h.al==7) /* monochrome mode ? */
+ screen=1;
+ else
+ screen=0;
+ return regs.h.ah;
+}
+
+/* Getting the number of rows is hard. Most screens support 25 only,
+ * EGA/VGA also support 43/50 lines, and some OEM's even more.
+ * Unfortunately, there is no standard bios variable for the number
+ * of lines, and the bios screen memory size is always rounded up
+ * to 0x1000. So, we'll really have to cheat.
+ * When using the screen memory size, keep in mind that each character
+ * byte has an associated attribute byte.
+ *
+ * uses: word at 40:4c contains memory size
+ * byte at 40:84 # of rows-1 (sometimes)
+ * byte at 40:4a # of columns
+ */
+
+int v_rows()
+{
+ int line, oldline;
+
+ /* screen size less then 4K? then we have 25 lines only */
+
+ if (*(int far *)(0x0040004cl)<=4096)
+ return 25;
+
+ /* VEGA vga uses the bios byte at 0x40:0x84 for # of rows.
+ * Use that byte, if it makes sense together with memory size.
+ */
+
+ if ((((*(unsigned char far *)(0x0040004aL)*2*
+ (*(unsigned char far *)(0x00400084L)+1))+0xfff)&(~0xfff))==
+ *(unsigned int far *)(0x0040004cL))
+ return *(unsigned char far *)(0x00400084L)+1;
+
+ /* uh oh. Emit '\n's until screen starts scrolling. */
+
+ v_move(oldline=0, 0);
+ for (;;)
+ {
+ video(0xe0a,(int *)0,(int *)0);
+ video(0x300,(int *)0,&line);
+ line>>=8;
+ if (oldline==line)
+ return line+1;
+ oldline=line;
+ }
+}
+
+/* the REAL bios interface -- used internally only. */
+
+static void video(ax, cx, dx)
+ int ax, *cx, *dx;
+{
+ union REGS regs;
+
+ regs.x.ax=ax;
+ if ((ax&0xff00)==0x600 || (ax&0xff00)==0x700)
+ regs.h.bh=attr[screen][vmode];
+ else
+ {
+ regs.h.bh=0;
+ regs.h.bl=attr[screen][vmode];
+ }
+ if (cx) regs.x.cx=*cx;
+ if (dx) regs.x.dx=*dx;
+ int86(0x10, &regs, &regs);
+ if (dx) *dx=regs.x.dx;
+ if (cx) *cx=regs.x.cx;
+}
+
+/* The following function determines which character is used for
+ * commandline-options by command.com. This system call is undocumented
+ * and valid for versions < 4.00 only.
+ */
+
+int switchar()
+{
+ union REGS regs;
+ regs.x.ax=0x3700;
+ int86(0x21, &regs, &regs);
+ return regs.h.dl;
+}
+
+#endif
diff --git a/profile.sh b/profile.sh
new file mode 100644
index 0000000..22143c3
--- /dev/null
+++ b/profile.sh
@@ -0,0 +1,2 @@
+set TERM=vt52
+set SHELL=shell
diff --git a/recycle.c b/recycle.c
new file mode 100644
index 0000000..319f507
--- /dev/null
+++ b/recycle.c
@@ -0,0 +1,111 @@
+/* recycle.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains the functions perform garbage collection and allocate
+ * reusable blocks.
+ */
+
+#include "config.h"
+#include "vi.h"
+
+#ifndef NO_RECYCLE
+/* this whole file would have be skipped if NO_RECYCLE is defined */
+
+extern long lseek();
+
+#define BTST(bitno, byte) ((byte) & (1 << (bitno)))
+#define BSET(bitno, byte) ((byte) |= (1 << (bitno)))
+#define BCLR(bitno, byte) ((byte) &= ~(1 << (bitno)))
+
+#define TST(blkno) ((blkno) < MAXBIT ? BTST((blkno) & 7, bitmap[(blkno) >> 3]) : 1)
+#define SET(blkno) if ((blkno) < MAXBIT) BSET((blkno) & 7, bitmap[(blkno) >> 3])
+#define CLR(blkno) if ((blkno) < MAXBIT) BCLR((blkno) & 7, bitmap[(blkno) >> 3])
+
+/* bitmap of free blocks in first 4096k of tmp file */
+static unsigned char bitmap[512];
+#define MAXBIT (sizeof bitmap << 3)
+
+/* this function locates all free blocks in the current tmp file */
+void garbage()
+{
+ int i;
+ BLK oldhdr;
+
+ /* start by assuming every block is free */
+ for (i = 0; i < sizeof bitmap; i++)
+ {
+ bitmap[i] = 255;
+ }
+
+ /* header block isn't free */
+#ifndef lint
+ CLR(0);
+#endif
+
+ /* blocks needed for current hdr aren't free */
+ for (i = 1; i < MAXBLKS; i++)
+ {
+ CLR(hdr.n[i]);
+ }
+
+ /* blocks needed for undo version aren't free */
+ lseek(tmpfd, 0L, 0);
+ if (read(tmpfd, &oldhdr, (unsigned)sizeof oldhdr) != sizeof oldhdr)
+ {
+ msg("garbage() failed to read oldhdr??");
+ for (i = 0; i < sizeof bitmap; i++)
+ {
+ bitmap[i] = 0;
+ }
+ return;
+ }
+ for (i = 1; i < MAXBLKS; i++)
+ {
+ CLR(oldhdr.n[i]);
+ }
+
+ /* blocks needed for cut buffers aren't free */
+ for (i = cutneeds(&oldhdr) - 1; i >= 0; i--)
+ {
+ CLR(oldhdr.n[i]);
+ }
+}
+
+/* This function allocates the first available block in the tmp file */
+long allocate()
+{
+ int i;
+ long offset;
+
+ /* search for the first byte with a free bit set */
+ for (i = 0; i < sizeof bitmap && bitmap[i] == 0; i++)
+ {
+ }
+
+ /* if we hit the end of the bitmap, return the end of the file */
+ if (i == sizeof bitmap)
+ {
+ offset = lseek(tmpfd, 0L, 2);
+ }
+ else /* compute the offset for the free block */
+ {
+ for (i <<= 3; TST(i) == 0; i++)
+ {
+ }
+ offset = (long)i * (long)BLKSIZE;
+
+ /* mark the block as "allocated" */
+ CLR(i);
+ }
+
+ return offset;
+}
+
+#endif
diff --git a/redraw.c b/redraw.c
new file mode 100644
index 0000000..6a58842
--- /dev/null
+++ b/redraw.c
@@ -0,0 +1,999 @@
+/* redraw.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains functions that draw text on the screen. The major entry
+ * points are:
+ * redrawrange() - called from modify.c to give hints about what parts
+ * of the screen need to be redrawn.
+ * redraw() - redraws the screen (or part of it) and positions
+ * the cursor where it belongs.
+ * idx2col() - converts a markidx() value to a logical column number.
+ */
+
+#include "config.h"
+#include "vi.h"
+
+/* This variable contains the line number that smartdrawtext() knows best */
+static long smartlno;
+
+/* This function remebers where changes were made, so that the screen can be
+ * redraw in a more efficient manner.
+ */
+static long redrawafter; /* line# of first line that must be redrawn */
+static long preredraw; /* line# of last line changed, before change */
+static long postredraw; /* line# of last line changed, after change */
+void redrawrange(after, pre, post)
+ long after; /* lower bound of redrawafter */
+ long pre; /* upper bound of preredraw */
+ long post; /* upper bound of postredraw */
+{
+ if (after == redrawafter)
+ {
+ /* multiple insertions/deletions at the same place -- combine
+ * them
+ */
+ preredraw -= (post - pre);
+ if (postredraw < post)
+ {
+ preredraw += (post - postredraw);
+ postredraw = post;
+ }
+ if (redrawafter > preredraw)
+ {
+ redrawafter = preredraw;
+ }
+ if (redrawafter < 1L)
+ {
+ redrawafter = 0L;
+ preredraw = postredraw = INFINITY;
+ }
+ }
+ else if (postredraw > 0L)
+ {
+ /* multiple changes in different places -- redraw everything
+ * after "after".
+ */
+ postredraw = preredraw = INFINITY;
+ if (after < redrawafter)
+ redrawafter = after;
+ }
+ else
+ {
+ /* first change */
+ redrawafter = after;
+ preredraw = pre;
+ postredraw = post;
+ }
+}
+
+
+#ifndef NO_CHARATTR
+/* see if a given line uses character attribute strings */
+static int hasattr(lno, text)
+ long lno; /* the line# of the cursor */
+ REG char *text; /* the text of the line, from fetchline */
+{
+ static long plno; /* previous line number */
+ static long chgs; /* previous value of changes counter */
+ static int panswer;/* previous answer */
+ char *scan;
+
+ /* if charattr is off, then the answer is "no, it doesn't" */
+ if (!*o_charattr)
+ {
+ chgs = 0; /* <- forces us to check if charattr is later set */
+ return FALSE;
+ }
+
+ /* if we already know the answer, return it... */
+ if (lno == plno && chgs == changes)
+ {
+ return panswer;
+ }
+
+ /* get the line & look for "\fX" */
+ if (!text[0] || !text[1] || !text[2])
+ {
+ panswer = FALSE;
+ }
+ else
+ {
+ for (scan = text; scan[2] && !(scan[0] == '\\' && scan[1] == 'f'); scan++)
+ {
+ }
+ panswer = (scan[2] != '\0');
+ }
+
+ /* save the results */
+ plno = lno;
+ chgs = changes;
+
+ /* return the results */
+ return panswer;
+}
+#endif
+
+
+
+/* This function converts a MARK to a column number. It doesn't automatically
+ * adjust for leftcol; that must be done by the calling function
+ */
+int idx2col(curs, text, inputting)
+ MARK curs; /* the line# & index# of the cursor */
+ REG char *text; /* the text of the line, from fetchline */
+ int inputting; /* boolean: called from input() ? */
+{
+ static MARK pcursor;/* previous cursor, for possible shortcut */
+ static MARK pcol; /* column number for pcol */
+ static long chgs; /* previous value of changes counter */
+ REG int col; /* used to count column numbers */
+ REG int idx; /* used to count down the index */
+ REG int i;
+
+ /* for now, assume we have to start counting at the left edge */
+ col = 0;
+ idx = markidx(curs);
+
+ /* if the file hasn't changed & line number is the same & it has no
+ * embedded character attribute strings, can we do shortcuts?
+ */
+ if (chgs == changes
+ && !((curs ^ pcursor) & ~(BLKSIZE - 1))
+#ifndef NO_CHARATTR
+ && !hasattr(markline(curs), text)
+#endif
+ )
+ {
+ /* no movement? */
+ if (curs == pcursor)
+ {
+ /* return the column of the char; for tabs, return its last column */
+ if (text[idx] == '\t' && !inputting && !*o_list)
+ {
+ return pcol + *o_tabstop - (pcol % *o_tabstop) - 1;
+ }
+ else
+ {
+ return pcol;
+ }
+ }
+
+ /* movement to right? */
+ if (curs > pcursor)
+ {
+ /* start counting from previous place */
+ col = pcol;
+ idx = markidx(curs) - markidx(pcursor);
+ text += markidx(pcursor);
+ }
+ }
+
+ /* count over to the char after the idx position */
+ while (idx > 0 && (i = *text)) /* yes, ASSIGNMENT! */
+ {
+ if (i == '\t' && !*o_list)
+ {
+ col += *o_tabstop;
+ col -= col % *o_tabstop;
+ }
+ else if (i >= '\0' && i < ' ' || i == '\177')
+ {
+ col += 2;
+ }
+#ifndef NO_CHARATTR
+ else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr)
+ {
+ text += 2; /* plus one more at bottom of loop */
+ idx -= 2;
+ }
+#endif
+ else
+ {
+ col++;
+ }
+ text++;
+ idx--;
+ }
+
+ /* save stuff to speed next call */
+ pcursor = curs;
+ pcol = col;
+ chgs = changes;
+
+ /* return the column of the char; for tabs, return its last column */
+ if (*text == '\t' && !inputting && !*o_list)
+ {
+ return col + *o_tabstop - (col % *o_tabstop) - 1;
+ }
+ else
+ {
+ return col;
+ }
+}
+
+
+/* This function is similar to idx2col except that it takes care of sideways
+ * scrolling - for the given line, at least.
+ */
+int mark2phys(m, text, inputting)
+ MARK m; /* a mark to convert */
+ char *text; /* the line that m refers to */
+ int inputting; /* boolean: caled from input() ? */
+{
+ int i;
+
+ i = idx2col(m, text, inputting);
+ while (i < leftcol)
+ {
+ leftcol -= *o_sidescroll;
+ mustredraw = TRUE;
+ redrawrange(1L, INFINITY, INFINITY);
+ qaddch('\r');
+ /* drawtext(text); */
+ }
+ while (i > rightcol)
+ {
+ leftcol += *o_sidescroll;
+ mustredraw = TRUE;
+ redrawrange(1L, INFINITY, INFINITY);
+ qaddch('\r');
+ /* drawtext(text); */
+ }
+ physcol = i - leftcol;
+ physrow = markline(m) - topline;
+
+ return physcol;
+}
+
+/* This function draws a single line of text on the screen. The screen's
+ * cursor is assumed to be located at the leftmost column of the appropriate
+ * row.
+ */
+static void drawtext(text, clr)
+ REG char *text; /* the text to draw */
+ int clr; /* boolean: do a clrtoeol? */
+{
+ REG int col; /* column number */
+ REG int i;
+ REG int tabstop; /* *o_tabstop */
+ REG int limitcol; /* leftcol or leftcol + COLS */
+ int abnormal; /* boolean: charattr != A_NORMAL? */
+
+#ifndef NO_SENTENCE
+ /* if we're hiding format lines, and this is one of them, then hide it */
+ if (*o_hideformat && *text == '.')
+ {
+ clrtoeol();
+#if OSK
+ qaddch('\l');
+#else
+ qaddch('\n');
+#endif
+ return;
+ }
+#endif
+
+ /* move some things into registers... */
+ limitcol = leftcol;
+ tabstop = *o_tabstop;
+ abnormal = FALSE;
+
+#ifndef CRUNCH
+ if (clr)
+ clrtoeol();
+#endif
+ /* skip stuff that was scrolled off left edge */
+ for (col = 0;
+ (i = *text) && col < limitcol; /* yes, ASSIGNMENT! */
+ text++)
+ {
+ if (i == '\t' && !*o_list)
+ {
+ col = col + tabstop - (col % tabstop);
+ }
+ else if (i >= 0 && i < ' ' || i == '\177')
+ {
+ col += 2;
+ }
+#ifndef NO_CHARATTR
+ else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr)
+ {
+ text += 2; /* plus one more as part of "for" loop */
+
+ /* since this attribute might carry over, we need it */
+ switch (*text)
+ {
+ case 'R':
+ case 'P':
+ attrset(A_NORMAL);
+ abnormal = FALSE;
+ break;
+
+ case 'B':
+ attrset(A_BOLD);
+ abnormal = TRUE;
+ break;
+
+ case 'U':
+ attrset(A_UNDERLINE);
+ abnormal = TRUE;
+ break;
+
+ case 'I':
+ attrset(A_ALTCHARSET);
+ abnormal = TRUE;
+ break;
+ }
+ }
+#endif
+ else
+ {
+ col++;
+ }
+ }
+
+ /* adjust for control char that was partially visible */
+ while (col > limitcol)
+ {
+ qaddch(' ');
+ limitcol++;
+ }
+
+ /* now for the visible characters */
+ for (limitcol = leftcol + COLS;
+ (i = *text) && col < limitcol;
+ text++)
+ {
+ if (i == '\t' && !*o_list)
+ {
+ i = col + tabstop - (col % tabstop);
+ if (i < limitcol)
+ {
+#ifdef CRUNCH
+ if (!clr && has_PT && !((i - leftcol) & 7))
+#else
+ if (has_PT && !((i - leftcol) & 7))
+#endif
+ {
+ do
+ {
+ qaddch('\t');
+ col += 8; /* not exact! */
+ } while (col < i);
+ col = i; /* NOW it is exact */
+ }
+ else
+ {
+ do
+ {
+ qaddch(' ');
+ col++;
+ } while (col < i);
+ }
+ }
+ else /* tab ending after screen? next line! */
+ {
+ col = limitcol;
+ if (has_AM)
+ {
+ addch('\n'); /* GB */
+ }
+ }
+ }
+ else if (i >= 0 && i < ' ' || i == '\177')
+ {
+ col += 2;
+ qaddch('^');
+ if (col <= limitcol)
+ {
+ qaddch(i ^ '@');
+ }
+ }
+#ifndef NO_CHARATTR
+ else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr)
+ {
+ text += 2; /* plus one more as part of "for" loop */
+ switch (*text)
+ {
+ case 'R':
+ case 'P':
+ attrset(A_NORMAL);
+ abnormal = FALSE;
+ break;
+
+ case 'B':
+ attrset(A_BOLD);
+ abnormal = TRUE;
+ break;
+
+ case 'U':
+ attrset(A_UNDERLINE);
+ abnormal = TRUE;
+ break;
+
+ case 'I':
+ attrset(A_ALTCHARSET);
+ abnormal = TRUE;
+ break;
+ }
+ }
+#endif
+ else
+ {
+ col++;
+ qaddch(i);
+ }
+ }
+
+ /* get ready for the next line */
+#ifndef NO_CHARATTR
+ if (abnormal)
+ {
+ attrset(A_NORMAL);
+ }
+#endif
+ if (*o_list && col < limitcol)
+ {
+ qaddch('$');
+ col++;
+ }
+#ifdef CRUNCH
+ if (clr && col < limitcol)
+ {
+ clrtoeol();
+ }
+#endif
+ if (!has_AM || col < limitcol)
+ {
+ addch('\n');
+ }
+}
+
+
+#ifndef CRUNCH
+static void nudgecursor(same, scan, new, lno)
+ int same; /* number of chars to be skipped over */
+ char *scan; /* where the same chars end */
+ char *new; /* where the visible part of the line starts */
+ long lno; /* line number of this line */
+{
+ if (same > 0)
+ {
+ if (same < 5)
+ {
+ /* move the cursor by overwriting */
+ while (same > 0)
+ {
+ qaddch(scan[-same]);
+ same--;
+ }
+ }
+ else
+ {
+ /* move the cursor by calling move() */
+ move((int)(lno - topline), (int)(scan - new));
+ }
+ }
+}
+#endif /* not CRUNCH */
+
+/* This function draws a single line of text on the screen, possibly with
+ * some cursor optimization. The cursor is repositioned before drawing
+ * begins, so its position before doesn't really matter.
+ */
+static void smartdrawtext(text, lno)
+ REG char *text; /* the text to draw */
+ long lno; /* line number of the text */
+{
+#ifdef CRUNCH
+ move((int)(lno - topline), 0);
+ drawtext(text, TRUE);
+#else /* not CRUNCH */
+ static char old[256]; /* how the line looked last time */
+ char new[256]; /* how it looks now */
+ char *build; /* used to put chars into new[] */
+ char *scan; /* used for moving thru new[] or old[] */
+ char *end; /* last non-blank changed char */
+ char *shift; /* used to insert/delete chars */
+ int same; /* length of a run of unchanged chars */
+ int limitcol;
+ int col;
+ int i;
+
+# ifndef NO_CHARATTR
+ /* if this line has attributes, do it the dumb way instead */
+ if (hasattr(lno, text))
+ {
+ move((int)(lno - topline), 0);
+ drawtext(text, TRUE);
+ return;
+ }
+# endif
+# ifndef NO_SENTENCE
+ /* if this line is a format line, & we're hiding format lines, then
+ * let the dumb drawtext() function handle it
+ */
+ if (*o_hideformat && *text == '.')
+ {
+ move((int)(lno - topline), 0);
+ drawtext(text, TRUE);
+ return;
+ }
+# endif
+
+ /* skip stuff that was scrolled off left edge */
+ limitcol = leftcol;
+ for (col = 0;
+ (i = *text) && col < limitcol; /* yes, ASSIGNMENT! */
+ text++)
+ {
+ if (i == '\t' && !*o_list)
+ {
+ col = col + *o_tabstop - (col % *o_tabstop);
+ }
+ else if (i >= 0 && i < ' ' || i == '\177')
+ {
+ col += 2;
+ }
+ else
+ {
+ col++;
+ }
+ }
+
+ /* adjust for control char that was partially visible */
+ build = new;
+ while (col > limitcol)
+ {
+ *build++ = ' ';
+ limitcol++;
+ }
+
+ /* now for the visible characters */
+ for (limitcol = leftcol + COLS;
+ (i = *text) && col < limitcol;
+ text++)
+ {
+ if (i == '\t' && !*o_list)
+ {
+ i = col + *o_tabstop - (col % *o_tabstop);
+ while (col < i && col < limitcol)
+ {
+ *build++ = ' ';
+ col++;
+ }
+ }
+ else if (i >= 0 && i < ' ' || i == '\177')
+ {
+ col += 2;
+ *build++ = '^';
+ if (col <= limitcol)
+ {
+ *build++ = (i ^ '@');
+ }
+ }
+ else
+ {
+ col++;
+ *build++ = i;
+ }
+ }
+ if (col < limitcol && *o_list)
+ {
+ *build++ = '$';
+ col++;
+ }
+ end = build;
+ while (col < limitcol)
+ {
+ *build++ = ' ';
+ col++;
+ }
+
+ /* locate the last non-blank character */
+ while (end > new && end[-1] == ' ')
+ {
+ end--;
+ }
+
+ /* can we optimize the displaying of this line? */
+ if (lno != smartlno)
+ {
+ /* nope, can't optimize - different line */
+ move((int)(lno - topline), 0);
+ for (scan = new, build = old; scan < end; )
+ {
+ qaddch(*scan);
+ *build++ = *scan++;
+ }
+ if (end < new + COLS)
+ {
+ clrtoeol();
+ while (build < old + COLS)
+ {
+ *build++ = ' ';
+ }
+ }
+ smartlno = lno;
+ return;
+ }
+
+ /* skip any initial unchanged characters */
+ for (scan = new, build = old; scan < end && *scan == *build; scan++, build++)
+ {
+ }
+ move((int)(lno - topline), (int)(scan - new));
+
+ /* The in-between characters must be changed */
+ same = 0;
+ while (scan < end)
+ {
+ /* is this character a match? */
+ if (scan[0] == build[0])
+ {
+ same++;
+ }
+ else /* do we want to insert? */
+ if (scan < end - 1 && scan[1] == build[0] && (has_IC || has_IM))
+ {
+ nudgecursor(same, scan, new, lno);
+ same = 0;
+
+ insch(*scan);
+ for (shift = old + COLS; --shift > build; )
+ {
+ shift[0] = shift[-1];
+ }
+ *build = *scan;
+ }
+ else /* do we want to delete? */
+ if (build < old + COLS - 1 && scan[0] == build[1] && has_DC)
+ {
+ nudgecursor(same, scan, new, lno);
+ same = 0;
+
+ delch();
+ same++;
+ for (shift = build; shift < old + COLS - 1; shift++)
+ {
+ shift[0] = shift[1];
+ }
+ *shift = ' ';
+ }
+ else /* we must overwrite */
+ {
+ nudgecursor(same, scan, new, lno);
+ same = 0;
+
+ addch(*scan);
+ *build = *scan;
+ }
+
+ build++;
+ scan++;
+ }
+
+ /* maybe clear to EOL */
+ while (build < old + COLS && *build == ' ')
+ {
+ build++;
+ }
+ if (build < old + COLS)
+ {
+ nudgecursor(same, scan, new, lno);
+ same = 0;
+
+ clrtoeol();
+ while (build < old + COLS)
+ {
+ *build++ = ' ';
+ }
+ }
+#endif /* not CRUNCH */
+}
+
+
+/* This function is used in visual mode for drawing the screen (or just parts
+ * of the screen, if that's all thats needed). It also takes care of
+ * scrolling.
+ */
+void redraw(curs, inputting)
+ MARK curs; /* where to leave the screen's cursor */
+ int inputting; /* boolean: being called from input() ? */
+{
+ char *text; /* a line of text to display */
+ static long chgs; /* previous changes level */
+ long l;
+ int i;
+
+ /* if curs == MARK_UNSET, then we should reset internal vars */
+ if (curs == MARK_UNSET)
+ {
+ if (topline < 1 || topline > nlines)
+ {
+ topline = 1L;
+ }
+ else
+ {
+ move(LINES - 1, 0);
+ clrtoeol();
+ }
+ leftcol = 0;
+ mustredraw = TRUE;
+ redrawafter = INFINITY;
+ preredraw = 0L;
+ postredraw = 0L;
+ chgs = 0;
+ smartlno = 0L;
+ return;
+ }
+
+ /* figure out which column the cursor will be in */
+ l = markline(curs);
+ text = fetchline(l);
+ mark2phys(curs, text, inputting);
+
+ /* adjust topline, if necessary, to get the cursor on the screen */
+ if (l >= topline && l <= botline)
+ {
+ /* it is on the screen already */
+
+ /* if the file was changed but !mustredraw, then redraw line */
+ if (chgs != changes && !mustredraw)
+ {
+ smartdrawtext(text, l);
+ }
+ }
+ else if (l < topline && l > topline - LINES && (has_SR || has_AL))
+ {
+ /* near top - scroll down */
+ if (!mustredraw)
+ {
+ move(0,0);
+ while (l < topline)
+ {
+ topline--;
+ if (has_SR)
+ {
+ do_SR();
+ }
+ else
+ {
+ insertln();
+ }
+ text = fetchline(topline);
+ drawtext(text, FALSE);
+ do_UP();
+ }
+
+ /* blank out the last line */
+ move(LINES - 1, 0);
+ clrtoeol();
+ }
+ else
+ {
+ topline = l;
+ redrawafter = INFINITY;
+ preredraw = 0L;
+ postredraw = 0L;
+ }
+ }
+ else if (l > topline && l < botline + LINES)
+ {
+ /* near bottom -- scroll up */
+ if (!mustredraw
+#if 1
+ || redrawafter == preredraw && preredraw == botline && postredraw == l
+#endif
+ )
+ {
+ move(LINES - 1,0);
+ clrtoeol();
+ while (l > botline)
+ {
+ topline++; /* <-- also adjusts botline */
+ text = fetchline(botline);
+ drawtext(text, FALSE);
+ }
+ mustredraw = FALSE;
+ }
+ else
+ {
+ topline = l - (LINES - 2);
+ redrawafter = INFINITY;
+ preredraw = 0L;
+ postredraw = 0L;
+ }
+ }
+ else
+ {
+ /* distant line - center it & force a redraw */
+ topline = l - (LINES / 2) - 1;
+ if (topline < 1)
+ {
+ topline = 1;
+ }
+ mustredraw = TRUE;
+ redrawafter = INFINITY;
+ preredraw = 0L;
+ postredraw = 0L;
+ }
+
+ /* Now... do we really have to redraw? */
+ if (mustredraw)
+ {
+ /* If redrawfter (and friends) aren't set, assume we should
+ * redraw everything.
+ */
+ if (redrawafter == INFINITY)
+ {
+ redrawafter = 0L;
+ preredraw = postredraw = INFINITY;
+ }
+
+ /* adjust smartlno to correspond with inserted/deleted lines */
+ if (smartlno >= redrawafter)
+ {
+ if (smartlno < preredraw)
+ {
+ smartlno = 0L;
+ }
+ else
+ {
+ smartlno += (postredraw - preredraw);
+ }
+ }
+
+ /* should we insert some lines into the screen? */
+ if (preredraw < postredraw && preredraw <= botline)
+ {
+ /* lines were inserted into the file */
+
+ /* decide where insertion should start */
+ if (preredraw < topline)
+ {
+ l = topline;
+ }
+ else
+ {
+ l = preredraw;
+ }
+
+ /* insert the lines... maybe */
+ if (l + postredraw - preredraw > botline || !has_AL)
+ {
+ /* Whoa! a whole screen full - just redraw */
+ preredraw = postredraw = INFINITY;
+ }
+ else
+ {
+ /* really insert 'em */
+ move((int)(l - topline), 0);
+ for (i = postredraw - preredraw; i > 0; i--)
+ {
+ insertln();
+ }
+
+ /* NOTE: the contents of those lines will be
+ * drawn as part of the regular redraw loop.
+ */
+
+ /* clear the last line */
+ move(LINES - 1, 0);
+ clrtoeol();
+ }
+ }
+
+ /* do we want to delete some lines from the screen? */
+ if (preredraw > postredraw && postredraw <= botline)
+ {
+ if (preredraw > botline || !has_DL)
+ {
+ postredraw = preredraw = INFINITY;
+ }
+ else /* we'd best delete some lines from the screen */
+ {
+ /* clear the last line, so it doesn't look
+ * ugly as it gets pulled up into the screen
+ */
+ move(LINES - 1, 0);
+ clrtoeol();
+
+ /* delete the lines */
+ move((int)(postredraw - topline), 0);
+ for (l = postredraw;
+ l < preredraw && l <= botline;
+ l++)
+ {
+ deleteln();
+ }
+
+ /* draw the lines that are now newly visible
+ * at the bottom of the screen
+ */
+ i = LINES - 1 + (postredraw - preredraw);
+ move(i, 0);
+ for (l = topline + i; l <= botline; l++)
+ {
+ /* clear this line */
+ clrtoeol();
+
+ /* draw the line, or ~ for non-lines */
+ if (l <= nlines)
+ {
+ text = fetchline(l);
+ drawtext(text, FALSE);
+ }
+ else
+ {
+ addstr("~\n");
+ }
+ }
+ }
+ }
+
+ /* redraw the current line */
+ l = markline(curs);
+ pfetch(l);
+ smartdrawtext(ptext, l);
+
+ /* decide where we should start redrawing from */
+ if (redrawafter < topline)
+ {
+ l = topline;
+ }
+ else
+ {
+ l = redrawafter;
+ }
+ move((int)(l - topline), 0);
+
+ /* draw the other lines */
+ for (; l <= botline && l < postredraw; l++)
+ {
+ /* we already drew the current line, so skip it now */
+ if (l == smartlno)
+ {
+#if OSK
+ qaddch('\l');
+#else
+ qaddch('\n');
+#endif
+ continue;
+ }
+
+ /* draw the line, or ~ for non-lines */
+ if (l <= nlines)
+ {
+ text = fetchline(l);
+ drawtext(text, TRUE);
+ }
+ else
+ {
+ qaddch('~');
+ clrtoeol();
+ addch('\n');
+ }
+ }
+
+ mustredraw = FALSE;
+ }
+
+ /* force total (non-partial) redraw next time if not set */
+ redrawafter = INFINITY;
+ preredraw = 0L;
+ postredraw = 0L;
+
+ /* move the cursor to where it belongs */
+ move((int)(markline(curs) - topline), physcol);
+ wqrefresh(stdscr);
+
+ chgs = changes;
+}
diff --git a/ref.c b/ref.c
new file mode 100644
index 0000000..c896142
--- /dev/null
+++ b/ref.c
@@ -0,0 +1,140 @@
+/* ref.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This program looks up the declarations of library functions. */
+
+#include <stdio.h>
+
+/* This is the list of files that are searched. */
+#ifdef OSK
+char *refslist[] = {
+ "refs",
+ "/dd/usr/src/lib/refs",
+ "../lib/refs",
+ "/dd/usr/local/lib/refs",
+};
+#else
+char *refslist[] = {
+ "refs",
+ "/usr/src/lib/refs",
+ "../lib/refs",
+ "/usr/local/lib/refs"
+};
+#endif
+#define NREFS (sizeof refslist / sizeof(char *))
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int i; /* used to step through the refslist */
+
+ /* make sure our arguments are OK */
+ if (argc != 2)
+ {
+ fprintf(stderr, "usage: %s function_name\n", *argv);
+ exit(2);
+ }
+
+ /* check for the function in each database */
+ for (i = 0; i < NREFS; i++)
+ {
+ if (lookinfor(refslist[i], argv[1]))
+ {
+ exit(0);
+ }
+ }
+
+ fprintf(stderr, "%s: don't know about %s\n", argv[0], argv[1]);
+ exit(2);
+}
+
+
+/* This function checks a single file for the function. Returns 1 if found */
+int lookinfor(filename, func)
+ char *filename; /* name of file to look in */
+ char *func; /* name of function to look for */
+{
+ FILE *fp; /* stream used to access the database */
+ char linebuf[300];
+ /* NOTE: in actual use, the first character of linebuf is */
+ /* set to ' ' and then we use all EXCEPT the 1st character */
+ /* everywhere in this function. This is because the func */
+ /* which examines the linebuf could, in some circumstances */
+ /* examine the character before the used part of linebuf; */
+ /* we need to control what happens then. */
+
+
+ /* open the database file */
+ fp = fopen(filename, "r");
+ if (!fp)
+ {
+ return 0;
+ }
+
+ /* find the desired entry */
+ *linebuf = ' ';
+ do
+ {
+ if (!fgets(linebuf + 1, (sizeof linebuf) - 1, fp))
+ {
+ fclose(fp);
+ return 0;
+ }
+ } while (!desired(linebuf + 1, func));
+
+ /* print it */
+ do
+ {
+ fputs(linebuf + 1, stdout);
+ } while (fgets(linebuf + 1, sizeof linebuf, fp) && linebuf[1] == '\t');
+
+ /* cleanup & exit */
+ fclose(fp);
+ return 1;
+}
+
+
+/* This function checks to see if a given line is the first line of the */
+/* entry the user wants to see. If it is, return non-0 else return 0 */
+desired(line, word)
+ char *line; /* the line buffer */
+ char *word; /* the string it should contain */
+{
+ static wlen = -1;/* length of the "word" variable's value */
+ register char *scan;
+
+ /* if this line starts with a tab, it couldn't be desired */
+ if (*line == '\t')
+ {
+ return 0;
+ }
+
+ /* if we haven't found word's length yet, do so */
+ if (wlen < 0)
+ {
+ wlen = strlen(word);
+ }
+
+ /* search for the word in the line */
+ for (scan = line; *scan != '('; scan++)
+ {
+ }
+ while (*--scan == ' ')
+ {
+ }
+ scan -= wlen;
+ if (scan < line - 1 || *scan != ' ' && *scan != '\t' && *scan != '*')
+ {
+ return 0;
+ }
+ scan++;
+ return !strncmp(scan, word, wlen);
+}
diff --git a/refont.c b/refont.c
new file mode 100644
index 0000000..5ad3fdf
--- /dev/null
+++ b/refont.c
@@ -0,0 +1,475 @@
+/* refont.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains the complete source code for the refont program */
+
+/* The refont program converts font designations to the format of your choice.
+ * Known font formats are:
+ * -b overtype notation, using backspaces
+ * -c overtype notation, using carriage returns
+ * -d the "dot" command notation used by nroff (doesn't work well)
+ * -e Epson-compatible line printer codes
+ * -f the "\f" notation
+ * -x none (strip out the font codes)
+ *
+ * Other flags are:
+ * -I recognize \f and dot notations in input
+ * -F output a formfeed character between files
+ */
+
+#include <stdio.h>
+#include "config.h"
+
+/* the program name, for diagnostics */
+char *progname;
+
+/* remembers which output format to use */
+int outfmt = 'f';
+
+/* do we allow "dot" and "backslash-f" on input? */
+int infmt = 0;
+
+/* do we insert formfeeds between input files? */
+int add_form_feed = 0;
+
+main(argc, argv)
+ int argc; /* number of command-line args */
+ char **argv; /* values of the command line args */
+{
+ FILE *fp;
+ int i, j;
+ int retcode;
+
+ progname = argv[0];
+
+ /* parse the flags */
+ i = 1;
+ if (i < argc && argv[i][0] == '-' && argv[i][1])
+ {
+ for (j = 1; argv[i][j]; j++)
+ {
+ switch (argv[i][j])
+ {
+ case 'b':
+#if !OSK
+ case 'c':
+#endif
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'x':
+ outfmt = argv[i][j];
+ break;
+
+ case 'I':
+ infmt = 'I';
+ break;
+
+ case 'F':
+ add_form_feed = 1;
+ break;
+
+ default:
+ usage();
+ }
+ }
+ i++;
+ }
+
+ retcode = 0;
+ if (i == argc)
+ {
+ /* probably shouldn't read from keyboard */
+ if (isatty(fileno(stdin)))
+ {
+ usage();
+ }
+
+ /* no files named, so use stdin */
+ refont(stdin);
+ }
+ else
+ {
+ for (; i < argc; i++)
+ {
+ fp = fopen(argv[i], "r");
+ if (!fp)
+ {
+ perror(argv[i]);
+ retcode = 1;
+ }
+ else
+ {
+ refont(fp);
+ if (i < argc - 1 && add_form_feed)
+ {
+ putchar('\f');
+ }
+ fclose(fp);
+ }
+ }
+ }
+
+ exit(retcode);
+}
+
+usage()
+{
+ fputs("usage: ", stderr);
+ fputs(progname, stderr);
+ fputs(" [-bcdefxFI] [filename]...\n", stderr);
+ exit(2);
+}
+
+/* This function does the refont thing to a single file */
+/* I apologize for the length of this function. It is gross. */
+refont(fp)
+ FILE *fp;
+{
+ char textbuf[1025]; /* chars of a line of text */
+ char fontbuf[1025]; /* fonts of chars in fontbuf */
+ int col; /* where we are in the line */
+ int font; /* remembered font */
+ int more; /* more characters to be output? */
+ int ch;
+
+ /* reset some stuff */
+ for (col = sizeof fontbuf; --col >= 0; )
+ {
+ fontbuf[col] = 'R';
+ textbuf[col] = '\0';
+ }
+ col = 0;
+ font = 'R';
+
+ /* get the first char - quit if eof */
+ while ((ch = getc(fp)) != EOF)
+ {
+ /* if "dot" command, read the rest of the command */
+ if (infmt == 'I' && ch == '.' && col == 0)
+ {
+
+ textbuf[col++] = '.';
+ textbuf[col++] = getc(fp);
+ textbuf[col++] = getc(fp);
+ textbuf[col++] = ch = getc(fp);
+
+ /* is it a font line? */
+ font = 0;
+ if (textbuf[1] == 'u' && textbuf[2] == 'l')
+ {
+ font = 'U';
+ }
+ else if (textbuf[1] == 'b' && textbuf[2] == 'o')
+ {
+ font = 'B';
+ }
+ else if (textbuf[1] == 'i' && textbuf[2] == 't')
+ {
+ font = 'I';
+ }
+
+ /* if it is, discard the stuff so far but remember font */
+ if (font)
+ {
+ while (col > 0)
+ {
+ textbuf[--col] = '\0';
+ }
+ }
+ else /* some other format line - write it literally */
+ {
+ while (ch != '\n')
+ {
+ textbuf[col++] = ch = getc(fp);
+ }
+ fputs(textbuf, fp);
+ while (col > 0)
+ {
+ textbuf[--col] = '\0';
+ }
+ }
+ continue;
+ }
+
+ /* is it a "\f" formatted font? */
+ if (infmt == 'I' && ch == '\\')
+ {
+ ch = getc(fp);
+ if (ch == 'f')
+ {
+ font = getc(fp);
+ }
+ else
+ {
+ textbuf[col++] = '\\';
+ textbuf[col++] = ch;
+ }
+ continue;
+ }
+
+ /* is it an Epson font? */
+ if (ch == '\033')
+ {
+ ch = getc(fp);
+ switch (ch)
+ {
+ case '4':
+ font = 'I';
+ break;
+
+ case 'E':
+ case 'G':
+ font = 'B';
+ break;
+
+ case '5':
+ case 'F':
+ case 'H':
+ font = 'R';
+ break;
+
+ case '-':
+ font = (getc(fp) & 1) ? 'U' : 'R';
+ break;
+ }
+ continue;
+ }
+
+ /* control characters? */
+ if (ch == '\b')
+ {
+ if (col > 0)
+ col--;
+ continue;
+ }
+ else if (ch == '\t')
+ {
+ do
+ {
+ if (textbuf[col] == '\0')
+ {
+ textbuf[col] = ' ';
+ }
+ col++;
+ } while (col & 7);
+ continue;
+ }
+#if !OSK
+ else if (ch == '\r')
+ {
+ col = 0;
+ continue;
+ }
+#endif
+ else if (ch == ' ')
+ {
+ if (textbuf[col] == '\0')
+ {
+ textbuf[col] = ' ';
+ fontbuf[col] = font;
+ col++;
+ }
+ continue;
+ }
+
+ /* newline? */
+ if (ch == '\n')
+ {
+ more = 0;
+ for (col = 0, font = 'R'; textbuf[col]; col++)
+ {
+ if (fontbuf[col] != font)
+ {
+ switch (outfmt)
+ {
+ case 'd':
+ putchar('\n');
+ switch (fontbuf[col])
+ {
+ case 'B':
+ fputs(".bo\n", stdout);
+ break;
+
+ case 'I':
+ fputs(".it\n", stdout);
+ break;
+
+ case 'U':
+ fputs(".ul\n", stdout);
+ break;
+ }
+ while (textbuf[col] == ' ')
+ {
+ col++;
+ }
+ break;
+
+ case 'e':
+ switch (fontbuf[col])
+ {
+ case 'B':
+ fputs("\033E", stdout);
+ break;
+
+ case 'I':
+ fputs("\0334", stdout);
+ break;
+
+ case 'U':
+ fputs("\033-1", stdout);
+ break;
+
+ default:
+ switch (font)
+ {
+ case 'B':
+ fputs("\033F", stdout);
+ break;
+
+ case 'I':
+ fputs("\0335", stdout);
+ break;
+
+ case 'U':
+ fputs("\033-0", stdout);
+ break;
+ }
+ }
+ break;
+
+ case 'f':
+ putchar('\\');
+ putchar('f');
+ putchar(fontbuf[col]);
+ break;
+ }
+
+ font = fontbuf[col];
+ }
+
+ if (fontbuf[col] != 'R' && textbuf[col] != ' ')
+ {
+ switch (outfmt)
+ {
+ case 'b':
+ if (fontbuf[col] == 'B')
+ {
+ putchar(textbuf[col]);
+ }
+ else
+ {
+ putchar('_');
+ }
+ putchar('\b');
+ break;
+#if !OSK
+ case 'c':
+ more = col + 1;
+ break;
+#endif
+ }
+ }
+
+ putchar(textbuf[col]);
+ }
+
+#if !OSK
+ /* another pass? */
+ if (more > 0)
+ {
+ putchar('\r');
+ for (col = 0; col < more; col++)
+ {
+ switch (fontbuf[col])
+ {
+ case 'I':
+ case 'U':
+ putchar('_');
+ break;
+
+ case 'B':
+ putchar(textbuf[col]);
+ break;
+
+ default:
+ putchar(' ');
+ }
+ }
+ }
+#endif /* not OSK */
+
+ /* newline */
+ if (font != 'R')
+ {
+ switch (outfmt)
+ {
+ case 'f':
+ putchar('\\');
+ putchar('f');
+ putchar('R');
+ break;
+
+ case 'e':
+ switch (font)
+ {
+ case 'B':
+ fputs("\033F", stdout);
+ break;
+
+ case 'I':
+ fputs("\0335", stdout);
+ break;
+
+ case 'U':
+ fputs("\033-0", stdout);
+ break;
+ }
+ }
+ }
+ putchar('\n');
+
+ /* reset some stuff */
+ for (col = sizeof fontbuf; --col >= 0; )
+ {
+ fontbuf[col] = 'R';
+ textbuf[col] = '\0';
+ }
+ col = 0;
+ font = 'R';
+ continue;
+ }
+
+ /* normal character */
+ if (font != 'R')
+ {
+ textbuf[col] = ch;
+ fontbuf[col] = font;
+ }
+ else if (textbuf[col] == '_')
+ {
+ textbuf[col] = ch;
+ fontbuf[col] = 'U';
+ }
+ else if (textbuf[col] && textbuf[col] != ' ' && ch == '_')
+ {
+ fontbuf[col] = 'U';
+ }
+ else if (textbuf[col] == ch)
+ {
+ fontbuf[col] = 'B';
+ }
+ else
+ {
+ textbuf[col] = ch;
+ }
+ col++;
+ }
+}
diff --git a/regexp.c b/regexp.c
new file mode 100644
index 0000000..eb9ccb9
--- /dev/null
+++ b/regexp.c
@@ -0,0 +1,830 @@
+/* regexp.c */
+
+/* This file contains the code that compiles regular expressions and executes
+ * them. It supports the same syntax and features as vi's regular expression
+ * code. Specifically, the meta characters are:
+ * ^ matches the beginning of a line
+ * $ matches the end of a line
+ * \< matches the beginning of a word
+ * \> matches the end of a word
+ * . matches any single character
+ * [] matches any character in a character class
+ * \( delimits the start of a subexpression
+ * \) delimits the end of a subexpression
+ * * repeats the preceding 0 or more times
+ * NOTE: You cannot follow a \) with a *.
+ *
+ * The physical structure of a compiled RE is as follows:
+ * - First, there is a one-byte value that says how many character classes
+ * are used in this regular expression
+ * - Next, each character class is stored as a bitmap that is 256 bits
+ * (32 bytes) long.
+ * - A mixture of literal characters and compiled meta characters follows.
+ * This begins with M_BEGIN(0) and ends with M_END(0). All meta chars
+ * are stored as a \n followed by a one-byte code, so they take up two
+ * bytes apiece. Literal characters take up one byte apiece. \n can't
+ * be used as a literal character.
+ *
+ * If NO_MAGIC is defined, then a different set of functions is used instead.
+ * That right, this file contains TWO versions of the code.
+ */
+
+#include <setjmp.h>
+#include <ctype.h>
+#include "config.h"
+#include "vi.h"
+#include "regexp.h"
+
+
+
+static char *previous; /* the previous regexp, used when null regexp is given */
+
+
+#ifndef NO_MAGIC
+/* THE REAL REGEXP PACKAGE IS USED UNLESS "NO_MAGIC" IS DEFINED */
+
+/* These are used to classify or recognize meta-characters */
+#define META '\0'
+#define BASE_META(m) ((m) - 256)
+#define INT_META(c) ((c) + 256)
+#define IS_META(m) ((m) >= 256)
+#define IS_CLASS(m) ((m) >= M_CLASS(0) && (m) <= M_CLASS(9))
+#define IS_START(m) ((m) >= M_START(0) && (m) <= M_START(9))
+#define IS_END(m) ((m) >= M_END(0) && (m) <= M_END(9))
+#define IS_CLOSURE(m) ((m) >= M_SPLAT && (m) <= M_QMARK)
+#define ADD_META(s,m) (*(s)++ = META, *(s)++ = BASE_META(m))
+#define GET_META(s) (*(s) == META ? INT_META(*++(s)) : *s)
+
+/* These are the internal codes used for each type of meta-character */
+#define M_BEGLINE 256 /* internal code for ^ */
+#define M_ENDLINE 257 /* internal code for $ */
+#define M_BEGWORD 258 /* internal code for \< */
+#define M_ENDWORD 259 /* internal code for \> */
+#define M_ANY 260 /* internal code for . */
+#define M_SPLAT 261 /* internal code for * */
+#define M_PLUS 262 /* internal code for \+ */
+#define M_QMARK 263 /* internal code for \? */
+#define M_CLASS(n) (264+(n)) /* internal code for [] */
+#define M_START(n) (274+(n)) /* internal code for \( */
+#define M_END(n) (284+(n)) /* internal code for \) */
+
+/* These are used during compilation */
+static int class_cnt; /* used to assign class IDs */
+static int start_cnt; /* used to assign start IDs */
+static int end_stk[NSUBEXP];/* used to assign end IDs */
+static int end_sp;
+static char *retext; /* points to the text being compiled */
+
+/* error-handling stuff */
+jmp_buf errorhandler;
+#define FAIL(why) regerror(why); longjmp(errorhandler, 1)
+
+
+
+
+
+/* This function builds a bitmap for a particular class */
+static char *makeclass(text, bmap)
+ REG char *text; /* start of the class */
+ REG char *bmap; /* the bitmap */
+{
+ REG int i;
+ int complement = 0;
+
+# if TRACE
+ printf("makeclass(\"%s\", 0x%lx)\n", text, (long)bmap);
+# endif
+
+ /* zero the bitmap */
+ for (i = 0; bmap && i < 32; i++)
+ {
+ bmap[i] = 0;
+ }
+
+ /* see if we're going to complement this class */
+ if (*text == '^')
+ {
+ text++;
+ complement = 1;
+ }
+
+ /* add in the characters */
+ while (*text && *text != ']')
+ {
+ /* is this a span of characters? */
+ if (text[1] == '-' && text[2])
+ {
+ /* spans can't be backwards */
+ if (text[0] > text[2])
+ {
+ FAIL("Backwards span in []");
+ }
+
+ /* add each character in the span to the bitmap */
+ for (i = text[0]; bmap && i <= text[2]; i++)
+ {
+ bmap[i >> 3] |= (1 << (i & 7));
+ }
+
+ /* move past this span */
+ text += 3;
+ }
+ else
+ {
+ /* add this single character to the span */
+ i = *text++;
+ if (bmap)
+ {
+ bmap[i >> 3] |= (1 << (i & 7));
+ }
+ }
+ }
+
+ /* make sure the closing ] is missing */
+ if (*text++ != ']')
+ {
+ FAIL("] missing");
+ }
+
+ /* if we're supposed to complement this class, then do so */
+ if (complement && bmap)
+ {
+ for (i = 0; i < 32; i++)
+ {
+ bmap[i] = ~bmap[i];
+ }
+ }
+
+ return text;
+}
+
+
+
+
+/* This function gets the next character or meta character from a string.
+ * The pointer is incremented by 1, or by 2 for \-quoted characters. For [],
+ * a bitmap is generated via makeclass() (if re is given), and the
+ * character-class text is skipped.
+ */
+static int gettoken(sptr, re)
+ char **sptr;
+ regexp *re;
+{
+ int c;
+
+ c = **sptr;
+ ++*sptr;
+ if (c == '\\')
+ {
+ c = **sptr;
+ ++*sptr;
+ switch (c)
+ {
+ case '<':
+ return M_BEGWORD;
+
+ case '>':
+ return M_ENDWORD;
+
+ case '(':
+ if (start_cnt >= NSUBEXP)
+ {
+ FAIL("Too many \\(s");
+ }
+ end_stk[end_sp++] = start_cnt;
+ return M_START(start_cnt++);
+
+ case ')':
+ if (end_sp <= 0)
+ {
+ FAIL("Mismatched \\)");
+ }
+ return M_END(end_stk[--end_sp]);
+
+ case '*':
+ return (*o_magic ? c : M_SPLAT);
+
+ case '.':
+ return (*o_magic ? c : M_ANY);
+
+ case '+':
+ return M_PLUS;
+
+ case '?':
+ return M_QMARK;
+
+ default:
+ return c;
+ }
+ }
+ else if (*o_magic)
+ {
+ switch (c)
+ {
+ case '^':
+ if (*sptr == retext + 1)
+ {
+ return M_BEGLINE;
+ }
+ return c;
+
+ case '$':
+ if (!**sptr)
+ {
+ return M_ENDLINE;
+ }
+ return c;
+
+ case '.':
+ return M_ANY;
+
+ case '*':
+ return M_SPLAT;
+
+ case '[':
+ /* make sure we don't have too many classes */
+ if (class_cnt >= 10)
+ {
+ FAIL("Too many []s");
+ }
+
+ /* process the character list for this class */
+ if (re)
+ {
+ /* generate the bitmap for this class */
+ *sptr = makeclass(*sptr, re->program + 1 + 32 * class_cnt);
+ }
+ else
+ {
+ /* skip to end of the class */
+ *sptr = makeclass(*sptr, (char *)0);
+ }
+ return M_CLASS(class_cnt++);
+
+ default:
+ return c;
+ }
+ }
+ else /* unquoted nomagic */
+ {
+ switch (c)
+ {
+ case '^':
+ if (*sptr == retext + 1)
+ {
+ return M_BEGLINE;
+ }
+ return c;
+
+ case '$':
+ if (!**sptr)
+ {
+ return M_ENDLINE;
+ }
+ return c;
+
+ default:
+ return c;
+ }
+ }
+ /*NOTREACHED*/
+}
+
+
+
+
+/* This function calculates the number of bytes that will be needed for a
+ * compiled RE. Its argument is the uncompiled version. It is not clever
+ * about catching syntax errors; that is done in a later pass.
+ */
+static unsigned calcsize(text)
+ char *text;
+{
+ unsigned size;
+ int token;
+
+ retext = text;
+ class_cnt = 0;
+ start_cnt = 1;
+ end_sp = 0;
+ size = 5;
+ while ((token = gettoken(&text, (regexp *)0)) != 0)
+ {
+ if (IS_CLASS(token))
+ {
+ size += 34;
+ }
+ else if (IS_META(token))
+ {
+ size += 2;
+ }
+ else
+ {
+ size++;
+ }
+ }
+
+ return size;
+}
+
+
+
+/* This function compiles a regexp. */
+regexp *regcomp(text)
+ char *text;
+{
+ int needfirst;
+ unsigned size;
+ int token;
+ int peek;
+ char *build;
+ regexp *re;
+
+
+ /* prepare for error handling */
+ re = (regexp *)0;
+ if (setjmp(errorhandler))
+ {
+ if (re)
+ {
+ free(re);
+ }
+ return (regexp *)0;
+ }
+
+ /* if an empty regexp string was given, use the previous one */
+ if (*text == 0)
+ {
+ if (!previous)
+ {
+ FAIL("No previous RE");
+ }
+ text = previous;
+ }
+ else /* non-empty regexp given, so remember it */
+ {
+ if (previous)
+ free(previous);
+ previous = (char *)malloc((unsigned)(strlen(text) + 1));
+ if (previous)
+ strcpy(previous, text);
+ }
+
+ /* allocate memory */
+ class_cnt = 0;
+ start_cnt = 1;
+ end_sp = 0;
+ retext = text;
+ size = calcsize(text) + sizeof(regexp);
+#ifdef lint
+ re = ((regexp *)0) + size;
+#else
+ re = (regexp *)malloc((unsigned)size);
+#endif
+ if (!re)
+ {
+ FAIL("Not enough memory for this RE");
+ }
+
+ /* compile it */
+ build = &re->program[1 + 32 * class_cnt];
+ re->program[0] = class_cnt;
+ for (token = 0; token < NSUBEXP; token++)
+ {
+ re->startp[token] = re->endp[token] = (char *)0;
+ }
+ re->first = 0;
+ re->bol = 0;
+ re->minlen = 0;
+ needfirst = 1;
+ class_cnt = 0;
+ start_cnt = 1;
+ end_sp = 0;
+ retext = text;
+ for (token = M_START(0), peek = gettoken(&text, re);
+ token;
+ token = peek, peek = gettoken(&text, re))
+ {
+ /* special processing for the closure operator */
+ if (IS_CLOSURE(peek))
+ {
+ /* detect misuse of closure operator */
+ if (IS_START(token))
+ {
+ FAIL("* or \\+ or \\? follows nothing");
+ }
+ else if (IS_META(token) && token != M_ANY && !IS_CLASS(token))
+ {
+ FAIL("* or \\+ or \\? can only follow a normal character or . or []");
+ }
+
+ /* it is okay -- make it prefix instead of postfix */
+ ADD_META(build, peek);
+
+ /* take care of "needfirst" - is this the first char? */
+ if (needfirst && peek == M_PLUS && !IS_META(token))
+ {
+ re->first = token;
+ }
+ needfirst = 0;
+
+ /* we used "peek" -- need to refill it */
+ peek = gettoken(&text, re);
+ if (IS_CLOSURE(peek))
+ {
+ FAIL("* or \\+ or \\? doubled up");
+ }
+ }
+ else if (!IS_META(token))
+ {
+ /* normal char is NOT argument of closure */
+ if (needfirst)
+ {
+ re->first = token;
+ needfirst = 0;
+ }
+ re->minlen++;
+ }
+ else if (token == M_ANY || IS_CLASS(token))
+ {
+ /* . or [] is NOT argument of closure */
+ needfirst = 0;
+ re->minlen++;
+ }
+
+ /* the "token" character is not closure -- process it normally */
+ if (token == M_BEGLINE)
+ {
+ /* set the BOL flag instead of storing M_BEGLINE */
+ re->bol = 1;
+ }
+ else if (IS_META(token))
+ {
+ ADD_META(build, token);
+ }
+ else
+ {
+ *build++ = token;
+ }
+ }
+
+ /* end it with a \) which MUST MATCH the opening \( */
+ ADD_META(build, M_END(0));
+ if (end_sp > 0)
+ {
+ FAIL("Not enough \\)s");
+ }
+
+ return re;
+}
+
+
+
+/*---------------------------------------------------------------------------*/
+
+
+/* This function checks for a match between a character and a token which is
+ * known to represent a single character. It returns 0 if they match, or
+ * 1 if they don't.
+ */
+int match1(re, ch, token)
+ regexp *re;
+ REG char ch;
+ REG int token;
+{
+ if (!ch)
+ {
+ /* the end of a line can't match any RE of width 1 */
+ return 1;
+ }
+ if (token == M_ANY)
+ {
+ return 0;
+ }
+ else if (IS_CLASS(token))
+ {
+ if (re->program[1 + 32 * (token - M_CLASS(0)) + (ch >> 3)] & (1 << (ch & 7)))
+ return 0;
+ }
+ else if (ch == token
+ || (*o_ignorecase && isupper(ch) && tolower(ch) == token))
+ {
+ return 0;
+ }
+ return 1;
+}
+
+
+
+/* This function checks characters up to and including the next closure, at
+ * which point it does a recursive call to check the rest of it. This function
+ * returns 0 if everything matches, or 1 if something doesn't match.
+ */
+int match(re, str, prog, here)
+ regexp *re; /* the regular expression */
+ char *str; /* the string */
+ REG char *prog; /* a portion of re->program, an compiled RE */
+ REG char *here; /* a portion of str, the string to compare it to */
+{
+ REG int token;
+ REG int nmatched;
+ REG int closure;
+
+ for (token = GET_META(prog); !IS_CLOSURE(token); prog++, token = GET_META(prog))
+ {
+ switch (token)
+ {
+ /*case M_BEGLINE: can't happen; re->bol is used instead */
+ case M_ENDLINE:
+ if (*here)
+ return 1;
+ break;
+
+ case M_BEGWORD:
+ if (here != str &&
+ (here[-1] == '_' ||
+ isascii(here[-1]) && isalnum(here[-1])))
+ return 1;
+ break;
+
+ case M_ENDWORD:
+ if (here[0] == '_' || isascii(here[0]) && isalnum(here[0]))
+ return 1;
+ break;
+
+ case M_START(0):
+ case M_START(1):
+ case M_START(2):
+ case M_START(3):
+ case M_START(4):
+ case M_START(5):
+ case M_START(6):
+ case M_START(7):
+ case M_START(8):
+ case M_START(9):
+ re->startp[token - M_START(0)] = (char *)here;
+ break;
+
+ case M_END(0):
+ case M_END(1):
+ case M_END(2):
+ case M_END(3):
+ case M_END(4):
+ case M_END(5):
+ case M_END(6):
+ case M_END(7):
+ case M_END(8):
+ case M_END(9):
+ re->endp[token - M_END(0)] = (char *)here;
+ if (token == M_END(0))
+ {
+ return 0;
+ }
+ break;
+
+ default: /* literal, M_CLASS(n), or M_ANY */
+ if (match1(re, *here, token) != 0)
+ return 1;
+ here++;
+ }
+ }
+
+ /* C L O S U R E */
+
+ /* step 1: see what we have to match against, and move "prog" to point
+ * the the remainder of the compiled RE.
+ */
+ closure = token;
+ prog++, token = GET_META(prog);
+ prog++;
+
+ /* step 2: see how many times we can match that token against the string */
+ for (nmatched = 0;
+ (closure != M_QMARK || nmatched < 1) && *here && match1(re, *here, token) == 0;
+ nmatched++, here++)
+ {
+ }
+
+ /* step 3: try to match the remainder, and back off if it doesn't */
+ while (nmatched >= 0 && match(re, str, prog, here) != 0)
+ {
+ nmatched--;
+ here--;
+ }
+
+ /* so how did it work out? */
+ if (nmatched >= ((closure == M_PLUS) ? 1 : 0))
+ return 0;
+ return 1;
+}
+
+
+
+/* This function searches through a string for text that matches an RE. */
+int regexec(re, str, bol)
+ regexp *re; /* the compiled regexp to search for */
+ char *str; /* the string to search through */
+ int bol; /* boolean: does str start at the beginning of a line? */
+{
+ char *prog; /* the entry point of re->program */
+ int len; /* length of the string */
+ REG char *here;
+
+ /* if must start at the beginning of a line, and this isn't, then fail */
+ if (re->bol && !bol)
+ {
+ return 0;
+ }
+
+ len = strlen(str);
+ prog = re->program + 1 + 32 * re->program[0];
+
+ /* search for the RE in the string */
+ if (re->bol)
+ {
+ /* must occur at BOL */
+ if ((re->first
+ && match1(re, *(char *)str, re->first))/* wrong first letter? */
+ || len < re->minlen /* not long enough? */
+ || match(re, (char *)str, prog, str)) /* doesn't match? */
+ return 0; /* THEN FAIL! */
+ }
+#ifndef CRUNCH
+ else if (!*o_ignorecase)
+ {
+ /* can occur anywhere in the line, noignorecase */
+ for (here = (char *)str;
+ (re->first && re->first != *here)
+ || match(re, (char *)str, prog, here);
+ here++, len--)
+ {
+ if (len < re->minlen)
+ return 0;
+ }
+ }
+#endif
+ else
+ {
+ /* can occur anywhere in the line, ignorecase */
+ for (here = (char *)str;
+ (re->first && match1(re, *here, (int)re->first))
+ || match(re, (char *)str, prog, here);
+ here++, len--)
+ {
+ if (len < re->minlen)
+ return 0;
+ }
+ }
+
+ /* if we didn't fail, then we must have succeeded */
+ return 1;
+}
+
+#else /* NO_MAGIC */
+
+regexp *regcomp(exp)
+ char *exp;
+{
+ char *src;
+ char *dest;
+ regexp *re;
+ int i;
+
+ /* allocate a big enough regexp structure */
+#ifdef lint
+ re = (regexp *)0;
+#else
+ re = (regexp *)malloc((unsigned)(strlen(exp) + 1 + sizeof(struct regexp)));
+#endif
+ if (!re)
+ {
+ regerror("Could not malloc a regexp structure");
+ return (regexp *)0;
+ }
+
+ /* initialize all fields of the structure */
+ for (i = 0; i < NSUBEXP; i++)
+ {
+ re->startp[i] = re->endp[i] = (char *)0;
+ }
+ re->minlen = 0;
+ re->first = 0;
+ re->bol = 0;
+
+ /* copy the string into it, translating ^ and $ as needed */
+ for (src = exp, dest = re->program + 1; *src; src++)
+ {
+ switch (*src)
+ {
+ case '^':
+ if (src == exp)
+ {
+ re->bol += 1;
+ }
+ else
+ {
+ *dest++ = '^';
+ re->minlen++;
+ }
+ break;
+
+ case '$':
+ if (!src[1])
+ {
+ re->bol += 2;
+ }
+ else
+ {
+ *dest++ = '$';
+ re->minlen++;
+ }
+ break;
+
+ case '\\':
+ if (src[1])
+ {
+ *dest++ = *++src;
+ re->minlen++;
+ }
+ else
+ {
+ regerror("extra \\ at end of regular expression");
+ }
+ break;
+
+ default:
+ *dest++ = *src;
+ re->minlen++;
+ }
+ }
+ *dest = '\0';
+
+ return re;
+}
+
+
+/* This "helper" function checks for a match at a given location. It returns
+ * 1 if it matches, 0 if it doesn't match here but might match later on in the
+ * string, or -1 if it could not possibly match
+ */
+static int reghelp(prog, string, bolflag)
+ struct regexp *prog;
+ char *string;
+ int bolflag;
+{
+ char *scan;
+ char *str;
+
+ /* if ^, then require bolflag */
+ if ((prog->bol & 1) && !bolflag)
+ {
+ return -1;
+ }
+
+ /* if it matches, then it will start here */
+ prog->startp[0] = string;
+
+ /* compare, possibly ignoring case */
+ if (*o_ignorecase)
+ {
+ for (scan = &prog->program[1]; *scan; scan++, string++)
+ if (tolower(*scan) != tolower(*string))
+ return *string ? 0 : -1;
+ }
+ else
+ {
+ for (scan = &prog->program[1]; *scan; scan++, string++)
+ if (*scan != *string)
+ return *string ? 0 : -1;
+ }
+
+ /* if $, then require string to end here, too */
+ if ((prog->bol & 2) && *string)
+ {
+ return 0;
+ }
+
+ /* if we get to here, it matches */
+ prog->endp[0] = string;
+ return 1;
+}
+
+
+
+int regexec(prog, string, bolflag)
+ struct regexp *prog;
+ char *string;
+ int bolflag;
+{
+ int rc;
+
+ /* keep trying to match it */
+ for (rc = reghelp(prog, string, bolflag); rc == 0; rc = reghelp(prog, string, 0))
+ {
+ string++;
+ }
+
+ /* did we match? */
+ return rc == 1;
+}
+#endif
diff --git a/regexp.h b/regexp.h
new file mode 100644
index 0000000..6d043b0
--- /dev/null
+++ b/regexp.h
@@ -0,0 +1,21 @@
+/*
+ * Definitions etc. for regexp(3) routines.
+ *
+ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof],
+ * not the System V one.
+ */
+#define NSUBEXP 10
+
+typedef struct regexp {
+ char *startp[NSUBEXP];
+ char *endp[NSUBEXP];
+ int minlen; /* length of shortest possible match */
+ char first; /* first character, if known; else \0 */
+ char bol; /* boolean: must start at beginning of line? */
+ char program[1]; /* Unwarranted chumminess with compiler. */
+} regexp;
+
+extern regexp *regcomp();
+extern int regexec();
+extern void regsub();
+extern void regerror();
diff --git a/regsub.c b/regsub.c
new file mode 100644
index 0000000..e0512fe
--- /dev/null
+++ b/regsub.c
@@ -0,0 +1,203 @@
+/* regsub.c */
+
+/* This file contains the regsub() function, which performs substitutions
+ * after a regexp match has been found.
+ */
+
+#include <ctype.h>
+#include "config.h"
+#include "vi.h"
+#include "regexp.h"
+
+static char *previous; /* a copy of the text from the previous substitution */
+
+/* perform substitutions after a regexp match */
+void regsub(re, src, dst)
+ regexp *re;
+ REG char *src;
+ REG char *dst;
+{
+ REG char *cpy;
+ REG char *end;
+ REG char c;
+ char *start;
+#ifndef CRUNCH
+ int mod;
+
+ mod = 0;
+#endif
+
+ start = src;
+ while ((c = *src++) != '\0')
+ {
+#ifndef NO_MAGIC
+ /* recognize any meta characters */
+ if (c == '&' && *o_magic)
+ {
+ cpy = re->startp[0];
+ end = re->endp[0];
+ }
+ else if (c == '~' && *o_magic)
+ {
+ cpy = previous;
+ if (cpy)
+ end = cpy + strlen(cpy);
+ }
+ else
+#endif /* not NO_MAGIC */
+ if (c == '\\')
+ {
+ c = *src++;
+ switch (c)
+ {
+#ifndef NO_MAGIC
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ /* \0 thru \9 mean "copy subexpression" */
+ c -= '0';
+ cpy = re->startp[c];
+ end = re->endp[c];
+ break;
+# ifndef CRUNCH
+ case 'U':
+ case 'u':
+ case 'L':
+ case 'l':
+ /* \U and \L mean "convert to upper/lowercase" */
+ mod = c;
+ continue;
+
+ case 'E':
+ case 'e':
+ /* \E ends the \U or \L */
+ mod = 0;
+ continue;
+# endif /* not CRUNCH */
+ case '&':
+ /* "\&" means "original text" */
+ if (*o_magic)
+ {
+ *dst++ = c;
+ continue;
+ }
+ cpy = re->startp[0];
+ end = re->endp[0];
+ break;
+
+ case '~':
+ /* "\~" means "previous text, if any" */
+ if (*o_magic)
+ {
+ *dst++ = c;
+ continue;
+ }
+ cpy = previous;
+ if (cpy)
+ end = cpy + strlen(cpy);
+ break;
+#else /* NO_MAGIC */
+ case '&':
+ /* "\&" means "original text" */
+ cpy = re->startp[0];
+ end = re->endp[0];
+ break;
+
+ case '~':
+ /* "\~" means "previous text, if any" */
+ cpy = previous;
+ if (cpy)
+ end = cpy + strlen(cpy);
+ break;
+#endif /* NO_MAGIC */
+ default:
+ /* ordinary char preceded by backslash */
+ *dst++ = c;
+ continue;
+ }
+ }
+ else
+ {
+ /* ordinary character, so just copy it */
+ *dst++ = c;
+ continue;
+ }
+
+ /* Note: to reach this point in the code, we must have evaded
+ * all "continue" statements. To do that, we must have hit
+ * a metacharacter that involves copying.
+ */
+
+ /* if there is nothing to copy, loop */
+ if (!cpy)
+ continue;
+
+ /* copy over a portion of the original */
+ while (cpy < end)
+ {
+#ifndef NO_MAGIC
+# ifndef CRUNCH
+ switch (mod)
+ {
+ case 'U':
+ case 'u':
+ /* convert to uppercase */
+ if (isascii(*cpy) && islower(*cpy))
+ {
+ *dst++ = toupper(*cpy);
+ cpy++;
+ }
+ else
+ {
+ *dst++ = *cpy++;
+ }
+ break;
+
+ case 'L':
+ case 'l':
+ /* convert to lowercase */
+ if (isascii(*cpy) && isupper(*cpy))
+ {
+ *dst++ = tolower(*cpy);
+ cpy++;
+ }
+ else
+ {
+ *dst++ = *cpy++;
+ }
+ break;
+
+ default:
+ /* copy without any conversion */
+ *dst++ = *cpy++;
+ }
+
+ /* \u and \l end automatically after the first char */
+ if (mod && (mod == 'u' || mod == 'l'))
+ {
+ mod = 0;
+ }
+# else /* CRUNCH */
+ *dst++ = *cpy++;
+# endif /* CRUNCH */
+#else /* NO_MAGIC */
+ *dst++ = *cpy++;
+#endif /* NO_MAGIC */
+ }
+ }
+ *dst = '\0';
+
+ /* remember what text we inserted this time */
+ if (previous)
+ free(previous);
+ previous = (char *)malloc((unsigned)(strlen(start) + 1));
+ if (previous)
+ strcpy(previous, start);
+}
diff --git a/shell.c b/shell.c
new file mode 100644
index 0000000..c7801f5
--- /dev/null
+++ b/shell.c
@@ -0,0 +1,231 @@
+/* shell.c */
+
+/* Author:
+ * Guntram Blohm
+ * Buchenstrasse 19
+ * 7904 Erbach, West Germany
+ * Tel. ++49-7305-6997
+ * sorry - no regular network connection
+ */
+
+/*
+ * This file contains a minimal version of a shell for TOS. It allows the
+ * setting of an environment, calling programs, and exiting.
+ * If you don't have another one, this might be sufficient, but you should
+ * prefer *any* other shell.
+ * You may, however, want to set your SHELL environment variable to this
+ * shell: it implements the -c switch, which is required by Elvis, and
+ * not supported by most other atari shells.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <osbind.h>
+extern char *getenv(), *malloc();
+extern char **environ;
+long _stksize=16384;
+
+#define MAXENV 50
+
+struct
+{
+ char *name;
+ char *value;
+} myenv[MAXENV];
+
+int cmd_set(), cmd_exit();
+
+struct buildins
+{
+ char *name;
+ int (*func)();
+} buildins[]=
+{ "exit", cmd_exit,
+ "set", cmd_set,
+ 0,
+};
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char buf[128];
+ int i;
+
+ for (i=0; environ[i] && strncmp(environ[i],"ARGV=",5); i++)
+ cmd_set(environ[i]);
+ script("profile.sh");
+
+ if (argc>1 && !strcmp(argv[1], "-c"))
+ {
+ buf[0]='\0';
+ for (i=2; i<argc; i++)
+ { if (i>2)
+ strcat(buf, " ");
+ strcat(buf, argv[i]);
+ }
+ execute(buf);
+ }
+ else
+ while (fputs("$ ", stdout), gets(buf))
+ execute(buf);
+}
+
+execute(buf)
+ char *buf;
+{
+ char *scan=buf;
+ char cmd[80];
+ char line[128];
+ char env[4096], *ep=env;
+ int i;
+
+ while (*scan==' ')
+ scan++;
+ if (!*scan)
+ return;
+ while (*scan && *scan!=' ')
+ scan++;
+ if (*scan)
+ *scan++='\0';
+
+ for (i=0; buildins[i].name; i++)
+ if (!strcmp(buf, buildins[i].name))
+ return (*buildins[i].func)(scan);
+
+ if (!searchpath(buf, cmd))
+ { printf("%s: not found\n", buf);
+ return -1;
+ }
+
+ strcpy(line+1, scan);
+ line[0]=strlen(scan);
+ for (i=0; i<MAXENV && myenv[i].name; i++)
+ { strcpy(ep, myenv[i].name);
+ strcat(ep, "=");
+ strcat(ep, myenv[i].value);
+ ep+=strlen(ep)+1;
+ }
+
+ *ep='\0';
+
+ return Pexec(0, cmd, line, env);
+}
+
+searchpath(from, to)
+ char *from, *to;
+{
+ char *path="";
+ char *scan;
+ char *end;
+ char *q;
+ int i;
+
+ for (i=0; i<MAXENV && myenv[i].name; i++)
+ if (!strcmp(myenv[i].name,"PATH"))
+ path=myenv[i].value;
+ for (scan=from; *scan; scan++)
+ if (*scan==':' || *scan=='\\')
+ { path=0;
+ break;
+ }
+ if (!path)
+ { strcpy(to, from);
+ end=to+strlen(to);
+ strcpy(end, ".prg"); if (try(to)) return 1;
+ strcpy(end, ".ttp"); if (try(to)) return 1;
+ strcpy(end, ".tos"); if (try(to)) return 1;
+ *to='\0'; return 0;
+ }
+ for (scan=path; *scan; )
+ {
+ for (q=to; *scan && *scan!=';' && *scan!=','; scan++)
+ *q++=*scan;
+ if (*scan==';' || *scan==',')
+ scan++;
+ *q++='\\';
+ *q='\0';
+ strcpy(q, from);
+ end=q+strlen(q);
+ strcpy(end, ".prg"); if (try(to)) return 1;
+ strcpy(end, ".ttp"); if (try(to)) return 1;
+ strcpy(end, ".tos"); if (try(to)) return 1;
+ }
+ *to='\0';
+ return 0;
+}
+
+try(name)
+ char *name;
+{
+ if (Fattrib(name, 0, 0) < 0)
+ return 0;
+ return 1;
+}
+
+cmd_exit()
+{
+ exit(0);
+}
+
+cmd_set(line)
+ char *line;
+{
+ char *value;
+ int i;
+
+ if (!*line)
+ {
+ for (i=0; i<MAXENV && myenv[i].name; i++)
+ printf("%s=%s\n", myenv[i].name, myenv[i].value);
+ return 0;
+ }
+
+ for (value=line; *value && *value!='='; value++)
+ ;
+ if (!*value)
+ { printf("Usage: set name=var\n");
+ return -1;
+ }
+ *value++='\0';
+ doset(line, value);
+}
+
+doset(line, value)
+ char *line, *value;
+{
+ int i;
+
+ for (i=0; i<MAXENV && myenv[i].name && strcmp(myenv[i].name, line); i++)
+ ;
+ if (i==MAXENV)
+ { printf("No Space\n");
+ return -1;
+ }
+ if (!myenv[i].name)
+ { myenv[i].name=malloc(strlen(line)+1);
+ strcpy(myenv[i].name, line);
+ }
+ if (myenv[i].value)
+ free(myenv[i].value);
+ myenv[i].value=malloc(strlen(value)+1);
+ strcpy(myenv[i].value, value);
+ return 0;
+}
+
+script(name)
+ char *name;
+{
+ FILE *fp;
+ char buf[128], *p;
+
+ if ((fp=fopen(name, "r"))==0)
+ return;
+ while (fgets(buf, sizeof buf, fp))
+ {
+ if ((p=strchr(buf, '\n'))!=0)
+ *p='\0';
+ execute(buf);
+ }
+ fclose(fp);
+}
diff --git a/sysdos.c b/sysdos.c
new file mode 100644
index 0000000..0f76ac4
--- /dev/null
+++ b/sysdos.c
@@ -0,0 +1,158 @@
+/* sysdos.c -- DOS version of system.c */
+
+/* Author:
+ * Guntram Blohm
+ * Buchenstrasse 19
+ * 7904 Erbach, West Germany
+ * Tel. ++49-7305-6997
+ * sorry - no regular network connection
+ */
+
+
+/* This file is derived from Steve Kirkendall's system.c.
+ *
+ * Entry points are:
+ * system(cmd) - run a single shell command
+ * wildcard(names) - expand wildcard characters in filanames
+ *
+ * This file is for use with DOS and TOS. For OS/2, slight modifications
+ * might be sufficient. For UNIX, use system.c. For Amiga, completely
+ * rewrite this stuff.
+ *
+ * Another system function, filter, is the same on DOS and UNIX and thus
+ * can be found in the original system.c.
+ */
+
+#include "config.h"
+#include "vi.h"
+extern char **environ;
+
+
+#if MSDOS
+#include <process.h>
+extern unsigned char _osmajor;
+#endif
+#if TOS
+#include <osbind.h>
+#endif
+
+
+#if MSDOS || TOS
+#include <string.h>
+
+/*
+ * Calling command is a bit nasty because of the undocumented yet sometimes
+ * used feature to change the option char to something else than /.
+ * Versions 2.x and 3.x support this, 4.x doesn't.
+ *
+ * For Atari, some shells define a shortcut entry which is faster than
+ * shell -c. Also, Mark Williams uses a special ARGV environment variable
+ * to pass more than 128 chars to a called command.
+ * We try to support all of these features here.
+ */
+
+int system(cmd)
+ const char *cmd;
+{
+#if MSDOS
+ char *cmdswitch="/c";
+ if (_osmajor<4)
+ cmdswitch[0]=switchar();
+ return spawnle(P_WAIT, o_shell, o_shell, cmdswitch, cmd, (char *)0, environ);
+#else
+ long ssp;
+ int (*shell)();
+ char line[130];
+ char env[4096], *ep=env;
+ int i;
+
+/* does our shell have a shortcut, that we can use? */
+
+ ssp = Super(0L);
+ shell = *((int (**)())0x4F6);
+ Super(ssp);
+ if (shell)
+ return (*shell)(cmd);
+
+/* else we'll have to call a shell ... */
+
+ for (i=0; environ[i] && strncmp(environ[i], "ARGV=", 5); i++)
+ { strcpy(ep, environ[i]);
+ ep+=strlen(ep)+1;
+ }
+ if (environ[i])
+ {
+ strcpy(ep, environ[i]); ep+=strlen(ep)+1;
+ strcpy(ep, o_shell); ep+=strlen(ep)+1;
+ strcpy(ep, "-c"); ep+=3;
+ strcpy(ep, cmd); ep+=strlen(ep)+1;
+ }
+ *ep='\0';
+ strcpy(line+1, "-c ");
+ strncat(line+1, cmd, 126);
+ line[0]=strlen(line+1);
+ return Pexec(0, o_shell, line, env);
+#endif
+}
+
+/* This private function opens a pipe from a filter. It is similar to the
+ * system() function above, and to popen(cmd, "r").
+ * sorry - i cant use cmdstate until rpclose, but get it from spawnle.
+ */
+
+static int cmdstate;
+static char output[80];
+
+int rpipe(cmd, in)
+ char *cmd; /* the filter command to use */
+ int in; /* the fd to use for stdin */
+{
+ int fd, old0, old1, old2;
+
+ /* create the file that will collect the filter's output */
+ strcpy(output, o_directory);
+ if ((fd=strlen(output)) && !strchr("/\\:", output[fd-1]))
+ output[fd++]=SLASH;
+ strcpy(output+fd, SCRATCHIN+3);
+ mktemp(output);
+ close(creat(output, 0666));
+ if ((fd=open(output, O_RDWR))==-1)
+ {
+ unlink(output);
+ return -1;
+ }
+
+ /* save and redirect stdin, stdout, and stderr */
+ old0=dup(0);
+ old1=dup(1);
+ if (in)
+ {
+ dup2(in, 0);
+ close(in);
+ }
+ dup2(fd, 1);
+
+ /* call command */
+ cmdstate=system(cmd);
+
+ /* restore old std... */
+ dup2(old0, 0); close(old0);
+ dup2(old1, 1); close(old1);
+
+ /* rewind command output */
+ lseek(fd, 0L, 0);
+ return fd;
+}
+
+/* This function closes the pipe opened by rpipe(), and returns 0 for success */
+int rpclose(fd)
+ int fd;
+{
+ int status;
+
+ close(fd);
+ unlink(output);
+ return cmdstate;
+}
+
+#endif
diff --git a/system.c b/system.c
new file mode 100644
index 0000000..0279c55
--- /dev/null
+++ b/system.c
@@ -0,0 +1,434 @@
+/* system.c -- UNIX version */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains a new version of the system() function and related stuff.
+ *
+ * Entry points are:
+ * system(cmd) - run a single shell command
+ * wildcard(names) - expand wildcard characters in filanames
+ * filter(m,n,cmd) - run text lines through a filter program
+ *
+ * This is probably the single least portable file in the program. The code
+ * shown here should work correctly if it links at all; it will work on UNIX
+ * and any O.S./Compiler combination which adheres to UNIX forking conventions.
+ */
+
+#include "config.h"
+#include "vi.h"
+#include <signal.h>
+extern char **environ;
+
+#if ANY_UNIX
+
+/* This is a new version of the system() function. The only difference
+ * between this one and the library one is: this one uses the o_shell option.
+ */
+int system(cmd)
+ char *cmd; /* a command to run */
+{
+ int status; /* exit status of the command */
+
+ /* warn the user if the file hasn't been saved yet */
+ if (*o_warn && tstflag(file, MODIFIED))
+ {
+ if (mode == MODE_VI)
+ {
+ mode = MODE_COLON;
+ }
+ msg("Warning: \"%s\" has been modified but not yet saved", origname);
+ }
+
+ signal(SIGINT, SIG_IGN);
+ switch (fork())
+ {
+ case -1: /* error */
+ msg("fork() failed");
+ status = -1;
+ break;
+
+ case 0: /* child */
+ /* for the child, close all files except stdin/out/err */
+ for (status = 3; status < 60 && (close(status), errno != EINVAL); status++)
+ {
+ }
+
+ signal(SIGINT, SIG_DFL);
+ if (cmd == o_shell)
+ {
+ execle(o_shell, o_shell, (char *)0, environ);
+ }
+ else
+ {
+ execle(o_shell, o_shell, "-c", cmd, (char *)0, environ);
+ }
+ msg("execle(\"%s\", ...) failed", o_shell);
+ exit(1); /* if we get here, the exec failed */
+
+ default: /* parent */
+ wait(&status);
+ signal(SIGINT, trapint);
+ }
+
+ return status;
+}
+
+/* This private function opens a pipe from a filter. It is similar to the
+ * system() function above, and to popen(cmd, "r").
+ */
+static int rpipe(cmd, in)
+ char *cmd; /* the filter command to use */
+ int in; /* the fd to use for stdin */
+{
+ int r0w1[2];/* the pipe fd's */
+
+ /* make the pipe */
+ if (pipe(r0w1) < 0)
+ {
+ return -1; /* pipe failed */
+ }
+
+ /* The parent process (elvis) ignores signals while the filter runs.
+ * The child (the filter program) will reset this, so that it can
+ * catch the signal.
+ */
+ signal(SIGINT, SIG_IGN);
+
+ switch (fork())
+ {
+ case -1: /* error */
+ return -1;
+
+ case 0: /* child */
+ /* close the "read" end of the pipe */
+ close(r0w1[0]);
+
+ /* redirect stdout to go to the "write" end of the pipe */
+ close(1);
+ dup(r0w1[1]);
+ close(2);
+ dup(r0w1[1]);
+ close(r0w1[1]);
+
+ /* redirect stdin */
+ if (in != 0)
+ {
+ close(0);
+ dup(in);
+ close(in);
+ }
+
+ /* the filter should accept SIGINT signals */
+ signal(SIGINT, SIG_DFL);
+
+ /* exec the shell to run the command */
+ execle(o_shell, o_shell, "-c", cmd, (char *)0, environ);
+ exit(1); /* if we get here, exec failed */
+
+ default: /* parent */
+ /* close the "write" end of the pipe */
+ close(r0w1[1]);
+
+ return r0w1[0];
+ }
+}
+
+#endif /* non-DOS */
+
+#if OSK
+
+/* This private function opens a pipe from a filter. It is similar to the
+ * system() function above, and to popen(cmd, "r").
+ */
+static int rpipe(cmd, in)
+ char *cmd; /* the filter command to use */
+ int in; /* the fd to use for stdin */
+{
+
+ char **argblk;
+ char *p, *buffer, *command;
+ int stdinp, stdoutp;
+ unsigned addstack = 0;
+ int words, len, loop = 1;
+ int fp, pipe_pid;
+ extern int os9forkc();
+ extern char *index();
+
+ command = cmd;
+ words = 0;
+ if ((buffer = (char*) malloc(strlen(cmd))) == (char*) 0)
+ return 0;
+
+ do {
+ if (!(p = index(command, ' '))) {
+ loop--;
+ len = strlen(command);
+ }
+ else
+ len = p - command;
+ words++;
+ while (command[len] && command[len] == ' ')
+ len++;
+ if (!command[len])
+ break;
+ command = command + len;
+ }
+ while (loop);
+ if ((argblk = (char **)malloc((words+1) * sizeof(char*))) == (char **)0)
+ return 0;
+ command = cmd;
+ words = 0;
+ do {
+ if (!(p = index(command, ' '))) {
+ loop--;
+ len = strlen(command);
+ }
+ else
+ len = p - command;
+ strncpy(buffer, command, len);
+ argblk[words++] = buffer;
+ buffer += len;
+ *buffer++ = '\0';
+ while (command[len] && command[len] == ' ')
+ len++;
+ if (!command[len])
+ break;
+ command = command + len;
+ } while (loop);
+ if (argblk[words - 1][0] == '#')
+ addstack = 1024 * atoi(&argblk[--words][1]);
+ argblk[words] = 0;
+
+ stdoutp = dup(1);
+ close(1); /* close stdout */
+ if ((fp = open("/pipe",S_IREAD)) < 0) {
+ dup(stdoutp);
+ close(stdoutp);
+ return 0;
+ }
+ if (in != 0) {
+ stdinp = dup(0);
+ close(0);
+ dup(in);
+ close(in);
+ }
+ pipe_pid = os9exec(os9forkc,argblk[0],argblk,environ,addstack,0,3);
+ if (pipe_pid == -1) {
+ fclose(fp);
+ dup(stdoutp);
+ close(stdoutp);
+ if (in != 0) {
+ close(0);
+ dup(stdinp);
+ }
+ return 0;
+ }
+ fp = (short)dup(1); /* save pipe */
+ close(1); /* get rid of the pipe */
+ dup(stdoutp); /* restore old stdout */
+ close(stdoutp); /* close path to stdout copy */
+ if (in != 0) {
+ close(0);
+ dup(stdinp);
+ }
+ return fp;
+}
+#endif
+
+#if ANY_UNIX || OSK
+
+/* This function closes the pipe opened by rpipe(), and returns 0 for success */
+static int rpclose(fd)
+ int fd;
+{
+ int status;
+
+ close(fd);
+ wait(&status);
+ signal(SIGINT, trapint);
+ return status;
+}
+
+#endif /* non-DOS */
+
+/* This function expands wildcards in a filename or filenames. It does this
+ * by running the "echo" command on the filenames via the shell; it is assumed
+ * that the shell will expand the names for you. If for any reason it can't
+ * run echo, then it returns the names unmodified.
+ */
+
+#if MSDOS || TOS
+#define PROG "wildcard "
+#define PROGLEN 9
+#include <string.h>
+#else
+#define PROG "echo "
+#define PROGLEN 5
+#endif
+
+char *wildcard(names)
+ char *names;
+{
+ int i, j, fd;
+ REG char *s, *d;
+
+
+ /* build the echo command */
+ if (names != tmpblk.c)
+ {
+ /* the names aren't in tmpblk.c, so we can do it the easy way */
+ strcpy(tmpblk.c, PROG);
+ strcat(tmpblk.c, names);
+ }
+ else
+ {
+ /* the names are already in tmpblk.c, so shift them to make
+ * room for the word "echo "
+ */
+ for (s = names + strlen(names) + 1, d = s + PROGLEN; s > names; )
+ {
+ *--d = *--s;
+ }
+ strncpy(names, PROG, PROGLEN);
+ }
+
+ /* run the command & read the resulting names */
+ fd = rpipe(tmpblk.c, 0);
+ if (fd < 0) return names;
+ i = 0;
+ do
+ {
+ j = tread(fd, tmpblk.c + i, BLKSIZE - i);
+ i += j;
+ } while (j > 0);
+
+ /* successful? */
+ if (rpclose(fd) == 0 && j == 0 && i < BLKSIZE && i > 0)
+ {
+ tmpblk.c[i-1] = '\0'; /* "i-1" so we clip off the newline */
+ return tmpblk.c;
+ }
+ else
+ {
+ return names;
+ }
+}
+
+/* This function runs a range of lines through a filter program, and replaces
+ * the original text with the filtered version. As a special case, if "to"
+ * is MARK_UNSET, then it runs the filter program with stdin coming from
+ * /dev/null, and inserts any output lines.
+ */
+int filter(from, to, cmd)
+ MARK from, to; /* the range of lines to filter */
+ char *cmd; /* the filter command */
+{
+ int scratch; /* fd of the scratch file */
+ int fd; /* fd of the pipe from the filter */
+ char scrout[50]; /* name of the scratch out file */
+ MARK new; /* place where new text should go */
+ int i;
+
+ /* write the lines (if specified) to a temp file */
+ if (to)
+ {
+ /* we have lines */
+#if MSDOS || TOS
+ strcpy(scrout, o_directory);
+ if ((i=strlen(scrout)) && strchr("\\/:", scrout[i-1]))
+ scrout[i++]=SLASH;
+ strcpy(scrout+i, SCRATCHOUT+3);
+#else
+ sprintf(scrout, SCRATCHOUT, o_directory);
+#endif
+ mktemp(scrout);
+ cmd_write(from, to, CMD_BANG, 0, scrout);
+
+ /* use those lines as stdin */
+ scratch = open(scrout, O_RDONLY);
+ if (scratch < 0)
+ {
+ unlink(scrout);
+ return -1;
+ }
+ }
+ else
+ {
+ scratch = 0;
+ }
+
+ /* start the filter program */
+ fd = rpipe(cmd, scratch);
+ if (fd < 0)
+ {
+ if (to)
+ {
+ close(scratch);
+ unlink(scrout);
+ }
+ return -1;
+ }
+
+ ChangeText
+ {
+ /* adjust MARKs for whole lines, and set "new" */
+ from &= ~(BLKSIZE - 1);
+ if (to)
+ {
+ to &= ~(BLKSIZE - 1);
+ to += BLKSIZE;
+ new = to;
+ }
+ else
+ {
+ new = from + BLKSIZE;
+ }
+
+ /* repeatedly read in new text and add it */
+ while ((i = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
+ {
+ tmpblk.c[i] = '\0';
+ add(new, tmpblk.c);
+ for (i = 0; tmpblk.c[i]; i++)
+ {
+ if (tmpblk.c[i] == '\n')
+ {
+ new = (new & ~(BLKSIZE - 1)) + BLKSIZE;
+ }
+ else
+ {
+ new++;
+ }
+ }
+ }
+ }
+
+ /* delete old text, if any */
+ if (to)
+ {
+ delete(from, to);
+ }
+
+ /* Reporting... */
+ rptlabel = "more";
+ if (rptlines < 0)
+ {
+ rptlines = -rptlines;
+ rptlabel = "less";
+ }
+
+ /* cleanup */
+ rpclose(fd);
+ if (to)
+ {
+ close(scratch);
+ unlink(scrout);
+ }
+ return 0;
+}
diff --git a/tinytcap.c b/tinytcap.c
new file mode 100644
index 0000000..6e8f46f
--- /dev/null
+++ b/tinytcap.c
@@ -0,0 +1,154 @@
+/* tinytcap.c */
+
+/* This file contains functions which simulate the termcap functions, but which
+ * can only describe the capabilities of the ANSI.SYS and NANSI.SYS drivers on
+ * an MS-DOS system or the VT-52 emulator of an Atari-ST. These functions
+ * do *NOT* access a "termcap" database file.
+ */
+
+#include "config.h"
+#if MSDOS || TOS || MINIX || COHERENT
+
+#define CAP(str) CAP2((str)[0], (str)[1])
+#define CAP2(a,b) (((a) << 8) + ((b) & 0xff))
+
+#if MSDOS
+# define VAL2(v,a) (a)
+# define VAL3(v,a,n) (nansi ? (n) : (a))
+static int nansi;
+#endif
+
+#if TOS
+# define VAL2(v,a) (v)
+# define VAL3(v,a,n) (v)
+#endif
+
+#if MINIX || COHERENT
+# define VAL2(v,a) (a)
+# define VAL3(v,a,n) (n)
+#endif
+
+
+/*ARGSUSED*/
+int tgetent(bp, name)
+ char *bp; /* buffer for storing the entry -- ignored */
+ char *name; /* name of the entry */
+{
+#if MSDOS
+ nansi = strcmp(name, "ansi");
+#endif
+ return 1;
+}
+
+int tgetnum(id)
+ char *id;
+{
+ switch (CAP(id))
+ {
+ case CAP2('l','i'): return 25;
+ case CAP2('c','o'): return 80;
+ case CAP2('s','g'): return 0;
+ case CAP2('u','g'): return 0;
+ default: return -1;
+ }
+}
+
+int tgetflag(id)
+ char *id;
+{
+ switch (CAP(id))
+ {
+#if !MINIX || COHERENT
+ case CAP2('a','m'): return 1;
+#endif
+ case CAP2('b','s'): return 1;
+ case CAP2('m','i'): return 1;
+ default: return 0;
+ }
+}
+
+/*ARGSUSED*/
+char *tgetstr(id, bp)
+ char *id;
+ char **bp; /* pointer to pointer to buffer - ignored */
+{
+ switch (CAP(id))
+ {
+ case CAP2('c','e'): return VAL2("\033K", "\033[K");
+ case CAP2('c','l'): return VAL2("\033E", "\033[2J");
+
+ case CAP2('a','l'): return VAL3("\033L", (char *)0, "\033[L");
+ case CAP2('d','l'): return VAL3("\033M", (char *)0, "\033[M");
+
+ case CAP2('c','m'): return VAL2("\033Y%i%+ %+ ", "\033[%i%d;%dH");
+ case CAP2('d','o'): return VAL2("\033B", "\033[B");
+ case CAP2('n','d'): return VAL2("\033C", "\033[C");
+ case CAP2('u','p'): return VAL2("\033A", "\033[A");
+ case CAP2('t','i'): return VAL2("\033e", "");
+ case CAP2('t','e'): return VAL2("", "");
+
+ case CAP2('s','o'): return VAL2("\033p", "\033[7m");
+ case CAP2('s','e'): return VAL2("\033q", "\033[m");
+ case CAP2('u','s'): return VAL2((char *)0, "\033[4m");
+ case CAP2('u','e'): return VAL2((char *)0, "\033[m");
+ case CAP2('m','d'): return VAL2((char *)0, "\033[1m");
+ case CAP2('m','e'): return VAL2((char *)0, "\033[m");
+
+#if MINIX || COHERENT
+ case CAP2('k','u'): return "\033[A";
+ case CAP2('k','d'): return "\033[B";
+ case CAP2('k','l'): return "\033[D";
+ case CAP2('k','r'): return "\033[C";
+ case CAP2('k','P'): return "\033[V";
+ case CAP2('k','N'): return "\033[U";
+ case CAP2('k','h'): return "\033[H";
+# if MINIX
+ case CAP2('k','H'): return "\033[Y";
+# else /* COHERENT */
+ case CAP2('k','H'): return "\033[24H";
+# endif
+#else /* MS-DOS or TOS */
+ case CAP2('k','u'): return "#H";
+ case CAP2('k','d'): return "#P";
+ case CAP2('k','l'): return "#K";
+ case CAP2('k','r'): return "#M";
+ case CAP2('k','h'): return "#G";
+ case CAP2('k','H'): return "#O";
+ case CAP2('k','P'): return "#I";
+ case CAP2('k','N'): return "#Q";
+#endif
+
+ default: return (char *)0;
+ }
+}
+
+/*ARGSUSED*/
+char *tgoto(cm, destcol, destrow)
+ char *cm; /* cursor movement string -- ignored */
+ int destcol;/* destination column, 0 - 79 */
+ int destrow;/* destination row, 0 - 24 */
+{
+ static char buf[30];
+
+#if MSDOS || MINIX || COHERENT
+ sprintf(buf, "\033[%d;%dH", destrow + 1, destcol + 1);
+#endif
+#if TOS
+ sprintf(buf, "\033Y%c%c", ' ' + destrow, ' ' + destcol);
+#endif
+ return buf;
+}
+
+/*ARGSUSED*/
+void tputs(cp, affcnt, outfn)
+ char *cp; /* the string to output */
+ int affcnt; /* number of affected lines -- ignored */
+ int (*outfn)(); /* the output function */
+{
+ while (*cp)
+ {
+ (*outfn)(*cp);
+ cp++;
+ }
+}
+#endif
diff --git a/tio.c b/tio.c
new file mode 100644
index 0000000..076372e
--- /dev/null
+++ b/tio.c
@@ -0,0 +1,864 @@
+/* tio.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains terminal I/O functions */
+
+#include "config.h"
+#if BSD || COHERENT
+# include <setjmp.h>
+#endif
+#include <signal.h>
+#include "vi.h"
+
+
+/* This function reads in a line from the terminal. */
+int vgets(prompt, buf, bsize)
+ char prompt; /* the prompt character, or '\0' for none */
+ char *buf; /* buffer into which the string is read */
+ int bsize; /* size of the buffer */
+{
+ int len; /* how much we've read so far */
+ int ch; /* a character from the user */
+ int quoted; /* is the next char quoted? */
+ int tab; /* column position of cursor */
+ char widths[132]; /* widths of characters */
+#ifndef NO_DIGRAPH
+ int erased; /* 0, or first char of a digraph */
+#endif
+
+ /* show the prompt */
+ move(LINES - 1, 0);
+ tab = 0;
+ if (prompt)
+ {
+ addch(prompt);
+ tab = 1;
+ }
+ clrtoeol();
+ refresh();
+
+ /* read in the line */
+#ifndef NO_DIGRAPH
+ erased =
+#endif
+ quoted = len = 0;
+ for (;;)
+ {
+ ch = getkey(quoted ? 0 : WHEN_EX);
+
+ /* some special conversions */
+ if (ch == ctrl('D') && len == 0)
+ ch = ctrl('[');
+#ifndef NO_DIGRAPH
+ if (*o_digraph && erased != 0 && ch != '\b')
+ {
+ ch = digraph(erased, ch);
+ erased = 0;
+ }
+#endif
+
+ /* inhibit detection of special chars (except ^J) after a ^V */
+ if (quoted && ch != '\n')
+ {
+ ch |= 256;
+ }
+
+ /* process the character */
+ switch(ch)
+ {
+ case ctrl('V'):
+ qaddch('^');
+ qaddch('\b');
+ quoted = TRUE;
+ break;
+
+ case ctrl('['):
+ return -1;
+
+ case '\n':
+#if OSK
+ case '\l':
+#else
+ case '\r':
+#endif
+ clrtoeol();
+ goto BreakBreak;
+
+ case '\b':
+ if (len > 0)
+ {
+ len--;
+#ifndef NO_DIGRAPH
+ erased = buf[len];
+#endif
+ for (ch = widths[len]; ch > 0; ch--)
+ addch('\b');
+ if (mode == MODE_EX)
+ {
+ clrtoeol();
+ }
+ tab -= widths[len];
+ }
+ else
+ {
+ return -1;
+ }
+ break;
+
+ default:
+ /* strip off quotation bit */
+ if (ch & 256)
+ {
+ ch &= ~256;
+ quoted = FALSE;
+ qaddch(' ');
+ qaddch('\b');
+ }
+ /* add & echo the char */
+ if (len < bsize - 1)
+ {
+ if (ch == '\t')
+ {
+ widths[len] = *o_tabstop - (tab % *o_tabstop);
+ addstr(" " + 8 - widths[len]);
+ tab += widths[len];
+ }
+ else if (ch > 0 && ch < ' ') /* > 0 by GB */
+ {
+ addch('^');
+ addch(ch + '@');
+ widths[len] = 2;
+ tab += 2;
+ }
+ else if (ch == '\177')
+ {
+ addch('^');
+ addch('?');
+ widths[len] = 2;
+ tab += 2;
+ }
+ else
+ {
+ addch(ch);
+ widths[len] = 1;
+ tab++;
+ }
+ buf[len++] = ch;
+ }
+ else
+ {
+ beep();
+ }
+ }
+ }
+BreakBreak:
+ refresh();
+ buf[len] = '\0';
+ return len;
+}
+
+
+/* ring the terminal's bell */
+void beep()
+{
+ if (*o_vbell)
+ {
+ do_VB();
+ refresh();
+ }
+ else if (*o_errorbells)
+ {
+ ttywrite("\007", 1);
+ }
+}
+
+static int manymsgs; /* This variable keeps msgs from overwriting each other */
+static char pmsg[80]; /* previous message (waiting to be displayed) */
+
+
+static int showmsg()
+{
+ /* if there is no message to show, then don't */
+ if (!manymsgs)
+ return FALSE;
+
+ /* display the message */
+ move(LINES - 1, 0);
+ if (*pmsg)
+ {
+ standout();
+ qaddch(' ');
+ qaddstr(pmsg);
+ qaddch(' ');
+ standend();
+ }
+ clrtoeol();
+
+ manymsgs = FALSE;
+ return TRUE;
+}
+
+
+void endmsgs()
+{
+ if (manymsgs)
+ {
+ showmsg();
+ addch('\n');
+ }
+}
+
+/* Write a message in an appropriate way. This should really be a varargs
+ * function, but there is no such thing as vwprintw. Hack!!!
+ *
+ * In MODE_EX or MODE_COLON, the message is written immediately, with a
+ * newline at the end.
+ *
+ * In MODE_VI, the message is stored in a character buffer. It is not
+ * displayed until getkey() is called. msg() will call getkey() itself,
+ * if necessary, to prevent messages from being lost.
+ *
+ * msg("") - clears the message line
+ * msg("%s %d", ...) - does a printf onto the message line
+ */
+/*VARARGS1*/
+void msg(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
+ char *fmt;
+ long arg1, arg2, arg3, arg4, arg5, arg6, arg7;
+{
+ if (mode != MODE_VI)
+ {
+ sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+ qaddstr(pmsg);
+ addch('\n');
+ exrefresh();
+ }
+ else
+ {
+ /* wait for keypress between consecutive msgs */
+ if (manymsgs)
+ {
+ getkey(WHEN_MSG);
+ }
+
+ /* real message */
+ sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+ manymsgs = TRUE;
+ }
+}
+
+
+/* This function calls refresh() if the option exrefresh is set */
+void exrefresh()
+{
+ char *scan;
+
+ /* If this ex command wrote ANYTHING set exwrote so vi's : command
+ * can tell that it must wait for a user keystroke before redrawing.
+ */
+ for (scan=kbuf; scan<stdscr; scan++)
+ if (*scan == '\n')
+ exwrote = TRUE;
+
+#if MICROSOFT /* avoid compiler bug */
+ scan = stdscr;
+#define stdscr scan
+#endif
+ /* now we do the refresh thing */
+ if (*o_exrefresh)
+ {
+ refresh();
+ }
+ else
+ {
+ wqrefresh(stdscr);
+ }
+#if MICROSOFT
+#undef stdscr
+ stdscr = scan;
+#endif
+ if (mode != MODE_VI)
+ {
+ manymsgs = FALSE;
+ }
+}
+
+
+
+/* These are used for typeahead, and also for fudging the visual @ command */
+static char keybuf[100]; /* array of already-read keys */
+static int nkeys; /* total number of keys in keybuf */
+static int next; /* index of next key to return */
+#ifndef NO_AT
+static int atnext; /* index of next key for "@", or 0 normally */
+int fromcutbuf(cbname)
+ int cbname;
+{
+ int len;
+
+ /* fail if we're already doing an @ macro */
+ if (atnext > 0 && keybuf[atnext])
+ {
+ msg("Can't nest @ commands");
+ return FALSE;
+ }
+
+ /* use the empty portion of keybuf[] to get chars from the cut buffer */
+ len = cb2str(cbname, keybuf + nkeys, sizeof keybuf - nkeys);
+ if (len < 0)
+ {
+ msg("Invalid cut buffer name. Must be a-z");
+ return FALSE;
+ }
+ if (len == 0)
+ {
+ msg("Cut buffer \"%c is empty", cbname);
+ return FALSE;
+ }
+ else if (len >= sizeof keybuf - nkeys)
+ {
+ msg("Cut buffer \"%c is too large to execute", cbname);
+ return FALSE;
+ }
+
+ /* prepare to "read" those keys on subsequent getkey() calls */
+ atnext = nkeys;
+ return TRUE;
+}
+#endif
+
+/* This array describes mapped key sequences */
+static struct _keymap
+{
+ char *name; /* name of the key, or NULL */
+ char rawin[LONGKEY]; /* the unmapped version of input */
+ char cooked[80]; /* the mapped version of input */
+ int len; /* length of the unmapped version */
+ int when; /* when is this key mapped? */
+}
+ mapped[MAXMAPS];
+
+#if !MSDOS && !TOS
+# if BSD || COHERENT
+static jmp_buf env_timeout;
+static int dummy()
+{
+ longjmp(env_timeout, 1);
+ return 0;
+}
+# else
+static int dummy()
+{
+}
+# endif
+#endif
+
+/* This function reads in a keystroke for VI mode. It automatically handles
+ * key mapping.
+ */
+int getkey(when)
+ int when; /* which bits must be ON? */
+{
+ static char *cooked; /* rawin, or pointer to converted key */
+ static int oldwhen; /* "when" from last time */
+ static int oldleft;
+ static long oldtop;
+ static long oldnlines;
+ static char *cshape; /* current cursor shape */
+ REG char *kptr; /* &keybuf[next] */
+ REG struct _keymap *km; /* used to count through keymap */
+ REG int i, j, k;
+
+#ifdef DEBUG
+ watch();
+#endif
+
+ /* if this key is needed for delay between multiple error messages,
+ * then reset the manymsgs flag and abort any mapped key sequence.
+ */
+ if (showmsg())
+ {
+ if (when == WHEN_MSG)
+ {
+ qaddstr("[More...]");
+ refresh();
+ cooked = (char *)0;
+ }
+ else if (when == WHEN_VIINP || when == WHEN_VIREP)
+ {
+ redraw(cursor, TRUE);
+ }
+ }
+
+#ifndef NO_AT
+ /* if we're in the middle of a visual @ macro, take atnext */
+ if (atnext > 0)
+ {
+ if (keybuf[atnext])
+ {
+ return keybuf[atnext++];
+ }
+ atnext = 0;
+ }
+#endif
+
+ /* if we're doing a mapped key, get the next char */
+ if (cooked && *cooked)
+ {
+ return *cooked++;
+ }
+
+ /* if keybuf is empty, fill it */
+ if (next == nkeys)
+ {
+#ifndef NO_CURSORSHAPE
+ /* make sure the cursor is the right shape */
+ if (has_CQ)
+ {
+ cooked = cshape;
+ switch (when)
+ {
+ case WHEN_EX: cooked = CX; break;
+ case WHEN_VICMD: cooked = CV; break;
+ case WHEN_VIINP: cooked = CI; break;
+ case WHEN_VIREP: cooked = CR; break;
+ }
+ if (cooked != cshape)
+ {
+ cshape = cooked;
+ switch (when)
+ {
+ case WHEN_EX: do_CX(); break;
+ case WHEN_VICMD: do_CV(); break;
+ case WHEN_VIINP: do_CI(); break;
+ case WHEN_VIREP: do_CR(); break;
+ }
+ }
+ cooked = (char *)0;
+ }
+#endif
+
+#ifndef NO_SHOWMODE
+ /* if "showmode" then say which mode we're in */
+ if (*o_smd
+ && mode == MODE_VI
+ && (when != oldwhen || topline != oldtop || leftcol != oldleft || nlines != oldnlines))
+ {
+ oldwhen = when;
+ oldtop = topline;
+ oldleft = leftcol;
+ oldnlines = nlines;
+
+ if (when & WHEN_VICMD)
+ {
+ redraw(cursor, FALSE);
+ move(LINES - 1, COLS - 10);
+ standout();
+ addstr("Command");
+ standend();
+ redraw(cursor, FALSE);
+ }
+ else if (when & WHEN_VIINP)
+ {
+ redraw(cursor, TRUE);
+ move(LINES - 1, COLS - 10);
+ standout();
+ addstr(" Input ");
+ standend();
+ redraw(cursor, TRUE);
+ }
+ else if (when & WHEN_VIREP)
+ {
+ redraw(cursor, TRUE);
+ move(LINES - 1, COLS - 10);
+ standout();
+ addstr("Replace");
+ standend();
+ redraw(cursor, TRUE);
+ }
+ }
+ else
+#endif
+
+ /* redraw if getting a VI command */
+ if (when & WHEN_VICMD)
+ {
+ redraw(cursor, FALSE);
+ }
+
+ /* read the rawin keystrokes */
+ refresh();
+ while ((nkeys = ttyread(keybuf, sizeof keybuf)) < 0)
+ {
+ /* terminal was probably resized */
+ *o_lines = LINES;
+ *o_columns = COLS;
+ if (when & (WHEN_VICMD|WHEN_VIINP|WHEN_VIREP))
+ {
+ redraw(MARK_UNSET, FALSE);
+ redraw(cursor, (when & WHEN_VICMD) == 0);
+ refresh();
+ }
+ }
+ next = 0;
+
+ /* if nkeys == 0 then we've reached EOF of an ex script. */
+ if (nkeys == 0)
+ {
+ tmpabort(TRUE);
+ move(LINES - 1, 0);
+ clrtoeol();
+ refresh();
+ endwin();
+ exit(1);
+ }
+ }
+
+ /* see how many mapped keys this might be */
+ kptr = &keybuf[next];
+ for (i = j = 0, k = -1, km = mapped; i < MAXMAPS; i++, km++)
+ {
+ if ((km->when & when) && km->len > 0 && *km->rawin == *kptr)
+ {
+ if (km->len > nkeys - next)
+ {
+ if (!strncmp(km->rawin, kptr, nkeys - next))
+ {
+ j++;
+ }
+ }
+ else
+ {
+ if (!strncmp(km->rawin, kptr, km->len))
+ {
+ j++;
+ k = i;
+ }
+ }
+ }
+ }
+
+ /* if more than one, try to read some more */
+ while (j > 1)
+ {
+#if BSD || COHERENT
+ if (setjmp(env_timeout))
+ {
+ /* we timed out - assume no mapping */
+ j = 0;
+ break;
+ }
+#endif
+#if ANY_UNIX
+ signal(SIGALRM, dummy);
+#endif
+#if OSK
+ signal(SIGQUIT, dummy);
+#endif
+ alarm((unsigned)*o_keytime);
+ i = nkeys;
+ if ((k = ttyread(keybuf + nkeys, sizeof keybuf - nkeys)) >= 0)
+ {
+ nkeys += k;
+ }
+ alarm(0);
+#if OSK
+# ifndef DEBUG
+ signal(SIGQUIT, SIG_IGN);
+# endif
+#endif
+
+ /* if we couldn't read any more, pretend 0 mapped keys */
+ if (i == nkeys)
+ {
+ j = 0;
+ }
+ else /* else we got some more - try again */
+ {
+ for (i = j = 0, k = -1, km = mapped; i < MAXMAPS; i++, km++)
+ {
+ if ((km->when & when) && km->len > 0 && *km->rawin == *kptr)
+ {
+ if (km->len > nkeys - next)
+ {
+ if (!strncmp(km->rawin, kptr, nkeys - next))
+ {
+ j++;
+ }
+ }
+ else
+ {
+ if (!strncmp(km->rawin, kptr, km->len))
+ {
+ j++;
+ k = i;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* if unambiguously mapped key, use it! */
+ if (j == 1 && k >= 0)
+ {
+ next += mapped[k].len;
+ cooked = mapped[k].cooked;
+#ifndef NO_EXTENSIONS
+ if ((when & (WHEN_VIINP|WHEN_VIREP))
+ && (mapped[k].when & WHEN_INMV))
+ {
+ return 0; /* special case, means "a movement char follows" */
+ }
+ else
+#endif
+ {
+ return *cooked++;
+ }
+ }
+ else
+ /* assume key is unmapped, but still translate weird erase key to '\b' */
+ if (keybuf[next] == ERASEKEY && when != 0)
+ {
+ next++;
+ return '\b';
+ }
+ else if (keybuf[next] == '\0')
+ {
+ next++;
+ return ('A' & 0x1f);
+ }
+ else
+ {
+ return keybuf[next++];
+ }
+}
+
+
+/* This function maps or unmaps a key */
+void mapkey(rawin, cooked, when, name)
+ char *rawin; /* the input key sequence, before mapping */
+ char *cooked;/* after mapping */
+ short when; /* bitmap of when mapping should happen */
+ char *name; /* name of the key, if any */
+{
+ int i, j;
+
+#ifndef NO_EXTENSIONS
+ /* if the mapped version starts with the word "visual" then set WHEN_INMV */
+ if (!strncmp(cooked, "visual ", 7))
+ {
+ when |= WHEN_INMV;
+ cooked += 7;
+ }
+ /* if WHEN_INMV is set, then WHEN_VIINP and WHEN_VIREP must be set */
+ if (when & WHEN_INMV)
+ {
+ when |= (WHEN_VIINP | WHEN_VIREP);
+ }
+#endif
+
+ /* see if the key sequence was mapped before */
+ j = strlen(rawin);
+ for (i = 0; i < MAXMAPS; i++)
+ {
+ if (mapped[i].len == j
+ && !strncmp(mapped[i].rawin, rawin, j)
+ && (mapped[i].when & when))
+ {
+ break;
+ }
+ }
+
+ /* if not already mapped, then try to find a new slot to use */
+ if (i == MAXMAPS)
+ {
+ for (i = 0; i < MAXMAPS && mapped[i].len > 0; i++)
+ {
+ }
+ }
+
+ /* no room for the new key? */
+ if (i == MAXMAPS)
+ {
+ msg("No room left in the key map table");
+ return;
+ }
+
+ /* map the key */
+ if (cooked && *cooked)
+ {
+ /* Map the key */
+ mapped[i].len = j;
+ strncpy(mapped[i].rawin, rawin, j);
+ strcpy(mapped[i].cooked, cooked);
+ mapped[i].when = when;
+ mapped[i].name = name;
+ }
+ else /* unmap the key */
+ {
+ mapped[i].len = 0;
+ }
+}
+
+/* Dump keys of a given type - WHEN_VICMD dumps the ":map" keys, and
+ * WHEN_VIINP|WHEN_VIREP dumps the ":map!" keys
+ */
+void dumpkey(when)
+ int when; /* WHEN_XXXX of mappings to be dumped */
+{
+ int i, len, mlen;
+ char *scan;
+ char *mraw;
+
+ for (i = 0; i < MAXMAPS; i++)
+ {
+ /* skip unused entries, or entries that don't match "when" */
+ if (mapped[i].len <= 0 || !(mapped[i].when & when))
+ {
+ continue;
+ }
+
+ /* dump the key label, if any */
+ len = 8;
+ if (mapped[i].name)
+ {
+ qaddstr(mapped[i].name);
+ len -= strlen(mapped[i].name);
+ }
+ do
+ {
+ qaddch(' ');
+ } while (len-- > 0);
+
+ /* dump the raw version */
+ len = 0;
+ mlen = mapped[i].len;
+ mraw = mapped[i].rawin;
+ for (scan = mraw; scan < mraw + mlen; scan++)
+ {
+ if (UCHAR(*scan) < ' ' || *scan == '\177')
+ {
+ qaddch('^');
+ qaddch(*scan ^ '@');
+ len += 2;
+ }
+ else
+ {
+ qaddch(*scan);
+ len++;
+ }
+ }
+ do
+ {
+ qaddch(' ');
+ } while (++len < 8);
+
+ /* dump the mapped version */
+#ifndef NO_EXTENSIONS
+ if ((mapped[i].when & WHEN_INMV) && (when & (WHEN_VIINP|WHEN_VIREP)))
+ {
+ qaddstr("visual ");
+ }
+#endif
+ for (scan = mapped[i].cooked; *scan; scan++)
+ {
+ if (UCHAR(*scan) < ' ' || *scan == '\177')
+ {
+ qaddch('^');
+ qaddch(*scan ^ '@');
+ }
+ else
+ {
+ qaddch(*scan);
+ }
+ }
+
+ addch('\n');
+ exrefresh();
+ }
+}
+
+
+
+#ifndef MKEXRC
+/* This function saves the current configuration of mapped keys to a file */
+void savekeys(fd)
+ int fd; /* file descriptor to save them to */
+{
+ int i;
+ char buf[80];
+
+ /* now write a map command for each key other than the arrows */
+ for (i = 0; i < MAXMAPS; i++)
+ {
+ /* ignore keys that came from termcap */
+ if (mapped[i].name)
+ {
+ continue;
+ }
+
+ /* If this isn't used, ignore it */
+ if (mapped[i].len <= 0)
+ {
+ continue;
+ }
+
+ /* write the map command */
+#ifndef NO_EXTENSIONS
+ if (mapped[i].when & WHEN_INMV)
+ {
+#if OSK
+ char fmt[80];
+ sprintf(fmt, "map%%s %%.%ds %%s\n", mapped[i].len);
+ sprintf(buf, fmt,
+ (mapped[i].when & WHEN_VICMD) ? "" : "!",
+#else
+ sprintf(buf, "map%s %.*s visual %s\n",
+ (mapped[i].when & WHEN_VICMD) ? "" : "!",
+ mapped[i].len,
+#endif
+ mapped[i].rawin,
+ mapped[i].cooked);
+ twrite(fd, buf, strlen(buf));
+ }
+ else
+#endif
+ {
+ if (mapped[i].when & WHEN_VICMD)
+ {
+#if OSK
+ char fmt[80];
+ sprintf(fmt, "map %%.%ds %%s\n", mapped[i].len);
+ sprintf(buf, fmt,
+#else
+ sprintf(buf, "map %.*s %s\n", mapped[i].len,
+#endif
+ mapped[i].rawin,
+ mapped[i].cooked);
+ twrite(fd, buf, strlen(buf));
+ }
+ if (mapped[i].when & (WHEN_VIINP | WHEN_VIREP))
+ {
+#if OSK
+ char fmt[80];
+ sprintf(fmt, "map! %%.%ds %%s\n", mapped[i].len);
+ sprintf(buf, fmt,
+#else
+ sprintf(buf, "map! %.*s %s\n", mapped[i].len,
+#endif
+ mapped[i].rawin,
+ mapped[i].cooked);
+ twrite(fd, buf, strlen(buf));
+ }
+ }
+ }
+}
+#endif
diff --git a/tmp.c b/tmp.c
new file mode 100644
index 0000000..fae0c87
--- /dev/null
+++ b/tmp.c
@@ -0,0 +1,591 @@
+/* tmpfile.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains functions which create & readback a TMPFILE */
+
+
+#include "config.h"
+#include <ctype.h>
+#include "vi.h"
+#if TOS
+# include <stat.h>
+#else
+# if OSK
+# include "osk.h"
+# else
+# include <sys/stat.h>
+# endif
+#endif
+
+
+#ifndef NO_MODELINE
+static void do_modeline(l, stop)
+ long l; /* line number to start at */
+ long stop; /* line number to stop at */
+{
+ char *str; /* used to scan through the line */
+ char *start; /* points to the start of the line */
+ char buf[80];
+
+ /* if modelines are disabled, then do nothing */
+ if (!*o_modeline)
+ {
+ return;
+ }
+
+ /* for each line... */
+ for (l = 1; l <= stop; l++)
+ {
+ /* for each position in the line.. */
+ for (str = fetchline(l); *str; str++)
+ {
+ /* if it is the start of a modeline command... */
+ if ((str[0] == 'e' && str[1] == 'x'
+ || str[0] == 'v' && str[1] == 'i')
+ && str[2] == ':')
+ {
+ start = str += 3;
+
+ /* find the end */
+ while (*str && *str != ':')
+ {
+ str++;
+ }
+
+ /* if it is a well-formed modeline, execute it */
+ if (*str && str - start < sizeof buf)
+ {
+ strncpy(buf, start, (int)(str - start));
+ buf[str - start] = '\0';
+ doexcmd(buf);
+ break;
+ }
+ }
+ }
+ }
+}
+#endif
+
+
+/* The FAIL() macro prints an error message and then exits. */
+#define FAIL(why,arg) mode = MODE_EX; msg(why, arg); endwin(); exit(9)
+
+/* This is the name of the temp file */
+static char tmpname[80];
+
+/* This function creates the temp file and copies the original file into it.
+ * Returns if successful, or stops execution if it fails.
+ */
+int tmpstart(filename)
+ char *filename; /* name of the original file */
+{
+ int origfd; /* fd used for reading the original file */
+ struct stat statb; /* stat buffer, used to examine inode */
+ REG BLK *this; /* pointer to the current block buffer */
+ REG BLK *next; /* pointer to the next block buffer */
+ int inbuf; /* number of characters in a buffer */
+ int nread; /* number of bytes read */
+ REG int j, k;
+ int i;
+ int sum; /* used for calculating a checksum for this */
+ char *scan;
+ long nbytes;
+
+ /* switching to a different file certainly counts as a change */
+ changes++;
+ redraw(MARK_UNSET, FALSE);
+
+ /* open the original file for reading */
+ *origname = '\0';
+ if (filename && *filename)
+ {
+ strcpy(origname, filename);
+ origfd = open(origname, O_RDONLY);
+ if (origfd < 0 && errno != ENOENT)
+ {
+ msg("Can't open \"%s\"", origname);
+ return tmpstart("");
+ }
+ if (origfd >= 0)
+ {
+ if (stat(origname, &statb) < 0)
+ {
+ FAIL("Can't stat \"%s\"", origname);
+ }
+#if TOS
+ if (origfd >= 0 && (statb.st_mode & S_IJDIR))
+#else
+# if OSK
+ if (origfd >= 0 && (statb.st_mode & S_IFDIR))
+# else
+ if (origfd >= 0 && (statb.st_mode & S_IFMT) != S_IFREG)
+# endif
+#endif
+ {
+ msg("\"%s\" is not a regular file", origname);
+ return tmpstart("");
+ }
+ }
+ else
+ {
+ stat(".", &statb);
+ }
+ if (origfd >= 0)
+ {
+ origtime = statb.st_mtime;
+#if MSDOS || OSK
+ if (*o_readonly || !(statb.st_mode & S_IWRITE))
+#endif
+#if TOS
+ if (*o_readonly || (statb.st_mode & S_IJRON))
+#endif
+#if ANY_UNIX
+ if (*o_readonly || !(statb.st_mode &
+ (statb.st_uid != geteuid() ? 0022 : 0200)))
+#endif
+ {
+ setflag(file, READONLY);
+ }
+ }
+ else
+ {
+ origtime = 0L;
+ }
+ }
+ else
+ {
+ setflag(file, NOFILE);
+ origfd = -1;
+ origtime = 0L;
+ stat(".", &statb);
+ }
+
+ /* generate a checksum from the file's name */
+ for (sum = 0, scan = origname + strlen(origname);
+ --scan >= origname && (isascii(*scan) && isalnum(*scan) || *scan == '.');
+ sum = sum + *scan)
+ {
+ }
+ sum &= 0xf;
+
+ /* make a name for the tmp file */
+#if MSDOS || TOS
+ /* MS-Dos doesn't allow multiple slashes, but supports drives
+ * with current directories.
+ * This relies on TMPNAME beginning with "%s\\"!!!!
+ */
+ strcpy(tmpname, o_directory);
+ if ((i = strlen(tmpname)) && !strchr(":/\\", tmpname[i-1]))
+ tmpname[i++]=SLASH;
+ sprintf(tmpname+i, TMPNAME+3, sum, statb.st_ino, statb.st_dev);
+#else
+ sprintf(tmpname, TMPNAME, o_directory, sum, statb.st_ino, statb.st_dev);
+#endif
+
+ /* make sure nobody else is editing the same file */
+ if (access(tmpname, 0) == 0)
+ {
+ if (*origname)
+ {
+ msg("\"%s\" is busy", filename);
+ return tmpstart("");
+ }
+ FAIL("\"%s\" is busy", filename);
+ }
+
+ /* create the temp file */
+#if ANY_UNIX
+ close(creat(tmpname, 0600)); /* only we can read it */
+#else
+ close(creat(tmpname, FILEPERMS)); /* anybody body can read it, alas */
+#endif
+ tmpfd = open(tmpname, O_RDWR | O_BINARY);
+ if (tmpfd < 0)
+ {
+ FAIL("Can't create temporary file, errno=%d", errno);
+ return 1;
+ }
+
+ /* allocate space for the header in the file */
+ write(tmpfd, hdr.c, (unsigned)BLKSIZE);
+
+#ifndef NO_RECYCLE
+ /* initialize the block allocator */
+ /* This must already be done here, before the first attempt
+ * to write to the new file! GB */
+ garbage();
+#endif
+
+ /* initialize lnum[] */
+ for (i = 1; i < MAXBLKS; i++)
+ {
+ lnum[i] = INFINITY;
+ }
+ lnum[0] = 0;
+
+ /* if there is no original file, then create a 1-line file */
+ if (origfd < 0)
+ {
+ hdr.n[0] = 0; /* invalid inode# denotes new file */
+
+ this = blkget(1); /* get the new text block */
+ strcpy(this->c, "\n"); /* put a line in it */
+
+ lnum[1] = 1L; /* block 1 ends with line 1 */
+ nlines = 1L; /* there is 1 line in the file */
+ nbytes = 1L;
+
+ if (*origname)
+ {
+ msg("\"%s\" [NEW FILE] 1 line, 1 char", origname);
+ }
+ else
+ {
+ msg("\"[NO FILE]\" 1 line, 1 char");
+ }
+ }
+ else /* there is an original file -- read it in */
+ {
+ hdr.n[0] = statb.st_ino;
+ nbytes = nlines = 0;
+
+ /* preallocate 1 "next" buffer */
+ i = 1;
+ next = blkget(i);
+ inbuf = 0;
+
+ /* loop, moving blocks from orig to tmp */
+ for (;;)
+ {
+ /* "next" buffer becomes "this" buffer */
+ this = next;
+
+ /* read [more] text into this block */
+ nread = tread(origfd, &this->c[inbuf], BLKSIZE - 1 - inbuf);
+ if (nread < 0)
+ {
+ close(origfd);
+ close(tmpfd);
+ tmpfd = -1;
+ unlink(tmpname);
+ FAIL("Error reading \"%s\"", origname);
+ }
+
+ /* convert NUL characters to something else */
+ for (k = inbuf; k < inbuf + nread; k++)
+ {
+ if (!this->c[k])
+ {
+ setflag(file, HADNUL);
+ this->c[k] = 0x80;
+ }
+ }
+ inbuf += nread;
+
+ /* if the buffer is empty, quit */
+ if (inbuf == 0)
+ {
+ goto FoundEOF;
+ }
+
+#if MSDOS || TOS
+/* BAH! MS text mode read fills inbuf, then compresses eliminating \r
+ but leaving garbage at end of buf. The same is true for TURBOC. GB. */
+
+ memset(this->c + inbuf, '\0', BLKSIZE - inbuf);
+#endif
+
+ /* search backward for last newline */
+ for (k = inbuf; --k >= 0 && this->c[k] != '\n';)
+ {
+ }
+ if (k++ < 0)
+ {
+ if (inbuf >= BLKSIZE - 1)
+ {
+ k = 80;
+ }
+ else
+ {
+ k = inbuf;
+ }
+ }
+
+ /* allocate next buffer */
+ next = blkget(++i);
+
+ /* move fragmentary last line to next buffer */
+ inbuf -= k;
+ for (j = 0; k < BLKSIZE; j++, k++)
+ {
+ next->c[j] = this->c[k];
+ this->c[k] = 0;
+ }
+
+ /* if necessary, add a newline to this buf */
+ for (k = BLKSIZE - inbuf; --k >= 0 && !this->c[k]; )
+ {
+ }
+ if (this->c[k] != '\n')
+ {
+ setflag(file, ADDEDNL);
+ this->c[k + 1] = '\n';
+ }
+
+ /* count the lines in this block */
+ for (k = 0; k < BLKSIZE && this->c[k]; k++)
+ {
+ if (this->c[k] == '\n')
+ {
+ nlines++;
+ }
+ nbytes++;
+ }
+ lnum[i - 1] = nlines;
+ }
+FoundEOF:
+
+ /* if this is a zero-length file, add 1 line */
+ if (nlines == 0)
+ {
+ this = blkget(1); /* get the new text block */
+ strcpy(this->c, "\n"); /* put a line in it */
+
+ lnum[1] = 1; /* block 1 ends with line 1 */
+ nlines = 1; /* there is 1 line in the file */
+ nbytes = 1;
+ }
+
+#if MSDOS || TOS
+ /* each line has an extra CR that we didn't count yet */
+ nbytes += nlines;
+#endif
+
+ /* report the number of lines in the file */
+ msg("\"%s\" %s %ld line%s, %ld char%s",
+ origname,
+ (tstflag(file, READONLY) ? "[READONLY]" : ""),
+ nlines,
+ nlines == 1 ? "" : "s",
+ nbytes,
+ nbytes == 1 ? "" : "s");
+ }
+
+ /* initialize the cursor to start of line 1 */
+ cursor = MARK_FIRST;
+
+ /* close the original file */
+ close(origfd);
+
+ /* any other messages? */
+ if (tstflag(file, HADNUL))
+ {
+ msg("This file contained NULs. They've been changed to \\x80 chars");
+ }
+ if (tstflag(file, ADDEDNL))
+ {
+ msg("Newline characters have been inserted to break up long lines");
+ }
+
+#ifndef NO_MODELINE
+ if (nlines > 10)
+ {
+ do_modeline(1L, 5L);
+ do_modeline(nlines - 4L, nlines);
+ }
+ else
+ {
+ do_modeline(1L, nlines);
+ }
+#endif
+ return 0;
+}
+
+
+
+/* This function copies the temp file back onto an original file.
+ * Returns TRUE if successful, or FALSE if the file could NOT be saved.
+ */
+int tmpsave(filename, bang)
+ char *filename; /* the name to save it to */
+ int bang; /* forced write? */
+{
+ int fd; /* fd of the file we're writing to */
+ REG int len; /* length of a text block */
+ REG BLK *this; /* a text block */
+ long bytes; /* byte counter */
+ REG int i;
+
+ /* if no filename is given, assume the original file name */
+ if (!filename || !*filename)
+ {
+ filename = origname;
+ }
+
+ /* if still no file name, then fail */
+ if (!*filename)
+ {
+ msg("Don't know a name for this file -- NOT WRITTEN");
+ return FALSE;
+ }
+
+ /* can't rewrite a READONLY file */
+ if (!strcmp(filename, origname) && *o_readonly && !bang)
+ {
+ msg("\"%s\" [READONLY] -- NOT WRITTEN", filename);
+ return FALSE;
+ }
+
+ /* open the file */
+ if (*filename == '>' && filename[1] == '>')
+ {
+ filename += 2;
+ while (*filename == ' ' || *filename == '\t')
+ {
+ filename++;
+ }
+#ifdef O_APPEND
+ fd = open(filename, O_WRONLY|O_APPEND);
+#else
+ fd = open(filename, O_WRONLY);
+ lseek(fd, 0L, 2);
+#endif
+ }
+ else
+ {
+ /* either the file must not exist, or it must be the original
+ * file, or we must have a bang
+ */
+ if (strcmp(filename, origname) && access(filename, 0) == 0 && !bang)
+ {
+ msg("File already exists - Use :w! to overwrite");
+ return FALSE;
+ }
+ fd = creat(filename, FILEPERMS);
+ }
+ if (fd < 0)
+ {
+ msg("Can't write to \"%s\" -- NOT WRITTEN", filename);
+ return FALSE;
+ }
+
+ /* write each text block to the file */
+ bytes = 0L;
+ for (i = 1; i < MAXBLKS && (this = blkget(i)) && this->c[0]; i++)
+ {
+ for (len = 0; len < BLKSIZE && this->c[len]; len++)
+ {
+ }
+ twrite(fd, this->c, len);
+ bytes += len;
+ }
+
+ /* reset the "modified" flag */
+ clrflag(file, MODIFIED);
+ significant = FALSE;
+
+ /* report lines & characters */
+#if MSDOS || TOS
+ bytes += nlines; /* for the inserted carriage returns */
+#endif
+ if (strncmp(filename, o_directory, strlen(o_directory)))
+ {
+ msg("Wrote \"%s\" %ld lines, %ld characters", filename, nlines, bytes);
+ }
+
+ /* close the file */
+ close(fd);
+
+ return TRUE;
+}
+
+
+/* This function deletes the temporary file. If the file has been modified
+ * and "bang" is FALSE, then it returns FALSE without doing anything; else
+ * it returns TRUE.
+ *
+ * If the "autowrite" option is set, then instead of returning FALSE when
+ * the file has been modified and "bang" is false, it will call tmpend().
+ */
+int tmpabort(bang)
+ int bang;
+{
+ /* if there is no file, return successfully */
+ if (tmpfd < 0)
+ {
+ return TRUE;
+ }
+
+ /* see if we must return FALSE -- can't quit */
+ if (!bang && tstflag(file, MODIFIED))
+ {
+ /* if "autowrite" is set, then act like tmpend() */
+ if (*o_autowrite)
+ return tmpend(bang);
+ else
+ return FALSE;
+ }
+
+ /* delete the tmp file */
+ cutswitch(tmpname);
+ close(tmpfd);
+ tmpfd = -1;
+ unlink(tmpname);
+ strcpy(prevorig, origname);
+ prevline = markline(cursor);
+ *origname = '\0';
+ origtime = 0L;
+ blkinit();
+ nlines = 0;
+ initflags();
+ return TRUE;
+}
+
+/* This function saves the file if it has been modified, and then deletes
+ * the temporary file. Returns TRUE if successful, or FALSE if the file
+ * needs to be saved but can't be. When it returns FALSE, it will not have
+ * deleted the tmp file, either.
+ */
+int tmpend(bang)
+ int bang;
+{
+ /* save the file if it has been modified */
+ if (tstflag(file, MODIFIED) && !tmpsave((char *)0, FALSE) && !bang)
+ {
+ return FALSE;
+ }
+
+ /* delete the tmp file */
+ tmpabort(TRUE);
+
+ return TRUE;
+}
+
+
+/* If the tmp file has been changed, then this function will force those
+ * changes to be written to the disk, so that the tmp file will survive a
+ * system crash or power failure.
+ */
+#if MSDOS || TOS || OSK
+sync()
+{
+# if OSK
+ /* OS9 doesn't need an explicit sync operation, but the linker
+ * demands something called sync(), so this is a dummy function.
+ */
+#else
+ /* MS-DOS and TOS don't flush their buffers until the file is closed,
+ * so here we close the tmp file and then immediately reopen it.
+ */
+ close(tmpfd);
+ tmpfd = open(tmpname, O_RDWR | O_BINARY);
+#endif
+}
+#endif
diff --git a/vars.c b/vars.c
new file mode 100644
index 0000000..9fc0fc2
--- /dev/null
+++ b/vars.c
@@ -0,0 +1,93 @@
+/* vars.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains variables which weren't happy anyplace else */
+
+#include "config.h"
+#include "vi.h"
+
+/*------------------------------------------------------------------------*/
+
+/* used to remember whether the file has been modified */
+struct _viflags viflags;
+
+/* used to access the tmp file */
+long lnum[MAXBLKS];
+long nlines;
+int tmpfd = -1;
+
+/* used to keep track of the current file & alternate file */
+long origtime;
+char origname[256];
+char prevorig[256];
+long prevline = 1;
+
+/* used to track various places in the text */
+MARK mark[NMARKS]; /* marks 'a through 'z, plus mark '' */
+MARK cursor; /* the cursor position within the file */
+
+/* which mode of the editor we're in */
+int mode; /* vi mode? ex mode? quitting? */
+
+/* used to manage the args list */
+char args[BLKSIZE]; /* list of filenames to edit */
+int argno; /* index of current file in args list */
+int nargs; /* number of filenames in args[] */
+
+/* dummy var, never explicitly referenced */
+int bavar; /* used only in BeforeAfter macros */
+
+/* have we made a multi-line change? */
+int mustredraw; /* must we redraw the whole screen? */
+
+/* used to detect changes that invalidate cached text/blocks */
+long changes; /* incremented when file is changed */
+int significant; /* boolean: was a *REAL* change made? */
+
+/* used to support the pfetch() macro */
+int plen; /* length of the line */
+long pline; /* line number that len refers to */
+long pchgs; /* "changes" level that len refers to */
+char *ptext; /* text of previous line, if valid */
+
+/* misc temporary storage - mostly for strings */
+BLK tmpblk; /* a block used to accumulate changes */
+
+/* screen oriented stuff */
+long topline; /* file line number of top line */
+int leftcol; /* column number of left col */
+int physcol; /* physical column number that cursor is on */
+int physrow; /* physical row number that cursor is on */
+
+/* used to help minimize that "[Hit a key to continue]" message */
+int exwrote; /* Boolean: was the last ex command wordy? */
+
+/* This variable affects the behaviour of certain functions -- most importantly
+ * the input function.
+ */
+int doingdot; /* boolean: are we doing the "." command? */
+
+/* This variable affects the behaviour of the ":s" command, and it is also
+ * used to detect & prohibit nesting of ":g" commands
+ */
+int doingglobal; /* boolean: are doing a ":g" command? */
+/* These are used for reporting multi-line changes to the user */
+long rptlines; /* number of lines affected by a command */
+char *rptlabel; /* description of how lines were affected */
+
+/* These store info that pertains to the shift-U command */
+long U_line; /* line# of the undoable line, or 0l for none */
+char U_text[BLKSIZE]; /* contents of the undoable line */
+
+/* Bigger stack req'ed for TOS */
+
+#if TOS
+long _stksize = 16384;
+#endif
diff --git a/vcmd.c b/vcmd.c
new file mode 100644
index 0000000..7e3aee5
--- /dev/null
+++ b/vcmd.c
@@ -0,0 +1,764 @@
+/* vcmd.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains the functions that handle VI commands */
+
+
+#include "config.h"
+#include "vi.h"
+#if MSDOS
+#include <process.h>
+#include <string.h>
+#endif
+#if TOS
+#include <osbind.h>
+#include <string.h>
+#endif
+#if OSK
+# include <stdio.h>
+#endif
+
+
+/* This function puts the editor in EX mode */
+MARK v_quit()
+{
+ move(LINES - 1, 0);
+ mode = MODE_EX;
+ return cursor;
+}
+
+/* This function causes the screen to be redrawn */
+MARK v_redraw()
+{
+ redraw(MARK_UNSET, FALSE);
+ return cursor;
+}
+
+/* This function executes a single EX command, and waits for a user keystroke
+ * before returning to the VI screen. If that keystroke is another ':', then
+ * another EX command is read and executed.
+ */
+/*ARGSUSED*/
+MARK v_1ex(m, text)
+ MARK m; /* the current line */
+ char *text; /* the first command to execute */
+{
+ /* run the command. be careful about modes & output */
+ exwrote = (mode == MODE_COLON);
+ doexcmd(text);
+ exrefresh();
+
+ /* if mode is no longer MODE_VI, then we should quit right away! */
+ if (mode != MODE_VI && mode != MODE_COLON)
+ return cursor;
+
+ /* The command did some output. Wait for a keystoke. */
+ if (exwrote)
+ {
+ mode = MODE_VI;
+ msg("[Hit <RETURN> to continue]");
+ if (getkey(0) == ':')
+ { mode = MODE_COLON;
+ addch('\n');
+ }
+ else
+ redraw(MARK_UNSET, FALSE);
+ }
+
+ return cursor;
+}
+
+/* This function undoes the last change */
+/*ARGSUSED*/
+MARK v_undo(m)
+ MARK m; /* (ignored) */
+{
+ if (undo())
+ {
+ redraw(MARK_UNSET, FALSE);
+ }
+ return cursor;
+}
+
+/* This function deletes the character(s) that the cursor is on */
+MARK v_xchar(m, cnt, cmd)
+ MARK m; /* where to start deletions */
+ long cnt; /* number of chars to delete */
+ int cmd; /* either 'x' or 'X' */
+{
+ DEFAULT(1);
+
+ /* for 'X', adjust so chars are deleted *BEFORE* cursor */
+ if (cmd == 'X')
+ {
+ if (markidx(m) < cnt)
+ return MARK_UNSET;
+ m -= cnt;
+ }
+
+ /* make sure we don't try to delete more thars than there are */
+ pfetch(markline(m));
+ if (markidx(m + cnt) > plen)
+ {
+ cnt = plen - markidx(m);
+ }
+ if (cnt == 0L)
+ {
+ return MARK_UNSET;
+ }
+
+ /* do it */
+ ChangeText
+ {
+ cut(m, m + cnt);
+ delete(m, m + cnt);
+ }
+ return m;
+}
+
+/* This function defines a mark */
+/*ARGSUSED*/
+MARK v_mark(m, count, key)
+ MARK m; /* where the mark will be */
+ long count; /* (ignored) */
+ int key; /* the ASCII label of the mark */
+{
+ if (key < 'a' || key > 'z')
+ {
+ msg("Marks must be from a to z");
+ }
+ else
+ {
+ mark[key - 'a'] = m;
+ }
+ return m;
+}
+
+/* This function toggles upper & lower case letters */
+MARK v_ulcase(m, cnt)
+ MARK m; /* where to make the change */
+ long cnt; /* number of chars to flip */
+{
+ REG char *pos;
+ REG int i, j;
+ static char flip[] =
+ "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ[](){}<>";
+
+ DEFAULT(1);
+
+ /* fetch the current version of the line */
+ pfetch(markline(m));
+
+ /* for each position in the line */
+ for (j = 0, i = markidx(m); j < cnt && ptext[i]; j++, i++)
+ {
+ tmpblk.c[j] = 0;
+
+ /* one of the standard chars? */
+ for (pos = flip; *pos && *pos != ptext[i]; pos++)
+ {
+ }
+ if (*pos)
+ {
+ tmpblk.c[j] = flip[(int)(pos - flip) ^ 1];
+ }
+#ifndef NO_DIGRAPH
+ else /* one of the non-standard chars? */
+ {
+ for (pos = o_flipcase; *pos && *pos != ptext[i]; pos++)
+ {
+ }
+ if (*pos)
+ {
+ tmpblk.c[j] = o_flipcase[(int)(pos - o_flipcase) ^ 1];
+ }
+ }
+#endif
+
+ /* if nothing special, then don't change it */
+ if (tmpblk.c[j] == 0)
+ {
+ tmpblk.c[j] = ptext[i];
+ }
+ }
+
+ /* if the new text is different from the old, then change it */
+ if (strncmp(tmpblk.c, &ptext[markidx(m)], j))
+ {
+ ChangeText
+ {
+ tmpblk.c[j] = '\0';
+ change(m, m + j, tmpblk.c);
+ }
+ }
+
+ return m + j;
+}
+
+
+MARK v_replace(m, cnt, key)
+ MARK m; /* first char to be replaced */
+ long cnt; /* number of chars to replace */
+ int key; /* what to replace them with */
+{
+ REG char *text;
+ REG int i;
+
+ DEFAULT(1);
+
+ /* map ^M to '\n' */
+ if (key == '\r')
+ {
+ key = '\n';
+ }
+
+ /* make sure the resulting line isn't too long */
+ if (cnt > BLKSIZE - 2 - markidx(m))
+ {
+ cnt = BLKSIZE - 2 - markidx(m);
+ }
+
+ /* build a string of the desired character with the desired length */
+ for (text = tmpblk.c, i = cnt; i > 0; i--)
+ {
+ *text++ = key;
+ }
+ *text = '\0';
+
+ /* make sure cnt doesn't extend past EOL */
+ pfetch(markline(m));
+ key = markidx(m);
+ if (key + cnt > plen)
+ {
+ cnt = plen - key;
+ }
+
+ /* do the replacement */
+ ChangeText
+ {
+ change(m, m + cnt, tmpblk.c);
+ }
+
+ if (*tmpblk.c == '\n')
+ {
+ return (m & ~(BLKSIZE - 1)) + cnt * BLKSIZE;
+ }
+ else
+ {
+ return m + cnt - 1;
+ }
+}
+
+MARK v_overtype(m)
+ MARK m; /* where to start overtyping */
+{
+ MARK end; /* end of a substitution */
+ static long width; /* width of a single-line replace */
+
+ /* the "doingdot" version of replace is really a substitution */
+ if (doingdot)
+ {
+ /* was the last one really repeatable? */
+ if (width < 0)
+ {
+ msg("Can't repeat a multi-line overtype command");
+ return MARK_UNSET;
+ }
+
+ /* replacing nothing by nothing? Don't bother */
+ if (width == 0)
+ {
+ return m;
+ }
+
+ /* replace some chars by repeated text */
+ return v_subst(m, width);
+ }
+
+ /* Normally, we input starting here, in replace mode */
+ ChangeText
+ {
+ end = input(m, m, WHEN_VIREP);
+ }
+
+ /* if we ended on the same line we started on, then this
+ * overtype is repeatable via the dot key.
+ */
+ if (markline(end) == markline(m) && end >= m - 1L)
+ {
+ width = end - m + 1L;
+ }
+ else /* it isn't repeatable */
+ {
+ width = -1L;
+ }
+
+ return end;
+}
+
+
+/* This function selects which cut buffer to use */
+/*ARGSUSED*/
+MARK v_selcut(m, cnt, key)
+ MARK m;
+ long cnt;
+ int key;
+{
+ cutname(key);
+ return m;
+}
+
+/* This function pastes text from a cut buffer */
+/*ARGSUSED*/
+MARK v_paste(m, cnt, cmd)
+ MARK m; /* where to paste the text */
+ long cnt; /* (ignored) */
+ int cmd; /* either 'p' or 'P' */
+{
+ ChangeText
+ {
+ m = paste(m, cmd == 'p', FALSE);
+ }
+ return m;
+}
+
+/* This function yanks text into a cut buffer */
+MARK v_yank(m, n)
+ MARK m, n; /* range of text to yank */
+{
+ cut(m, n);
+ return m;
+}
+
+/* This function deletes a range of text */
+MARK v_delete(m, n)
+ MARK m, n; /* range of text to delete */
+{
+ /* illegal to try and delete nothing */
+ if (n <= m)
+ {
+ return MARK_UNSET;
+ }
+
+ /* Do it */
+ ChangeText
+ {
+ cut(m, n);
+ delete(m, n);
+ }
+ return m;
+}
+
+
+/* This starts input mode without deleting anything */
+MARK v_insert(m, cnt, key)
+ MARK m; /* where to start (sort of) */
+ long cnt; /* repeat how many times? */
+ int key; /* what command is this for? {a,A,i,I,o,O} */
+{
+ int wasdot;
+ long reps;
+ int after; /* are we appending or inserting? */
+
+ DEFAULT(1);
+
+ ChangeText
+ {
+ /* tweak the insertion point, based on command key */
+ switch (key)
+ {
+ case 'i':
+ after = FALSE;
+ break;
+
+ case 'a':
+ pfetch(markline(m));
+ if (plen > 0)
+ {
+ m++;
+ }
+ after = TRUE;
+ break;
+
+ case 'I':
+ m = m_front(m, 1L);
+ after = FALSE;
+ break;
+
+ case 'A':
+ pfetch(markline(m));
+ m = (m & ~(BLKSIZE - 1)) + plen;
+ after = TRUE;
+ break;
+
+ case 'O':
+ m &= ~(BLKSIZE - 1);
+ add(m, "\n");
+ after = FALSE;
+ break;
+
+ case 'o':
+ m = (m & ~(BLKSIZE - 1)) + BLKSIZE;
+ add(m, "\n");
+ after = FALSE;
+ break;
+ }
+
+ /* insert the same text once or more */
+ for (reps = cnt, wasdot = doingdot; reps > 0; reps--, doingdot = TRUE)
+ {
+ m = input(m, m, WHEN_VIINP);
+ if (after)
+ {
+ m++;
+ }
+ }
+ if (after)
+ {
+ m--;
+ }
+
+ doingdot = wasdot;
+ }
+
+#ifndef CRUNCH
+# ifndef NO_EXTENSIONS
+ if (key == 'i' && *o_inputmode && mode == MODE_VI)
+ {
+ msg("Now in visual command mode! To return to input mode, hit <i>.");
+ }
+# endif
+#endif
+
+ return m;
+}
+
+/* This starts input mode with some text deleted */
+MARK v_change(m, n)
+ MARK m, n; /* the range of text to change */
+{
+ int lnmode; /* is this a line-mode change? */
+
+ /* swap them if they're in reverse order */
+ if (m > n)
+ {
+ MARK tmp;
+ tmp = m;
+ m = n;
+ n = tmp;
+ }
+
+ /* for line mode, retain the last newline char */
+ lnmode = (markidx(m) == 0 && markidx(n) == 0 && m != n);
+ if (lnmode)
+ {
+ n -= BLKSIZE;
+ pfetch(markline(n));
+ n = (n & ~(BLKSIZE - 1)) + plen;
+ }
+
+ ChangeText
+ {
+ cut(m, n);
+ m = input(m, n, WHEN_VIINP);
+ }
+
+ return m;
+}
+
+/* This function replaces a given number of characters with input */
+MARK v_subst(m, cnt)
+ MARK m; /* where substitutions start */
+ long cnt; /* number of chars to replace */
+{
+ DEFAULT(1);
+
+ /* make sure we don't try replacing past EOL */
+ pfetch(markline(m));
+ if (markidx(m) + cnt > plen)
+ {
+ cnt = plen - markidx(m);
+ }
+
+ /* Go for it! */
+ ChangeText
+ {
+ cut(m, m + cnt);
+ m = input(m, m + cnt, WHEN_VIINP);
+ }
+ return m;
+}
+
+/* This calls the ex "join" command to join some lines together */
+MARK v_join(m, cnt)
+ MARK m; /* the first line to be joined */
+ long cnt; /* number of other lines to join */
+{
+ MARK joint; /* where the lines were joined */
+
+ DEFAULT(1);
+
+ /* figure out where the joint will be */
+ pfetch(markline(m));
+ joint = (m & ~(BLKSIZE - 1)) + plen;
+
+ /* join the lines */
+ cmd_join(m, m + MARK_AT_LINE(cnt), CMD_JOIN, 0, "");
+ mustredraw = TRUE;
+
+ /* the cursor should be left at the joint */
+ return joint;
+}
+
+/* This calls the ex shifter command to shift some lines */
+static MARK shift_help(m, n, excmd)
+ MARK m, n; /* range of lines to shift */
+ CMD excmd; /* which way do we shift? */
+{
+ /* adjust for inclusive endmarks in ex */
+ n -= BLKSIZE;
+
+ cmd_shift(m, n, excmd, 0, "");
+ return m;
+}
+
+/* This calls the ex "<" command to shift some lines left */
+MARK v_lshift(m, n)
+ MARK m, n; /* range of lines to shift */
+{
+ return shift_help(m, n, CMD_SHIFTL);
+}
+
+/* This calls the ex ">" command to shift some lines right */
+MARK v_rshift(m, n)
+ MARK m, n; /* range of lines to shift */
+{
+ return shift_help(m, n, CMD_SHIFTR);
+}
+
+/* This runs some lines through a filter program */
+MARK v_filter(m, n)
+ MARK m, n; /* range of lines to shift */
+{
+ char cmdln[100]; /* a shell command line */
+
+ /* adjust for inclusive endmarks in ex */
+ n -= BLKSIZE;
+
+ if (vgets('!', cmdln, sizeof(cmdln)) > 0)
+ {
+ filter(m, n, cmdln);
+ }
+
+ redraw(MARK_UNSET, FALSE);
+ return m;
+}
+
+
+/* This function runs the ex "file" command to show the file's status */
+MARK v_status()
+{
+ cmd_file(cursor, cursor, CMD_FILE, 0, "");
+ return cursor;
+}
+
+
+/* This function runs the ":&" command to repeat the previous :s// */
+MARK v_again(m, n)
+ MARK m, n;
+{
+ cmd_substitute(m, n - BLKSIZE, CMD_SUBAGAIN, TRUE, "");
+ return cursor;
+}
+
+
+
+/* This function switches to the previous file, if possible */
+MARK v_switch()
+{
+ if (!*prevorig)
+ msg("No previous file");
+ else
+ { strcpy(tmpblk.c, prevorig);
+ cmd_edit(cursor, cursor, CMD_EDIT, 0, tmpblk.c);
+ }
+ return cursor;
+}
+
+/* This function does a tag search on a keyword */
+/*ARGSUSED*/
+MARK v_tag(keyword, m, cnt)
+ char *keyword;
+ MARK m;
+ long cnt;
+{
+ /* move the cursor to the start of the tag name, where m is */
+ cursor = m;
+
+ /* perform the tag search */
+ cmd_tag(cursor, cursor, CMD_TAG, 0, keyword);
+
+ return cursor;
+}
+
+#ifndef NO_EXTENSIONS
+/* This function looks up a keyword by calling the helpprog program */
+/*ARGSUSED*/
+MARK v_keyword(keyword, m, cnt)
+ char *keyword;
+ MARK m;
+ long cnt;
+{
+ int waswarn;
+ char cmdline[130];
+
+ move(LINES - 1, 0);
+ addstr("---------------------------------------------------------\n");
+ clrtoeol();
+ refresh();
+ sprintf(cmdline, "%s %s", o_keywordprg, keyword);
+ waswarn = *o_warn;
+ *o_warn = FALSE;
+ suspend_curses();
+ if (system(cmdline))
+ {
+ addstr("<<< failed >>>\n");
+ }
+ resume_curses(FALSE);
+ mode = MODE_VI;
+ redraw(MARK_UNSET, FALSE);
+ *o_warn = waswarn;
+
+ return m;
+}
+
+
+
+MARK v_increment(keyword, m, cnt)
+ char *keyword;
+ MARK m;
+ long cnt;
+{
+ static sign;
+ char newval[12];
+ long atol();
+
+ DEFAULT(1);
+
+ /* get one more keystroke, unless doingdot */
+ if (!doingdot)
+ {
+ sign = getkey(0);
+ }
+
+ /* adjust the number, based on that second keystroke */
+ switch (sign)
+ {
+ case '+':
+ case '#':
+ cnt = atol(keyword) + cnt;
+ break;
+
+ case '-':
+ cnt = atol(keyword) - cnt;
+ break;
+
+ case '=':
+ break;
+
+ default:
+ return MARK_UNSET;
+ }
+ sprintf(newval, "%ld", cnt);
+
+ ChangeText
+ {
+ change(m, m + strlen(keyword), newval);
+ }
+
+ return m;
+}
+#endif
+
+
+/* This function acts like the EX command "xit" */
+/*ARGSUSED*/
+MARK v_xit(m, cnt, key)
+ MARK m; /* ignored */
+ long cnt; /* ignored */
+ int key; /* must be a second 'Z' */
+{
+ /* if second char wasn't 'Z', fail */
+ if (key != 'Z')
+ {
+ return MARK_UNSET;
+ }
+
+ /* move the cursor to the bottom of the screen */
+ move(LINES - 1, 0);
+ clrtoeol();
+
+ /* do the xit command */
+ cmd_xit(m, m, CMD_XIT, FALSE, "");
+
+ /* return the cursor */
+ return m;
+}
+
+
+/* This function undoes changes to a single line, if possible */
+MARK v_undoline(m)
+ MARK m; /* where we hope to undo the change */
+{
+ /* make sure we have the right line in the buffer */
+ if (markline(m) != U_line)
+ {
+ return MARK_UNSET;
+ }
+
+ /* fix it */
+ ChangeText
+ {
+ strcat(U_text, "\n");
+ change(MARK_AT_LINE(U_line), MARK_AT_LINE(U_line + 1), U_text);
+ }
+
+ /* nothing in the buffer anymore */
+ U_line = -1L;
+
+ /* return, with the cursor at the front of the line */
+ return m & ~(BLKSIZE - 1);
+}
+
+
+#ifndef NO_ERRLIST
+MARK v_errlist(m)
+ MARK m;
+{
+ cmd_errlist(m, m, CMD_ERRLIST, FALSE, "");
+ return cursor;
+}
+#endif
+
+
+#ifndef NO_AT
+/*ARGSUSED*/
+MARK v_at(m, cnt, key)
+ MARK m;
+ long cnt;
+ int key;
+{
+ if (!fromcutbuf(key))
+ {
+ return MARK_UNSET;
+ }
+ return cursor;
+}
+#endif
diff --git a/vi.c b/vi.c
new file mode 100644
index 0000000..9c55bbb
--- /dev/null
+++ b/vi.c
@@ -0,0 +1,767 @@
+/* vi.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+#include "config.h"
+#include <ctype.h>
+#include "vi.h"
+
+
+
+/* This array describes what each key does */
+#define NO_FUNC (MARK (*)())0
+#define NO_ARGS 0
+#define CURSOR_COUNT 1
+#define CURSOR 2
+#define CURSOR_CNT_KEY 3
+#define CURSOR_MOVED 4
+#define CURSOR_EOL 5
+#define ZERO 6
+#define DIGIT 7
+#define CURSOR_TEXT 8
+#define CURSOR_CNT_CMD 9
+#define KEYWORD 10
+#define NO_FLAGS 0x00
+#define MVMT 0x01 /* this is a movement command */
+#define PTMV 0x02 /* this can be *part* of a movement command */
+#define FRNT 0x04 /* after move, go to front of line */
+#define INCL 0x08 /* include last char when used with c/d/y */
+#define LNMD 0x10 /* use line mode of c/d/y */
+#define NCOL 0x20 /* this command can't change the column# */
+#define NREL 0x40 /* this is "non-relative" -- set the '' mark */
+#define SDOT 0x80 /* set the "dot" variables, for the "." cmd */
+static struct keystru
+{
+ MARK (*func)(); /* the function to run */
+ uchar args; /* description of the args needed */
+ uchar flags; /* other stuff */
+}
+ vikeys[] =
+{
+/* NUL not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* ^A not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* ^B page backward */ {m_scroll, CURSOR_CNT_CMD, FRNT},
+/* ^C not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* ^D scroll dn 1/2page*/ {m_scroll, CURSOR_CNT_CMD, NCOL},
+/* ^E scroll up */ {m_scroll, CURSOR_CNT_CMD, NCOL},
+/* ^F page forward */ {m_scroll, CURSOR_CNT_CMD, FRNT},
+/* ^G show file status */ {v_status, NO_ARGS, NO_FLAGS},
+/* ^H move left, like h*/ {m_left, CURSOR_COUNT, MVMT},
+/* ^I not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* ^J move down */ {m_updnto, CURSOR_CNT_CMD, MVMT|LNMD},
+/* ^K not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* ^L redraw screen */ {v_redraw, NO_ARGS, NO_FLAGS},
+/* ^M mv front next ln */ {m_updnto, CURSOR_CNT_CMD, MVMT|FRNT|LNMD},
+/* ^N move down */ {m_updnto, CURSOR_CNT_CMD, MVMT|LNMD},
+/* ^O not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* ^P move up */ {m_updnto, CURSOR_CNT_CMD, MVMT|LNMD},
+/* ^Q not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* ^R redraw screen */ {v_redraw, NO_ARGS, NO_FLAGS},
+/* ^S not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* ^T not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* ^U scroll up 1/2page*/ {m_scroll, CURSOR_CNT_CMD, NCOL},
+/* ^V not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* ^W not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* ^X not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* ^Y scroll down */ {m_scroll, CURSOR_CNT_CMD, NCOL},
+/* ^Z not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* ESC not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* ^\ not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* ^] keyword is tag */ {v_tag, KEYWORD, NO_FLAGS},
+/* ^^ previous file */ {v_switch, CURSOR, NO_FLAGS},
+/* ^_ not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* SPC move right,like l*/ {m_right, CURSOR_COUNT, MVMT},
+/* ! run thru filter */ {v_filter, CURSOR_MOVED, FRNT|LNMD|INCL},
+/* " select cut buffer*/ {v_selcut, CURSOR_CNT_KEY, PTMV},
+#ifndef NO_EXTENSIONS
+/* # increment number */ {v_increment, KEYWORD, SDOT},
+#else
+/* # not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+#endif
+/* $ move to rear */ {m_rear, CURSOR, MVMT|INCL},
+/* % move to match */ {m_match, CURSOR, MVMT|INCL},
+/* & repeat subst */ {v_again, CURSOR_MOVED, SDOT|NCOL|LNMD|INCL},
+/* ' move to a mark */ {m_tomark, CURSOR_CNT_KEY, MVMT|FRNT|NREL|LNMD|INCL},
+#ifndef NO_SENTENCE
+/* ( mv back sentence */ {m_bsentence, CURSOR_COUNT, MVMT},
+/* ) mv fwd sentence */ {m_fsentence, CURSOR_COUNT, MVMT},
+#else
+/* ( not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* ) not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+#endif
+#ifndef NO_ERRLIST
+/* * errlist */ {v_errlist, CURSOR, FRNT|NREL},
+#else
+/* * not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+#endif
+/* + mv front next ln */ {m_updnto, CURSOR_CNT_CMD, MVMT|FRNT|LNMD},
+#ifndef NO_CHARSEARCH
+/* , reverse [fFtT] cmd*/ {m__ch, CURSOR_CNT_CMD, MVMT|INCL},
+#else
+/* , not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+#endif
+/* - mv front prev ln */ {m_updnto, CURSOR_CNT_CMD, MVMT|FRNT|LNMD},
+/* . special... */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* / forward search */ {m_fsrch, CURSOR_TEXT, MVMT|NREL},
+/* 0 part of count? */ {NO_FUNC, ZERO, MVMT|PTMV},
+/* 1 part of count */ {NO_FUNC, DIGIT, PTMV},
+/* 2 part of count */ {NO_FUNC, DIGIT, PTMV},
+/* 3 part of count */ {NO_FUNC, DIGIT, PTMV},
+/* 4 part of count */ {NO_FUNC, DIGIT, PTMV},
+/* 5 part of count */ {NO_FUNC, DIGIT, PTMV},
+/* 6 part of count */ {NO_FUNC, DIGIT, PTMV},
+/* 7 part of count */ {NO_FUNC, DIGIT, PTMV},
+/* 8 part of count */ {NO_FUNC, DIGIT, PTMV},
+/* 9 part of count */ {NO_FUNC, DIGIT, PTMV},
+/* : run single EX cmd*/ {v_1ex, CURSOR_TEXT, NO_FLAGS},
+#ifndef NO_CHARSEARCH
+/* ; repeat [fFtT] cmd*/ {m__ch, CURSOR_CNT_CMD, MVMT|INCL},
+#else
+/* ; not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+#endif
+/* < shift text left */ {v_lshift, CURSOR_MOVED, SDOT|FRNT|LNMD|INCL},
+/* = not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* > shift text right */ {v_rshift, CURSOR_MOVED, SDOT|FRNT|LNMD|INCL},
+/* ? backward search */ {m_bsrch, CURSOR_TEXT, MVMT|NREL},
+#ifndef NO_AT
+/* @ execute a cutbuf */ {v_at, CURSOR_CNT_KEY, NO_FLAGS},
+#else
+/* @ undefined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+#endif
+/* A append at EOL */ {v_insert, CURSOR_CNT_CMD, SDOT},
+/* B move back Word */ {m_bword, CURSOR_CNT_CMD, MVMT},
+/* C change to EOL */ {v_change, CURSOR_EOL, SDOT},
+/* D delete to EOL */ {v_delete, CURSOR_EOL, SDOT},
+/* E move end of Word */ {m_eword, CURSOR_CNT_CMD, MVMT|INCL},
+#ifndef NO_CHARSEARCH
+/* F move bk to char */ {m_Fch, CURSOR_CNT_KEY, MVMT|INCL},
+#else
+/* F not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+#endif
+/* G move to line # */ {m_updnto, CURSOR_CNT_CMD, MVMT|NREL|LNMD|FRNT|INCL},
+/* H move to row */ {m_row, CURSOR_CNT_CMD, MVMT|FRNT},
+/* I insert at front */ {v_insert, CURSOR_CNT_CMD, SDOT},
+/* J join lines */ {v_join, CURSOR_COUNT, SDOT},
+#ifndef NO_EXTENSIONS
+/* K look up keyword */ {v_keyword, KEYWORD, NO_FLAGS},
+#else
+/* K not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+#endif
+/* L move to last row */ {m_row, CURSOR_CNT_CMD, MVMT|FRNT},
+/* M move to mid row */ {m_row, CURSOR_CNT_CMD, MVMT|FRNT},
+/* N reverse prev srch*/ {m_Nsrch, CURSOR, MVMT},
+/* O insert above line*/ {v_insert, CURSOR_CNT_CMD, SDOT},
+/* P paste before */ {v_paste, CURSOR_CNT_CMD, NO_FLAGS},
+/* Q quit to EX mode */ {v_quit, NO_ARGS, NO_FLAGS},
+/* R overtype */ {v_overtype, CURSOR, SDOT},
+/* S change line */ {v_change, CURSOR_MOVED, SDOT},
+#ifndef NO_CHARSEARCH
+/* T move bk to char */ {m_Tch, CURSOR_CNT_KEY, MVMT|INCL},
+#else
+/* T not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+#endif
+/* U undo whole line */ {v_undoline, CURSOR, FRNT},
+/* V not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* W move forward Word*/ {m_fword, CURSOR_CNT_CMD, MVMT},
+/* X delete to left */ {v_xchar, CURSOR_CNT_CMD, SDOT},
+/* Y yank text */ {v_yank, CURSOR_MOVED, NCOL},
+/* Z save file & exit */ {v_xit, CURSOR_CNT_KEY, NO_FLAGS},
+/* [ move back section*/ {m_bsection, CURSOR_CNT_KEY, MVMT|LNMD|NREL},
+/* \ not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* ] move fwd section */ {m_fsection, CURSOR_CNT_KEY, MVMT|LNMD|NREL},
+/* ^ move to front */ {m_front, CURSOR, MVMT},
+/* _ not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* ` move to mark */ {m_tomark, CURSOR_CNT_KEY, MVMT|NREL},
+/* a append at cursor */ {v_insert, CURSOR_CNT_CMD, SDOT},
+/* b move back word */ {m_bword, CURSOR_CNT_CMD, MVMT},
+/* c change text */ {v_change, CURSOR_MOVED, SDOT},
+/* d delete op */ {v_delete, CURSOR_MOVED, SDOT|NCOL},
+/* e move end word */ {m_eword, CURSOR_CNT_CMD, MVMT|INCL},
+#ifndef NO_CHARSEARCH
+/* f move fwd for char*/ {m_fch, CURSOR_CNT_KEY, MVMT|INCL},
+#else
+/* f not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+#endif
+/* g not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* h move left */ {m_left, CURSOR_COUNT, MVMT},
+/* i insert at cursor */ {v_insert, CURSOR_CNT_CMD, SDOT},
+/* j move down */ {m_updnto, CURSOR_CNT_CMD, MVMT|NCOL|LNMD},
+/* k move up */ {m_updnto, CURSOR_CNT_CMD, MVMT|NCOL|LNMD},
+/* l move right */ {m_right, CURSOR_COUNT, MVMT},
+/* m define a mark */ {v_mark, CURSOR_CNT_KEY, NO_FLAGS},
+/* n repeat prev srch */ {m_nsrch, CURSOR, MVMT},
+/* o insert below line*/ {v_insert, CURSOR_CNT_CMD, SDOT},
+/* p paste after */ {v_paste, CURSOR_CNT_CMD, NO_FLAGS},
+/* q not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* r replace chars */ {v_replace, CURSOR_CNT_KEY, SDOT},
+/* s subst N chars */ {v_subst, CURSOR_COUNT, SDOT},
+#ifndef NO_CHARSEARCH
+/* t move fwd to char */ {m_tch, CURSOR_CNT_KEY, MVMT|INCL},
+#else
+/* t not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+#endif
+/* u undo */ {v_undo, CURSOR, NO_FLAGS},
+/* v not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS},
+/* w move fwd word */ {m_fword, CURSOR_CNT_CMD, MVMT},
+/* x delete character */ {v_xchar, CURSOR_CNT_CMD, SDOT},
+/* y yank text */ {v_yank, CURSOR_MOVED, NCOL},
+/* z adjust scrn row */ {m_z, CURSOR_CNT_KEY, NCOL},
+/* { back paragraph */ {m_bparagraph, CURSOR_COUNT, MVMT|LNMD},
+/* | move to column */ {m_tocol, CURSOR_COUNT, NREL},
+/* } fwd paragraph */ {m_fparagraph, CURSOR_COUNT, MVMT|LNMD},
+/* ~ upper/lowercase */ {v_ulcase, CURSOR_COUNT, SDOT},
+/* DEL not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}
+};
+
+
+
+void vi()
+{
+ REG int key; /* keystroke from user */
+ long count; /* numeric argument to some functions */
+ REG struct keystru *keyptr;/* pointer to vikeys[] element */
+ MARK tcurs; /* temporary cursor */
+ int prevkey;/* previous key, if d/c/y/</>/! */
+ MARK range; /* start of range for d/c/y/</>/! */
+ char text[100];
+ int dotkey; /* last "key" of a change */
+ int dotpkey;/* last "prevkey" of a change */
+ int dotkey2;/* last extra "getkey()" of a change */
+ int dotcnt; /* last "count" of a change */
+ int firstkey;
+ REG int i;
+
+ /* tell the redraw() function to start from scratch */
+ redraw(MARK_UNSET, FALSE);
+
+#ifdef lint
+ /* lint says that "range" might be used before it is set. This
+ * can't really happen due to the way "range" and "prevkey" are used,
+ * but lint doesn't know that. This line is here ONLY to keep lint
+ * happy.
+ */
+ range = 0L;
+#endif
+
+ /* safeguard against '.' with no previous command */
+ dotkey = 0;
+
+ /* go immediately into insert mode, if ":set inputmode" */
+ firstkey = 0;
+#ifndef NO_EXTENSIONS
+ if (*o_inputmode)
+ {
+ firstkey = 'i';
+ }
+#endif
+
+ /* Repeatedly handle VI commands */
+ for (count = 0, prevkey = '\0'; mode == MODE_VI; )
+ {
+ /* if we've moved off the undoable line, then we can't undo it at all */
+ if (markline(cursor) != U_line)
+ {
+ U_line = 0L;
+ }
+
+ /* report any changes from the previous command */
+ if (rptlines >= *o_report)
+ {
+ redraw(cursor, FALSE);
+ msg("%ld lines %s", rptlines, rptlabel);
+ }
+ rptlines = 0L;
+
+ /* get the next command key. It must be ASCII */
+ if (firstkey)
+ {
+ key = firstkey;
+ firstkey = 0;
+ }
+ else
+ {
+ do
+ {
+ key = getkey(WHEN_VICMD);
+ } while (key < 0 || key > 127);
+ }
+
+ /* change cw and cW commands to ce and cE, respectively */
+ /* (Why? because the real vi does it that way!) */
+ if (prevkey == 'c')
+ {
+ if (key == 'w')
+ key = 'e';
+ else if (key == 'W')
+ key = 'E';
+
+ /* wouldn't work right at the end of a word unless we
+ * backspace one character before doing the move. This
+ * will fix most cases. !!! but not all.
+ */
+ if (markidx(cursor) > 0 && (key == 'e' || key == 'E'))
+ {
+ cursor--;
+ }
+ }
+
+ /* look up the structure describing this command */
+ keyptr = &vikeys[key];
+
+ /* if we're in the middle of a d/c/y/</>/! command, reject
+ * anything but movement or a doubled version like "dd".
+ */
+ if (prevkey && key != prevkey && !(keyptr->flags & (MVMT|PTMV)))
+ {
+ beep();
+ prevkey = 0;
+ count = 0;
+ continue;
+ }
+
+ /* set the "dot" variables, if we're supposed to */
+ if ((keyptr->flags & SDOT)
+ || (prevkey && vikeys[prevkey].flags & SDOT))
+ {
+ dotkey = key;
+ dotpkey = prevkey;
+ dotkey2 = '\0';
+ dotcnt = count;
+
+ /* remember the line before any changes are made */
+ if (U_line != markline(cursor))
+ {
+ U_line = markline(cursor);
+ strcpy(U_text, fetchline(U_line));
+ }
+ }
+
+ /* if this is "." then set other vars from the "dot" vars */
+ if (key == '.')
+ {
+ key = dotkey;
+ keyptr = &vikeys[key];
+ prevkey = dotpkey;
+ if (prevkey)
+ {
+ range = cursor;
+ }
+ if (count == 0)
+ {
+ count = dotcnt;
+ }
+ doingdot = TRUE;
+
+ /* remember the line before any changes are made */
+ if (U_line != markline(cursor))
+ {
+ U_line = markline(cursor);
+ strcpy(U_text, fetchline(U_line));
+ }
+ }
+ else
+ {
+ doingdot = FALSE;
+ }
+
+ /* process the key as a command */
+ tcurs = cursor;
+ switch (keyptr->args)
+ {
+ case ZERO:
+ if (count == 0)
+ {
+ tcurs = cursor & ~(BLKSIZE - 1);
+ break;
+ }
+ /* else fall through & treat like other digits... */
+
+ case DIGIT:
+ count = count * 10 + key - '0';
+ break;
+
+ case KEYWORD:
+ /* if not on a keyword, fail */
+ pfetch(markline(cursor));
+ key = markidx(cursor);
+ if (isascii(ptext[key])
+ && !isalnum(ptext[key]) && ptext[key] != '_')
+ {
+ tcurs = MARK_UNSET;
+ break;
+ }
+
+ /* find the start of the keyword */
+ while (key > 0 && (!isascii(ptext[key-1]) ||
+ isalnum(ptext[key - 1]) || ptext[key - 1] == '_'))
+ {
+ key--;
+ }
+ tcurs = (cursor & ~(BLKSIZE - 1)) + key;
+
+ /* copy it into a buffer, and NUL-terminate it */
+ i = 0;
+ do
+ {
+ text[i++] = ptext[key++];
+ } while (!isascii(ptext[key]) || isalnum(ptext[key]) || ptext[key] == '_');
+ text[i] = '\0';
+
+ /* call the function */
+ tcurs = (*keyptr->func)(text, tcurs, count);
+ count = 0L;
+ break;
+
+ case NO_ARGS:
+ if (keyptr->func)
+ {
+ (*keyptr->func)();
+ }
+ else
+ {
+ beep();
+ }
+ count = 0L;
+ break;
+
+ case CURSOR_COUNT:
+ tcurs = (*keyptr->func)(cursor, count);
+ count = 0L;
+ break;
+
+ case CURSOR:
+ tcurs = (*keyptr->func)(cursor);
+ count = 0L;
+ break;
+
+ case CURSOR_CNT_KEY:
+ if (doingdot)
+ {
+ tcurs = (*keyptr->func)(cursor, count, dotkey2);
+ }
+ else
+ {
+ /* get a key */
+ i = getkey(0);
+ if (i == '\033') /* ESC */
+ {
+ count = 0;
+ tcurs = MARK_UNSET;
+ break; /* exit from "case CURSOR_CNT_KEY" */
+ }
+ else if (i == ('V' & 0x1f))
+ {
+ i = getkey(0);
+ }
+
+ /* if part of an SDOT command, remember it */
+ if (keyptr->flags & SDOT
+ || (prevkey && vikeys[prevkey].flags & SDOT))
+ {
+ dotkey2 = i;
+ }
+
+ /* do it */
+ tcurs = (*keyptr->func)(cursor, count, i);
+ }
+ count = 0L;
+ break;
+
+ case CURSOR_MOVED:
+ /* '&' and uppercase keys always act like doubled */
+ if (key == '&' || isascii(key) && isupper(key))
+ {
+ prevkey = key;
+ }
+
+ if (prevkey)
+ {
+ /* doubling up a command */
+ if (!count) count = 1L;
+ range = cursor;
+ tcurs = range + MARK_AT_LINE(count - 1L);
+ count = 0L;
+ }
+ else
+ {
+ prevkey = key;
+ range = cursor;
+ key = -1; /* so we don't think we doubled yet */
+ }
+ break;
+
+ case CURSOR_EOL:
+ prevkey = key;
+ /* a zero-length line needs special treatment */
+ pfetch(markline(cursor));
+ if (plen == 0)
+ {
+ /* act on a zero-length section of text */
+ range = tcurs = cursor;
+ key = ' ';
+ }
+ else
+ {
+ /* act like CURSOR_MOVED with '$' movement */
+ range = cursor;
+ tcurs = m_rear(cursor, 1L);
+ key = '$';
+ }
+ count = 0L;
+ keyptr = &vikeys[key];
+ break;
+
+ case CURSOR_TEXT:
+ do
+ {
+ text[0] = key;
+ if (vgets(key, text + 1, sizeof text - 1) >= 0)
+ {
+ /* reassure user that <CR> was hit */
+ qaddch('\r');
+ refresh();
+
+ /* call the function with the text */
+ tcurs = (*keyptr->func)(cursor, text);
+ }
+ else
+ {
+ if (exwrote || mode == MODE_COLON)
+ {
+ redraw(MARK_UNSET, FALSE);
+ }
+ mode = MODE_VI;
+ }
+ } while (mode == MODE_COLON);
+ count = 0L;
+ break;
+
+ case CURSOR_CNT_CMD:
+ tcurs = (*keyptr->func)(cursor, count, key);
+ count = 0L;
+ break;
+ }
+
+ /* if that command took us out of vi mode, then exit the loop
+ * NOW, without tweaking the cursor or anything. This is very
+ * important when mode == MODE_QUIT.
+ */
+ if (mode != MODE_VI)
+ {
+ break;
+ }
+
+ /* now move the cursor, as appropriate */
+ if (keyptr->args == CURSOR_MOVED)
+ {
+ /* the < and > keys have FRNT,
+ * but it shouldn't be applied yet
+ */
+ tcurs = adjmove(cursor, tcurs, 0);
+ }
+ else
+ {
+ tcurs = adjmove(cursor, tcurs, (int)keyptr->flags);
+ }
+
+ /* was that the end of a d/c/y/</>/! command? */
+ if (prevkey && (prevkey == key || (keyptr->flags & MVMT)) && count == 0L)
+ {
+ /* if the movement command failed, cancel operation */
+ if (tcurs == MARK_UNSET)
+ {
+ prevkey = 0;
+ count = 0;
+ continue;
+ }
+
+ /* make sure range=front and tcurs=rear. Either way,
+ * leave cursor=range since that's where we started.
+ */
+ cursor = range;
+ if (tcurs < range)
+ {
+ range = tcurs;
+ tcurs = cursor;
+ }
+
+
+ /* adjust for line mode & inclusion of last char/line */
+ i = (keyptr->flags | vikeys[prevkey].flags);
+ if (key == prevkey)
+ {
+ i |= (INCL|LNMD);
+ }
+ switch (i & (INCL|LNMD))
+ {
+ case INCL:
+ tcurs++;
+ break;
+
+ case INCL|LNMD:
+ tcurs += BLKSIZE;
+ /* fall through... */
+
+ case LNMD:
+ range &= ~(BLKSIZE - 1);
+ tcurs &= ~(BLKSIZE - 1);
+ break;
+ }
+
+ /* run the function */
+ tcurs = (*vikeys[prevkey].func)(range, tcurs);
+ (void)adjmove(cursor, cursor, 0);
+ cursor = adjmove(cursor, tcurs, (int)vikeys[prevkey].flags);
+
+ /* cleanup */
+ prevkey = 0;
+ }
+ else if (!prevkey)
+ {
+ cursor = tcurs;
+ }
+ }
+}
+
+/* This function adjusts the MARK value that they return; here we make sure
+ * it isn't past the end of the line, and that the column hasn't been
+ * *accidentally* changed.
+ */
+MARK adjmove(old, new, flags)
+ MARK old; /* the cursor position before the command */
+ REG MARK new; /* the cursor position after the command */
+ int flags; /* various flags regarding cursor mvmt */
+{
+ static int colno; /* the column number that we want */
+ REG char *text; /* used to scan through the line's text */
+ REG int i;
+
+#ifdef DEBUG
+ watch();
+#endif
+
+ /* if the command failed, bag it! */
+ if (new == MARK_UNSET)
+ {
+ beep();
+ return old;
+ }
+
+ /* if this is a non-relative movement, set the '' mark */
+ if (flags & NREL)
+ {
+ mark[26] = old;
+ }
+
+ /* make sure it isn't past the end of the file */
+ if (markline(new) < 1)
+ {
+ new = MARK_FIRST;
+ }
+ else if (markline(new) > nlines)
+ {
+ new = MARK_LAST;
+ }
+
+ /* fetch the new line */
+ pfetch(markline(new));
+
+ /* move to the front, if we're supposed to */
+ if (flags & FRNT)
+ {
+ new = m_front(new, 1L);
+ }
+
+ /* change the column#, or change the mark to suit the column# */
+ if (!(flags & NCOL))
+ {
+ /* change the column# */
+ i = markidx(new);
+ if (i == BLKSIZE - 1)
+ {
+ new &= ~(BLKSIZE - 1);
+ if (plen > 0)
+ {
+ new += plen - 1;
+ }
+ colno = BLKSIZE * 8; /* one heck of a big colno */
+ }
+ else if (plen > 0)
+ {
+ if (i >= plen)
+ {
+ new = (new & ~(BLKSIZE - 1)) + plen - 1;
+ }
+ colno = idx2col(new, ptext, FALSE);
+ }
+ else
+ {
+ new &= ~(BLKSIZE - 1);
+ colno = 0;
+ }
+ }
+ else
+ {
+ /* adjust the mark to get as close as possible to column# */
+ for (i = 0, text = ptext; i <= colno && *text; text++)
+ {
+ if (*text == '\t' && !*o_list)
+ {
+ i += *o_tabstop - (i % *o_tabstop);
+ }
+ else if (UCHAR(*text) < ' ' || *text == 127)
+ {
+ i += 2;
+ }
+#ifndef NO_CHARATTR
+ else if (*o_charattr && text[0] == '\\' && text[1] == 'f' && text[2])
+ {
+ text += 2; /* plus one more in "for()" stmt */
+ }
+#endif
+ else
+ {
+ i++;
+ }
+ }
+ if (text > ptext)
+ {
+ text--;
+ }
+ new = (new & ~(BLKSIZE - 1)) + (int)(text - ptext);
+ }
+
+ return new;
+}
+
+
+#ifdef DEBUG
+watch()
+{
+ static wasset;
+
+ if (*origname)
+ {
+ wasset = TRUE;
+ }
+ else if (wasset)
+ {
+ msg("origname was clobbered");
+ endwin();
+ abort();
+ }
+
+ if (nlines == 0)
+ {
+ msg("nlines=0");
+ endwin();
+ abort();
+ }
+}
+#endif
diff --git a/vi.h b/vi.h
new file mode 100644
index 0000000..36c1397
--- /dev/null
+++ b/vi.h
@@ -0,0 +1,517 @@
+/* vi.h */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+
+/* This is the header file for my version of vi. */
+
+#define VERSION "ELVIS 1.4, by Steve Kirkendall"
+#define COPYING "This version of ELVIS is freely redistributable."
+
+#include <errno.h>
+extern int errno;
+#if TOS
+#define ENOENT (-AEFILNF)
+#endif
+
+#if TOS
+# include <types.h>
+# define O_RDONLY 0
+# define O_WRONLY 1
+# define O_RDWR 2
+#else
+# if OSK
+# include <modes.h>
+# define O_RDONLY S_IREAD
+# define O_WRONLY S_IWRITE
+# define O_RDWR (S_IREAD | S_IWRITE)
+# define ENOENT E_PNNF
+# else
+# include <sys/types.h>
+# if COHERENT
+# include <sys/fcntl.h>
+# else
+# include <fcntl.h>
+# endif
+# endif
+#endif
+
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+
+#include "curses.h"
+
+/*------------------------------------------------------------------------*/
+/* Miscellaneous constants. */
+
+#define INFINITY 2000000001L /* a very large integer */
+#define LONGKEY 10 /* longest possible raw :map key */
+#ifndef MAXRCLEN
+# define MAXRCLEN 1000 /* longest possible .exrc file */
+#endif
+
+/*------------------------------------------------------------------------*/
+/* These describe how temporary files are divided into blocks */
+
+#define BLKSIZE 1024 /* size of blocks */
+#define MAXBLKS (BLKSIZE / sizeof(unsigned short))
+typedef union
+{
+ char c[BLKSIZE]; /* for text blocks */
+ unsigned short n[MAXBLKS]; /* for the header block */
+}
+ BLK;
+
+/*------------------------------------------------------------------------*/
+/* These are used manipulate BLK buffers. */
+
+extern BLK hdr; /* buffer for the header block */
+extern BLK *blkget(); /* given index into hdr.c[], reads block */
+extern BLK *blkadd(); /* inserts a new block into hdr.c[] */
+
+/*------------------------------------------------------------------------*/
+/* These are used to keep track of various flags */
+extern struct _viflags
+{
+ short file; /* file flags */
+}
+ viflags;
+
+/* file flags */
+#define NEWFILE 0x0001 /* the file was just created */
+#define READONLY 0x0002 /* the file is read-only */
+#define HADNUL 0x0004 /* the file contained NUL characters */
+#define MODIFIED 0x0008 /* the file has been modified */
+#define NOFILE 0x0010 /* no name is known for the current text */
+#define ADDEDNL 0x0020 /* newlines were added to the file */
+
+/* macros used to set/clear/test flags */
+#define setflag(x,y) viflags.x |= y
+#define clrflag(x,y) viflags.x &= ~y
+#define tstflag(x,y) (viflags.x & y)
+#define initflags() viflags.file = 0;
+
+/* The options */
+extern char o_autoindent[1];
+extern char o_autoprint[1];
+extern char o_autowrite[1];
+#ifndef NO_ERRLIST
+extern char o_cc[30];
+#endif
+#ifndef NO_CHARATTR
+extern char o_charattr[1];
+#endif
+extern char o_columns[3];
+extern char o_digraph[1];
+extern char o_directory[30];
+extern char o_edcompatible[1];
+extern char o_errorbells[1];
+extern char o_exrefresh[1];
+#ifndef NO_DIGRAPH
+extern char o_flipcase[80];
+#endif
+#ifndef NO_SENTENCE
+extern char o_hideformat[1];
+#endif
+extern char o_ignorecase[1];
+#ifndef NO_EXTENSIONS
+extern char o_inputmode[1];
+#endif
+extern char o_keytime[3];
+extern char o_keywordprg[80];
+extern char o_lines[3];
+extern char o_list[1];
+#ifndef NO_MAGIC
+extern char o_magic[1];
+#endif
+#ifndef NO_ERRLIST
+extern char o_make[30];
+#endif
+#ifndef NO_MODELINE
+extern char o_modeline[1];
+#endif
+#ifndef NO_SENTENCE
+extern char o_paragraphs[30];
+#endif
+#if MSDOS
+extern char o_pcbios[1];
+#endif
+extern char o_readonly[1];
+extern char o_report[3];
+extern char o_scroll[3];
+#ifndef NO_SENTENCE
+extern char o_sections[30];
+#endif
+extern char o_shell[60];
+#ifndef NO_SHOWMATCH
+extern char o_showmatch[1];
+#endif
+#ifndef NO_SHOWMODE
+extern char o_smd[1];
+#endif
+extern char o_shiftwidth[3];
+extern char o_sidescroll[3];
+extern char o_sync[1];
+extern char o_tabstop[3];
+extern char o_term[30];
+extern char o_vbell[1];
+extern char o_warn[1];
+extern char o_wrapmargin[3];
+extern char o_wrapscan[1];
+
+/*------------------------------------------------------------------------*/
+/* These help support the single-line multi-change "undo" -- shift-U */
+
+extern char U_text[BLKSIZE];
+extern long U_line;
+
+/*------------------------------------------------------------------------*/
+/* These are used to refer to places in the text */
+
+typedef long MARK;
+#define markline(x) (long)((x) / BLKSIZE)
+#define markidx(x) (int)((x) & (BLKSIZE - 1))
+#define MARK_UNSET ((MARK)0)
+#define MARK_FIRST ((MARK)BLKSIZE)
+#define MARK_LAST ((MARK)(nlines * BLKSIZE))
+#define MARK_AT_LINE(x) ((MARK)((x) * BLKSIZE))
+
+#define NMARKS 29
+extern MARK mark[NMARKS]; /* marks a-z, plus mark ' and two temps */
+extern MARK cursor; /* mark where line is */
+
+/*------------------------------------------------------------------------*/
+/* These are used to keep track of the current & previous files. */
+
+extern long origtime; /* modification date&time of the current file */
+extern char origname[256]; /* name of the current file */
+extern char prevorig[256]; /* name of the preceding file */
+extern long prevline; /* line number from preceding file */
+
+/*------------------------------------------------------------------------*/
+/* misc housekeeping variables & functions */
+
+extern int tmpfd; /* fd used to access the tmp file */
+extern long lnum[MAXBLKS]; /* last line# of each block */
+extern long nlines; /* number of lines in the file */
+extern char args[BLKSIZE]; /* file names given on the command line */
+extern int argno; /* the current element of args[] */
+extern int nargs; /* number of filenames in args */
+extern long changes; /* counts changes, to prohibit short-cuts */
+extern int significant; /* boolean: was a *REAL* change made? */
+extern int mustredraw; /* boolean: force total redraw of screen? */
+extern BLK tmpblk; /* a block used to accumulate changes */
+extern long topline; /* file line number of top line */
+extern int leftcol; /* column number of left col */
+#define botline (topline + LINES - 2)
+#define rightcol (leftcol + COLS - 1)
+extern int physcol; /* physical column number that cursor is on */
+extern int physrow; /* physical row number that cursor is on */
+extern int exwrote; /* used to detect verbose ex commands */
+extern int doingdot; /* boolean: are we doing the "." command? */
+extern int doingglobal; /* boolean: are doing a ":g" command? */
+extern long rptlines; /* number of lines affected by a command */
+extern char *rptlabel; /* description of how lines were affected */
+extern char *fetchline(); /* read a given line from tmp file */
+extern char *parseptrn(); /* isolate a regexp in a line */
+extern MARK paste(); /* paste from cut buffer to a given point */
+extern char *wildcard(); /* expand wildcards in filenames */
+extern MARK input(); /* inserts characters from keyboard */
+extern char *linespec(); /* finds the end of a /regexp/ string */
+#define ctrl(ch) ((ch)&037)
+#ifndef NO_RECYCLE
+extern long allocate(); /* allocate a free block of the tmp file */
+#endif
+extern int trapint(); /* trap handler for SIGINT */
+extern void blkdirty(); /* marks a block as being "dirty" */
+extern void blkflush(); /* writes a single dirty block to the disk */
+extern void blksync(); /* forces all "dirty" blocks to disk */
+extern void blkinit(); /* resets the block cache to "empty" state */
+extern void beep(); /* rings the terminal's bell */
+extern void exrefresh(); /* writes text to the screen */
+extern void msg(); /* writes a printf-style message to the screen */
+extern void reset_msg(); /* resets the "manymsgs" flag */
+extern void endmsgs(); /* if "manymsgs" is set, then scroll up 1 line */
+extern void garbage(); /* reclaims any garbage blocks */
+extern void redraw(); /* updates the screen after a change */
+extern void resume_curses();/* puts the terminal in "cbreak" mode */
+extern void beforedo(); /* saves current revision before a new change */
+extern void afterdo(); /* marks end of a beforedo() change */
+extern void abortdo(); /* like "afterdo()" followed by "undo()" */
+extern int undo(); /* restores file to previous undo() */
+extern void dumpkey(); /* lists key mappings to the screen */
+extern void mapkey(); /* defines a new key mapping */
+extern void savekeys(); /* lists key mappings to a file */
+extern void redrawrange(); /* records clues from modify.c */
+extern void cut(); /* saves text in a cut buffer */
+extern void delete(); /* deletes text */
+extern void add(); /* adds text */
+extern void change(); /* deletes text, and then adds other text */
+extern void cutswitch(); /* updates cut buffers when we switch files */
+extern void do_abbr(); /* defines or lists abbreviations */
+extern void do_digraph(); /* defines or lists digraphs */
+extern void exstring(); /* execute a string as EX commands */
+extern void dumpopts();
+extern void setopts();
+extern void saveopts();
+#ifndef NO_DIGRAPH
+extern void savedigs();
+#endif
+#ifndef NO_ABBR
+extern void saveabbr();
+#endif
+extern void cutname();
+extern void cutname();
+extern void initopts();
+extern void cutend();
+
+/*------------------------------------------------------------------------*/
+/* macros that are used as control structures */
+
+#define BeforeAfter(before, after) for((before),bavar=1;bavar;(after),bavar=0)
+#define ChangeText BeforeAfter(beforedo(FALSE),afterdo())
+
+extern int bavar; /* used only in BeforeAfter macros */
+
+/*------------------------------------------------------------------------*/
+/* These are the movement commands. Each accepts a mark for the starting */
+/* location & number and returns a mark for the destination. */
+
+extern MARK m_updnto(); /* k j G */
+extern MARK m_right(); /* h */
+extern MARK m_left(); /* l */
+extern MARK m_tocol(); /* | */
+extern MARK m_front(); /* ^ */
+extern MARK m_rear(); /* $ */
+extern MARK m_fword(); /* w */
+extern MARK m_bword(); /* b */
+extern MARK m_eword(); /* e */
+extern MARK m_fWord(); /* W */
+extern MARK m_bWord(); /* B */
+extern MARK m_eWord(); /* E */
+extern MARK m_fparagraph(); /* } */
+extern MARK m_bparagraph(); /* { */
+extern MARK m_fsection(); /* ]] */
+extern MARK m_bsection(); /* [[ */
+extern MARK m_match(); /* % */
+#ifndef NO_SENTENCE
+ extern MARK m_fsentence(); /* ) */
+ extern MARK m_bsentence(); /* ( */
+#endif
+extern MARK m_tomark(); /* 'm */
+extern MARK m_nsrch(); /* n */
+extern MARK m_Nsrch(); /* N */
+extern MARK m_fsrch(); /* /regexp */
+extern MARK m_bsrch(); /* ?regexp */
+#ifndef NO_CHARSEARCH
+ extern MARK m__ch(); /* ; , */
+ extern MARK m_fch(); /* f */
+ extern MARK m_tch(); /* t */
+ extern MARK m_Fch(); /* F */
+ extern MARK m_Tch(); /* T */
+#endif
+extern MARK m_row(); /* H L M */
+extern MARK m_z(); /* z */
+extern MARK m_scroll(); /* ^B ^F ^E ^Y ^U ^D */
+
+/* Some stuff that is used by movement functions... */
+
+extern MARK adjmove(); /* a helper fn, used by move fns */
+
+/* This macro is used to set the default value of cnt */
+#define DEFAULT(val) if (cnt < 1) cnt = (val)
+
+/* These are used to minimize calls to fetchline() */
+extern int plen; /* length of the line */
+extern long pline; /* line number that len refers to */
+extern long pchgs; /* "changes" level that len refers to */
+extern char *ptext; /* text of previous line, if valid */
+extern void pfetch();
+extern char digraph();
+
+/* This is used to build a MARK that corresponds to a specific point in the
+ * line that was most recently pfetch'ed.
+ */
+#define buildmark(text) (MARK)(BLKSIZE * pline + (int)((text) - ptext))
+
+
+/*------------------------------------------------------------------------*/
+/* These are used to handle EX commands. */
+
+#define CMD_NULL 0 /* NOT A VALID COMMAND */
+#define CMD_ABBR 1 /* "define an abbreviation" */
+#define CMD_ARGS 2 /* "show me the args" */
+#define CMD_APPEND 3 /* "insert lines after this line" */
+#define CMD_AT 4 /* "execute a cut buffer's contents via EX" */
+#define CMD_BANG 5 /* "run a single shell command" */
+#define CMD_CC 6 /* "run `cc` and then do CMD_ERRLIST" */
+#define CMD_CD 7 /* "change directories" */
+#define CMD_CHANGE 8 /* "change some lines" */
+#define CMD_COPY 9 /* "copy the selected text to a given place" */
+#define CMD_DELETE 10 /* "delete the selected text" */
+#define CMD_DIGRAPH 11 /* "add a digraph, or display them all" */
+#define CMD_EDIT 12 /* "switch to a different file" */
+#define CMD_EQUAL 13 /* "display a line number" */
+#define CMD_ERRLIST 14 /* "locate the next error in a list" */
+#define CMD_FILE 15 /* "show the file's status" */
+#define CMD_GLOBAL 16 /* "globally search & do a command" */
+#define CMD_INSERT 17 /* "insert lines before the current line" */
+#define CMD_JOIN 18 /* "join the selected line & the one after" */
+#define CMD_LIST 19 /* "print lines, making control chars visible" */
+#define CMD_MAKE 20 /* "run `make` and then do CMD_ERRLIST" */
+#define CMD_MAP 21 /* "adjust the keyboard map" */
+#define CMD_MARK 22 /* "mark this line" */
+#define CMD_MKEXRC 23 /* "make a .exrc file" */
+#define CMD_MOVE 24 /* "move the selected text to a given place" */
+#define CMD_NEXT 25 /* "switch to next file in args" */
+#define CMD_NUMBER 26 /* "print lines from the file w/ line numbers" */
+#define CMD_PRESERVE 27 /* "act as though vi crashed" */
+#define CMD_PREVIOUS 28 /* "switch to the previous file in args" */
+#define CMD_PRINT 29 /* "print the selected text" */
+#define CMD_PUT 30 /* "insert any cut lines before this line" */
+#define CMD_QUIT 31 /* "quit without writing the file" */
+#define CMD_READ 32 /* "append the given file after this line */
+#define CMD_RECOVER 33 /* "recover file after vi crashes" - USE -r FLAG */
+#define CMD_REWIND 34 /* "rewind to first file" */
+#define CMD_SET 35 /* "set a variable's value" */
+#define CMD_SHELL 36 /* "run some lines through a command" */
+#define CMD_SHIFTL 37 /* "shift lines left" */
+#define CMD_SHIFTR 38 /* "shift lines right" */
+#define CMD_SOURCE 39 /* "interpret a file's contents as ex commands" */
+#define CMD_STOP 40 /* same as CMD_SUSPEND */
+#define CMD_SUBAGAIN 41 /* "repeat the previous substitution" */
+#define CMD_SUBSTITUTE 42 /* "substitute text in this line" */
+#define CMD_SUSPEND 43 /* "suspend the vi session" */
+#define CMD_TR 44 /* "transliterate chars in the selected lines" */
+#define CMD_TAG 45 /* "go to a particular tag" */
+#define CMD_UNABBR 46 /* "remove an abbreviation definition" */
+#define CMD_UNDO 47 /* "undo the previous command" */
+#define CMD_UNMAP 48 /* "remove a key sequence map */
+#define CMD_VERSION 49 /* "describe which version this is" */
+#define CMD_VGLOBAL 50 /* "apply a cmd to lines NOT containing an RE" */
+#define CMD_VISUAL 51 /* "go into visual mode" */
+#define CMD_WQUIT 52 /* "write this file out (any case) & quit" */
+#define CMD_WRITE 53 /* "write the selected(?) text to a given file" */
+#define CMD_XIT 54 /* "write this file out (if modified) & quit" */
+#define CMD_YANK 55 /* "copy the selected text into the cut buffer" */
+#ifdef DEBUG
+# define CMD_DEBUG 56 /* access to internal data structures */
+# define CMD_VALIDATE 57 /* check for internal consistency */
+#endif
+typedef int CMD;
+
+extern void ex();
+extern void vi();
+extern void doexcmd();
+
+#ifndef NO_ABBR
+extern void cmd_abbr();
+#endif
+extern void cmd_append();
+extern void cmd_args();
+#ifndef NO_AT
+extern void cmd_at();
+#endif
+extern void cmd_cd();
+extern void cmd_delete();
+#ifndef NO_DIGRAPH
+extern void cmd_digraph();
+#endif
+extern void cmd_edit();
+#ifndef NO_ERRLIST
+extern void cmd_errlist();
+#endif
+extern void cmd_file();
+extern void cmd_global();
+extern void cmd_join();
+extern void cmd_mark();
+#ifndef NO_ERRLIST
+extern void cmd_make();
+#endif
+extern void cmd_map();
+#ifndef NO_MKEXRC
+extern void cmd_mkexrc();
+#endif
+extern void cmd_next();
+extern void cmd_print();
+extern void cmd_put();
+extern void cmd_read();
+extern void cmd_set();
+extern void cmd_shell();
+extern void cmd_shift();
+extern void cmd_source();
+extern void cmd_substitute();
+extern void cmd_tag();
+extern void cmd_undo();
+extern void cmd_version();
+extern void cmd_visual();
+extern void cmd_write();
+extern void cmd_xit();
+extern void cmd_move();
+#ifdef DEBUG
+extern void cmd_debug();
+extern void cmd_validate();
+#endif
+
+/*----------------------------------------------------------------------*/
+/* These are used to handle VI commands */
+
+extern MARK v_1ex(); /* : */
+extern MARK v_mark(); /* m */
+extern MARK v_quit(); /* Q */
+extern MARK v_redraw(); /* ^L ^R */
+extern MARK v_ulcase(); /* ~ */
+extern MARK v_undo(); /* u */
+extern MARK v_xchar(); /* x */
+extern MARK v_Xchar(); /* X */
+extern MARK v_replace(); /* r */
+extern MARK v_overtype(); /* R */
+extern MARK v_selcut(); /* " */
+extern MARK v_paste(); /* p P */
+extern MARK v_yank(); /* y Y */
+extern MARK v_delete(); /* d D */
+extern MARK v_join(); /* J */
+extern MARK v_insert(); /* a A i I o O */
+extern MARK v_change(); /* c C */
+extern MARK v_subst(); /* s */
+extern MARK v_lshift(); /* < */
+extern MARK v_rshift(); /* > */
+extern MARK v_filter(); /* ! */
+extern MARK v_status(); /* ^G */
+extern MARK v_switch(); /* ^^ */
+extern MARK v_tag(); /* ^] */
+extern MARK v_xit(); /* ZZ */
+extern MARK v_undoline(); /* U */
+extern MARK v_again(); /* & */
+#ifndef NO_EXTENSIONS
+ extern MARK v_keyword(); /* ^K */
+ extern MARK v_increment(); /* * */
+#endif
+#ifndef NO_ERRLIST
+ extern MARK v_errlist(); /* * */
+#endif
+#ifndef NO_AT
+ extern MARK v_at(); /* @ */
+#endif
+
+/*----------------------------------------------------------------------*/
+/* These describe what mode we're in */
+
+#define MODE_EX 1 /* executing ex commands */
+#define MODE_VI 2 /* executing vi commands */
+#define MODE_COLON 3 /* executing an ex command from vi mode */
+#define MODE_QUIT 4
+extern int mode;
+
+#define WHEN_VICMD 1 /* getkey: we're reading a VI command */
+#define WHEN_VIINP 2 /* getkey: we're in VI's INPUT mode */
+#define WHEN_VIREP 4 /* getkey: we're in VI's REPLACE mode */
+#define WHEN_EX 8 /* getkey: we're in EX mode */
+#define WHEN_MSG 16 /* getkey: we're at a "more" prompt */
+#define WHEN_INMV 256 /* in input mode, interpret the key in VICMD mode */
diff --git a/virec.c b/virec.c
new file mode 100644
index 0000000..a731f4f
--- /dev/null
+++ b/virec.c
@@ -0,0 +1,245 @@
+/* virec.c */
+
+/* Author:
+ * Steve Kirkendall
+ * 14407 SW Teal Blvd. #C
+ * Beaverton, OR 97005
+ * kirkenda@cs.pdx.edu
+ */
+
+/* This file contains the file recovery program */
+
+
+#include "config.h"
+#include <stdio.h>
+#include <ctype.h>
+#include "vi.h"
+#if TOS
+# include <stat.h>
+#else
+# if OSK
+# include "osk.h"
+# else
+# include <sys/stat.h>
+# endif
+#endif
+
+extern char *getenv();
+struct stat stbuf;
+BLK hdr;
+BLK text;
+
+/* the name of the directory where tmp files are stored. */
+char o_directory[30] = TMPDIR;
+
+char *progname;
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *tmp;
+ void recover();
+#if MSDOS || TOS
+ char **wildexpand();
+ argv = wildexpand(&argc, argv);
+#endif
+ progname = argv[0];
+ /* set the o_directory variable */
+ if ((tmp = getenv("TMP")) /* yes, ASSIGNMENT! */
+ || (tmp = getenv("TEMP"))) /* yes, ASSIGNMENT! */
+ {
+ strcpy(o_directory, tmp);
+ }
+ if (argc >= 3 && !strcmp(argv[1], "-d"))
+ {
+ strcpy(o_directory, argv[2]);
+ argc -= 2;
+ argv += 2;
+ }
+ /* process the arguments */
+ if (argc < 2)
+ {
+ /* maybe stdin comes from a file? */
+ if (isatty(0))
+ {
+ fprintf(stderr, "usage: %s [-d tmpdir] lostfile...\n", progname);
+ }
+ else if (read(0, &hdr, (unsigned)BLKSIZE) != BLKSIZE)
+ {
+ fprintf(stderr, "couldn't get header\n");
+ }
+ else
+ {
+ copytext(0, stdout);
+ }
+ }
+ else
+ {
+ while (--argc > 0)
+ {
+ recover(*++argv);
+ }
+ }
+ exit(0);
+}
+
+
+/* This function recovers a single file */
+void recover(filename)
+ char *filename;
+{
+ char tmpname[100];
+ int tmpfd;
+ FILE *fp;
+ long mtime;
+ int i, j;
+ int sum; /* used for calculating a checksum for this */
+ char *scan;
+
+ /* get the file's status info */
+ if (stat(filename, &stbuf) < 0)
+ {
+ /* if serious error, give up on this file */
+ if (errno != ENOENT)
+ {
+ perror(filename);
+ return;
+ }
+
+ /* else fake it for a new file */
+ stat(".", &stbuf);
+#if OSK
+ stbuf.st_mode = S_IREAD;
+#else
+ stbuf.st_mode = S_IFREG;
+#endif
+ stbuf.st_mtime = 0L;
+ }
+
+ /* generate a checksum from the file's name */
+ for (sum = 0, scan = filename + strlen(filename);
+ --scan >= filename && (isascii(*scan) && isalnum(*scan) || *scan == '.');
+ sum = sum + *scan)
+ {
+ }
+ sum &= 0xf;
+
+ /* find the tmp file */
+#if MSDOS || TOS
+ /* MS-Dos doesn't allow multiple slashes, but supports drives
+ * with current directories.
+ * This relies on TMPNAME beginning with "%s\\"!!!!
+ */
+ strcpy(tmpname, o_directory);
+ if ((i = strlen(tmpname)) && !strchr(":/\\", tmpname[i-1]))
+ tmpname[i++]=SLASH;
+ sprintf(tmpname+i, TMPNAME+3, sum, stbuf.st_ino, stbuf.st_dev);
+#else
+ sprintf(tmpname, TMPNAME, o_directory, sum, stbuf.st_ino, stbuf.st_dev);
+#endif
+ tmpfd = open(tmpname, O_RDONLY | O_BINARY);
+ if (tmpfd < 0)
+ {
+ perror(tmpname);
+ return;
+ }
+
+ /* make sure the file hasn't been modified more recently */
+ mtime = stbuf.st_mtime;
+ fstat(tmpfd, &stbuf);
+ if (stbuf.st_mtime < mtime)
+ {
+ printf("\"%s\" has been modified more recently than its recoverable version\n", filename);
+ puts("Do you still want to recover it?\n");
+ puts("\ty - Yes, discard the current version and recover it.\n");
+ puts("\tn - No, discard the recoverable version and keep the current version\n");
+ puts("\tq - Quit without doing anything for this file.\n");
+ puts("Enter y, n, or q --> ");
+ fflush(stdout);
+ for (;;)
+ {
+ switch (getchar())
+ {
+ case 'y':
+ case 'Y':
+ goto BreakBreak;
+
+ case 'n':
+ case 'N':
+ close(tmpfd);
+ unlink(tmpname);
+ return;
+
+ case 'q':
+ case 'Q':
+ close(tmpfd);
+ return;
+ }
+ }
+BreakBreak:;
+ }
+
+ /* make sure this tmp file is intact */
+ if (read(tmpfd, &hdr, (unsigned)BLKSIZE) != BLKSIZE)
+ {
+ fprintf(stderr, "%s: bad header in tmp file\n", filename);
+ close(tmpfd);
+ unlink(tmpname);
+ return;
+ }
+ for (i = j = 1; i < MAXBLKS && hdr.n[i]; i++)
+ {
+ if (hdr.n[i] > j)
+ {
+ j = hdr.n[i];
+ }
+ }
+ lseek(tmpfd, (long)j * (long)BLKSIZE, 0);
+ if (read(tmpfd, &text, (unsigned)BLKSIZE) != BLKSIZE)
+ {
+ fprintf(stderr, "%s: bad data block in tmp file\n", filename);
+ close(tmpfd);
+ unlink(tmpname);
+ return;
+ }
+
+ /* open the normal text file for writing */
+ fp = fopen(filename, "w");
+ if (!fp)
+ {
+ perror(filename);
+ close(tmpfd);
+ return;
+ }
+
+ /* copy the text */
+ copytext(tmpfd, fp);
+
+ /* cleanup */
+ close(tmpfd);
+ fclose(fp);
+ unlink(tmpname);
+}
+
+
+/* This function moves text from the tmp file to the normal file */
+copytext(tmpfd, fp)
+ int tmpfd; /* fd of the tmp file */
+ FILE *fp; /* the stream to write it to */
+{
+ int i;
+
+ /* write the data blocks to the normal text file */
+ for (i = 1; i < MAXBLKS && hdr.n[i]; i++)
+ {
+ lseek(tmpfd, (long)hdr.n[i] * (long)BLKSIZE, 0);
+ read(tmpfd, &text, (unsigned)BLKSIZE);
+ fputs(text.c, fp);
+ }
+}
+
+#if MSDOS || TOS
+#define WILDCARD_NO_MAIN
+#include "wildcard.c"
+#endif
diff --git a/wildcard.c b/wildcard.c
new file mode 100644
index 0000000..2e2ea0d
--- /dev/null
+++ b/wildcard.c
@@ -0,0 +1,140 @@
+/* wildcard.c */
+
+/* Author:
+ * Guntram Blohm
+ * Buchenstrasse 19
+ * 7904 Erbach, West Germany
+ * Tel. ++49-7305-6997
+ * sorry - no regular network connection
+ */
+
+/* this program implements wildcard expansion for elvis/dos. It works
+ * like UNIX echo, but uses the dos wildcard conventions
+ * (*.* matches all files, * matches files without extension only,
+ * filespecs may contain drive letters, wildcards not allowed in directory
+ * names).
+ *
+ * It is also #included into ctags.c, ref.c, ...; in this case,
+ * we don't want a main function here.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#ifdef __TURBOC__
+#include <dir.h>
+#endif
+#ifdef M_I86
+#define findfirst(a,b,c) _dos_findfirst(a,c,b)
+#define findnext _dos_findnext
+#define ffblk find_t
+#define ff_name name
+#include <dos.h>
+#endif
+#ifdef M68000
+#include <stat.h>
+#include <osbind.h>
+#define findfirst(a,b,c) (Fsetdta(b), (Fsfirst(a,c)))
+#define findnext(x) (Fsnext())
+#define ff_name d_fname
+#endif
+#define MAXFILES 1000
+
+int pstrcmp();
+extern char *calloc();
+
+char *files[MAXFILES];
+int nfiles;
+
+#ifndef WILDCARD_NO_MAIN
+
+main(argc, argv)
+ char **argv;
+{
+ int i;
+
+ for (i=1; i<argc; i++)
+ expand(argv[i]);
+ if (nfiles)
+ printf("%s", files[0]);
+ for (i=1; i<nfiles; i++)
+ {
+ printf(" %s", files[i]);
+ }
+ putchar('\n');
+ return 0;
+}
+
+#else
+char **wildexpand(argc, argv)
+ int *argc;
+ char **argv;
+{
+ int i;
+
+ for (i=0; i<*argc; i++)
+ expand(argv[i]);
+ *argc=nfiles;
+ return files;
+}
+#endif
+
+expand(name)
+ char *name;
+{
+ char *filespec;
+ int wildcard=0;
+#ifdef M68000
+ DMABUFFER findbuf;
+#else
+ struct ffblk findbuf;
+#endif
+ int err;
+ char buf[80];
+ int lastn;
+
+ strcpy(buf, name);
+ for (filespec=buf; *filespec; filespec++)
+ ;
+
+ while (--filespec>=buf)
+ { if (*filespec=='?' || *filespec=='*')
+ wildcard=1;
+ if (*filespec=='/' || *filespec=='\\' || *filespec==':')
+ break;
+ }
+ if (!wildcard)
+ addfile(buf);
+ else
+ {
+ lastn=nfiles;
+ filespec++;
+ if ((err=findfirst(buf, &findbuf, 0))!=0)
+ addfile(buf);
+ while (!err)
+ {
+ strcpy(filespec, findbuf.ff_name);
+ addfile(buf);
+ err=findnext(&findbuf);
+ }
+ if (lastn!=nfiles)
+ qsort(files+lastn, nfiles-lastn, sizeof(char *), pstrcmp);
+ }
+}
+
+addfile(buf)
+ char *buf;
+{
+ char *p;
+
+ for (p=buf; *p; p++)
+ *p=tolower(*p);
+
+ if (nfiles<MAXFILES && (files[nfiles]=calloc(strlen(buf)+1, 1))!=0)
+ strcpy(files[nfiles++], buf);
+}
+
+int pstrcmp(a, b)
+ char **a, **b;
+{
+ return strcmp(*a, *b);
+}