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!" 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 oxxxxxxxxxxi (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 b/Makefile new file mode 120000 index 0000000..62fdcba --- /dev/null +++ b/Makefile @@ -0,0 +1 @@ +Makefile.mix \ No newline at end of file diff --git a/Makefile.mix b/Makefile.mix new file mode 100644 index 0000000..20dcdd9 --- /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 +# 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.uue + $(RM) elvis3.tar* + +elvis4.uue: $(SRC4) + tar cf elvis4.tar $(SRC4) + compress -b13 elvis4.tar + uuencode 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.uue + $(RM) elvis5.tar* + +elvis6.uue: $(SRC6) + tar cf elvis6.tar $(SRC6) + compress -b13 elvis6.tar + uuencode 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.uue + $(RM) elvis7.tar* + +elvis8.uue: $(SRC8) + tar cf elvis8.tar $(SRC8) + compress -b13 elvis8.tar + uuencode 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 +#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 + +/* 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; jlogical == 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 +#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 */ + + /* 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 +#include "config.h" +#include "vi.h" +#include "regexp.h" +#if TOS +# include +#else +# if OSK +# include "osk.h" +# else +# include +# 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 = " 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 +#include +#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 +# undef TIOCWINSZ /* we can't handle it correctly yet */ +# else +# include +# endif +#endif + +#if TOS +# include +#endif + +#if OSK +# include +#endif + +#include + +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 key */ +char *EN; /* :EN=: key sequence sent by the key */ +char *PU; /* :PU=: key sequence sent by the key */ +char *PD; /* :PD=: key sequence sent by the 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, <buf); + ltbuf.t_suspc = oldswitch; + ltbuf.t_lnextc = oldquote; + ioctl(2, TIOCSLTC, <buf); +# 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, <buf); + 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, <buf); +# 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 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>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 /* needed for getpid */ +#endif +#if TOS +#include +#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 diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..b9ce0af --- /dev/null +++ b/debian/changelog @@ -0,0 +1,207 @@ +elvis-tiny (1.4-23.1) UNRELEASED; urgency=medium + + * Non-maintainer upload. + * Generates UDEB for Debian-Installer (d-i) + + -- Jose R Rodriguez Fri, 16 Sep 2016 04:21:50 -0700 + +elvis-tiny (1.4-23) unstable; urgency=low + + * convert to new source format + * convert to debhelper + * fix some more gcc warnings + * link with -ltinfo instead of -lncurses + * change maintainer email address to debian email address + + -- Miquel van Smoorenburg Sat, 30 Jun 2012 10:13:28 +0200 + +elvis-tiny (1.4-22) unstable; urgency=low + + * make window-resizing work (closes: #168231) + * use sigsetjmp/siglongjmp so that jumping out of a + signal handler sort of works (it's a bad idea anyway) (closes: #161734) + + -- Miquel van Smoorenburg Wed, 30 Apr 2008 23:05:37 +0200 + +elvis-tiny (1.4-21) unstable; urgency=low + + * Honour DEB_BUILD_OPTIONS debug and nostrip (closes: #436816) + + -- Miquel van Smoorenburg Sat, 19 Apr 2008 22:45:21 +0200 + +elvis-tiny (1.4-20) unstable; urgency=low + + * Fix overflow in command line processing (closes: #203258). + Note that elvis-tiny is riddled with strcpy's and fixing all of them + would be a major undertaking akin to a rewrite. + * Disable CTRL-Z as elvis-tiny has no suspend support + (closes: #136862, #206518) + + -- Miquel van Smoorenburg Sat, 26 Nov 2005 14:58:47 +0100 + +elvis-tiny (1.4-19) unstable; urgency=low + + * QA Group upload orphaning this package + * debian/rules: invoke dpkg-gencontrol with -isp + * debian/control: rephrase package description synopsis + + -- Andrew Pollock Wed, 5 Oct 2005 07:02:38 +1000 + +elvis-tiny (1.4-18.1) unstable; urgency=low + + * 0-day NMU from the QA meeting. + * Correcting conflicting types for 'malloc' (closes: #258640) + * Replacing "gcc" with "$(CC)" for being able to cross-compile + (closes: #285295) + * Removing obsolete code for /usr/doc support (closes: #254913) + + -- Axel Beckert Sun, 11 Sep 2005 02:08:26 +0200 + +elvis-tiny (1.4-18) unstable; urgency=low + + * Include string.h in tmp.c to solve prototype problem on + 64-bit platforms (closes: #124028) + * tmp.c: fix problem where a TAB expanded exactly at the right margin + and screen drawing was wrong (closes: #79385) + * Set LC_ALL=POSIX in debian/rules + + -- Miquel van Smoorenburg Wed, 23 Jan 2002 22:52:47 +0100 + +elvis-tiny (1.4-17) unstable; urgency=low + + * Remove empty /usr/doc directory from package (closes: #121549) + + -- Miquel van Smoorenburg Wed, 28 Nov 2001 17:13:37 +0100 + +elvis-tiny (1.4-16) unstable; urgency=high + + * Change arguments to ln -sf in postinst (closes: #100283,#112124) + * Small adjustements for complete migration to termios + instead of termio (closes: #106099) + + -- Miquel van Smoorenburg Fri, 5 Oct 2001 13:10:51 +0200 + +elvis-tiny (1.4-15) unstable; urgency=low + + * Make sure /etc/alternatives/vi also points to /bin/elvis-tiny + and not /usr/bin/elvis-tiny (closes: #90094, #92633) + + -- Miquel van Smoorenburg Thu, 5 Apr 2001 17:56:50 +0200 + +elvis-tiny (1.4-14) unstable; urgency=low + + * Fix small bug in the range-segfault.patch patch (closes: #88119) + + -- Miquel van Smoorenburg Thu, 1 Mar 2001 17:21:09 +0100 + +elvis-tiny (1.4-13) unstable; urgency=low + + * Oops. Should go into unstable only, with urgency=low... + + -- Miquel van Smoorenburg Wed, 21 Feb 2001 14:07:11 +0100 + +elvis-tiny (1.4-12) stable unstable; urgency=high + + * Patch by David Douthitt to fix subsitition bug (closes: #73059) + * Patch by David Douthitt to fix range bug (closes: #55407) + * Move elvis-tiny to /bin (closes: #37571) + * Install wrapper in /bin/vi that execs /usr/bin/vi if it is + present and /bin/elvis-tiny otherwise (closes: #72889) + * Install alternative for 'editor' (closes: #85318) + + -- Miquel van Smoorenburg Tue, 20 Feb 2001 14:08:19 +0100 + +elvis-tiny (1.4-11) stable unstable; urgency=high + + * The patch by Topi Miettinen to fix critical bug #74976 broke + file recovery, but also the :w command. Re-engineered the patch + so that the bug is fixed without any side-effects (closes: #77918) + * Add explanation by the author to clear up copyright notice (closes: #72021) + + -- Miquel van Smoorenburg Tue, 28 Nov 2000 13:13:40 +0100 + +elvis-tiny (1.4-10) stable unstable; urgency=high + + * Close tempfile hole. This is a critical bug, since it can be + exploited locally. Patch by Topi Miettinen + Upload to both stable and unstable. (closes: #74976) + * Check for files > 500000 bytes (closes: #44601) + * There is no changelog file, say so (closes: #60003) + * Runs fine in 80x30 for me (closes: #62720) + * Compile warnings aren't bugs on old sources (closes: #62807) + * Add Build-Depends (closes: #70311) + * Fix update-alternatives call (closes: #71212) + * Compile with gcc -fsigned-char (closes: #62807) + + -- Miquel van Smoorenburg Wed, 22 Nov 2000 15:04:54 +0100 + +elvis-tiny (1.4-9) frozen unstable; urgency=high + + * Add patch for Alpha (closes: #58672) + + -- Miquel van Smoorenburg Tue, 7 Mar 2000 23:43:04 +0100 + +elvis-tiny (1.4-8) unstable; urgency=high + + * Set SHELL=/bin/bash in debian/rules (closes: #54607) + * MU this time (closes: #54146) + * Move man and doc to /usr/share + + -- Miquel van Smoorenburg Fri, 14 Jan 2000 13:35:10 +0100 + +elvis-tiny (1.4-7.1) unstable; urgency=low + + * Non-maintainer upload during bug-squashing-party. + * Recompilation with ncurses5. + * Fixed rules-file to work with /bin/ash. + + -- Christian Kurz Sat, 8 Jan 2000 12:56:19 +0100 + +elvis-tiny (1.4-7) unstable; urgency=low + + * Link with ncurses4 + + -- Miquel van Smoorenburg Thu, 29 Oct 1998 19:14:40 +0100 + +elvis-tiny (1.4-6) unstable; urgency=low + + * Fixes bugs: + #22773: elvis-tiny: incorrect manpage permissions + + -- Miquel van Smoorenburg Mon, 5 Oct 1998 13:00:37 +0200 + +elvis-tiny (1.4-5) unstable; urgency=low + + * Lintian fixes + * Fixes bugs: + #12284: elvis-tiny unable to display long lines. + #14391: /usr/doc/$(PACKAGE)/copyright should not be compressed + + -- Miquel van Smoorenburg Sun, 22 Feb 1998 15:43:36 +0100 + +elvis-tiny (1.4-4) unstable; urgency=low + + * libc6 version + + -- Miquel van Smoorenburg Wed, 24 Sep 1997 14:05:38 +0200 + +elvis-tiny (1.4-3) frozen unstable; urgency=high + + * Corrected alternatives priority (0 -> 10). + + -- Miquel van Smoorenburg Sun, 1 Jun 1997 15:55:16 +0200 + +elvis-tiny (1.4-2) frozen unstable; urgency=high + + * Corrected alternatives priority (99 -> 0). + + -- Miquel van Smoorenburg Fri, 30 May 1997 14:10:28 +0200 + +elvis-tiny (1.4-1) unstable; urgency=low + + * Initial release of a tiny vi compatible editor. + (it was uploaded to "Incoming" before with distribution=experimental + but never got processed - would be nice if that actually worked.) + + -- Miquel van Smoorenburg Tue, 20 May 1997 14:38:24 +0200 + diff --git a/debian/changelog.upstream b/debian/changelog.upstream new file mode 100644 index 0000000..af3cff5 --- /dev/null +++ b/debian/changelog.upstream @@ -0,0 +1,3 @@ + +The elvis-tiny source code doesn't come with an upstream changelog. + diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +8 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..78d44c8 --- /dev/null +++ b/debian/control @@ -0,0 +1,23 @@ +Source: elvis-tiny +Section: base +Priority: standard +Maintainer: Miquel van Smoorenburg +Build-Depends: libncurses5-dev, debhelper (>= 8) +Standards-Version: 3.5.1.0 + +Package: elvis-tiny-udeb +XC-Package-Type: udeb +Section: debian-installer +Priority: optional +Architecture: any +Depends: ${shlibs:Depends} +Description: elvis-tiny - udeb + Do not install it on a normal system. + +Package: elvis-tiny +Architecture: any +Pre-depends: ${shlibs:Depends} +Description: Tiny vi compatible editor for the base system + Elvis-tiny is based on a 1991 Minix version of elvis. You should install + another vi-editor (such as "vim", "elvis" or "nvi") if you want a vi + editor that is full featured and has no bugs. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..54c80fd --- /dev/null +++ b/debian/copyright @@ -0,0 +1,61 @@ + +Copyright for elvis 1.4 (elvis-tiny) + +This file contains the copyright part from the original README, +and a clarification by the author. + +Copyright (C) Steve Kirkendall 1991 + +==================== original copyright =========================== + +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 is freely redistributable, in either source form or executable form. +There are no restrictions on how you may use it. + +Author: Steve Kirkendall + +E-mail: kirkenda@cs.pdx.edu + +Snail 14407 SW Teal Blvd. Apt.C + Mail: Beaverton, OR 97005 + +Phone: (503) 643-6980 + +==================== clarification ================================ + +From: Steve Kirkendall +Reply-To: kirkenda@cs.pdx.edu +To: Miquel van Smoorenburg +Subject: Re: elvis 1.4 +Message-ID: <3A21A76D.CE7C4D2@uswest.net> +Date: Sun, 26 Nov 2000 16:14:37 -0800 +Organization: Elvis +References: <20001122144219.A6118@cistron.nl> + +Miquel van Smoorenburg wrote: +> +> Hello, +> +> I've packaged elvis 1.4 up as "elvis tiny" to have a small +> vi clone for use on bootfloppies etc for the Debian/Linux distribution. +> +> The README says: +> +> Elvis is freely redistributable, in either source form or executable form. +> There are no restrictions on how you may use it. +> +> Did you mean that it is allright to distribute modified versions +> without any restrictions as well (I assume so, but someone filed +> a bugreport against the elvis-tiny package claiming it wasn't +> DFSG-free) + +Yes, modified versions can be freely redistributed. + +Elvis 2.0 was distributed with a slightly more restrictive license, +which made some people uncomfortable; that's probably where the bug +report came from. Elvis 2.1 and later are distributed under the Perl +"Artistic" license. + + diff --git a/debian/elvis-tiny.1 b/debian/elvis-tiny.1 new file mode 100644 index 0000000..ef6e4c9 --- /dev/null +++ b/debian/elvis-tiny.1 @@ -0,0 +1,10 @@ +.TH ELVIS-TINY 1 "May 13, 1997" "" "Debian GNU/Linux Manual" +.SH NAME +elvis-tiny \- Tiny vi-compatible editor +.SH DESCRIPTION +Elvis-tiny is a very small vi-compatible editor with limited +capabilities. If you want more features (and documentation), install +its big brother \fBelvis\fP or any of the other vi clones such as +\fBnvi\fP or \fPvim\fP. +.SH "SEE ALSO" +ae(1). diff --git a/debian/elvis-tiny.docs b/debian/elvis-tiny.docs new file mode 100644 index 0000000..207de02 --- /dev/null +++ b/debian/elvis-tiny.docs @@ -0,0 +1 @@ +KNOWN.BUGS diff --git a/debian/elvis-tiny.lintian-overrides b/debian/elvis-tiny.lintian-overrides new file mode 100644 index 0000000..6d9d754 --- /dev/null +++ b/debian/elvis-tiny.lintian-overrides @@ -0,0 +1,3 @@ +elvis-tiny binary: unknown-section base +elvis-tiny binary: binary-without-manpage bin/vi +elvis-tiny binary: command-with-path-in-maintainer-script postinst:24 /bin/ls diff --git a/debian/elvis-tiny.manpages b/debian/elvis-tiny.manpages new file mode 100644 index 0000000..4805d33 --- /dev/null +++ b/debian/elvis-tiny.manpages @@ -0,0 +1 @@ +debian/elvis-tiny.1 diff --git a/debian/elvis-tiny.postinst b/debian/elvis-tiny.postinst new file mode 100644 index 0000000..7c8b358 --- /dev/null +++ b/debian/elvis-tiny.postinst @@ -0,0 +1,41 @@ +#! /bin/sh + +set -e + +case "$1" in + configure) + ;; + abort-upgrade|abort-remove|abort-deconfigure) + exit 0 + ;; +esac +umask 022 + +# +# elvis-tiny moved from /usr/bin/elvis-tiny to +# /bin/elvis-tiny, so adjust symlink if nessecary +# +# We don't need to adjust the 'editor' alternative since +# it didn't exist in packages before the move. +# +link= +if [ -L /etc/alternatives/vi ] +then + link=`/bin/ls -ld /etc/alternatives/vi | sed -e 's/^.*-> //'` +fi +if [ "$link" = "/usr/bin/elvis-tiny" ] +then + ln -sf /bin/elvis-tiny /etc/alternatives/vi + update-alternatives --auto vi +fi + +# Alternative for /usr/bin/vi +update-alternatives --install /usr/bin/vi vi /bin/elvis-tiny 10 \ + --slave /usr/share/man/man1/vi.1.gz vi.1.gz \ + /usr/share/man/man1/elvis-tiny.1.gz + +# Alternative for /usr/bin/editor +update-alternatives --install /usr/bin/editor editor /bin/elvis-tiny 10 \ + --slave /usr/share/man/man1/editor.1.gz editor.1.gz \ + /usr/share/man/man1/elvis-tiny.1.gz + diff --git a/debian/elvis-tiny.prerm b/debian/elvis-tiny.prerm new file mode 100644 index 0000000..f0441a9 --- /dev/null +++ b/debian/elvis-tiny.prerm @@ -0,0 +1,12 @@ +#! /bin/sh + +set -e + +if [ "$1" = "remove" ] +then + update-alternatives --remove vi /usr/bin/elvis-tiny + update-alternatives --remove vi /bin/elvis-tiny + update-alternatives --remove editor /bin/elvis-tiny +fi + +exit 0 diff --git a/debian/patches/argsize.patch b/debian/patches/argsize.patch new file mode 100644 index 0000000..4ba0ac2 --- /dev/null +++ b/debian/patches/argsize.patch @@ -0,0 +1,37 @@ +diff '--exclude=debian' -ruN x/elvis-tiny-1.4.orig/main.c elvis-tiny-1.4/main.c +--- x/elvis-tiny-1.4.orig/main.c 2012-06-29 19:22:52.702106206 +0000 ++++ elvis-tiny-1.4/main.c 2012-06-29 17:54:18.000000000 +0000 +@@ -195,11 +196,29 @@ + #if ! ( MSDOS || TOS ) + firstarg = argv[i]; + #endif +- strcpy(args, argv[i]); +- while (++i < argc && strlen(args) + 1 + strlen(argv[i]) < sizeof args) ++ args[0] = 0; ++ while (i < argc) + { +- strcat(args, " "); +- strcat(args, argv[i]); ++ if (strlen(argv[i]) > 128) ++ { ++ msg("file name too long"); ++ endmsgs(); ++ refresh(); ++ sleep(1); ++ endwin(); ++ exit(1); ++ } ++ if (strlen(args) + strlen(argv[i]) + 1 >= sizeof(args)) ++ { ++ msg("too many files on command line"); ++ endmsgs(); ++ refresh(); ++ sleep(1); ++ endwin(); ++ exit(1); ++ } ++ if (args[0]) strcat(args, " "); ++ strcat(args, argv[i++]); + } + } + #if ! ( MSDOS || TOS ) diff --git a/debian/patches/failsleep.patch b/debian/patches/failsleep.patch new file mode 100644 index 0000000..a9cf937 --- /dev/null +++ b/debian/patches/failsleep.patch @@ -0,0 +1,23 @@ +diff '--exclude=debian' -ruN x/elvis-tiny-1.4.orig/tmp.c elvis-tiny-1.4/tmp.c +--- x/elvis-tiny-1.4.orig/tmp.c 2012-06-29 19:22:09.285371591 +0000 ++++ elvis-tiny-1.4/tmp.c 2012-06-29 17:54:18.000000000 +0000 +@@ -76,7 +76,7 @@ + + + /* The FAIL() macro prints an error message and then exits. */ +-#define FAIL(why,arg) mode = MODE_EX; msg(why, arg); endwin(); exit(9) ++#define FAIL(why,arg) mode = MODE_EX; msg(why, arg); sleep(1); endwin(); exit(9) + + /* This is the name of the temp file */ + static char tmpname[80]; +diff '--exclude=debian' -ruN x/elvis-tiny-1.4.orig/main.c elvis-tiny-1.4/main.c +--- x/elvis-tiny-1.4.orig/main.c 2012-06-29 19:22:52.702106206 +0000 ++++ elvis-tiny-1.4/main.c 2012-06-29 17:54:18.000000000 +0000 +@@ -124,6 +124,7 @@ + msg("Use the `virec` program to recover lost files"); + endmsgs(); + refresh(); ++ sleep(1); + endwin(); + exit(0); + break; diff --git a/debian/patches/filesize.patch b/debian/patches/filesize.patch new file mode 100644 index 0000000..a971495 --- /dev/null +++ b/debian/patches/filesize.patch @@ -0,0 +1,33 @@ +diff '--exclude=debian' -ruN x/elvis-tiny-1.4.orig/cmd2.c elvis-tiny-1.4/cmd2.c +--- x/elvis-tiny-1.4.orig/cmd2.c 1991-08-04 21:20:31.000000000 +0000 ++++ elvis-tiny-1.4/cmd2.c 2012-06-29 17:54:18.000000000 +0000 +@@ -591,6 +591,12 @@ + msg("\"%s\" is not a regular file", extra); + return; + } ++ if (statb.st_size > 500000) ++ { ++ msg("\"%s\" is too large (>500000 bytes)", extra); ++ close(fd); ++ return; ++ } + #endif /* not CRUNCH */ + + /* get blocks from the file, and add them */ +diff '--exclude=debian' -ruN x/elvis-tiny-1.4.orig/tmp.c elvis-tiny-1.4/tmp.c +--- x/elvis-tiny-1.4.orig/tmp.c 2012-06-29 19:22:09.285371591 +0000 ++++ elvis-tiny-1.4/tmp.c 2012-06-29 17:54:18.000000000 +0000 +@@ -133,6 +133,13 @@ + msg("\"%s\" is not a regular file", origname); + return tmpstart(""); + } ++ /* Can't edit files > 500.000 bytes */ ++ if (statb.st_size > 500000) ++ { ++ msg("\"%s\" is too big (>500000 bytes)", ++ origname); ++ return tmpstart(""); ++ } + } + else + { diff --git a/debian/patches/makefile.patch b/debian/patches/makefile.patch new file mode 100644 index 0000000..1e2dedc --- /dev/null +++ b/debian/patches/makefile.patch @@ -0,0 +1,33 @@ +--- elvis-tiny-1.4.orig/Makefile.mix 1991-08-04 21:20:19.000000000 +0000 ++++ elvis-tiny-1.4/Makefile.mix 2012-06-29 20:26:25.262635031 +0000 +@@ -36,6 +36,30 @@ + # DUMMY usually nothing, but OS9 needs "dummy" + # DOC name of "doc" directory, with a trailing slash + ++#---- These settings are recommended for Linux ---- ++O= .o ++E= ++EXTRA= ++EXTRA2= ++LIBS= -ltinfo ++BIN= /usr/bin ++CFLAGS= -O2 -DM_SYSV -DCRUNCH -DNO_MKEXRC -DNO_CURSORSHAPE -DNO_CHARATTR \ ++ -DNO_SHOWMODE -DNO_MODELINE -DNO_OPTCOLS -DNO_DIGRAPH -DNO_ABBR \ ++ -DNO_AT -DNO_SENTENCE -DNO_ERRLIST -fsigned-char $(EXTRA_CFLAGS) ++OF= -o ++RF= -c ++DATE= -DDATE=\'\"`date`\"\' ++EVAL= eval ++PROGS= elvis ++CHMEM= ++SORT= -DSORT ++INST= inst.unix ++RM= rm -f ++PR1= refont -c ++PR2= | lpr ++DUMMY= ++DOC= doc/ ++ + #---- These settings are recommended for System-V UNIX and SCO XENIX-386 ---- + #O= .o + #E= diff --git a/debian/patches/patch-bug-258640.diff b/debian/patches/patch-bug-258640.diff new file mode 100644 index 0000000..296a618 --- /dev/null +++ b/debian/patches/patch-bug-258640.diff @@ -0,0 +1,16 @@ +--- elvis-tiny-1.4.orig/config.h 1991-08-04 21:20:32.000000000 +0000 ++++ elvis-tiny/config.h 2012-06-29 20:37:02.505417127 +0000 +@@ -178,13 +178,6 @@ + # 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 diff --git a/debian/patches/patch-fix-resize.diff b/debian/patches/patch-fix-resize.diff new file mode 100644 index 0000000..6900b72 --- /dev/null +++ b/debian/patches/patch-fix-resize.diff @@ -0,0 +1,41 @@ + +* Use sigaction() instead of signal() for SIGWINCH, and do not set SA_RESTART. + This way read() will return -1/EINTR and elvis will resize the screen. +* Include for TIOCGWINSZ + +diff -ruN t/elvis-tiny-1.4/curses.c elvis-tiny-1.4/curses.c +--- t/elvis-tiny-1.4/curses.c 2008-04-30 23:09:50.000000000 +0200 ++++ elvis-tiny-1.4/curses.c 2008-04-30 23:28:49.000000000 +0200 +@@ -26,6 +26,10 @@ + # endif + #endif + ++#ifdef __linux__ ++# include ++#endif ++ + #if TOS + # include + #endif +@@ -483,13 +487,20 @@ + { + int lines; + int cols; ++#ifdef SIGWINCH ++ struct sigaction sa; ++#endif + #ifdef TIOCGWINSZ + struct winsize size; + #endif + + #ifdef SIGWINCH + /* reset the signal vector */ +- signal(SIGWINCH, getsize); ++ if (signo == 0) { ++ memset(&sa, 0, sizeof(sa)); ++ sa.sa_handler = getsize; ++ sigaction(SIGWINCH, &sa, 0); ++ } + #endif + + /* get the window size, one way or another. */ diff --git a/debian/patches/patch-range-segfault.diff b/debian/patches/patch-range-segfault.diff new file mode 100644 index 0000000..1c6bb68 --- /dev/null +++ b/debian/patches/patch-range-segfault.diff @@ -0,0 +1,55 @@ +Date: Mon, 17 Jan 2000 00:35:57 -0800 +To: bugs@debian.org +From: Ross Boylan +Subject: vi (?) segfaults +Mime-Version: 1.0 +Content-Type: text/plain; charset="us-ascii" + +Package: boot-floppies +Version: 2.2.4 (potato i386) + +I suspect this problem is really for elvis-tiny 1.4-7, which dpkg showed as +installed, but I'm not 100% sure. Please reassign as appropriate. If I vi +sources.list and execute the command + :1,44s/stable/potato/g +I get a segmentation fault. Using % in place of 1,44 works alright. + +After the seg fault, the file is locked, and can't be edited further. I +had to reboot the system (I know there's a better way; fee free to +enlighten me) to access the file. (Well, I also did a mv sources.list +s.l.bak; cp s.l.bak sources.list; rm s.l.bak. But there's a better way +than that too, right?) + +But to return the original subject: the file doesn't have 44 lines. But a +segfault seems like a poor reaction to that problem... + + +From: "David Douthitt" +To: miquels@cistron.nl +Date: Wed, 13 Dec 2000 09:36:44 -0600 +Subject: elvis-tiny +Message-ID: <3A37432C.147.3BB4F415@localhost> + +I have a few fixes for elvis-tiny, one for bug #55407 (vi segfaults) +and one for bug #73059 (substitute & fails on third substitution). + +I've included them in the text below. They're two separate patches; +so cut them out into two files. + +I don't know if I qualify as "maintainer" but I'll see if I can keep +fixing and learning as time goes on. + +--- elvis-tiny-1.4/ex.c.orig Sun Aug 4 23:20:37 1991 ++++ elvis-tiny-1.4/ex.c Sun Jan 14 16:25:29 2001 +@@ -390,6 +390,11 @@ + msg("first address exceeds the second"); + return; + } ++ if (markline(tomark) > markline(MARK_LAST)) ++ { ++ msg("there are only %d lines in the file", nlines); ++ return; ++ } + } + isdfl = (scan == cmdbuf); + diff --git a/debian/patches/patch-siglongjmp.diff b/debian/patches/patch-siglongjmp.diff new file mode 100644 index 0000000..5ca5baf --- /dev/null +++ b/debian/patches/patch-siglongjmp.diff @@ -0,0 +1,59 @@ + +setjmp/longjmp are used to jump out of signal handlers. That +causes weirdness. Use sigsetjmp/siglongjmp. + +diff -ruN t/elvis-tiny-1.4/main.c elvis-tiny-1.4/main.c +--- t/elvis-tiny-1.4/main.c 2008-04-30 23:09:50.000000000 +0200 ++++ elvis-tiny-1.4/main.c 2008-04-30 22:54:52.000000000 +0200 +@@ -17,7 +17,7 @@ + + extern trapint(); /* defined below */ + extern char *getenv(); +-jmp_buf jmpenv; ++sigjmp_buf jmpenv; + + #ifndef NO_DIGRAPH + static init_digraphs(); +@@ -307,7 +307,7 @@ + */ + while (mode != MODE_QUIT) + { +- if (setjmp(jmpenv)) ++ if (sigsetjmp(jmpenv, 1)) + { + /* Maybe we just aborted a change? */ + abortdo(); +@@ -368,7 +368,7 @@ + #else + signal(signo, trapint); + #endif +- longjmp(jmpenv, 1); ++ siglongjmp(jmpenv, 1); + + return 0; + } +diff -ruN t/elvis-tiny-1.4/tio.c elvis-tiny-1.4/tio.c +--- t/elvis-tiny-1.4/tio.c 2008-04-30 23:09:50.000000000 +0200 ++++ elvis-tiny-1.4/tio.c 2008-04-30 22:54:57.000000000 +0200 +@@ -347,10 +347,10 @@ + + #if !MSDOS && !TOS + # if BSD || COHERENT +-static jmp_buf env_timeout; ++static sigjmp_buf env_timeout; + static int dummy() + { +- longjmp(env_timeout, 1); ++ siglongjmp(env_timeout, 1); + return 0; + } + # else +@@ -549,7 +549,7 @@ + while (j > 1) + { + #if BSD || COHERENT +- if (setjmp(env_timeout)) ++ if (sigsetjmp(env_timeout, 1)) + { + /* we timed out - assume no mapping */ + j = 0; diff --git a/debian/patches/patch-substitute-fails.diff b/debian/patches/patch-substitute-fails.diff new file mode 100644 index 0000000..7c6ca36 --- /dev/null +++ b/debian/patches/patch-substitute-fails.diff @@ -0,0 +1,79 @@ +From: "David Douthitt" +To: submit@bugs.debian.org +Date: Tue, 3 Oct 2000 11:01:13 -0500 +Subject: Substitution command & fails on third (second?) try +Reply-to: ddouthitt@mennonite.minister.net +Message-ID: <39D9BC79.32551.23FF0C@localhost> + +Package: elvis-tiny +Version: 1.4 + +Do the following on a file: + +:s/tcp/TCP!/ +j +& +j +& + +The first substitution works, then the first & , then on the second & +it replaces the substitution string with a '~' character. + +For example, given: + +my-tcp +his-tcp +her-tcp +their-tcp + +The results would be: + +my-TCP! +his-TCP! +her-~ +their-~ + +This is consistent. +From: "David Douthitt" +To: miquels@cistron.nl +Date: Wed, 13 Dec 2000 09:36:44 -0600 +Subject: elvis-tiny +Reply-to: n9ubh@callsign.net +Message-ID: <3A37432C.147.3BB4F415@localhost> + +I have a few fixes for elvis-tiny, one for bug #55407 (vi segfaults) +and one for bug #73059 (substitute & fails on third substitution). + +I've included them in the text below. They're two separate patches; +so cut them out into two files. + +I don't know if I qualify as "maintainer" but I'll see if I can keep +fixing and learning as time goes on. + +--- elvis-tiny-1.4/regsub.c.orig Sun Jan 14 16:23:39 2001 ++++ elvis-tiny-1.4/regsub.c Sun Jan 14 16:24:13 2001 +@@ -194,10 +194,18 @@ + } + *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); ++#ifndef NO_MAGIC ++ /* Don't copy the pattern if it is '~'; leave previous copy alone */ ++ if (! (*start == '~' && *o_magic)) ++ { ++#endif ++ /* remember what text we inserted this time */ ++ if (previous) ++ free(previous); ++ previous = (char *)malloc((unsigned)(strlen(start) + 1)); ++ if (previous) ++ strcpy(previous, start); ++#ifndef NO_MAGIC ++ } ++#endif + } diff --git a/debian/patches/patch-tempfile.diff b/debian/patches/patch-tempfile.diff new file mode 100644 index 0000000..bdf8f5c --- /dev/null +++ b/debian/patches/patch-tempfile.diff @@ -0,0 +1,209 @@ +From: Topi Miettinen +To: submit@bugs.debian.org +Date: Tue, 17 Oct 2000 20:30:00 +0300 +Subject: Bug#74976: elvis-tiny: temporary file problems + +Package: elvis-tiny +Version: 1.4-9 +Severity: grave + +Elvis-tiny (probably full elvis also) has serious problems with temporary +file use. Those files are created with a predictable pattern and O_EXCL +flag is not used when opening. This makes elvis users vulnerable to race +conditions and/or data lossage. + +======================================================================= + +Topi included a patch to fix these problems, but the patch itself +was broken in 2 ways: + +1. Recovery of lost files didn't work anymore +2. :w to an existing file broke. + +The patch below by Miquel van Smoorenburg, based on Topi's bugreport +and patch, should fix the bug without any side effects. + + +--- elvis-tiny-1.4/Makefile.mix.orig 2012-06-29 20:26:25.262635031 +0000 ++++ elvis-tiny-1.4/Makefile.mix 2012-06-29 20:23:07.000000000 +0000 +@@ -45,7 +45,8 @@ + BIN= /usr/bin + CFLAGS= -O2 -DM_SYSV -DCRUNCH -DNO_MKEXRC -DNO_CURSORSHAPE -DNO_CHARATTR \ + -DNO_SHOWMODE -DNO_MODELINE -DNO_OPTCOLS -DNO_DIGRAPH -DNO_ABBR \ +- -DNO_AT -DNO_SENTENCE -DNO_ERRLIST -fsigned-char $(EXTRA_CFLAGS) ++ -DNO_AT -DNO_SENTENCE -DNO_ERRLIST -DUSE_MKSTEMP -DUSE_SNPRINTF \ ++ -fsigned-char $(EXTRA_CFLAGS) + OF= -o + RF= -c + DATE= -DDATE=\'\"`date`\"\' +diff -ruN elvis-tiny-1.4.b4/cmd1.c elvis-tiny-1.4/cmd1.c +--- elvis-tiny-1.4.b4/cmd1.c Tue Nov 28 11:50:09 2000 ++++ elvis-tiny-1.4/cmd1.c Tue Nov 28 11:53:23 2000 +@@ -158,6 +158,8 @@ + mark[*extra - 'a'] = tomark; + } + ++void cmd_write2(MARK frommark, MARK tomark, int fd); ++ + /*ARGSUSED*/ + void cmd_write(frommark, tomark, cmd, bang, extra) + MARK frommark; +@@ -168,9 +170,6 @@ + { + 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) +@@ -221,6 +220,16 @@ + return; + } + } ++ cmd_write2(frommark, tomark, fd); ++ close(fd); ++} ++ ++void cmd_write2(MARK frommark, MARK tomark, int fd) ++{ ++ REG long l; ++ REG char *scan; ++ REG int i; ++ + for (l = markline(frommark); l <= markline(tomark); l++) + { + /* get the next line */ +@@ -231,7 +240,6 @@ + /* print the line */ + twrite(fd, scan, i); + } +- close(fd); + } + + +diff -ruN elvis-tiny-1.4.b4/system.c elvis-tiny-1.4/system.c +--- elvis-tiny-1.4.b4/system.c Tue Nov 28 11:50:09 2000 ++++ elvis-tiny-1.4/system.c Tue Nov 28 12:00:54 2000 +@@ -22,6 +22,8 @@ + + #include "config.h" + #include "vi.h" ++#include ++#include + #include + extern char **environ; + +@@ -331,7 +333,7 @@ + { + 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 */ ++ char *scrout = NULL; /* name of the scratch out file */ + MARK new; /* place where new text should go */ + int i; + +@@ -339,6 +341,9 @@ + if (to) + { + /* we have lines */ ++ scrout = malloc(strlen(o_directory) + 9 + 1); /* strlen("/soXXXXXX") */ ++ if (!scrout) ++ return -1; + #if MSDOS || TOS + strcpy(scrout, o_directory); + if ((i=strlen(scrout)) && strchr("\\/:", scrout[i-1])) +@@ -347,6 +352,7 @@ + #else + sprintf(scrout, SCRATCHOUT, o_directory); + #endif ++#if !USE_MKSTEMP + mktemp(scrout); + cmd_write(from, to, CMD_BANG, 0, scrout); + +@@ -357,6 +363,13 @@ + unlink(scrout); + return -1; + } ++#else ++ if ((scratch = mkstemp(scrout)) < 0) ++ return -1; ++ /* use those lines as stdin */ ++ cmd_write2(from, to, scratch); ++ lseek(scratch, 0L, SEEK_SET); ++#endif + } + else + { +@@ -371,6 +384,7 @@ + { + close(scratch); + unlink(scrout); ++ free(scrout); + } + return -1; + } +@@ -429,6 +443,7 @@ + { + close(scratch); + unlink(scrout); ++ free(scrout); + } + return 0; + } +diff -ruN elvis-tiny-1.4.b4/tmp.c elvis-tiny-1.4/tmp.c +--- elvis-tiny-1.4.b4/tmp.c Tue Nov 28 11:50:09 2000 ++++ elvis-tiny-1.4/tmp.c Tue Nov 28 13:02:18 2000 +@@ -23,6 +23,7 @@ + # include + # endif + #endif ++#include + + + #ifndef NO_MODELINE +@@ -193,7 +194,12 @@ + tmpname[i++]=SLASH; + sprintf(tmpname+i, TMPNAME+3, sum, statb.st_ino, statb.st_dev); + #else ++# if USE_SNPRINTF ++ snprintf(tmpname, sizeof(tmpname), TMPNAME, ++ o_directory, sum, statb.st_ino, statb.st_dev); ++# else + sprintf(tmpname, TMPNAME, o_directory, sum, statb.st_ino, statb.st_dev); ++# endif + #endif + + /* make sure nobody else is editing the same file */ +@@ -209,11 +215,28 @@ + + /* create the temp file */ + #if ANY_UNIX +- close(creat(tmpname, 0600)); /* only we can read it */ ++# if USE_MKSTEMP ++ scan = malloc(strlen(o_directory) + 10 + 1); /* "/elvXXXXXX" */ ++ if (scan == NULL) { ++ FAIL("No memory: %s", strerror(errno)); ++ } ++ sprintf(scan, "%s/elvXXXXXX", o_directory); ++ if ((tmpfd = mkstemp(scan)) >= 0) { ++ if (link(scan, tmpname) < 0) { ++ close(tmpfd); ++ tmpfd = -1; ++ } ++ unlink(scan); ++ } ++ free(scan); ++# else ++ tmpfd = open(tmpname, O_CREAT|O_WRONLY|O_TRUNC|O_EXCL, 0600); ++ /* only we can read it */ ++# endif + #else + close(creat(tmpname, FILEPERMS)); /* anybody body can read it, alas */ +-#endif + tmpfd = open(tmpname, O_RDWR | O_BINARY); ++#endif + if (tmpfd < 0) + { + FAIL("Can't create temporary file, errno=%d", errno); diff --git a/debian/patches/redraw.patch b/debian/patches/redraw.patch new file mode 100644 index 0000000..b259a12 --- /dev/null +++ b/debian/patches/redraw.patch @@ -0,0 +1,37 @@ +diff '--exclude=debian' -ruN x/elvis-tiny-1.4.orig/redraw.c elvis-tiny-1.4/redraw.c +--- x/elvis-tiny-1.4.orig/redraw.c 1991-08-04 21:20:49.000000000 +0000 ++++ elvis-tiny-1.4/redraw.c 2012-06-29 17:54:18.000000000 +0000 +@@ -354,7 +354,16 @@ + if (i == '\t' && !*o_list) + { + i = col + tabstop - (col % tabstop); ++#if 1 ++ /* ++ * With modern terminals, autowrap occurs only ++ * if a new character is added after limitcol. ++ * -- miquels@cistron.nl 23-Jan-2002 ++ */ ++ if (i < limitcol || (has_AM && i <= limitcol)) ++#else + if (i < limitcol) ++#endif + { + #ifdef CRUNCH + if (!clr && has_PT && !((i - leftcol) & 7)) +@@ -450,7 +459,16 @@ + clrtoeol(); + } + #endif ++#if 1 ++ /* ++ * With modern terminals, autowrap occurs only ++ * if a new character is added after limitcol. ++ * -- miquels@cistron.nl 22-Feb-1998 ++ */ ++ if (!has_AM || col <= limitcol) ++#else + if (!has_AM || col < limitcol) ++#endif + { + addch('\n'); + } diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 0000000..def6f80 --- /dev/null +++ b/debian/patches/series @@ -0,0 +1,13 @@ +makefile.patch +argsize.patch +failsleep.patch +filesize.patch +redraw.patch +termios.patch +patch-tempfile.diff +warnings.patch +patch-substitute-fails.diff +patch-range-segfault.diff +patch-bug-258640.diff +patch-siglongjmp.diff +patch-fix-resize.diff diff --git a/debian/patches/termios.patch b/debian/patches/termios.patch new file mode 100644 index 0000000..48af18b --- /dev/null +++ b/debian/patches/termios.patch @@ -0,0 +1,69 @@ +diff '--exclude=debian' -ruN x/elvis-tiny-1.4.orig/curses.c elvis-tiny-1.4/curses.c +--- x/elvis-tiny-1.4.orig/curses.c 2012-06-29 19:23:00.014231174 +0000 ++++ elvis-tiny-1.4/curses.c 2012-06-29 17:54:18.000000000 +0000 +@@ -19,7 +19,7 @@ + + #if ANY_UNIX + # if UNIXV +-# include ++# include + # undef TIOCWINSZ /* we can't handle it correctly yet */ + # else + # include +@@ -105,8 +105,8 @@ + + #if ANY_UNIX + # if UNIXV +-static struct termio oldtermio; /* original tty mode */ +-static struct termio newtermio; /* cbreak/noecho tty mode */ ++static struct termios oldtermio; /* original tty mode */ ++static struct termios newtermio; /* cbreak/noecho tty mode */ + # else + static struct sgttyb oldsgttyb; /* original tty mode */ + static struct sgttyb newsgttyb; /* cbreak/nl/noecho tty mode */ +@@ -164,7 +164,7 @@ + /* change the terminal mode to cbreak/noecho */ + #if ANY_UNIX + # if UNIXV +- ioctl(2, TCGETA, &oldtermio); ++ tcgetattr(2, &oldtermio); + # else + ioctl(2, TIOCGETP, &oldsgttyb); + # endif +@@ -213,7 +213,7 @@ + /* change the terminal mode back the way it was */ + #if ANY_UNIX + # if UNIXV +- ioctl(2, TCSETAW, &oldtermio); ++ tcsetattr(2, TCSANOW, &oldtermio); + # else + ioctl(2, TIOCSETP, &oldsgttyb); + +@@ -243,19 +243,25 @@ + /* change the terminal mode to cbreak/noecho */ + #if ANY_UNIX + # if UNIXV +- ospeed = (oldtermio.c_cflag & CBAUD); ++ ospeed = cfgetospeed(&oldtermio); + 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 */ ++#ifdef VSUSP ++ newtermio.c_cc[VSUSP] = 0; /* disable ^Z for elvis */ ++#endif ++#ifdef VLNEXT ++ newtermio.c_cc[VLNEXT] = 0; /* disable ^V for elvis */ ++#endif + newtermio.c_cc[VMIN] = 1; + newtermio.c_cc[VTIME] = 0; + # ifdef VSWTCH + newtermio.c_cc[VSWTCH] = 0; + # endif +- ioctl(2, TCSETAW, &newtermio); ++ tcsetattr(2, TCSANOW, &newtermio); + # else /* BSD or V7 or Coherent or Minix */ + struct tchars tbuf; + # ifdef TIOCSLTC diff --git a/debian/patches/warnings.patch b/debian/patches/warnings.patch new file mode 100644 index 0000000..b30111f --- /dev/null +++ b/debian/patches/warnings.patch @@ -0,0 +1,326 @@ +diff -ruN elvis-tiny-1.4.orig/main.c elvis-tiny-1.4.test2/main.c +--- elvis-tiny-1.4.orig/main.c 2012-06-29 21:27:00.000000000 +0000 ++++ elvis-tiny-1.4/main.c 2012-06-29 22:01:48.651235702 +0000 +@@ -13,6 +13,9 @@ + #include "config.h" + #include + #include ++#include ++#include ++#include + #include "vi.h" + + extern trapint(); /* defined below */ +@@ -25,7 +25,7 @@ + + /*---------------------------------------------------------------------*/ + +-void main(argc, argv) ++int main(argc, argv) + int argc; + char *argv[]; + { +@@ -330,7 +349,7 @@ + refresh(); + endwin(); + +- exit(0); ++ return(0); + /*NOTREACHED*/ + } + +diff -ruN elvis-tiny-1.4.orig/tio.c elvis-tiny-1.4.test2/tio.c +--- elvis-tiny-1.4.orig/tio.c 2012-06-29 21:27:00.000000000 +0000 ++++ elvis-tiny-1.4/tio.c 2012-06-29 21:59:40.945080102 +0000 +@@ -15,6 +15,9 @@ + # include + #endif + #include ++#include ++#include ++#include + #include "vi.h" + + +@@ -354,7 +354,7 @@ + return 0; + } + # else +-static int dummy() ++static void dummy() + { + } + # endif +diff -ruN elvis-tiny-1.4.orig/tmp.c elvis-tiny-1.4.test2/tmp.c +--- elvis-tiny-1.4.orig/tmp.c 2012-06-29 21:27:00.000000000 +0000 ++++ elvis-tiny-1.4/tmp.c 2012-06-29 21:58:55.172307677 +0000 +@@ -13,6 +13,9 @@ + + #include "config.h" + #include ++#include ++#include ++#include + #include "vi.h" + #if TOS + # include +@@ -24,7 +24,7 @@ + # endif + #endif + #include +- ++#include + + #ifndef NO_MODELINE + static void do_modeline(l, stop) +diff -ruN elvis-tiny-1.4.orig/cmd1.c elvis-tiny-1.4.test2/cmd1.c +--- elvis-tiny-1.4.orig/cmd1.c 2012-06-29 21:27:00.000000000 +0000 ++++ elvis-tiny-1.4/cmd1.c 2012-06-29 22:04:33.470016427 +0000 +@@ -14,6 +14,8 @@ + + #include "config.h" + #include ++#include ++#include + #include "vi.h" + #include "regexp.h" + +@@ -270,7 +272,7 @@ + suspend_curses(); + if (frommark == 0L) + { +- system(extra); ++ elvis_system(extra); + } + else /* pipe lines from the file through the command */ + { +diff -ruN elvis-tiny-1.4.orig/cmd2.c elvis-tiny-1.4.test2/cmd2.c +--- elvis-tiny-1.4.orig/cmd2.c 2012-06-29 21:27:00.000000000 +0000 ++++ elvis-tiny-1.4/cmd2.c 2012-06-29 22:05:11.710661704 +0000 +@@ -11,6 +11,9 @@ + /* This file contains some of the commands - mostly ones that change text */ + + #include ++#include ++#include ++#include + #include "config.h" + #include "vi.h" + #include "regexp.h" +@@ -23,7 +26,7 @@ + # include + # endif + #endif +- ++#include + + /*ARGSUSED*/ + void cmd_substitute(frommark, tomark, cmd, bang, extra) +diff -ruN elvis-tiny-1.4.orig/config.h elvis-tiny-1.4.test2/config.h +--- elvis-tiny-1.4.orig/config.h 2012-06-29 21:27:00.000000000 +0000 ++++ elvis-tiny-1.4/config.h 2012-06-29 21:56:09.341509847 +0000 +@@ -148,7 +148,6 @@ + #if BSD || UNIX7 || OSK + # define strchr index + #endif +-extern char *strchr(); + + /* BSD uses bcopy() instead of memcpy() */ + #if BSD +@@ -219,6 +211,10 @@ + # endif + #endif + ++#ifndef ANY_UNIX ++# define elvis_system system ++#endif ++ + #if MSDOS || TOS + /* do not change TMPNAME, CUTNAME and SCRATCH*: they MUST begin with '%s\\'! */ + # ifndef TMPDIR +diff -ruN elvis-tiny-1.4.orig/curses.c elvis-tiny-1.4.test2/curses.c +--- elvis-tiny-1.4.orig/curses.c 2012-06-29 21:27:00.000000000 +0000 ++++ elvis-tiny-1.4/curses.c 2012-06-29 21:40:40.521843315 +0000 +@@ -35,6 +35,8 @@ + #endif + + #include ++#include ++#include + + extern char *getenv(); + static void starttcap(); +diff -ruN elvis-tiny-1.4.orig/cut.c elvis-tiny-1.4.test2/cut.c +--- elvis-tiny-1.4.orig/cut.c 1991-08-04 21:20:36.000000000 +0000 ++++ elvis-tiny-1.4/cut.c 2012-06-29 22:02:36.956050189 +0000 +@@ -20,6 +20,10 @@ + #define rename(a,b) Frename(0,a,b) + #endif + ++#include ++#include ++#include ++ + # define NANNONS 9 /* number of annonymous buffers */ + + static struct cutbuf +Binary files elvis-tiny-1.4.orig/elvis and elvis-tiny-1.4.test2/elvis differ +diff -ruN elvis-tiny-1.4.orig/ex.c elvis-tiny-1.4.test2/ex.c +--- elvis-tiny-1.4.orig/ex.c 2012-06-29 21:27:00.000000000 +0000 ++++ elvis-tiny-1.4/ex.c 2012-06-29 21:41:51.715043811 +0000 +@@ -12,6 +12,7 @@ + + #include "config.h" + #include ++#include + #include "vi.h" + + #ifndef isascii +diff -ruN elvis-tiny-1.4.orig/input.c elvis-tiny-1.4.test2/input.c +--- elvis-tiny-1.4.orig/input.c 1991-08-04 21:20:39.000000000 +0000 ++++ elvis-tiny-1.4/input.c 2012-06-29 22:01:40.723101264 +0000 +@@ -15,7 +15,7 @@ + #include + #include "config.h" + #include "vi.h" +- ++#include + + #ifndef NO_DIGRAPH + static struct _DIG +diff -ruN elvis-tiny-1.4.orig/misc.c elvis-tiny-1.4.test2/misc.c +--- elvis-tiny-1.4.orig/misc.c 1991-08-04 21:20:40.000000000 +0000 ++++ elvis-tiny-1.4/misc.c 2012-06-29 21:44:19.449535042 +0000 +@@ -12,6 +12,7 @@ + + #include "config.h" + #include "vi.h" ++#include + + + /* find a particular line & return a pointer to a copy of its text */ +diff -ruN elvis-tiny-1.4.orig/modify.c elvis-tiny-1.4.test2/modify.c +--- elvis-tiny-1.4.orig/modify.c 1991-08-04 21:20:41.000000000 +0000 ++++ elvis-tiny-1.4/modify.c 2012-06-29 21:42:08.743330889 +0000 +@@ -8,6 +8,7 @@ + + #include "config.h" + #include "vi.h" ++#include + + #ifdef DEBUG + # include +diff -ruN elvis-tiny-1.4.orig/move2.c elvis-tiny-1.4.test2/move2.c +--- elvis-tiny-1.4.orig/move2.c 1991-08-04 21:20:43.000000000 +0000 ++++ elvis-tiny-1.4/move2.c 2012-06-29 21:42:35.279778404 +0000 +@@ -13,6 +13,7 @@ + #include "config.h" + #include "vi.h" + #include "regexp.h" ++#include + + extern long atol(); + +diff -ruN elvis-tiny-1.4.orig/opts.c elvis-tiny-1.4.test2/opts.c +--- elvis-tiny-1.4.orig/opts.c 1991-08-04 21:20:46.000000000 +0000 ++++ elvis-tiny-1.4/opts.c 2012-06-29 22:01:00.602424228 +0000 +@@ -14,6 +14,8 @@ + + #include "config.h" + #include "vi.h" ++#include ++#include + #ifndef NULL + #define NULL (char *)0 + #endif +diff -ruN elvis-tiny-1.4.orig/regexp.c elvis-tiny-1.4.test2/regexp.c +--- elvis-tiny-1.4.orig/regexp.c 1991-08-04 21:20:51.000000000 +0000 ++++ elvis-tiny-1.4/regexp.c 2012-06-29 21:40:49.137988598 +0000 +@@ -31,6 +31,8 @@ + + #include + #include ++#include ++#include + #include "config.h" + #include "vi.h" + #include "regexp.h" +diff -ruN elvis-tiny-1.4.orig/regsub.c elvis-tiny-1.4.test2/regsub.c +--- elvis-tiny-1.4.orig/regsub.c 2012-06-29 21:27:00.000000000 +0000 ++++ elvis-tiny-1.4/regsub.c 2012-06-29 21:41:02.030205985 +0000 +@@ -5,6 +5,8 @@ + */ + + #include ++#include ++#include + #include "config.h" + #include "vi.h" + #include "regexp.h" +diff -ruN elvis-tiny-1.4.orig/system.c elvis-tiny-1.4.test2/system.c +--- elvis-tiny-1.4.orig/system.c 2012-06-29 21:27:00.000000000 +0000 ++++ elvis-tiny-1.4/system.c 2012-06-29 22:00:32.609951944 +0000 +@@ -23,14 +23,17 @@ + #include + #include + #include ++#include ++#include ++#include + 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) ++int elvis_system(cmd) + char *cmd; /* a command to run */ + { + int status; /* exit status of the command */ +diff -ruN elvis-tiny-1.4.orig/vcmd.c elvis-tiny-1.4.test2/vcmd.c +--- elvis-tiny-1.4.orig/vcmd.c 1991-08-04 21:20:58.000000000 +0000 ++++ elvis-tiny-1.4/vcmd.c 2012-06-29 21:58:03.495435859 +0000 +@@ -13,6 +13,8 @@ + + #include "config.h" + #include "vi.h" ++#include ++#include + #if MSDOS + #include + #include +@@ -627,7 +629,7 @@ + waswarn = *o_warn; + *o_warn = FALSE; + suspend_curses(); +- if (system(cmdline)) ++ if (elvis_system(cmdline)) + { + addstr("<<< failed >>>\n"); + } +diff -ruN elvis-tiny-1.4.orig/vi.c elvis-tiny-1.4.test2/vi.c +--- elvis-tiny-1.4.orig/vi.c 1991-08-04 21:21:00.000000000 +0000 ++++ elvis-tiny-1.4/vi.c 2012-06-29 21:59:19.628720297 +0000 +@@ -10,6 +10,7 @@ + + #include "config.h" + #include ++#include + #include "vi.h" + + +diff -ruN elvis-tiny-1.4.orig/vi.h elvis-tiny-1.4.test2/vi.h +--- elvis-tiny-1.4.orig/vi.h 1991-08-04 21:21:02.000000000 +0000 ++++ elvis-tiny-1.4/vi.h 2012-06-29 21:57:35.398961810 +0000 +@@ -409,6 +409,9 @@ + extern void ex(); + extern void vi(); + extern void doexcmd(); ++#ifndef elvis_system ++extern int elvis_system(); ++#endif + + #ifndef NO_ABBR + extern void cmd_abbr(); diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..a07e92a --- /dev/null +++ b/debian/rules @@ -0,0 +1,43 @@ +#! /usr/bin/make -f + +tmp = $(shell pwd)/debian/elvis-tiny +tmp1 = $(shell pwd)/debian/elvis-tiny-udeb +ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS))) +EXTRA_CFLAGS = -g +endif + +%: + dh $@ + +override_dh_auto_build: + make -f Makefile.mix EXTRA_CFLAGS="$(EXTRA_CFLAGS)" + $(CC) $(EXTRA_CFLAGS) -O2 -o wrapper debian/wrapper.c + +override_dh_auto_install: + install -m 755 -d $(tmp)/bin + install -m 755 elvis $(tmp)/bin/elvis-tiny + install -m 755 wrapper $(tmp)/bin/vi + install -m 755 -d $(tmp1)/bin + install -m 755 elvis $(tmp1)/bin/elvis-tiny + install -m 755 wrapper $(tmp1)/bin/vi + +override_dh_auto_clean: + make -f Makefile.mix clobber + +override_dh_installchangelogs: + dh_installchangelogs debian/changelog.upstream + +# We used to strip as much as possible to save a few bytes (it made +# the vi wrapper fit in a 4K block on i386 - just) but it's probably +# a bad idea to mess with ELF comments and notes. +#override_dh_strip: +# dh_strip +#ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) +# strip --remove-section=.comment --remove-section=.note $(tmp)/bin/vi +# strip --remove-section=.comment --remove-section=.note $(tmp)/bin/elvis-tiny +#endif + +override_dh_clean: + dh_clean + rm -f wrapper elvis + diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/debian/wrapper.c b/debian/wrapper.c new file mode 100644 index 0000000..d61c70e --- /dev/null +++ b/debian/wrapper.c @@ -0,0 +1,72 @@ +/* + * vi-wrapper Should be installed as /bin/vi. This program first + * checks if /usr/bin/vi exists. If it does, it checks if + * it isn't a link to /bin/vi, and executes it. + * + * If /usr/bin/vi isn't present it does the same for + * /bin/elvis-tiny. + * + * Copyright 2000 Miquel van Smoorenburg miquels@cistron.nl + * License: GPL v2 or later. + * + */ +#include +#include +#include +#include +#include +#include + +#define REALVI "/usr/bin/vi" +#define TINYVI "/bin/elvis-tiny" +#define WRAPVI "/bin/vi" + +/* + * Avoid stdio - it saves a few hundred bytes, and so keeps the + * size of the executable _just_ under 4096 bytes. + */ +#define err(x) write(2, x, sizeof(x) - 1) + +int main(int argc, char **argv) +{ + struct stat real, tiny, wrap; + char *r = NULL; + int e; + + if (stat(WRAPVI, &wrap) != 0) { + err("vi: wrapper should be installed as " WRAPVI "\n"); + exit(1); + } + if (stat(REALVI, &real) == 0) { + if (real.st_dev == wrap.st_dev && real.st_ino == wrap.st_ino) { + err("vi: " REALVI + " is the same as the wrapper in " + WRAPVI "\n"); + exit(1); + } + execv(REALVI, argv); + r = REALVI; + e = errno; + } + if (stat(TINYVI, &tiny) == 0) { + if (tiny.st_dev == wrap.st_dev && tiny.st_ino == wrap.st_ino) { + err("vi: " TINYVI + " is the same as the wrapper in " + WRAPVI "\n"); + exit(1); + } + execv(TINYVI, argv); + r = TINYVI; + e = errno; + } + + if (r) { + errno = e; + perror(r); + exit(1); + } + err("vi: wrapper couldn't execute " REALVI + " nor " TINYVI "\n"); + return 1; +} + 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 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 key + :kl=: sequence sent by the key + :kr=: sequence sent by the key + :ku=: sequence sent by the key + :kP=: sequence sent by the key + :kN=: sequence sent by the key + :kh=: sequence sent by the key + :kH=: sequence sent by the key + + + + + + + + + + Elvis 1.4 TERMCAP Page 10-2 + + + Originally, termcap didn't have any names for the , + , , and 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 and , 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 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 you will have + switched to visual command mode. So, one way or another, after + 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 lines (default 1/2 screen) +count ^E scroll up 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 lines (default 1/2 screen) + ^V --- + ^W --- + ^X --- +count ^Y scroll down 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 sentences MOVE +count ) move forward 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 # (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 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 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 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 chars by a given character EDIT +count s inp replace 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 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 paragraphs MOVE +count | move to column (the leftmost column is 1) +count } move forward 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 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 , , , and keys work in + input mode, too. The 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 + , 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 +#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 +#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 +#include +#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, ""); + if (has_KD) mapkey(has_KD, "j", WHEN_VICMD|WHEN_INMV, ""); + if (has_KL) mapkey(has_KL, "h", WHEN_VICMD|WHEN_INMV, ""); + if (has_KR) mapkey(has_KR, "l", WHEN_VICMD|WHEN_INMV, ""); + if (has_HM) mapkey(has_HM, "^", WHEN_VICMD|WHEN_INMV, ""); + if (has_EN) mapkey(has_EN, "$", WHEN_VICMD|WHEN_INMV, ""); + if (has_PU) mapkey(has_PU, "\002", WHEN_VICMD|WHEN_INMV, ""); + if (has_PD) mapkey(has_PD, "\006", WHEN_VICMD|WHEN_INMV, ""); +#if MSDOS + if (*o_pcbios) + { + mapkey("#R", "i", WHEN_VICMD|WHEN_INMV, ""); + mapkey("#S", "x", WHEN_VICMD|WHEN_INMV, ""); + mapkey("#s", "B", WHEN_VICMD|WHEN_INMV, "^"); + mapkey("#t", "W", WHEN_VICMD|WHEN_INMV, "^"); + } +#else + if (ERASEKEY != '\177') + { + mapkey("\177", "x", WHEN_VICMD|WHEN_INMV, ""); + } +#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 +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 +#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 +#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 +#include "osk.h" +#include +#include +#endif +#include +#include +#include +#include + +/* + * 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 +#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 + +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, ®s, ®s); + 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, ®s, ®s); + 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, ®s, ®s); + 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 + +/* 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 +#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 +#include +#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 +#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 +#include +#include +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; i2) + 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 +extern unsigned char _osmajor; +#endif +#if TOS +#include +#endif + + +#if MSDOS || TOS +#include + +/* + * 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 +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 +#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 +#endif +#include +#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 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 +#include "vi.h" +#if TOS +# include +#else +# if OSK +# include "osk.h" +# else +# include +# 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 +#include +#endif +#if TOS +#include +#include +#endif +#if OSK +# include +#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 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 ."); + } +# 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 +#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 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 +extern int errno; +#if TOS +#define ENOENT (-AEFILNF) +#endif + +#if TOS +# include +# define O_RDONLY 0 +# define O_WRONLY 1 +# define O_RDWR 2 +#else +# if OSK +# include +# define O_RDONLY S_IREAD +# define O_WRONLY S_IWRITE +# define O_RDWR (S_IREAD | S_IWRITE) +# define ENOENT E_PNNF +# else +# include +# if COHERENT +# include +# else +# include +# 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 +#include +#include "vi.h" +#if TOS +# include +#else +# if OSK +# include "osk.h" +# else +# include +# 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 +#include +#ifdef __TURBOC__ +#include +#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 +#endif +#ifdef M68000 +#include +#include +#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=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