From 1853fcefb421c253b7a364b380cc5d0401497254 Mon Sep 17 00:00:00 2001 From: Metztli Date: Wed, 21 Sep 2016 00:35:39 -0700 Subject: [PATCH] first commit elvis-tiny for debian --- Elvis.lnk | 8 + Elvis.mak | 123 ++ Elvis.prj | 29 + KNOWN.BUGS | 43 + Makefile | 1 + Makefile.mix | 472 ++++++++ README | 32 + alias.c | 102 ++ atari.c | 94 ++ blk.c | 468 ++++++++ cmd1.c | 1268 ++++++++++++++++++++ cmd2.c | 896 ++++++++++++++ config.h | 351 ++++++ ctags.c | 350 ++++++ curses.c | 729 +++++++++++ curses.h | 252 ++++ cut.c | 668 +++++++++++ date.c | 0 debian/changelog | 207 ++++ debian/changelog.upstream | 3 + debian/compat | 1 + debian/control | 23 + debian/copyright | 61 + debian/elvis-tiny.1 | 10 + debian/elvis-tiny.docs | 1 + debian/elvis-tiny.lintian-overrides | 3 + debian/elvis-tiny.manpages | 1 + debian/elvis-tiny.postinst | 41 + debian/elvis-tiny.prerm | 12 + debian/patches/argsize.patch | 37 + debian/patches/failsleep.patch | 23 + debian/patches/filesize.patch | 33 + debian/patches/makefile.patch | 33 + debian/patches/patch-bug-258640.diff | 16 + debian/patches/patch-fix-resize.diff | 41 + debian/patches/patch-range-segfault.diff | 55 + debian/patches/patch-siglongjmp.diff | 59 + debian/patches/patch-substitute-fails.diff | 79 ++ debian/patches/patch-tempfile.diff | 209 ++++ debian/patches/redraw.patch | 37 + debian/patches/series | 13 + debian/patches/termios.patch | 69 ++ debian/patches/warnings.patch | 326 +++++ debian/rules | 43 + debian/source/format | 1 + debian/wrapper.c | 72 ++ doc/cflags.doc | 264 ++++ doc/ctags.man | 65 + doc/cutbufs.doc | 198 +++ doc/differ.doc | 198 +++ doc/elvis.man | 89 ++ doc/environ.doc | 66 + doc/ex.doc | 528 ++++++++ doc/index.doc | 264 ++++ doc/internal.doc | 198 +++ doc/intro.doc | 66 + doc/options.doc | 594 +++++++++ doc/ref.man | 21 + doc/refont.man | 67 ++ doc/regexp.doc | 132 ++ doc/termcap.doc | 132 ++ doc/versions.doc | 264 ++++ doc/virec.man | 51 + doc/visual.doc | 330 +++++ ex.c | 675 +++++++++++ input.c | 817 +++++++++++++ main.c | 422 +++++++ misc.c | 103 ++ modify.c | 479 ++++++++ move1.c | 623 ++++++++++ move2.c | 238 ++++ move3.c | 163 +++ move4.c | 213 ++++ move5.c | 230 ++++ opts.c | 680 +++++++++++ osk.c | 205 ++++ osk.h | 26 + pc.c | 270 +++++ profile.sh | 2 + recycle.c | 111 ++ redraw.c | 999 +++++++++++++++ ref.c | 140 +++ refont.c | 475 ++++++++ regexp.c | 830 +++++++++++++ regexp.h | 21 + regsub.c | 203 ++++ shell.c | 231 ++++ sysdos.c | 158 +++ system.c | 434 +++++++ tinytcap.c | 154 +++ tio.c | 864 +++++++++++++ tmp.c | 591 +++++++++ vars.c | 93 ++ vcmd.c | 764 ++++++++++++ vi.c | 767 ++++++++++++ vi.h | 517 ++++++++ virec.c | 245 ++++ wildcard.c | 140 +++ 98 files changed, 23805 insertions(+) create mode 100644 Elvis.lnk create mode 100644 Elvis.mak create mode 100644 Elvis.prj create mode 100644 KNOWN.BUGS create mode 120000 Makefile create mode 100644 Makefile.mix create mode 100644 README create mode 100644 alias.c create mode 100644 atari.c create mode 100644 blk.c create mode 100644 cmd1.c create mode 100644 cmd2.c create mode 100644 config.h create mode 100644 ctags.c create mode 100644 curses.c create mode 100644 curses.h create mode 100644 cut.c create mode 100644 date.c create mode 100644 debian/changelog create mode 100644 debian/changelog.upstream create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/elvis-tiny.1 create mode 100644 debian/elvis-tiny.docs create mode 100644 debian/elvis-tiny.lintian-overrides create mode 100644 debian/elvis-tiny.manpages create mode 100644 debian/elvis-tiny.postinst create mode 100644 debian/elvis-tiny.prerm create mode 100644 debian/patches/argsize.patch create mode 100644 debian/patches/failsleep.patch create mode 100644 debian/patches/filesize.patch create mode 100644 debian/patches/makefile.patch create mode 100644 debian/patches/patch-bug-258640.diff create mode 100644 debian/patches/patch-fix-resize.diff create mode 100644 debian/patches/patch-range-segfault.diff create mode 100644 debian/patches/patch-siglongjmp.diff create mode 100644 debian/patches/patch-substitute-fails.diff create mode 100644 debian/patches/patch-tempfile.diff create mode 100644 debian/patches/redraw.patch create mode 100644 debian/patches/series create mode 100644 debian/patches/termios.patch create mode 100644 debian/patches/warnings.patch create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 debian/wrapper.c create mode 100644 doc/cflags.doc create mode 100644 doc/ctags.man create mode 100644 doc/cutbufs.doc create mode 100644 doc/differ.doc create mode 100644 doc/elvis.man create mode 100644 doc/environ.doc create mode 100644 doc/ex.doc create mode 100644 doc/index.doc create mode 100644 doc/internal.doc create mode 100644 doc/intro.doc create mode 100644 doc/options.doc create mode 100644 doc/ref.man create mode 100644 doc/refont.man create mode 100644 doc/regexp.doc create mode 100644 doc/termcap.doc create mode 100644 doc/versions.doc create mode 100644 doc/virec.man create mode 100644 doc/visual.doc create mode 100644 ex.c create mode 100644 input.c create mode 100644 main.c create mode 100644 misc.c create mode 100644 modify.c create mode 100644 move1.c create mode 100644 move2.c create mode 100644 move3.c create mode 100644 move4.c create mode 100644 move5.c create mode 100644 opts.c create mode 100644 osk.c create mode 100644 osk.h create mode 100644 pc.c create mode 100644 profile.sh create mode 100644 recycle.c create mode 100644 redraw.c create mode 100644 ref.c create mode 100644 refont.c create mode 100644 regexp.c create mode 100644 regexp.h create mode 100644 regsub.c create mode 100644 shell.c create mode 100644 sysdos.c create mode 100644 system.c create mode 100644 tinytcap.c create mode 100644 tio.c create mode 100644 tmp.c create mode 100644 vars.c create mode 100644 vcmd.c create mode 100644 vi.c create mode 100644 vi.h create mode 100644 virec.c create mode 100644 wildcard.c 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