[comp.windows.news] NeWS version of elvis

mh@roger.imsd.contel.com (Mike H.) (01/11/91)

The following postings in comp.windows.news are modifications I made to
version 1.4 of elvis to run under the NeWS toolkit (tNt).  If you are
not familiar with elvis it is a vi clone written by Steve Kirkendall
it's much better than stevie and the various wimply emacs vi emulators
so I suggest giving it a shot if you are a vi fan.

Although I intend to make some more additions to the elvis I am at the
point now where I'm going to stop for a bit until I get the new version
of tNt. As a result I figured I'd post what I've got at the moment
since this newsgroup has been extremely dead lately.

What exists now works well but does'nt have much in the way of neat
'window' things in it yet. I does however have some fixes for the more
annoying things present in the stock 1.4 version fixed. See the change
notes file in the following postings for more info on that subject.

I did not include the doc directory normally found in the elvis
release. you can get that from the stock version of elvis 1.4 which can
be had in the gnu ftp directory on prep.ai.mit.edu. Elvis 1.4 was also
posted to alt.sources awhile back by the author so if you know where an
alt.sources archive is you can get the docs there also.

p.s. - 

If anybody adds any neat things let me know. I'd love to have them.
Also, Josh Siegel at sun was kind enough to make mods to this version
of elvis to work under the new version of tNt. If there is a demand for
those mods i'll post those too.

mike hoegeman, mh@roger.imsd.contel.com

mh@roger.imsd.contel.com (Mike H.) (01/11/91)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	elvis.ps
#	elvis_cps.cps
# This archive created: Thu Jan 10 17:21:05 1991
export PATH; PATH=/bin:$PATH
if test -f 'elvis.ps'
then
	echo shar: will not over-write existing file "'elvis.ps'"
else
cat << \SHAR_EOF > 'elvis.ps'
%
% NeWS postscript machinery for the elvis editor
% Author: Mike Hoegeman, mh@wlbr.imsd.contel.com
%

systemdict dup /ElvisCanvas known %%
pop false 
{
    pop
} {

ClassCanvas
dictbegin
    /StandOut? false def
    /OldCursor? false def
    /CursorEnabled? true def
    /CursorX 0 def
    /CursorY 0 def
    /BoldTextColor ColorDict /Black get def
    /TextDescent 0 def
dictend
classbegin
    
% Class Variables:

% Defaults:

    % TO BE DONE: chances are there is a way to install a key mapping directly
    % into the interest
    /ElvisKeyDict dictbegin
	0 1 256 { dup def } for
	127 8 def		% remap delete to backspace
    dictend def

    /LeftMargin 0 def
    /OrigMatrix null def
    % Bug extrordinaire , only non scalable fonts work!!
    % I really have to fix this
    /TextFamily /7x14 def
    /TextSize 13 def
    /TextSpacing 0 def
    /SavedTextColor null def

% Methods:

    /oldcursor { /OldCursor? exch def } def

    /settextstuff {
	/TextDescent TextFont fontdescent def
    } def

    /newinit {
	/newinit super send
	/SavedTextColor TextColor def
	/FillColor FillColor promote
	/TextColor TextColor promote
    } def

    /validate {
	/resetscale self send
	/validate super send
    } def

    %   (lifted from psterm, this stuff should be redone)
    %   We have some nasty code here that tries to save the
    %   canvas' original matrix.  The default canvas matrix will
    %   be altered by resetscale and we need to restore it before
    %   we can recalculate the new scale factors.
    %
    /reshape { % x y w h => -
        OrigMatrix null ne {
            gsave
                Canvas setcanvas OrigMatrix setmatrix
                clippath Canvas reshapecanvas
            grestore
        } if

	% adjust height to be even number of rows of text...
	TextFont fontheight TextSpacing add div truncate
	TextFont fontheight TextSpacing add mul 
	
	% do likewise for the columns
	exch 
	WidthScale div truncate WidthScale mul 
	exch
        
        /reshape super send

	% tell the C side a reshape has occured
	% damage repainting will ensue on it's own
	gsave MyScale
	    ElvisResizeTag tagprint 
	    size typedprint typedprint
	grestore
        
	/OrigMatrix matrix currentmatrix promote
        /invalidate self send
    } def
    
    /WidthScale {
	TextFont setfont (W) stringwidth pop 
    } def

    /MyScale {
	WidthScale
	TextFont fontheight TextSpacing add neg scale
    } def

    /resetscale {
	gsave 
	    Canvas setcanvas
	    OrigMatrix null ne {
		OrigMatrix setmatrix
	    } if

	    % flip
	    clippath pathbbox
	    0 exch TextFont fontheight sub TextSpacing sub translate 
	    pop pop pop

	    MyScale
	    settextstuff
	    LeftMargin 0 translate
	    
	    newpath clippath Canvas reshapecanvas
	grestore
    } def

    % display (string)
    %
    /ST { % (string) => -
	    HideCursor
	    Canvas setcanvas
	    % blank out old string
	    CursorX CursorY moveto dup length -1 rect
	    FillColor setcolor fill 
		
	    % paint string
	    CursorX CursorY TextDescent sub moveto
	    TextColor setcolor TextFont setfont show
	    currentpoint TextDescent add cm
    } def
    
    
    /HideCursor {
	% unpaint any previous cursor
	OldCursor? { 
	    gsave 5 setrasteropcode
		CursorX CursorY 2 copy moveto 
		CursorPath fill
		moveto
	    grestore
	} if
	false oldcursor
    } def

    %% cursorshape change commands 
    /cpathset { 
	HideCursor 
	/CursorPath exch load promote
	CursorX CursorY cm
    } def
    
    %
    /CursorPath {
	1 -1 rect
    } def
    
    % normal cursor shape
    /cQ { /cQpath  cpathset } def
    /cQpath { 
	1 -1 rect 
    } def
    % when in command mode shape
    /cV { cQ } def
    
    % ex command line cursor shape
    /cX { /cXpath cpathset } def
    /cXpath { 
	1 -.2 rect 
    } def
    
    % input mode cursor shape
    /cI { /cIpath cpathset } def
    /cIpath { 
	-.2 .2 rmoveto
	-.5 0 rlineto .5 -.5 rlineto .5 .5 rlineto   closepath
    } def
    
    % replace mode cursor shape
    /cR { cI } def
    
    /cm { % x y => -
	CursorEnabled? {
	    currentrasteropcode 3 1 roll
		5 setrasteropcode

		% unpaint any previous cursor
		OldCursor? { 
		    CursorX CursorY moveto 
		    CursorPath fill
		} if

		% move caret
		2 copy /CursorY exch def /CursorX exch def moveto

		% turn caret on
		currentpoint moveto  
		CursorPath fill
		true oldcursor
	    setrasteropcode
	} {
	    % move caret
	    2 copy /CursorY exch def /CursorX exch def moveto
	} ifelse
    } def

%
% termcap primitives 
%

    % begin standout mode
    %
    /so {
	TextColor FillColor
	/TextColor exch promote
	/FillColor exch promote
    } def

    % end standout mode
    %
    /se { so } def

    % start/end underline mode
    %
    /us { so } def
    /ue { se } def

    % start/end bold text mode
    %
    /VB { so } def
    /Vb { se } def

    /bc {
	CursorX 1 sub 0 max CursorY cm
    } def


    /Cr { 0 CursorY cm } def
    /Nl { 
	CursorY Height 1 sub ge {
	    % we have gone below the scroll area
	    % scroll everything up a line..
	    
	    0 0 moveto Width Height 1 sub rect 
	    0 -1 copyarea

	    % .. and clear out the garbage at the bottom.
	    
	    % we don't have to worry about the cursor cause it's
	    % in the garbage area (unless it's a mighty bizarre cursor!)
	    false oldcursor
	    
	    0 Height moveto Width LeftMargin sub -2 rect 
	    FillColor setcolor fill
	    CursorX CursorY cm
	} {
	    CursorX CursorY 1 add cm
	} ifelse
    } def

    /RSR {
	    % scroll window up 
	    dup % => lines lines
	    0 CursorY moveto Width Height neg rect  % => lines lines
	    0 exch neg copyarea % => lines

	    % blank out invalid stuff at bottom
	    % => lines
	    0 CursorY moveto Width LeftMargin sub exch neg rect % => -
	    FillColor setcolor fill
    } def

    /CrNl { 
	/CursorX 0 def
	Nl
    } def

    % upline (cursor up)
    %
    /up {
	CursorX CursorY 
	1 sub cm
    } def

    % visual bell (must not move the cursor)
    %
    /vb {
	gsave  
	    5 setrasteropcode 
	    fill pause pause fill 
	grestore
    } def

    % scroll down
    %
    /sr {
	1 SR
    } def

    % block version of sr
    %
    /SR { % number_of_lines_to_scroll_down => -
	false oldcursor
	dup
	gsave
	    % do the scroll, more than needed Height wise but so what
	    0 CursorY 1 sub moveto Width Height rect
	    0 exch copyarea

	    % blank out the area we vacated with the scroll
	    0 CursorY 1 sub moveto
	    Width LeftMargin sub exch rect 

	    FillColor setcolor fill 
	grestore
    } def

    % clear to end of line
    %
    /ce {
	false oldcursor
	CursorX CursorY 
	    2 copy moveto 
	    Width CursorX LeftMargin add sub -1 rect 
	    FillColor  setcolor  fill 
	cm
    } def

    % make cursor appear normal (undo vs/vi)
    %
    /ve { true EC } def

    % make cursor invisible
    %
    /vi { false EC } def

    % (Dis)/(En)able cursor
    %
    /EC {
	dup CursorEnabled? eq {
	    pop
	} {
	    dup {
		% enable , turn cursor on
		% we can just do this with an in place move cursor
		/CursorEnabled? exch def
		CursorX CursorY cm
	    } {
		% disable , turn cursor off
		HideCursor
		/CursorEnabled? exch def
	    } ifelse
	} ifelse
    } def

    /KeyBoard {
	/ElvisKeyTag where {
	    pop
	    /Name get ElvisKeyDict exch get
	    %% dup (% key\n) exch mark exch ] dbgprintf
	    dup type /nametype eq {
		self send
	    } {
		ElvisKeyTag tagprint 
		typedprint
	    } ifelse
	} {
	} ifelse
    } def

    /MakeInterests {
	/MakeInterests super send
	/KeyBoard self soften buildsend
	Canvas /defaultkeys ClassKeysInterest send
    } def

    /PaintCanvas { % - => -
	ElvisDamageTag tagprint flush
    } def

    /minsize { % - => w h
	/minsize super send
	128 max exch
	128 max exch
    } def

    /preferredsize { % - => width height
	gsave
	    /canvas self send setcanvas
	    WidthScale 80 mul
	    TextFont fontheight TextSpacing add 44 mul
	grestore
    } def 

classend /ElvisCanvas exch put

} ifelse

systemdict dup /ElvisFrame known %%
pop false 
{ 
    pop
} {
/defaultclass ClassBaseFrame send []
classbegin
    
    /FooterFraction .99 def % fraction of how much footer is used for 
    			    % the left footer
    /destroyfromuser {
	ElvisDestroyTag tagprint flush
	% /destroyfromuser super send
    } def

classend  /ElvisFrame exch put
} ifelse

% create a window
/win 
    [ElvisCanvas] [/Footer true] framebuffer /new ElvisFrame send 
def

/C /client win send soften def
C setcanvas

(Elvis) /setlabel win send

[/place /activate] {
    win send
} forall

/S { send } def
0 0 /cm C S 

(Elvis) /setlabel win send

currentprocess cvx /ProcessName (Elvis) put

/map win send pause

%newprocessgroup currentfile closefile
SHAR_EOF
fi # end of overwriting check
if test -f 'elvis_cps.cps'
then
	echo shar: will not over-write existing file "'elvis_cps.cps'"
else
cat << \SHAR_EOF > 'elvis_cps.cps'
%
% elvis_cps.cps
%
#define GET_DIMEN_TAG 10

% include the tags such that both cps code inthis file and c code
% eleswhere can use them

cdef ps_RunFile(string file_name)
     file_name run

cdef ps_RegisterTag(string tag_name, tag_value)
    tag_name cvn tag_value def

cdef ps_ST(cstring str)
    str /ST C S

cdef ps_scroll_down(int lines)
    lines /SR C S

cdef ps_pswrite(cpostscript source_code)
    source_code 

cdef ps_Cr()
    /Cr C S

cdef ps_Nl()
    /Nl C S

cdef ps_CrNl()
    /CrNl C S

cdef ps_beep()
    beep pause

cdef ps_enable_cursor()
    /ve C S

cdef ps_disable_cursor()
    /vi C S

cdef ps_rendermsg(string msg)
    msg null /setfooter win send

cdef ps_appendmsg(string old, string new)
    old new append null /setfooter win send

cdef ps_set_title(string title)
    title /setlabel win send
    %% so you can recognize the postscript half of elvis via 'psps'
    currentprocess cvx /ProcessName (Elvis-) title append put
    title /setlabel /Icon /sendsubframe win send

cdef ps_get_lines_and_cols(int lines, int cols) => GET_DIMEN_TAG(lines, cols)
    {   
	GET_DIMEN_TAG tagprint
	Height typedprint
	Width  typedprint
	flush 
    } C send
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0

mh@roger.imsd.contel.com (Mike H.) (01/11/91)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	CHANGE_NOTES
#	Makefile
#	Makefile.mix
#	config.h
#	curses.h
#	elvis_cps.h
#	osk.h
#	regexp.h
#	vi.h
#	wconfig.h
# This archive created: Thu Jan 10 17:21:06 1991
export PATH; PATH=/bin:$PATH
if test -f 'CHANGE_NOTES'
then
	echo shar: will not over-write existing file "'CHANGE_NOTES'"
else
cat << \SHAR_EOF > 'CHANGE_NOTES'
Mike Hoegeman's change log, this is not gospel of every single change i made

Dec  7 00:00 - date of my copy of elvis version 1.4
***BUG***
-rw-------  1 mh          16474 Dec 20 20:39 input.c
-rw-------  1 mh          13527 Dec 20 20:47 vcmd.c
    added indentref arg to input(). this is so autoindent works right
    with the 'O' command. I don't know about anybody else but for me
    this *has* to work like the standard vi since i use it constantly.
***BUG***

***BUG***
-rw-------  1 mh          20109 Dec 20 23:14 redraw.c
    A bug (i put a hack in to work around it.
           see the /* HACK */ comment)

    To exercise the bug do the following.  

	- move cursor to a line not at the top or bottom of the screen
	- make a change (like add a couples of X's at the beginning)
	- hit '-' to move the cursor up one line
	- hit 'dd' (it's more obvious if the line you dd is blank)

    notice that the line the cursor is on is displaying the old deleted line
    
    it looks like smartlno is not getting readjusted properly 
***BUG***

-rw-r--r--  1 mh           7618 Dec 20 10:01 elvis.ps
    Various futzing to get ps file closer to using scalable fonts 
    misc minor tweaks
-rw-r--r--  1 mh           1652 Dec 19 13:38 elvismug.im1
    elvismug.im1 is a sun raster format file that has a
    rather grainy picture of elvis. will use this for icon image when i get
    around to it. it would be nice to get a better picture. this one came
    from the pbm package bitmaps. It might look pretty good in
    color/grayscale.
-rw-r--r--  1 mh           1015 Dec 19 13:03 elvis_cps.cps
    elvis_cps.cps file mod to get filename on icon.
    

***BUG***
-rw-r--r--  1 mh          25955 Dec 20 17:25 Makefile
-rw-r--r--  1 mh           2525 Dec 20 17:23 tnamerec.c
-rw-------  1 mh           5248 Dec 20 17:10 virec.c
-rw-------  1 mh           8195 Jan  1 15:50 config.h
-rw-------  1 mh          12876 Dec 20 13:00 tmp.c
    Modified tmp.c virec.c config.h and added module tnamerec.c to
    allow editing multiple instances of the same file at once. May want
    to just put tnamerec.c into virec.c since that is the only place it
    is used. The temp file name now has the pid of the process added on
    to the end with a '-' in front of it (see TMPNAME in config.h).
    Also changed the tmpfile prefix from elv to El so that a filename
    would still fit in 14 chars (for sysV). see tnamerec.c for how
    virec handles this change.  Added tnamerec to Makefile of course.
    note that this won't work for DOG, etc.. but there are suitable
    ifdefs to make it backwards compatible for those unfortunates.
    
***BUG***

-rw-------  1 mh          20089 Dec 20 22:23 redraw.c
    replaced constant 5 in nudgecursor() with macro NUDGE_COST
    because a move is always cheaper in PostScript than a string
    display. this will also allow other ports to define their
    NUDGE_COST's. should this be put in (w)config.h ??

-rw-------  1 mh           8026 Dec 21 12:56 main.c
-rw-r--r--  1 mh           2353 Dec 21 15:06 wconfig.h
-rw-------  1 mh          13569 Dec 21 15:05 ow.c
    Added Customflags() macro to main(), this is to allow custom
    version of elvis to process any  version specific flags without
    having to clutter up the standard elvis code with ifdefs's. in
    wconfig.h the macro is defined to a no-op for the standard tty
    version of elvis. for the openwindows version Customflags() is
    defined to be ow_customflags in ow.c.  ow_customflags() is a good
    reference for someone wanting to make their own Customflags()
    function.

-rw-------  1 mh          13569 Dec 21 15:05 ow.c
-rw-------  1 mh          20227 Dec 21 14:57 redraw.c
-rw-r--r--  1 mh           2353 Dec 21 15:06 wconfig.h
    Made final fixes to ow termcap modules to switch to using the
    standard termcap functions if no server is running the Ow.tty
    global determines which display style to use.  
    
    Rearranged various ow related globals into a single structure so
    that conversion to threads will be easier. something similar will
    have to be done for the regular elvis globals to make it
    threadable.

-rw-------  1 mh           8026 Dec 30 21:52 main.c
    Changed a resume_curses() call in trapint() to the Resume_curses()
    macro front end. Somehow I missed this before. Without this fix
    your parent shell window will wack out if type ctrl C in it after
    starting an elvis window that is not backgrounded.

-rw-r--r--  1 mh           7852 Jan  1 14:47 elvis.ps
    Fixed a bug in the /RSR method of ElvisCanvas that left it's
    argument on the stack, this (eventually) would cause a stack
    overflow if you scrolled around in a file long enough

-rw-------  1 mh          15041 Jan  1 18:18 ow.c
-rw-------  1 mh          17635 Jan  1 18:23 tio.c
-rw-r--r--  1 mh           2533 Jan  1 18:21 wconfig.h
    isolated out the display specific parts of displaying a string
    on the msg line into the Rendermsg and Appendmsg macro's (in wconfig.h )
	:: for the standard termcap interface they are rendermsg
	and appendmsg ( in tio.c )
	:: for the OPENWINDOWS version as ow_rendermsg and ow_appendmsg
	now in ow.c

-rw-------  1 mh          16672 Jan  1 23:20 input.c
	Replaced beep()'s with Beep()'s. this would mess up the display
	in subtle ways by trying to render  a ^G on the display when
	using the display version

-rw-r--r--  1 mh           8580 Jan  1 23:06 elvis.ps
-rw-------  1 mh          15544 Jan  1 20:29 ow.c
	Put in the cQ cX cV cI cR capabilities for the OPENWINDOWS
	version of elvis. Now the cursor changes shape when you
	switch from insert mode to command mode to the :ex command
	line

-rw-r--r--  1 mh           8627 Jan  2 20:04 elvis.ps
	Fixed bug that did not show up until mutiple cursor types were
	implemented. The ST did not hide the cursor before it started
	drawing text. this was OK with a simple block cursor because
	the text always blanked over the of the old cursor completely.

-rw-------  1 mh          16880 Jan  4 00:31 input.c
	Fixed up autoindent a little more to make work work pretty much
	like the real vi. this was just a 4 or 5 line change below the
	interactive loop in input() see the MCH comment
SHAR_EOF
fi # end of overwriting check
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
# combined Makefile for ELVIS - a clone of `vi`
#
# After editing this Makefile as described below, you should...
#
# Use `make` to compile all programs
# Use `make install` to copy the programs to the BIN directory
# Use `make clean` to remove all object files
# Use `make clobber` to remove everything except source & documentation
# Use `make tags` to build new "tags" and "refs" files
# Use `make uue` to produce uuencoded compressed tar archives of the source
# Use `make sh` to produce shar archives of the source
# Use `make print` to print the Elvis documentation
#
# Several groups of Makefile settings are included below.  Choose *ONE* group
# of settings for your particular system, and leave the others commented out.
# The meanings of these settings are:
#	O	the filename extension for unlinked object files -- usually .o
#	E	the filename extension for executable files -- usually null
#	EXTRA	version-specific object files used in elvis
#	EXTRA2	version-specific object files used in elvis, virec, & refont
#	LIBS	any special libraries, such as "-ltermcap"
#	BIN	directory where executables should be installed
#	CC	the C compiler command, possibly with "memory model" flags
#	CFLAGS	compiler flags used to select compile-time options
#	OF	link flag to control the output file's name -- usually -o<space>
#	RF	flag used to denote "compile but don't link" -- usually -c
#	DATE	a "cc" flag that defines "DATE".  e.g. DATE=-DDATE=\"7/4/76\"
#	EVAL	the word "eval", if DATE requires it
#	PROGS	the list of all programs
#	CHMEM	any extra commands to be run after ELVIS is linked
#	SORT	if the "tags" file must be sorted, then SORT=-DSORT
#	INST	installation method: inst.dos, inst.tos, inst.os9, or inst.unix
#	RM	the name of a program that deletes files
#	PR1	used to print documentation -- typically "refont -c"
#	PR2	used to send text to printer -- typically "| lpr"
#	DUMMY	usually nothing, but OS9 needs "dummy"
#	DOC	name of "doc" directory, with a trailing slash

#---- These settings are recommended for System-V UNIX and SCO XENIX-386 ----
#O=	.o
#E=
#EXTRA=
#EXTRA2=
#LIBS=	-ltermcap
#BIN=	/usr/local/bin
#CFLAGS=	-DM_SYSV -O
#OF=	-o 
#RF=	-c
#DATE=	-DDATE=\'\"`date`\"\'
#EVAL=	eval 
#PROGS=	elvis$E ctags$E ref$E virec$E refont$E
#CHMEM=	
#SORT=	-DSORT
#INST=	inst.unix
#RM=	rm -f
#PR1=	refont -c
#PR2=	| lp
#DUMMY=
#DOC=	doc/

#---- These settings are recommended for SCO XENIX-286 ----
#O=	.o
#E=
#EXTRA=
#EXTRA2=
#LIBS=	-ltermcap
#BIN=	/usr/local/bin
#CC=	cc -M2s -i
#CFLAGS=	-DM_SYSV -Ox -DCS_IBMPC
#OF=	-o 
#RF=	-c
#DATE=	-DDATE=\'\"`date`\"\'
#EVAL=	eval 
#PROGS=	elvis$E ctags$E ref$E virec$E refont$E
#CHMEM=	
#SORT=	-DSORT
#INST=	inst.unix
#RM=	rm -f
#PR1=	refont -c
#PR2=	| lp
#DUMMY=
#DOC=	doc/

#---- These settings are recommended for BSD 4.3 UNIX ----
#O=	.o
#E=
#EXTRA=
#EXTRA2=
#LIBS=	-ltermcap
#BIN=	/usr/local/bin
#CFLAGS=	-Dbsd -O
#OF=	-o 
#RF=	-c
#DATE=	-DDATE=\'\"`date`\"\'
#EVAL=	eval 
#PROGS=	elvis$E ctags$E ref$E virec$E refont$E
#CHMEM=	
#SORT=	-DSORT
#INST=	inst.unix
#RM=	rm -f
#PR1=	refont -c
#PR2=	| lpr
#DUMMY=
#DOC=	doc/

#---- These settings are recommended for sunOS with OPENWINDOWS (X11/NeWS)
CHOWN=echo "Chown turned off, would be --> chown"
#CC=gcc -traditional -g
CC=cc -O
O=	.o
E=
EXTRA= ow$O
EXTRA2=
LIBS=-lwire -lcps -ltermcap
BIN=/usr/local/bin
PS=$(BIN)/elvis.ps
#PS=/usr/local/src/share/elvis.ps
CFLAGS=	-DMALLOC_DEBUG=0 -DSUNOS=1 -Dbsd -DRUNFILE=\"$(PS)\" -DOPENWINDOWS=1 \
	-I$(OPENWINHOME)/include -L$(OPENWINHOME)/lib
OF=-o
RF=	-c
DATE=	-DDATE=\'\"`date`\"\'
EVAL=	eval 
PROGS=	elvis_cps.h elvis.ps 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 tnamerec.c
	$(CC) $(CFLAGS) $(OF) virec$E virec.c tnamerec.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))
	-rm $(BIN)/vi; ln -s $(BIN)/elvis $(BIN)/vi
	-rm $(BIN)/ex; ln -s $(BIN)/elvis $(BIN)/ex
	-rm $(BIN)/view; ln -s $(BIN)/elvis $(BIN)/view
	-rm $(BIN)/input; ln -s $(BIN)/elvis $(BIN)/input

inst.dos: $(DUMMY)
	copy $(PROGS) $(BIN)
	copy $(BIN)/ex$E $(BIN)/vi$E
	copy $(BIN)/ex$E $(BIN)/view$E
	copy $(BIN)/ex$E $(BIN)/input$E

inst.tos: $(DUMMY)
	copy $(PROGS) $(BIN)

inst.os9: $(DUMMY)
	copy $(PROGS) -rw=$(BIN)
	chd $(BIN); attr -epenprnpw $(PROGS)

##############################################################################
clean: $(DUMMY)
	$(RM) *$O $(DOC)*.1 elvis?.uue elvis?.sh core

clobber: clean
	$(RM) tags refs $(PROGS)

##############################################################################
print: refont$E
	$(PR1) $(DOCS) $(PR2)

tags refs: ctags$E
	ctags -r *.c *.h

##############################################################################
uue: elvis1.uue elvis2.uue elvis3.uue elvis4.uue elvis5.uue \
	elvis6.uue elvis7.uue elvis8.uue

elvis1.uue: $(SRC1)
	tar cf elvis1.tar $(SRC1)
	compress -b13 elvis1.tar
	cp README elvis1.uue
	uue elvis1.tar.Z - >>elvis1.uue
	$(RM) elvis1.tar*

elvis2.uue: $(SRC2)
	tar cf elvis2.tar $(SRC2)
	compress -b13 elvis2.tar
	uue elvis2.tar.Z
	$(RM) elvis2.tar*

elvis3.uue: $(SRC3)
	tar cf elvis3.tar $(SRC3)
	compress -b13 elvis3.tar
	uuencode elvis3.tar.Z <elvis3.tar.Z >elvis3.uue
	$(RM) elvis3.tar*

elvis4.uue: $(SRC4)
	tar cf elvis4.tar $(SRC4)
	compress -b13 elvis4.tar
	uuencode elvis4.tar.Z <elvis4.tar.Z >elvis4.uue
	$(RM) elvis4.tar*

elvis5.uue: $(SRC5)
	tar cf elvis5.tar $(SRC5)
	compress -b13 elvis5.tar
	uuencode elvis5.tar.Z <elvis5.tar.Z >elvis5.uue
	$(RM) elvis5.tar*

elvis6.uue: $(SRC6)
	tar cf elvis6.tar $(SRC6)
	compress -b13 elvis6.tar
	uuencode elvis6.tar.Z <elvis6.tar.Z >elvis6.uue
	$(RM) elvis6.tar*

elvis7.uue: $(SRC7)
	tar cf elvis7.tar $(SRC7)
	compress -b13 elvis7.tar
	uuencode elvis7.tar.Z <elvis7.tar.Z >elvis7.uue
	$(RM) elvis7.tar*

elvis8.uue: $(SRC8)
	tar cf elvis8.tar $(SRC8)
	compress -b13 elvis8.tar
	uuencode elvis8.tar.Z <elvis8.tar.Z >elvis8.uue
	$(RM) elvis8.tar*

##############################################################################
sh: elvis1.sh elvis2.sh elvis3.sh elvis4.sh elvis5.sh elvis6.sh \
	elvis7.sh elvis8.sh

elvis1.sh: $(SRC1)
	cat   >elvis1.sh README
	echo >>elvis1.sh ': ------------------------ CUT HERE --------------------'
	echo >>elvis1.sh 'test -d doc || mkdir doc || exit 2'
	shar >>elvis1.sh -h $(SRC1)

elvis2.sh: $(SRC2)
	echo  >elvis2.sh ': ------------------------ CUT HERE --------------------'
	echo >>elvis2.sh 'test -d doc || mkdir doc || exit 2'
	shar >>elvis2.sh -h $(SRC2)

elvis3.sh: $(SRC3)
	shar $(SRC3) >elvis3.sh

elvis4.sh: $(SRC4)
	shar $(SRC4) >elvis4.sh

elvis5.sh: $(SRC5)
	shar $(SRC5) >elvis5.sh

elvis6.sh: $(SRC6)
	shar $(SRC6) >elvis6.sh

elvis7.sh: $(SRC7)
	shar $(SRC7) >elvis7.sh

elvis8.sh: $(SRC8)
	shar $(SRC8) >elvis8.sh

##############################################################################

# Under XENIX only!  This stores all sources on a 3.5" 720k floppy disk.
floppy: $(SRC1) $(SRC2) $(SRC3) $(SRC4) $(SRC5) $(SRC6) $(SRC7) $(SRC8)
	tar c5v $(SRC1) $(SRC2) $(SRC3) $(SRC4) $(SRC5) $(SRC6) $(SRC7) $(SRC8)

changes:
	for i in *.[ch] M* ; do \
	if test -f $i -a -w $i ; then \
		echo $i \
		fi;\
	done

dep:
	makedep -f $(CFLAGS) -I/usr/include *.c
	sed '/^#-- DO NOT EDIT FROM THIS LINE DOWN/,$$d' Makefile > tmp
	echo  "#-- DO NOT EDIT FROM THIS LINE DOWN" >> tmp
	cat dependencies >> tmp
	mv tmp Makefile
	rm dependencies

#-- DO NOT EDIT FROM THIS LINE DOWN
wildcard.o: \
	$(FRC) \
	/usr/include/stdio.h \
	/usr/include/ctype.h
virec.o: \
	$(FRC) \
	config.h \
	/usr/include/stdio.h \
	/usr/include/ctype.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h \
	osk.h \
	/usr/include/time.h \
	wildcard.c
vi.o: \
	$(FRC) \
	config.h \
	/usr/include/ctype.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h
vcmd.o: \
	$(FRC) \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h \
	/usr/include/string.h
vars.o: \
	$(FRC) \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h
tnamerec.o: \
	$(FRC) \
	config.h \
	/usr/include/dirent.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/dirent.h
tmp.o: \
	$(FRC) \
	config.h \
	/usr/include/ctype.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h \
	osk.h \
	/usr/include/time.h
tio.o: \
	$(FRC) \
	config.h \
	/usr/include/setjmp.h \
	/usr/include/machine/setjmp.h \
	/usr/include/sun3/setjmp.h \
	/usr/include/sun3x/setjmp.h \
	/usr/include/sun4/setjmp.h \
	/usr/include/sun4c/setjmp.h \
	/usr/include/signal.h \
	/usr/include/sys/signal.h \
	/usr/include/vm/faultcode.h \
	/usr/include/sys/stdtypes.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h \
	/usr/include/varargs.h
tinytcap.o: \
	$(FRC) \
	config.h
system.o: \
	$(FRC) \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h \
	/usr/include/signal.h \
	/usr/include/sys/signal.h \
	/usr/include/vm/faultcode.h \
	/usr/include/string.h
sysdos.o: \
	$(FRC) \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h \
	/usr/include/string.h
shell.o: \
	$(FRC) \
	/usr/include/stdio.h \
	/usr/include/string.h \
	/usr/include/sys/stdtypes.h
regsub.o: \
	$(FRC) \
	/usr/include/ctype.h \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h \
	regexp.h
regexp.o: \
	$(FRC) \
	/usr/include/setjmp.h \
	/usr/include/machine/setjmp.h \
	/usr/include/sun3/setjmp.h \
	/usr/include/sun3x/setjmp.h \
	/usr/include/sun4/setjmp.h \
	/usr/include/sun4c/setjmp.h \
	/usr/include/ctype.h \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h \
	regexp.h
refont.o: \
	$(FRC) \
	/usr/include/stdio.h \
	config.h
ref.o: \
	$(FRC) \
	/usr/include/stdio.h
redraw.o: \
	$(FRC) \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h
recycle.o: \
	$(FRC) \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h
pc.o: \
	$(FRC) \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h
ow_tcap.o: \
	$(FRC) \
	/usr/include/varargs.h \
	curses.h \
	wconfig.h \
	config.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h
ow.o: \
	$(FRC) \
	/usr/include/stdio.h \
	/usr/include/sys/time.h \
	/usr/include/time.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/signal.h \
	/usr/include/sys/signal.h \
	/usr/include/vm/faultcode.h \
	/usr/include/assert.h \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h \
	elvis_cps.h \
	/usr/include/sys/file.h \
	/usr/include/sgtty.h \
	/usr/include/sys/ioctl.h \
	/usr/include/sys/ttychars.h \
	/usr/include/sys/ttydev.h \
	/usr/include/sys/ttold.h \
	/usr/include/sys/ioccom.h \
	/usr/include/sys/ttycom.h \
	/usr/include/sys/filio.h \
	/usr/include/sys/sockio.h
osk.o: \
	$(FRC) \
	/usr/include/stdio.h \
	osk.h \
	/usr/include/time.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/signal.h \
	/usr/include/sys/signal.h \
	/usr/include/vm/faultcode.h
opts.o: \
	$(FRC) \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h
move5.o: \
	$(FRC) \
	/usr/include/ctype.h \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h
move4.o: \
	$(FRC) \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h
move3.o: \
	$(FRC) \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h
move2.o: \
	$(FRC) \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h \
	regexp.h
move1.o: \
	$(FRC) \
	config.h \
	/usr/include/ctype.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h
modify.o: \
	$(FRC) \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h
misc.o: \
	$(FRC) \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h
main.o: \
	$(FRC) \
	config.h \
	/usr/include/signal.h \
	/usr/include/sys/signal.h \
	/usr/include/vm/faultcode.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/setjmp.h \
	/usr/include/machine/setjmp.h \
	/usr/include/sun3/setjmp.h \
	/usr/include/sun3x/setjmp.h \
	/usr/include/sun4/setjmp.h \
	/usr/include/sun4c/setjmp.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h
input.o: \
	$(FRC) \
	/usr/include/ctype.h \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h
ex.o: \
	$(FRC) \
	config.h \
	/usr/include/ctype.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h
cut.o: \
	$(FRC) \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h
curses.o: \
	$(FRC) \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h \
	/usr/include/termio.h \
	/usr/include/sys/ioccom.h \
	/usr/include/sys/termios.h \
	/usr/include/sys/ttydev.h \
	/usr/include/sys/ttycom.h \
	/usr/include/sgtty.h \
	/usr/include/sys/ioctl.h \
	/usr/include/sys/ttychars.h \
	/usr/include/sys/ttold.h \
	/usr/include/sys/filio.h \
	/usr/include/sys/sockio.h \
	/usr/include/signal.h \
	/usr/include/sys/signal.h \
	/usr/include/vm/faultcode.h
ctags.o: \
	$(FRC) \
	/usr/include/ctype.h \
	/usr/include/stdio.h \
	config.h \
	wildcard.c
cmd2.o: \
	$(FRC) \
	/usr/include/ctype.h \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h \
	regexp.h \
	osk.h \
	/usr/include/time.h
cmd1.o: \
	$(FRC) \
	config.h \
	/usr/include/ctype.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h \
	regexp.h
blk.o: \
	$(FRC) \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h
atari.o: \
	$(FRC) \
	config.h \
	vi.h \
	/usr/include/errno.h \
	/usr/include/sys/errno.h \
	/usr/include/sys/types.h \
	/usr/include/sys/stdtypes.h \
	/usr/include/sys/sysmacros.h \
	/usr/include/sys/fcntl.h \
	/usr/include/sys/fcntlcom.h \
	/usr/include/sys/stat.h \
	/usr/include/fcntl.h \
	curses.h \
	wconfig.h \
	/usr/local/openwin/include/NeWS/wire.h \
	/usr/local/openwin/include/NeWS/psmacros.h \
	/usr/include/stdio.h \
	/usr/local/openwin/include/NeWS/psio.h \
	/usr/local/openwin/include/NeWS/wire_types.h
alias.o: \
	$(FRC) \
	/usr/include/stdio.h \
	config.h
SHAR_EOF
fi # end of overwriting check
if test -f 'Makefile.mix'
then
	echo shar: will not over-write existing file "'Makefile.mix'"
else
cat << \SHAR_EOF > 'Makefile.mix'
# combined Makefile for ELVIS - a clone of `vi`
#
# After editing this Makefile as described below, you should...
#
# Use `make` to compile all programs
# Use `make install` to copy the programs to the BIN directory
# Use `make clean` to remove all object files
# Use `make clobber` to remove everything except source & documentation
# Use `make tags` to build new "tags" and "refs" files
# Use `make uue` to produce uuencoded compressed tar archives of the source
# Use `make sh` to produce shar archives of the source
# Use `make print` to print the Elvis documentation
#
# Several groups of Makefile settings are included below.  Choose *ONE* group
# of settings for your particular system, and leave the others commented out.
# The meanings of these settings are:
#	O	the filename extension for unlinked object files -- usually .o
#	E	the filename extension for executable files -- usually null
#	EXTRA	version-specific object files used in elvis
#	EXTRA2	version-specific object files used in elvis, virec, & refont
#	LIBS	any special libraries, such as "-ltermcap"
#	BIN	directory where executables should be installed
#	CC	the C compiler command, possibly with "memory model" flags
#	CFLAGS	compiler flags used to select compile-time options
#	OF	link flag to control the output file's name -- usually -o<space>
#	RF	flag used to denote "compile but don't link" -- usually -c
#	DATE	a "cc" flag that defines "DATE".  e.g. DATE=-DDATE=\"7/4/76\"
#	EVAL	the word "eval", if DATE requires it
#	PROGS	the list of all programs
#	CHMEM	any extra commands to be run after ELVIS is linked
#	SORT	if the "tags" file must be sorted, then SORT=-DSORT
#	INST	installation method: inst.dos, inst.tos, inst.os9, or inst.unix
#	RM	the name of a program that deletes files
#	PR1	used to print documentation -- typically "refont -c"
#	PR2	used to send text to printer -- typically "| lpr"
#	DUMMY	usually nothing, but OS9 needs "dummy"
#	DOC	name of "doc" directory, with a trailing slash

#---- These settings are recommended for System-V UNIX and SCO XENIX-386 ----
#O=	.o
#E=
#EXTRA=
#EXTRA2=
#LIBS=	-ltermcap
#BIN=	/usr/local/bin
#CFLAGS=	-DM_SYSV -O
#OF=	-o 
#RF=	-c
#DATE=	-DDATE=\'\"`date`\"\'
#EVAL=	eval 
#PROGS=	elvis$E ctags$E ref$E virec$E refont$E
#CHMEM=	
#SORT=	-DSORT
#INST=	inst.unix
#RM=	rm -f
#PR1=	refont -c
#PR2=	| lp
#DUMMY=
#DOC=	doc/

#---- These settings are recommended for SCO XENIX-286 ----
#O=	.o
#E=
#EXTRA=
#EXTRA2=
#LIBS=	-ltermcap
#BIN=	/usr/local/bin
#CC=	cc -M2s -i
#CFLAGS=	-DM_SYSV -Ox -DCS_IBMPC
#OF=	-o 
#RF=	-c
#DATE=	-DDATE=\'\"`date`\"\'
#EVAL=	eval 
#PROGS=	elvis$E ctags$E ref$E virec$E refont$E
#CHMEM=	
#SORT=	-DSORT
#INST=	inst.unix
#RM=	rm -f
#PR1=	refont -c
#PR2=	| lp
#DUMMY=
#DOC=	doc/

#---- These settings are recommended for BSD 4.3 UNIX ----
#O=	.o
#E=
#EXTRA=
#EXTRA2=
#LIBS=	-ltermcap
#BIN=	/usr/local/bin
#CFLAGS=	-Dbsd -O
#OF=	-o 
#RF=	-c
#DATE=	-DDATE=\'\"`date`\"\'
#EVAL=	eval 
#PROGS=	elvis$E ctags$E ref$E virec$E refont$E
#CHMEM=	
#SORT=	-DSORT
#INST=	inst.unix
#RM=	rm -f
#PR1=	refont -c
#PR2=	| lpr
#DUMMY=
#DOC=	doc/

#---- These settings are recommended for Coherent ----
#O=.o
#E=
#EXTRA=
#EXTRA2=
#LIBS=	-lterm
#BIN=	/usr/bin
#CC=	cc 
#CFLAGS=	-O -DCOHERENT -DCRUNCH -DNO_CHARATTR -DNO_CURSORSHAPE \
#	-DNO_DIGRAPH -DNO_MKEXRC -VSUVAR
#OF=	-o 
#RF=	-c
#DATE=	-DDATE=\'\"`date`\"\'
#EVAL=	eval
#PROGS=	elvis$E ctags$E ref$E virec$E refont$E
#CHMEM=	fixstack 2000 elvis$E
#SORT=
#INST=	inst.unix
#RM=	rm -f
#PR1=	refont -b
#PR2=	| lpr
#DUMMY=
#DOC=	doc/

#---- These settings are recommended for Minix-ST ----
#O=	.o
#E=
#EXTRA=
#EXTRA2=
#LIBS=
#BIN=	/usr/bin
#CC=	cc
#CFLAGS=
#OF=	-o 
#RF=	-c
#DATE=	-DDATE=\'\"`date`\"\'
#EVAL=	eval 
#PROGS=	elvis$E ctags$E ref$E virec$E refont$E
#CHMEM=	chmem =18000 elvis
#SORT=
#INST=	inst.unix
#RM=	rm -f
#PR1=	lpr
#PR2=
#DUMMY=
#DOC=	doc/

#---- These settings are recommended for Minix-PC ----
#O=	.s
#E=
#EXTRA=	tinytcap$O
#EXTRA2=
#LIBS=
#BIN=	/usr/bin
#CC=	cc -i
#CFLAGS=	-O -DCRUNCH -DNO_MKEXRC -DNO_CURSORSHAPE -DNO_CHARATTR \
#	-DNO_SHOWMODE -DNO_MODELINE -DNO_OPTCOLS -DNO_DIGRAPH -DNO_ABBR \
#	-DNO_AT -DNO_SENTENCE -DNO_ERRLIST
#### (all but -DNO_EXTENSIONS, -DNO_RECYCLE, -DNO_MAGIC, and -DNO_CHARSEARCH)
#OF=	-o 
#RF=	-c
#DATE=	-DDATE=\'\"`date`\"\'
#EVAL=	eval 
#PROGS=	elvis$E ctags$E ref$E virec$E refont$E
#CHMEM=
#SORT=
#INST=	inst.unix
#RM=	rm -f
#PR1=	lpr
#PR2=	
#DUMMY=
#DOC=	doc/

#---- These settings are recommended for MS-DOS + MS-C + NDMAKE ----
#O=	.obj
#E=	.exe
#EXTRA=	pc$O sysdos$O tinytcap$O
#EXTRA2=
#LIBS=
#BIN=	c:\dos
#CC=	cl /AM
#CFLAGS=	-O -DCS_IBMPC -DCS_SPECIAL
#OF=	-o 
#RF=	-c
#DATE=
#EVAL=
#PROGS=	elvis$E ex$E ctags$E ref$E virec$E wildcard$E refont$E
#CHMEM=	
#SORT=
#INST=	inst.dos
#RM=	del
#PR1=	refont -c
#PR2=	>PRN
#DUMMY=
#DOC=	doc\

#---- These settings are recommended for Atari TOS + Mark Williams C ----
#O=.o
#E=.ttp
#EXTRA=	sysdos$O tinytcap$O
#EXTRA2=	atari$O
#LIBS=
#BIN=	c:\
#CC=	cc -VPEEP
#CFLAGS=	-O -DCS_IBMPC -DCS_SPECIAL
#OF=	-o 
#RF=	-c
#DATE=
#EVAL=
#PROGS=	elvis$E ctags$E ref$E virec$E wildcard$E shell$E refont$E
#CHMEM=
#SORT=
#INST=	inst.tos
#RM=	rm -f
#PR1=	refont -e
#PR2=	>PRT:
#DUMMY=
#DOC=	'doc\'

#---- These settings are recommended for OS-9/68K V2.3 ----
#O=	.r
#E=
#EXTRA=	date$O
#EXTRA2=	osk$O 
#LIBS=	-l=/dd/lib/termlib.l
#BIN=	/dd/usr/cmds
#CC=	cc
#ODIR=	/dd/usr/src/elvis
#CFLAGS=	-gq -m=2
#OF=	-f=$(ODIR)/
#RF=	-r
#DATE=
#EVAL=
#PROGS=	elvis$E vi$E view$E input$E ctags$E ref$E virec$E refont$E
#CHMEM=	touch date.r
#SORT=
#INST=	inst.os9
#RM=	del *.stb *.dbg
#PR1=	refont -b
#PR2=	>/p
#DUMMY=	dummy
#DOC=	doc/

###########################################################################
###########################################################################
###                                                                     ###
###     The rest of this Makefile contains no user-servicable parts     ###
###                                                                     ###
###########################################################################
###########################################################################

OBJS=	blk$O cmd1$O cmd2$O curses$O cut$O ex$O input$O main$O misc$O \
	modify$O move1$O move2$O move3$O move4$O move5$O opts$O recycle$O \
	redraw$O regexp$O regsub$O system$O tio$O tmp$O vars$O vcmd$O vi$O

ALIAS=	alias$O

DOCS=	$(DOC)index.doc $(DOC)intro.doc $(DOC)visual.doc $(DOC)ex.doc \
	$(DOC)regexp.doc $(DOC)options.doc $(DOC)cutbufs.doc $(DOC)differ.doc \
	$(DOC)internal.doc $(DOC)cflags.doc $(DOC)termcap.doc \
	$(DOC)environ.doc $(DOC)versions.doc

SRC1=	README KNOWN.BUGS $(DOC)intro.doc $(DOC)visual.doc $(DOC)ex.doc \
	$(DOC)versions.doc $(DOC)cflags.doc $(DOC)differ.doc
SRC2=	$(DOC)cutbufs.doc $(DOC)options.doc $(DOC)environ.doc $(DOC)regexp.doc \
	$(DOC)internal.doc $(DOC)termcap.doc $(DOC)index.doc $(DOC)ctags.man \
	$(DOC)elvis.man $(DOC)ref.man $(DOC)refont.man $(DOC)virec.man
SRC3=	Elvis.lnk Elvis.mak Elvis.prj Makefile.mix alias.c atari.c \
	ctags.c pc.c ref.c shell.c sysdos.c virec.c wildcard.c \
	profile.sh osk.c osk.h date.c
SRC4=	blk.c cmd1.c cmd2.c config.h curses.c
SRC5=	curses.h cut.c ex.c input.c main.c misc.c
SRC6=	modify.c move1.c move2.c move3.c move4.c move5.c opts.c recycle.c \
	redraw.c 
SRC7=	regexp.c regexp.h regsub.c system.c tinytcap.c tio.c tmp.c 
SRC8=	vars.c vcmd.c vi.c vi.h refont.c

###########################################################################

all: $(PROGS)
	@echo done.

elvis$E: $(OBJS) $(EXTRA) $(EXTRA2)
	$(CC) $(CFLAGS) $(OF)elvis$E $(OBJS) $(EXTRA) $(EXTRA2) $(LIBS)
	$(CHMEM)

ctags$E: ctags.c
	$(CC) $(CFLAGS) $(SORT) $(OF)ctags$E ctags.c

ref$E: ref.c
	$(CC) $(CFLAGS) $(OF)ref$E ref.c

virec$E: virec.c
	$(CC) $(CFLAGS) $(OF)virec$E virec.c $(EXTRA2)

view$E: $(ALIAS)
	$(CC) $(CFLAGS) $(OF)view$E $(ALIAS)

ex$E: $(ALIAS)
	$(CC) $(CFLAGS) $(OF)ex$E $(ALIAS)

vi$E: $(ALIAS)
	$(CC) $(CFLAGS) $(OF)vi$E $(ALIAS)

input$E: $(ALIAS)
	$(CC) $(CFLAGS) $(OF)input$E $(ALIAS)

shell$E: shell.c
	$(CC) $(CFLAGS) $(OF)shell$E shell.c

wildcard$E: wildcard.c
	$(CC) $(CFLAGS) $(OF)wildcard$E wildcard.c

refont$E: refont.c
	$(CC) $(CFLAGS) $(OF)refont$E refont.c $(EXTRA2)

##############################################################################

# The file cmd1.c is compiled with the extra flag -DDATE="today's date".
cmd1$O: cmd1.c vi.h config.h
	$(EVAL) $(CC) $(CFLAGS) $(RF) $(DATE) cmd1.c

# "It all depends..."
$(OBJS): vi.h curses.h config.h

# OS9 must create a custom date.c file, and compile it.
date$O: $(OBJS)
	@echo '/* compilation date of elvis */' >-date.c
	@echo -r 'char date[] = "' >+date.c
	@echo -r 'echo -r ' >-/dd/tmp/date.c
	@date >+/dd/tmp/date.c
	@shell /dd/tmp/date.c >+date.c
	@echo '";' >+date.c
	@del /dd/tmp/date.c
	$(CC) $(CFLAGS) $(RF) date.c

##############################################################################
install: $(INST)
	@echo Installation complete.

inst.unix: $(DUMMY)
	cp $(PROGS) $(BIN)
	(cd $(BIN); chmod 755 $(PROGS))
	(cd $(BIN); chown bin $(PROGS))
	-ln $(BIN)/elvis $(BIN)/vi
	-ln $(BIN)/elvis $(BIN)/ex
	-ln $(BIN)/elvis $(BIN)/view
	-ln $(BIN)/elvis $(BIN)/input

inst.dos: $(DUMMY)
	copy $(PROGS) $(BIN)
	copy $(BIN)/ex$E $(BIN)/vi$E
	copy $(BIN)/ex$E $(BIN)/view$E
	copy $(BIN)/ex$E $(BIN)/input$E

inst.tos: $(DUMMY)
	copy $(PROGS) $(BIN)

inst.os9: $(DUMMY)
	copy $(PROGS) -rw=$(BIN)
	chd $(BIN); attr -epenprnpw $(PROGS)

##############################################################################
clean: $(DUMMY)
	$(RM) *$O $(DOC)*.1 elvis?.uue elvis?.sh core

clobber: clean
	$(RM) tags refs $(PROGS)

##############################################################################
print: refont$E
	$(PR1) $(DOCS) $(PR2)

tags refs: ctags$E
	ctags -r *.c *.h

##############################################################################
uue: elvis1.uue elvis2.uue elvis3.uue elvis4.uue elvis5.uue \
	elvis6.uue elvis7.uue elvis8.uue

elvis1.uue: $(SRC1)
	tar cf elvis1.tar $(SRC1)
	compress -b13 elvis1.tar
	cp README elvis1.uue
	uue elvis1.tar.Z - >>elvis1.uue
	$(RM) elvis1.tar*

elvis2.uue: $(SRC2)
	tar cf elvis2.tar $(SRC2)
	compress -b13 elvis2.tar
	uue elvis2.tar.Z
	$(RM) elvis2.tar*

elvis3.uue: $(SRC3)
	tar cf elvis3.tar $(SRC3)
	compress -b13 elvis3.tar
	uuencode elvis3.tar.Z <elvis3.tar.Z >elvis3.uue
	$(RM) elvis3.tar*

elvis4.uue: $(SRC4)
	tar cf elvis4.tar $(SRC4)
	compress -b13 elvis4.tar
	uuencode elvis4.tar.Z <elvis4.tar.Z >elvis4.uue
	$(RM) elvis4.tar*

elvis5.uue: $(SRC5)
	tar cf elvis5.tar $(SRC5)
	compress -b13 elvis5.tar
	uuencode elvis5.tar.Z <elvis5.tar.Z >elvis5.uue
	$(RM) elvis5.tar*

elvis6.uue: $(SRC6)
	tar cf elvis6.tar $(SRC6)
	compress -b13 elvis6.tar
	uuencode elvis6.tar.Z <elvis6.tar.Z >elvis6.uue
	$(RM) elvis6.tar*

elvis7.uue: $(SRC7)
	tar cf elvis7.tar $(SRC7)
	compress -b13 elvis7.tar
	uuencode elvis7.tar.Z <elvis7.tar.Z >elvis7.uue
	$(RM) elvis7.tar*

elvis8.uue: $(SRC8)
	tar cf elvis8.tar $(SRC8)
	compress -b13 elvis8.tar
	uuencode elvis8.tar.Z <elvis8.tar.Z >elvis8.uue
	$(RM) elvis8.tar*

##############################################################################
sh: elvis1.sh elvis2.sh elvis3.sh elvis4.sh elvis5.sh elvis6.sh \
	elvis7.sh elvis8.sh

elvis1.sh: $(SRC1)
	cat   >elvis1.sh README
	echo >>elvis1.sh ': ------------------------ CUT HERE --------------------'
	echo >>elvis1.sh 'test -d doc || mkdir doc || exit 2'
	shar >>elvis1.sh -h $(SRC1)

elvis2.sh: $(SRC2)
	echo  >elvis2.sh ': ------------------------ CUT HERE --------------------'
	echo >>elvis2.sh 'test -d doc || mkdir doc || exit 2'
	shar >>elvis2.sh -h $(SRC2)

elvis3.sh: $(SRC3)
	shar $(SRC3) >elvis3.sh

elvis4.sh: $(SRC4)
	shar $(SRC4) >elvis4.sh

elvis5.sh: $(SRC5)
	shar $(SRC5) >elvis5.sh

elvis6.sh: $(SRC6)
	shar $(SRC6) >elvis6.sh

elvis7.sh: $(SRC7)
	shar $(SRC7) >elvis7.sh

elvis8.sh: $(SRC8)
	shar $(SRC8) >elvis8.sh

##############################################################################

# Under XENIX only!  This stores all sources on a 3.5" 720k floppy disk.
floppy: $(SRC1) $(SRC2) $(SRC3) $(SRC4) $(SRC5) $(SRC6) $(SRC7) $(SRC8)
	tar c5v $(SRC1) $(SRC2) $(SRC3) $(SRC4) $(SRC5) $(SRC6) $(SRC7) $(SRC8)
SHAR_EOF
fi # end of overwriting check
if test -f 'config.h'
then
	echo shar: will not over-write existing file "'config.h'"
else
cat << \SHAR_EOF > 'config.h'
/*
 * 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 OPENWINDOWS
# define CREDIT "Ported to X11/NeWS by Mike Hoegeman"
#endif

#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 && !OPENWINDOWS
# define ttyread(buf, len)	read(0, buf, (unsigned)len)	/* raw read */
#endif
#if !TOS && !OPENWINDOWS
# 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 MAXTMPNAMELEN 256 /* length for tmp file name buffer */
# define TMPNAME	"%s/El%01x%04x%03x-%03x" /* temp file */
# define CUTNAME	"%s/El_%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
# define MAXTMPNAMELEN 80 /* length for tmp file name buffer */
# 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
SHAR_EOF
fi # end of overwriting check
if test -f 'curses.h'
then
	echo shar: will not over-write existing file "'curses.h'"
else
cat << \SHAR_EOF > 'curses.h'
/* curses.h */

/* Author:
 *	Steve Kirkendall
 *	14407 SW Teal Blvd. #C
 *	Beaverton, OR 97005
 *	kirkenda@cs.pdx.edu
 */

#include "wconfig.h"


/* 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 */
#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     *BC;		/* :bc=: backspace char string */
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()
SHAR_EOF
fi # end of overwriting check
if test -f 'elvis_cps.h'
then
	echo shar: will not over-write existing file "'elvis_cps.h'"
else
cat << \SHAR_EOF > 'elvis_cps.h'
/* DO NOT EDIT THIS FILE.  It contains C->PostScript communication
   definitions that were automatically generated from elvis_cps.cps */

#define ps_RunFile(P__0)  pprintf(PostScript, _CPS_ps_RunFile, 6, P__0)
static char _CPS_ps_RunFile[] = "%srun ";
#define ps_RegisterTag(P__0,P__1)  pprintf(PostScript, _CPS_ps_RegisterTag, 7, P__0, P__1)
static char _CPS_ps_RegisterTag[] = "%s\2469%d\264";
#define ps_ST(P__0,L__0)  pprintf(PostScript, _CPS_ps_ST, 11, L__0, P__0)
static char _CPS_ps_ST[] = "%*s/ST C S ";
#define ps_scroll_down(P__0)  pprintf(PostScript, _CPS_ps_scroll_down, 10, P__0)
static char _CPS_ps_scroll_down[] = "%d/SR C S ";
#define ps_pswrite(P__0,L__0)  pprintf(PostScript, _CPS_ps_pswrite, 3, L__0, P__0)
static char _CPS_ps_pswrite[] = "%*p";
#define ps_Cr()  pprintf(PostScript, _CPS_ps_Cr, 8)
static char _CPS_ps_Cr[] = "/Cr C S ";
#define ps_Nl()  pprintf(PostScript, _CPS_ps_Nl, 8)
static char _CPS_ps_Nl[] = "/Nl C S ";
#define ps_CrNl()  pprintf(PostScript, _CPS_ps_CrNl, 10)
static char _CPS_ps_CrNl[] = "/CrNl C S ";
#define ps_beep()  pprintf(PostScript, _CPS_ps_beep, 6)
static char _CPS_ps_beep[] = "beep\246\222";
#define ps_enable_cursor()  pprintf(PostScript, _CPS_ps_enable_cursor, 8)
static char _CPS_ps_enable_cursor[] = "/ve C S ";
#define ps_disable_cursor()  pprintf(PostScript, _CPS_ps_disable_cursor, 8)
static char _CPS_ps_disable_cursor[] = "/vi C S ";
#define ps_rendermsg(P__0)  pprintf(PostScript, _CPS_ps_rendermsg, 24, P__0)
static char _CPS_ps_rendermsg[] = "%s\246\220/setfooter win send ";
#define ps_appendmsg(P__0,P__1)  pprintf(PostScript, _CPS_ps_appendmsg, 32, P__0, P__1)
static char _CPS_ps_appendmsg[] = "%s%sappend\246\220/setfooter win send ";
#define ps_set_title(P__0)  pprintf(PostScript, _CPS_ps_set_title, 94, P__0, P__0, P__0)
static char _CPS_ps_set_title[] = "%s/setlabel win send\2463\246</ProcessName\226Elvis-%sappend\246\226%s/setlabel /Icon /sendsubframe win send ";
#define ps_get_lines_and_cols(P__0,P__1) ( pprintf(PostScript, _CPS_ps_get_lines_and_cols, 35), ps_checkfor(PostScriptInput, PSIO_WAIT_TAG, 10) > 0 && (_CPS_RETURN_ps_get_lines_and_cols(P__0, P__1), 1))
#define _CPS_RETURN_ps_get_lines_and_cols(P__0, P__1) pscanf(PostScriptInput,"dd", P__0, P__1)
static char _CPS_ps_get_lines_and_cols[] = "{\200\n\246\311Height\246\321Width\246\321flush } C send ";
#ifndef PSMACROS_H
#include <NeWS/psmacros.h>		/* standard cdef macros */
#endif
SHAR_EOF
fi # end of overwriting check
if test -f 'osk.h'
then
	echo shar: will not over-write existing file "'osk.h'"
else
cat << \SHAR_EOF > 'osk.h'
/*
 * OS9 stat : @(#)stat.h	1.2 87/19/12
 */
/* @(#)stat.h	6.1 */
/*
 * Structure of the result of stat
 */

#ifndef CLK_TCK
#include <time.h>
#endif

struct	stat
{
	int	st_dev;
	long	st_ino;
	unsigned short	st_mode;
	unsigned short	st_nlink;
	unsigned short	st_uid;
	unsigned short	st_gid;
	int	st_rdev;
	long	st_size;
	time_t	st_atime;
	time_t	st_mtime;
	time_t	st_ctime;
};
SHAR_EOF
fi # end of overwriting check
if test -f 'regexp.h'
then
	echo shar: will not over-write existing file "'regexp.h'"
else
cat << \SHAR_EOF > 'regexp.h'
/*
 * 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();
SHAR_EOF
fi # end of overwriting check
if test -f 'vi.h'
then
	echo shar: will not over-write existing file "'vi.h'"
else
cat << \SHAR_EOF > 'vi.h'
/* vi.h */

/* Author:
 *	Steve Kirkendall
 *	14407 SW Teal Blvd. #C
 *	Beaverton, OR 97005
 *	kirkenda@cs.pdx.edu
 */


/* This is the header file for my version of vi. */

#define VERSION "ELVIS 1.4, by Steve Kirkendall"
#define COPYING	"This version of ELVIS is freely redistributable."

#include <errno.h>
extern int errno;
#if TOS
#define ENOENT (-AEFILNF)
#endif

#if TOS
# include <types.h>
# define O_RDONLY	0
# define O_WRONLY	1
# define O_RDWR		2
#else
# if OSK
#  include <modes.h>
#  define O_RDONLY	S_IREAD
#  define O_WRONLY	S_IWRITE
#  define O_RDWR	(S_IREAD | S_IWRITE)
#  define ENOENT	E_PNNF
# else
#  include <sys/types.h>
#  if COHERENT
#   include <sys/fcntl.h>
#  else
#   include <fcntl.h>
#  endif
# endif
#endif

#ifndef O_BINARY
# define O_BINARY	0
#endif

#include "curses.h"

/*------------------------------------------------------------------------*/
/* Miscellaneous constants.						  */

#define INFINITY	2000000001L	/* a very large integer */
#define LONGKEY		10		/* longest possible raw :map key */
#ifndef MAXRCLEN
# define MAXRCLEN	1000		/* longest possible .exrc file */
#endif

/*------------------------------------------------------------------------*/
/* These describe how temporary files are divided into blocks             */

#define BLKSIZE	1024		/* size of blocks */
#define MAXBLKS	(BLKSIZE / sizeof(unsigned short))
typedef union
{
	char		c[BLKSIZE];	/* for text blocks */
	unsigned short	n[MAXBLKS];	/* for the header block */
}
	BLK;

/*------------------------------------------------------------------------*/
/* These are used manipulate BLK buffers.                                 */

extern BLK	hdr;		/* buffer for the header block */
extern BLK	*blkget();	/* given index into hdr.c[], reads block */
extern BLK	*blkadd();	/* inserts a new block into hdr.c[] */

/*------------------------------------------------------------------------*/
/* These are used to keep track of various flags                          */
extern struct _viflags
{
	short	file;		/* file flags */
}
	viflags;

/* file flags */
#define NEWFILE		0x0001	/* the file was just created */
#define READONLY	0x0002	/* the file is read-only */
#define HADNUL		0x0004	/* the file contained NUL characters */
#define MODIFIED	0x0008	/* the file has been modified */
#define NOFILE		0x0010	/* no name is known for the current text */
#define ADDEDNL		0x0020	/* newlines were added to the file */

/* macros used to set/clear/test flags */
#define setflag(x,y)	viflags.x |= y
#define clrflag(x,y)	viflags.x &= ~y
#define tstflag(x,y)	(viflags.x & y)
#define initflags()	viflags.file = 0;

/* The options */
extern char	o_autoindent[1];
extern char	o_autoprint[1];
extern char	o_autowrite[1];
#ifndef NO_ERRLIST
extern char	o_cc[30];
#endif
#ifndef NO_CHARATTR
extern char	o_charattr[1];
#endif
extern char	o_columns[3];
extern char	o_digraph[1];
extern char	o_directory[30];
extern char	o_edcompatible[1];
extern char	o_errorbells[1];
extern char	o_exrefresh[1];
#ifndef NO_DIGRAPH
extern char	o_flipcase[80];
#endif
#ifndef NO_SENTENCE
extern char	o_hideformat[1];
#endif
extern char	o_ignorecase[1];
#ifndef NO_EXTENSIONS
extern char	o_inputmode[1];
#endif
extern char	o_keytime[3];
extern char	o_keywordprg[80];
extern char	o_lines[3];
extern char	o_list[1];
#ifndef NO_MAGIC
extern char	o_magic[1];
#endif
#ifndef NO_ERRLIST
extern char	o_make[30];
#endif
#ifndef NO_MODELINE
extern char	o_modeline[1];
#endif
#ifndef NO_SENTENCE
extern char	o_paragraphs[30];
#endif
#if MSDOS
extern char	o_pcbios[1];
#endif
extern char	o_readonly[1];
extern char	o_report[3];
extern char	o_scroll[3];
#ifndef NO_SENTENCE
extern char	o_sections[30];
#endif
extern char	o_shell[60];
#ifndef NO_SHOWMATCH
extern char	o_showmatch[1];
#endif
#ifndef NO_SHOWMODE
extern char	o_smd[1];
#endif
extern char	o_shiftwidth[3];
extern char	o_sidescroll[3];
extern char	o_sync[1];
extern char	o_tabstop[3];
extern char	o_term[30];
extern char	o_vbell[1];
extern char	o_warn[1];
extern char	o_wrapmargin[3];
extern char	o_wrapscan[1];

/*------------------------------------------------------------------------*/
/* These help support the single-line multi-change "undo" -- shift-U      */

extern char	U_text[BLKSIZE];
extern long	U_line;

/*------------------------------------------------------------------------*/
/* These are used to refer to places in the text 			  */

typedef long	MARK;
#define markline(x)	(long)((x) / BLKSIZE)
#define markidx(x)	(int)((x) & (BLKSIZE - 1))
#define MARK_UNSET	((MARK)0)
#define MARK_FIRST	((MARK)BLKSIZE)
#define MARK_LAST	((MARK)(nlines * BLKSIZE))
#define MARK_AT_LINE(x)	((MARK)((x) * BLKSIZE))

#define NMARKS	29
extern MARK	mark[NMARKS];	/* marks a-z, plus mark ' and two temps */
extern MARK	cursor;		/* mark where line is */

/*------------------------------------------------------------------------*/
/* These are used to keep track of the current & previous files.	  */

extern long	origtime;	/* modification date&time of the current file */
extern char	origname[256];	/* name of the current file */
extern char	prevorig[256];	/* name of the preceding file */
extern long	prevline;	/* line number from preceding file */

/*------------------------------------------------------------------------*/
/* misc housekeeping variables & functions				  */

extern int	tmpfd;		/* fd used to access the tmp file */
extern long	lnum[MAXBLKS];	/* last line# of each block */
extern long	nlines;		/* number of lines in the file */
extern char	args[BLKSIZE];	/* file names given on the command line */
extern int	argno;		/* the current element of args[] */
extern int	nargs;		/* number of filenames in args */
extern long	changes;	/* counts changes, to prohibit short-cuts */
extern int	significant;	/* boolean: was a *REAL* change made? */
extern int	mustredraw;	/* boolean: force total redraw of screen? */
extern BLK	tmpblk;		/* a block used to accumulate changes */
extern long	topline;	/* file line number of top line */
extern int	leftcol;	/* column number of left col */
#define		botline	 (topline + LINES - 2)
#define		rightcol (leftcol + COLS - 1)
extern int	physcol;	/* physical column number that cursor is on */
extern int	physrow;	/* physical row number that cursor is on */
extern int	exwrote;	/* used to detect verbose ex commands */
extern int	doingdot;	/* boolean: are we doing the "." command? */
extern int	doingglobal;	/* boolean: are doing a ":g" command? */
extern long	rptlines;	/* number of lines affected by a command */
extern char	*rptlabel;	/* description of how lines were affected */
extern char	*fetchline();	/* read a given line from tmp file */
extern char	*parseptrn();	/* isolate a regexp in a line */
extern MARK	paste();	/* paste from cut buffer to a given point */
extern char	*wildcard();	/* expand wildcards in filenames */
extern MARK	input();	/* inserts characters from keyboard */
extern char	*linespec();	/* finds the end of a /regexp/ string */
#define		ctrl(ch) ((ch)&037)
#ifndef NO_RECYCLE
extern long	allocate();	/* allocate a free block of the tmp file */
#endif
extern int	trapint();	/* trap handler for SIGINT */
extern void	blkdirty();	/* marks a block as being "dirty" */
extern void	blkflush();	/* writes a single dirty block to the disk */
extern void	blksync();	/* forces all "dirty" blocks to disk */
extern void	blkinit();	/* resets the block cache to "empty" state */
extern void	beep();		/* rings the terminal's bell */
extern void	exrefresh();	/* writes text to the screen */
extern void	msg();		/* writes a printf-style message to the screen */
extern void	reset_msg();	/* resets the "manymsgs" flag */
extern void	endmsgs();	/* if "manymsgs" is set, then scroll up 1 line */
extern void	garbage();	/* reclaims any garbage blocks */
extern void	redraw();	/* updates the screen after a change */
extern void	resume_curses();/* puts the terminal in "cbreak" mode */
extern void	beforedo();	/* saves current revision before a new change */
extern void	afterdo();	/* marks end of a beforedo() change */
extern void	abortdo();	/* like "afterdo()" followed by "undo()" */
extern int	undo();		/* restores file to previous undo() */
extern void	dumpkey();	/* lists key mappings to the screen */
extern void	mapkey();	/* defines a new key mapping */
extern void	savekeys();	/* lists key mappings to a file */
extern void	redrawrange();	/* records clues from modify.c */
extern void	cut();		/* saves text in a cut buffer */
extern void	delete();	/* deletes text */
extern void	add();		/* adds text */
extern void	change();	/* deletes text, and then adds other text */
extern void	cutswitch();	/* updates cut buffers when we switch files */
extern void	do_abbr();	/* defines or lists abbreviations */
extern void	do_digraph();	/* defines or lists digraphs */
extern void	exstring();	/* execute a string as EX commands */
extern void	dumpopts();
extern void	setopts();
extern void	saveopts();
#ifndef NO_DIGRAPH
extern void	savedigs();
#endif
#ifndef NO_ABBR
extern void	saveabbr();
#endif
extern void	cutname();
extern void	cutname();
extern void	initopts();
extern void	cutend();

/*------------------------------------------------------------------------*/
/* macros that are used as control structures                             */

#define BeforeAfter(before, after) for((before),bavar=1;bavar;(after),bavar=0)
#define ChangeText	BeforeAfter(beforedo(FALSE),afterdo())

extern int	bavar;		/* used only in BeforeAfter macros */

/*------------------------------------------------------------------------*/
/* These are the movement commands.  Each accepts a mark for the starting */
/* location & number and returns a mark for the destination.		  */

extern MARK	m_updnto();		/* k j G */
extern MARK	m_right();		/* h */
extern MARK	m_left();		/* l */
extern MARK	m_tocol();		/* | */
extern MARK	m_front();		/* ^ */
extern MARK	m_rear();		/* $ */
extern MARK	m_fword();		/* w */
extern MARK	m_bword();		/* b */
extern MARK	m_eword();		/* e */
extern MARK	m_fWord();		/* W */
extern MARK	m_bWord();		/* B */
extern MARK	m_eWord();		/* E */
extern MARK	m_fparagraph();		/* } */
extern MARK	m_bparagraph();		/* { */
extern MARK	m_fsection();		/* ]] */
extern MARK	m_bsection();		/* [[ */
extern MARK	m_match();		/* % */
#ifndef NO_SENTENCE
 extern MARK	m_fsentence();		/* ) */
 extern MARK	m_bsentence();		/* ( */
#endif
extern MARK	m_tomark();		/* 'm */
extern MARK	m_nsrch();		/* n */
extern MARK	m_Nsrch();		/* N */
extern MARK	m_fsrch();		/* /regexp */
extern MARK	m_bsrch();		/* ?regexp */
#ifndef NO_CHARSEARCH
 extern MARK	m__ch();		/* ; , */
 extern MARK	m_fch();		/* f */
 extern MARK	m_tch();		/* t */
 extern MARK	m_Fch();		/* F */
 extern MARK	m_Tch();		/* T */
#endif
extern MARK	m_row();		/* H L M */
extern MARK	m_z();			/* z */
extern MARK	m_scroll();		/* ^B ^F ^E ^Y ^U ^D */

/* Some stuff that is used by movement functions... */

extern MARK	adjmove();		/* a helper fn, used by move fns */

/* This macro is used to set the default value of cnt */
#define DEFAULT(val)	if (cnt < 1) cnt = (val)

/* These are used to minimize calls to fetchline() */
extern int	plen;	/* length of the line */
extern long	pline;	/* line number that len refers to */
extern long	pchgs;	/* "changes" level that len refers to */
extern char	*ptext;	/* text of previous line, if valid */
extern void	pfetch();
extern char	digraph();

/* This is used to build a MARK that corresponds to a specific point in the
 * line that was most recently pfetch'ed.
 */
#define buildmark(text)	(MARK)(BLKSIZE * pline + (int)((text) - ptext))


/*------------------------------------------------------------------------*/
/* These are used to handle EX commands.				  */

#define  CMD_NULL	0	/* NOT A VALID COMMAND */
#define  CMD_ABBR	1	/* "define an abbreviation" */
#define  CMD_ARGS	2	/* "show me the args" */
#define  CMD_APPEND	3	/* "insert lines after this line" */
#define  CMD_AT		4	/* "execute a cut buffer's contents via EX" */
#define  CMD_BANG	5	/* "run a single shell command" */
#define  CMD_CC		6	/* "run `cc` and then do CMD_ERRLIST" */
#define  CMD_CD		7	/* "change directories" */
#define  CMD_CHANGE	8	/* "change some lines" */
#define  CMD_COPY	9	/* "copy the selected text to a given place" */
#define  CMD_DELETE	10	/* "delete the selected text" */
#define  CMD_DIGRAPH	11	/* "add a digraph, or display them all" */
#define  CMD_EDIT	12	/* "switch to a different file" */
#define  CMD_EQUAL	13	/* "display a line number" */
#define  CMD_ERRLIST	14	/* "locate the next error in a list" */
#define  CMD_FILE	15	/* "show the file's status" */
#define  CMD_GLOBAL	16	/* "globally search & do a command" */
#define  CMD_INSERT	17	/* "insert lines before the current line" */
#define  CMD_JOIN	18	/* "join the selected line & the one after" */
#define  CMD_LIST	19	/* "print lines, making control chars visible" */
#define  CMD_MAKE	20	/* "run `make` and then do CMD_ERRLIST" */
#define  CMD_MAP	21	/* "adjust the keyboard map" */
#define  CMD_MARK	22	/* "mark this line" */
#define  CMD_MKEXRC	23	/* "make a .exrc file" */
#define  CMD_MOVE	24	/* "move the selected text to a given place" */
#define  CMD_NEXT	25	/* "switch to next file in args" */
#define  CMD_NUMBER	26	/* "print lines from the file w/ line numbers" */
#define  CMD_PRESERVE	27	/* "act as though vi crashed" */
#define  CMD_PREVIOUS	28	/* "switch to the previous file in args" */
#define  CMD_PRINT	29	/* "print the selected text" */
#define  CMD_PUT	30	/* "insert any cut lines before this line" */
#define  CMD_QUIT	31	/* "quit without writing the file" */
#define  CMD_READ	32	/* "append the given file after this line */
#define  CMD_RECOVER	33	/* "recover file after vi crashes" - USE -r FLAG */
#define  CMD_REWIND	34	/* "rewind to first file" */
#define  CMD_SET	35	/* "set a variable's value" */
#define  CMD_SHELL	36	/* "run some lines through a command" */
#define  CMD_SHIFTL	37	/* "shift lines left" */
#define  CMD_SHIFTR	38	/* "shift lines right" */
#define  CMD_SOURCE	39	/* "interpret a file's contents as ex commands" */
#define  CMD_STOP	40	/* same as CMD_SUSPEND */
#define  CMD_SUBAGAIN	41	/* "repeat the previous substitution" */
#define  CMD_SUBSTITUTE	42	/* "substitute text in this line" */
#define  CMD_SUSPEND	43	/* "suspend the vi session" */
#define  CMD_TR		44	/* "transliterate chars in the selected lines" */
#define  CMD_TAG	45	/* "go to a particular tag" */
#define  CMD_UNABBR	46	/* "remove an abbreviation definition" */
#define  CMD_UNDO	47	/* "undo the previous command" */
#define  CMD_UNMAP	48	/* "remove a key sequence map */
#define  CMD_VERSION	49	/* "describe which version this is" */
#define  CMD_VGLOBAL	50	/* "apply a cmd to lines NOT containing an RE" */
#define  CMD_VISUAL	51	/* "go into visual mode" */
#define  CMD_WQUIT	52	/* "write this file out (any case) & quit" */
#define  CMD_WRITE	53	/* "write the selected(?) text to a given file" */
#define  CMD_XIT	54	/* "write this file out (if modified) & quit" */
#define  CMD_YANK	55	/* "copy the selected text into the cut buffer" */
#ifdef DEBUG
# define CMD_DEBUG	56	/* access to internal data structures */
# define CMD_VALIDATE	57	/* check for internal consistency */
#endif
typedef int CMD;

extern void	ex();
extern void	vi();
extern void	doexcmd();

#ifndef NO_ABBR
extern void	cmd_abbr();
#endif
extern void	cmd_append();
extern void	cmd_args();
#ifndef NO_AT
extern void	cmd_at();
#endif
extern void	cmd_cd();
extern void	cmd_delete();
#ifndef NO_DIGRAPH
extern void	cmd_digraph();
#endif
extern void	cmd_edit();
#ifndef NO_ERRLIST
extern void	cmd_errlist();
#endif
extern void	cmd_file();
extern void	cmd_global();
extern void	cmd_join();
extern void	cmd_mark();
#ifndef NO_ERRLIST
extern void	cmd_make();
#endif
extern void	cmd_map();
#ifndef NO_MKEXRC
extern void	cmd_mkexrc();
#endif
extern void	cmd_next();
extern void	cmd_print();
extern void	cmd_put();
extern void	cmd_read();
extern void	cmd_set();
extern void	cmd_shell();
extern void	cmd_shift();
extern void	cmd_source();
extern void	cmd_substitute();
extern void	cmd_tag();
extern void	cmd_undo();
extern void	cmd_version();
extern void	cmd_visual();
extern void	cmd_write();
extern void	cmd_xit();
extern void	cmd_move();
#ifdef DEBUG
extern void	cmd_debug();
extern void	cmd_validate();
#endif

/*----------------------------------------------------------------------*/
/* These are used to handle VI commands 				*/

extern MARK	v_1ex();	/* : */
extern MARK	v_mark();	/* m */
extern MARK	v_quit();	/* Q */
extern MARK	v_redraw();	/* ^L ^R */
extern MARK	v_ulcase();	/* ~ */
extern MARK	v_undo();	/* u */
extern MARK	v_xchar();	/* x */
extern MARK	v_Xchar();	/* X */
extern MARK	v_replace();	/* r */
extern MARK	v_overtype();	/* R */
extern MARK	v_selcut();	/* " */
extern MARK	v_paste();	/* p P */
extern MARK	v_yank();	/* y Y */
extern MARK	v_delete();	/* d D */
extern MARK	v_join();	/* J */
extern MARK	v_insert();	/* a A i I o O */
extern MARK	v_change();	/* c C */
extern MARK	v_subst();	/* s */
extern MARK	v_lshift();	/* < */
extern MARK	v_rshift();	/* > */
extern MARK	v_filter();	/* ! */
extern MARK	v_status();	/* ^G */
extern MARK	v_switch();	/* ^^ */
extern MARK	v_tag();	/* ^] */
extern MARK	v_xit();	/* ZZ */
extern MARK	v_undoline();	/* U */
extern MARK	v_again();	/* & */
#ifndef NO_EXTENSIONS
 extern MARK	v_keyword();	/* ^K */
 extern MARK	v_increment();	/* * */
#endif
#ifndef NO_ERRLIST
 extern MARK	v_errlist();	/* * */
#endif
#ifndef NO_AT
 extern MARK	v_at();		/* @ */
#endif

/*----------------------------------------------------------------------*/
/* These describe what mode we're in */

#define MODE_EX		1	/* executing ex commands */
#define	MODE_VI		2	/* executing vi commands */
#define	MODE_COLON	3	/* executing an ex command from vi mode */
#define	MODE_QUIT	4
extern int	mode;

#define WHEN_VICMD	1	/* getkey: we're reading a VI command */
#define WHEN_VIINP	2	/* getkey: we're in VI's INPUT mode */
#define WHEN_VIREP	4	/* getkey: we're in VI's REPLACE mode */
#define WHEN_EX		8	/* getkey: we're in EX mode */
#define WHEN_MSG	16	/* getkey: we're at a "more" prompt */
#define WHEN_INMV	256	/* in input mode, interpret the key in VICMD mode */
SHAR_EOF
fi # end of overwriting check
if test -f 'wconfig.h'
then
	echo shar: will not over-write existing file "'wconfig.h'"
else
cat << \SHAR_EOF > 'wconfig.h'
/* wconfig.h , window related configuration header file */

/*
   defining macros in this way allows us to fall back on using the dumb
   terminal functions if no window system is available at runtime

   the convention used is to make the macro the same name as the
   original dumb terminal name with the leading letter 'capped

   -MCH 
*/

#ifndef _elvis_w_macs_
#define _elvis_w_macs_ 1

#include "config.h"
#include <NeWS/wire.h>

#if OPENWINDOWS /* || SomeOtherWindowSys || ... */ /* using a window system */

#if OPENWINDOWS

/* keep all the flags in a single structure to make it easier to
convert to threads later */

struct ow_data {
    /* flags */
    int background;
    int tty;
    int wLINES;
    int wCOLS;
    
    /* */
    wire_Wire mainWire;
    int keyFromNews;
};
#define ow_data_init(op) {\
    (op)->background 		= 0;\
    (op)->tty 			= 0;\
    (op)->wLINES		= 44;\
    (op)->wCOLS			= 80;\
    (op)->mainWire 		= (wire_Wire) NULL;\
    (op)->keyFromNews		= -1;\
}
extern struct ow_data Ow;

/* termcap stuff */
#define Tgetent ow_tgetent
#define Tputs   ow_tputs
#define Tgetnum ow_tgetnum
#define Tgoto   ow_tgoto
#define Tgetstr ow_tgetstr
#define Tgetflag ow_tgetflag

#define Customflags(av, ac_p)	ow_customflags(av, ac_p)
#define Vi()			ow_vi()
#define Ex()			ow_ex()
#define Initscr()		ow_initscr()
#define Beep()			ow_beep()
#define Rendermsg(msg)		ow_rendermsg(msg);
#define Appendmsg(old,new)	ow_appendmsg(old,new);
#define FlushDisplay()		ow_flushdisplay();
#define Getsize			ow_getsize
#define Resume_curses(quietly)  ow_resume_curses((quietly))
#define Suspend_curses()        ow_suspend_curses()
#define Tmpstart(fname)		ow_tmpstart(fname)
#define Scrolldown(l,y,x)	ow_scrolldown(l,y,x)
#define Scrollup(l,y,x)		ow_scrollup(l,y,x)
#endif

#else

/* no window system , just use the dumb terminal functions */

/* termcap stuff */
#define Tgetent tgetent
#define Tputs   tputs
#define Tgetnum tgetnum
#define Tgoto   tgoto
#define Tgetstr tgetstr
#define Tgetflag tgetflag
 
#define Customflags(av, ac_p)	/* no-op */
#define Vi()			vi()
#define Ex()			ex()
#define Initscr()		initscr()
#define Beep()			beep()
#define Rendermsg(msg)		rendermsg(msg);
#define Appendmsg(old,new)	appendmsg(old,new);
#define FlushDisplay()
#define Getsize			getsize
#define Resume_curses(quietly)  resume_curses((quietly))
#define Suspend_curses()        suspend_curses()
#define Tmpstart(fname)		tmpstart(fname)
#define Scrolldown(l,y,x)	scrolldown(l,y,x)
#define Scrollup(l,y,x)		scrollup(l,y,x)

#endif

#endif
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0

mh@roger.imsd.contel.com (Mike H.) (01/11/91)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	alias.c
#	atari.c
#	blk.c
#	cmd1.c
#	cmd2.c
#	ctags.c
#	curses.c
#	cut.c
# This archive created: Thu Jan 10 17:21:07 1991
export PATH; PATH=/bin:$PATH
if test -f 'alias.c'
then
	echo shar: will not over-write existing file "'alias.c'"
else
cat << \SHAR_EOF > 'alias.c'
/* alias.c */

/* Author:
 *		Peter Reinig
 *		Universitaet Kaiserslautern
 *		Postfach 3049
 *		7650 Kaiserslautern
 *		W. Germany
 *		reinig@physik.uni-kl.de
 */

/* This tiny program executes elvis with the flags that are appropriate
 * for a given command name.  This program is used only on systems that
 * don't allow UNIX-style file links.
 *
 * The benefit of this program is: instead of having 5 copies of elvis
 * on your disk, you only need one copy of elvis and 4 copies of this
 * little program.
 */

#include <stdio.h>
#include "config.h"

#if OSK
#define ELVIS	"/dd/usr/cmds/elvis"
#else
#define ELVIS	"elvis"
#endif

extern char **environ;
extern int errno;
extern char *malloc();

main(argc, argv)
	int	argc;
	char	*argv[];
{
	int	pid, i, j;
	int	letter;
	char	**argblk;
#if OSK
	extern int chainc();
#endif

	/* allocate enough space for a copy of the argument list, plus a
	 * terminating NULL, plus maybe an added flag.
	 */
	argblk = (char **) malloc((argc + 2) * sizeof(char *));
	if (!argblk)
	{
#if OSK
		_errmsg(errno, "Can't get enough memory\n");
#else
		perror(argv[0]);
#endif
		exit(2);
	}

	/* find the last letter in the invocation name of this program */
	i = strlen(argv[0]);
#if MSDOS || TOS
	/* we almost certainly must bypass ".EXE" or ".TTP" from argv[0] */
	if (i > 4 && argv[0][i - 4] == '.')
		i -= 4;
#endif
	letter = argv[0][i - 1];

	/* copy argv to argblk, possibly inserting a flag such as "-R" */
	argblk[0] = ELVIS;
	i = j = 1;
	switch (letter)
	{
	  case 'w':			/* "view" */
	  case 'W':
		argblk[i++] = "-R";
		break;
#if !OSK
	  case 'x':			/* "ex" */
	  case 'X':
		argblk[i++] = "-e";
		break;
#endif
	  case 't':			/* "input" */
	  case 'T':
		argblk[i++] = "-i";
		break;
	}
	while (j < argc)
	{
		argblk[i++] = argv[j++];
	}
	argblk[i] = (char *)0;

	/* execute the real ELVIS program */
#if OSK
	pid = os9exec(chainc, argblk[0], argblk, environ, 0, 0, 3);
	fprintf(stderr, "%s: cannot execute\n", argblk[0]);
#else
	(void)execvp(argblk[0], argblk);
	perror(ELVIS);
#endif
}
SHAR_EOF
fi # end of overwriting check
if test -f 'atari.c'
then
	echo shar: will not over-write existing file "'atari.c'"
else
cat << \SHAR_EOF > 'atari.c'
/* atari.c */

/* Author:
 *	Guntram Blohm
 *	Buchenstrasse 19
 *	7904 Erbach, West Germany
 *	Tel. ++49-7305-6997
 *	sorry - no regular network connection
 */

/*
 * This file contains the 'standard' functions which are not supported
 * by Atari/Mark Williams, and some other TOS-only requirements.
 */
 
#include "config.h"
#include "vi.h"

#if TOS
#include <osbind.h>

/* vi uses mode==0 only ... */
int access(file, mode)
	char *file;
{
	int fd=Fopen(file, 0);
	if (fd<0)
		return -1;
	Fclose(fd);
	return 0;
}

char *mktemp(template)
	char *template;
{
	return template;
}

/* read -- text mode, compress \r\n to \n
 * warning: might fail when maxlen==1 and at eol
 */

int tread(fd, buf, maxlen)
	int fd;
	char *buf;
	int maxlen;
{
	int i, j, nread=read(fd, buf, (unsigned)maxlen);

	if (nread && buf[nread-1]=='\r')
	{	nread--;
		lseek(fd, -1l, 1);
	}
	for (i=j=0; j<nread; i++,j++)
	{	if (buf[j]=='\r' && buf[j+1]=='\n')
			j++;
		buf[i]=buf[j];
	}
	return i;
}

int twrite(fd, buf, maxlen)
	int fd;
	char *buf;
	int maxlen;
{
	int i, j, nwritten=0, hadnl=0;
	char writbuf[BLKSIZE];

	for (i=j=0; j<maxlen; )
	{
		if ((writbuf[i++]=buf[j++])=='\n')
		{	writbuf[i-1]='\r';
			if (i<BLKSIZE)
				writbuf[i++]='\n';
			else
				hadnl=1;
		}
		if (i==BLKSIZE)
		{
			write(fd, writbuf, (unsigned)i);
			i=0;
		}
		if (hadnl)
		{
			writbuf[i++]='\n';
			hadnl=0;
		}
	}
	if (i)
		write(fd, writbuf, (unsigned)i);
	return j;
}
#endif
SHAR_EOF
fi # end of overwriting check
if test -f 'blk.c'
then
	echo shar: will not over-write existing file "'blk.c'"
else
cat << \SHAR_EOF > 'blk.c'
/* blk.c */

/* Author:
 *	Steve Kirkendall
 *	14407 SW Teal Blvd. #C
 *	Beaverton, OR 97005
 *	kirkenda@cs.pdx.edu
 */


/* This file contains the functions that get/put blocks from the temp file.
 * It also contains the "do" and "undo" functions.
 */

#include "config.h"
#include "vi.h"

#ifndef NBUFS
# define NBUFS	5		/* must be at least 3 -- more is better */
#endif

extern long lseek();

/*------------------------------------------------------------------------*/

BLK		hdr;		/* buffer for the header block */

static int	b4cnt;		/* used to count context of beforedo/afterdo */
static struct _blkbuf
{
	BLK		buf;		/* contents of a text block */
	unsigned short	logical;	/* logical block number */
	int		dirty;		/* must the buffer be rewritten? */
}
		blk[NBUFS],	/* buffers for text[?] blocks */
		*toonew,	/* buffer which shouldn't be recycled yet */
		*newtoo,	/* another buffer which should be recycled */
		*recycle = blk;	/* next block to be recycled */





/* This function wipes out all buffers */
void blkinit()
{
	int	i;

	for (i = 0; i < NBUFS; i++)
	{
		blk[i].logical = 0;
		blk[i].dirty = FALSE;
	}
	for (i = 0; i < MAXBLKS; i++)
	{
		hdr.n[i] = 0;
	}
}

/* This function allocates a buffer and fills it with a given block's text */
BLK *blkget(logical)
	int	logical;	/* logical block number to fetch */
{
	REG struct _blkbuf	*this;	/* used to step through blk[] */
	REG int	i;

	/* if logical is 0, just return the hdr buffer */
	if (logical == 0)
	{
		return &hdr;
	}

	/* see if we have that block in mem already */
	for (this = blk; this < &blk[NBUFS]; this++)
	{
		if (this->logical == logical)
		{
			newtoo = toonew;
			toonew = this;
			return &this->buf;
		}
	}

	/* choose a block to be recycled */
	do
	{
		this = recycle++;
		if (recycle == &blk[NBUFS])
		{
			recycle = blk;
		}
	} while (this == toonew || this == newtoo);

	/* if it contains a block, flush that block */
	blkflush(this);

	/* fill this buffer with the desired block */
	this->logical = logical;
	if (hdr.n[logical])
	{
		/* it has been used before - fill it from tmp file */
		lseek(tmpfd, (long)hdr.n[logical] * (long)BLKSIZE, 0);
		if (read(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE)
		{
			msg("Error reading back from tmp file!");
		}
	}
	else
	{
		/* it is new - zero it */
		for (i = 0; i < BLKSIZE; i++)
		{
			this->buf.c[i] = 0;
		}
	}

	/* This isn't really a change, but it does potentially invalidate
	 * the kinds of shortcuts that the "changes" variable is supposed
	 * to protect us from... so count it as a change.
	 */
	changes++;

	/* mark it as being "not dirty" */
	this->dirty = 0;

	/* return it */
	newtoo = toonew;
	toonew = this;
	return &this->buf;
}



/* This function writes a block out to the temporary file */
void blkflush(this)
	REG struct _blkbuf	*this;	/* the buffer to flush */
{
	long		seekpos;	/* seek position of the new block */
	unsigned short	physical;	/* physical block number */

	/* if its empty (an orphan blkadd() maybe?) then make it dirty */
	if (this->logical && !*this->buf.c)
	{
		blkdirty(&this->buf);
	}

	/* if it's an empty buffer or a clean version is on disk, quit */
	if (!this->logical || hdr.n[this->logical] && !this->dirty)
	{
		return;
	}

	/* find a free place in the file */
#ifndef NO_RECYCLE
	seekpos = allocate();
	lseek(tmpfd, seekpos, 0);
#else
	seekpos = lseek(tmpfd, 0L, 2);
#endif
	physical = seekpos / BLKSIZE;

	/* put the block there */
	if (write(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE)
	{
		msg("Trouble writing to tmp file");
	}
	this->dirty = FALSE;

	/* update the header so it knows we put it there */
	hdr.n[this->logical] = physical;
}


/* This function sets a block's "dirty" flag or deletes empty blocks */
void blkdirty(bp)
	BLK	*bp;	/* buffer returned by blkget() */
{
	REG int		i, j;
	REG char	*scan;
	REG int		k;

	/* find the buffer */
	for (i = 0; i < NBUFS && bp != &blk[i].buf; i++)
	{
	}
#ifdef DEBUG
	if (i >= NBUFS)
	{
		msg("blkdirty() called with unknown buffer at 0x%lx", bp);
		return;
	}
	if (blk[i].logical == 0)
	{
		msg("blkdirty called with freed buffer");
		return;
	}
#endif

	/* if this block ends with line# INFINITY, then it must have been
	 * allocated unnecessarily during tmpstart().  Forget it.
	 */
	if (lnum[blk[i].logical] == INFINITY)
	{
#ifdef DEBUG
		if (blk[i].buf.c[0])
		{
			msg("bkldirty called with non-empty extra BLK");
		}
#endif
		blk[i].logical = 0;
		blk[i].dirty = FALSE;
		return;
	}

	/* count lines in this block */
	for (j = 0, scan = bp->c; *scan && scan < bp->c + BLKSIZE; scan++)
	{
		if (*scan == '\n')
		{
			j++;
		}
	}

	/* adjust lnum, if necessary */
	k = blk[i].logical;
	j += (lnum[k - 1] - lnum[k]);
	if (j != 0)
	{
		nlines += j;
		while (k < MAXBLKS && lnum[k] != INFINITY)
		{
			lnum[k++] += j;
		}
	}

	/* if it still has text, mark it as dirty */
	if (*bp->c)
	{
		blk[i].dirty = TRUE;
	}
	else /* empty block, so delete it */
	{
		/* adjust the cache */
		k = blk[i].logical;
		for (j = 0; j < NBUFS; j++)
		{
			if (blk[j].logical >= k)
			{
				blk[j].logical--;
			}
		}

		/* delete it from hdr.n[] and lnum[] */
		blk[i].logical = 0;
		blk[i].dirty = FALSE;
		while (k < MAXBLKS - 1)
		{
			hdr.n[k] = hdr.n[k + 1];
			lnum[k] = lnum[k + 1];
			k++;
		}
		hdr.n[MAXBLKS - 1] = 0;
		lnum[MAXBLKS - 1] = INFINITY;
	}
}


/* insert a new block into hdr, and adjust the cache */
BLK *blkadd(logical)
	int	logical;	/* where to insert the new block */
{
	REG int	i;

	/* adjust hdr and lnum[] */
	for (i = MAXBLKS - 1; i > logical; i--)
	{
		hdr.n[i] = hdr.n[i - 1];
		lnum[i] = lnum[i - 1];
	}
	hdr.n[logical] = 0;
	lnum[logical] = lnum[logical - 1];

	/* adjust the cache */
	for (i = 0; i < NBUFS; i++)
	{
		if (blk[i].logical >= logical)
		{
			blk[i].logical++;
		}
	}

	/* return the new block, via blkget() */
	return blkget(logical);
}


/* This function forces all dirty blocks out to disk */
void blksync()
{
	int	i;

	for (i = 0; i < NBUFS; i++)
	{
		/* blk[i].dirty = TRUE; */
		blkflush(&blk[i]);
	}
	if (*o_sync)
	{
		sync();
	}
}

/*------------------------------------------------------------------------*/

static MARK	undocurs;	/* where the cursor should go if undone */
static long	oldnlines;
static long	oldlnum[MAXBLKS];


/* This function should be called before each command that changes the text.
 * It defines the state that undo() will reset the file to.
 */
void beforedo(forundo)
	int		forundo;	/* boolean: is this for an undo? */
{
	REG int		i;
	REG long	l;

	/* if this is a nested call to beforedo, quit! Use larger context */
	if (b4cnt++ > 0)
	{
		return;
	}

	/* force all block buffers to disk */
	blksync();

#ifndef NO_RECYCLE
	/* perform garbage collection on blocks from tmp file */
	garbage();
#endif

	/* force the header out to disk */
	lseek(tmpfd, 0L, 0);
	if (write(tmpfd, hdr.c, (unsigned)BLKSIZE) != BLKSIZE)
	{
		msg("Trouble writing header to tmp file ");
	}

	/* copy or swap oldnlines <--> nlines, oldlnum <--> lnum */
	if (forundo)
	{
		for (i = 0; i < MAXBLKS; i++)
		{
			l = lnum[i];
			lnum[i] = oldlnum[i];
			oldlnum[i] = l;
		}
		l = nlines;
		nlines = oldnlines;
		oldnlines = l;
	}
	else
	{
		for (i = 0; i < MAXBLKS; i++)
		{
			oldlnum[i] = lnum[i];
		}
		oldnlines = nlines;
	}

	/* save the cursor position */
	undocurs = cursor;

	/* upon return, the calling function continues and makes changes... */
}

/* This function marks the end of a (nested?) change to the file */
void afterdo()
{
	if (--b4cnt)
	{
		/* after abortdo(), b4cnt may decribe nested beforedo/afterdo
		 * pairs incorrectly.  If it is decremented to often, then
		 * keep b4cnt sane but don't do anything else.
		 */
		if (b4cnt < 0)
			b4cnt = 0;

		return;
	}

	/* make sure the cursor wasn't left stranded in deleted text */
	if (markline(cursor) > nlines)
	{
		cursor = MARK_LAST;
	}
	/* NOTE: it is still possible that markidx(cursor) is after the
	 * end of a line, so the Vi mode will have to take care of that
	 * itself */

	/* if a significant change has been made to this file, then set the
	 * MODIFIED flag.
	 */
	if (significant)
	{
		setflag(file, MODIFIED);
	}	
}

/* This function cuts short the current set of changes.  It is called after
 * a SIGINT.
 */
void abortdo()
{
	/* finish the operation immediately. */
	if (b4cnt > 0)
	{
		b4cnt = 1;
		afterdo();
	}

	/* in visual mode, the screen is probably screwed up */
	if (mode == MODE_COLON)
	{
		mode = MODE_VI;
	}
	if (mode == MODE_VI)
	{
		redraw(MARK_UNSET, FALSE);
	}
}

/* This function discards all changes made since the last call to beforedo() */
int undo()
{
	BLK		oldhdr;

	/* if beforedo() has never been run, fail */
	if (!tstflag(file, MODIFIED))
	{
		msg("You haven't modified this file yet.");
		return FALSE;
	}

	/* read the old header form the tmp file */
	lseek(tmpfd, 0L, 0);
	if (read(tmpfd, oldhdr.c, (unsigned)BLKSIZE) != BLKSIZE)
	{
		msg("Trouble rereading the old header from tmp file");
	}

	/* "do" the changed version, so we can undo the "undo" */
	cursor = undocurs;
	beforedo(TRUE);
	afterdo();

	/* wipe out the block buffers - we can't assume they're correct */
	blkinit();

	/* use the old header -- and therefore the old text blocks */
	hdr = oldhdr;

	/* This is a change */
	changes++;

	return TRUE;
}
SHAR_EOF
fi # end of overwriting check
if test -f 'cmd1.c'
then
	echo shar: will not over-write existing file "'cmd1.c'"
else
cat << \SHAR_EOF > 'cmd1.c'
/* cmd1.c */

/* Author:
 *	Steve Kirkendall
 *	14407 SW Teal Blvd. #C
 *	Beaverton, OR 97005
 *	kirkenda@cs.pdx.edu
 */


/* This file contains some of the EX commands - mostly ones that deal with
 * files, options, etc. -- anything except text.
 */

#include "config.h"
#include <ctype.h>
#include "vi.h"
#include "regexp.h"

#if MSDOS
#define	DATE __DATE__
#endif

#ifdef DEBUG
/* print the selected lines with info on the blocks */
/*ARGSUSED*/
void cmd_debug(frommark, tomark, cmd, bang, extra)
	MARK	frommark;
	MARK	tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	REG char	*scan;
	REG long	l;
	REG int		i;
	int		len;

	/* scan lnum[] to determine which block its in */
	l = markline(frommark);
	for (i = 1; l > lnum[i]; i++)
	{
	}

	do
	{
		/* fetch text of the block containing that line */
		scan = blkget(i)->c;

		/* calculate its length */
		if (scan[BLKSIZE - 1])
		{
			len = BLKSIZE;
		}
		else
		{
			len = strlen(scan);
		}

		/* print block stats */
		msg("##### hdr[%d]=%d, lnum[%d-1]=%ld, lnum[%d]=%ld (%ld lines)",
			i, hdr.n[i], i, lnum[i-1], i, lnum[i], lnum[i] - lnum[i - 1]);
		msg("##### len=%d, buf=0x%lx, %sdirty",
			len, scan, ((int *)scan)[MAXBLKS + 1] ? "" : "not ");
		if (bang)
		{
			while (--len >= 0)
			{
				addch(*scan);
				scan++;
			}
		}
		exrefresh();

		/* next block */
		i++;
	} while (i < MAXBLKS && lnum[i] && lnum[i - 1] < markline(tomark));
}


/* This function checks a lot of conditions to make sure they aren't screwy */
/*ARGSUSED*/
void cmd_validate(frommark, tomark, cmd, bang, extra)
	MARK	frommark;
	MARK	tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	char	*scan;
	int	i;
	int	nlcnt;	/* used to count newlines */
	int	len;	/* counts non-NUL characters */

	/* check lnum[0] */
	if (lnum[0] != 0L)
	{
		msg("lnum[0] = %ld", lnum[0]);
	}

	/* check each block */
	for (i = 1; lnum[i] <= nlines; i++)
	{
		scan = blkget(i)->c;
		if (scan[BLKSIZE - 1])
		{
			msg("block %d has no NUL at the end", i);
		}
		else
		{
			for (nlcnt = len = 0; *scan; scan++, len++)
			{
				if (*scan == '\n')
				{
					nlcnt++;
				}
			}
			if (scan[-1] != '\n')
			{
				msg("block %d doesn't end with '\\n' (length %d)", i, len);
			}
			if (bang || nlcnt != lnum[i] - lnum[i - 1])
			{
				msg("block %d (line %ld?) has %d lines, but should have %ld",
					i, lnum[i - 1] + 1L, nlcnt, lnum[i] - lnum[i - 1]);
			}
		}
		exrefresh();
	}

	/* check lnum again */
	if (lnum[i] != INFINITY)
	{
		msg("hdr.n[%d] = %d, but lnum[%d] = %ld",
			i, hdr.n[i], i, lnum[i]);
	}

	msg("# = \"%s\", %% = \"%s\"", prevorig, origname);
}
#endif /* DEBUG */


/*ARGSUSED*/
void cmd_mark(frommark, tomark, cmd, bang, extra)
	MARK	frommark;
	MARK	tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	/* validate the name of the mark */
	if (!extra || *extra < 'a' || *extra > 'z' || extra[1])
	{
		msg("Invalid mark name");
		return;
	}

	mark[*extra - 'a'] = tomark;
}

/*ARGSUSED*/
void cmd_write(frommark, tomark, cmd, bang, extra)
	MARK	frommark;
	MARK	tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	int		fd;
	int		append;	/* boolean: write in "append" mode? */
	REG long	l;
	REG char	*scan;
	REG int		i;

	/* if all lines are to be written, use tmpsave() */
	if (frommark == MARK_FIRST && tomark == MARK_LAST)
	{
		tmpsave(extra, bang);
		return;
	}

	/* see if we're going to do this in append mode or not */
	append = FALSE;
	if (extra[0] == '>' && extra[1] == '>')
	{
		extra += 2;
		append = TRUE;
	}

	/* either the file must not exist, or we must have a ! or be appending */
	if (access(extra, 0) == 0 && !bang && !append)
	{
		msg("File already exists - Use :w! to overwrite");
		return;
	}

	/* else do it line-by-line, like cmd_print() */
	if (append)
	{
#ifdef O_APPEND
		fd = open(extra, O_WRONLY|O_APPEND);
#else
		fd = open(extra, O_WRONLY);
		if (fd >= 0)
		{
			lseek(fd, 0L, 2);
		}
#endif
	}
	else
	{
		fd = -1; /* so we know the file isn't open yet */
	}

	if (fd < 0)
	{
		fd = creat(extra, FILEPERMS);
		if (fd < 0)
		{
			msg("Can't write to \"%s\"", extra);
			return;
		}
	}
	for (l = markline(frommark); l <= markline(tomark); l++)
	{
		/* get the next line */
		scan = fetchline(l);
		i = strlen(scan);
		scan[i++] = '\n';

		/* print the line */
		twrite(fd, scan, i);
	}
	close(fd);
}	


/*ARGSUSED*/
void cmd_shell(frommark, tomark, cmd, bang, extra)
	MARK	frommark, tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	static char	prevextra[80];

	/* special case: ":sh" means ":!sh" */
	if (cmd == CMD_SHELL)
	{
		extra = o_shell;
		frommark = tomark = 0L;
	}

	/* if extra is "!", substute previous command */
	if (*extra == '!')
	{
		if (!*prevextra)
		{
			msg("No previous shell command to substitute for '!'");
			return;
		}
		extra = prevextra;
	}
	else if (cmd == CMD_BANG && strlen(extra) < sizeof(prevextra) - 1)
	{
		strcpy(prevextra, extra);
	}

	/* if no lines were specified, just run the command */
	Suspend_curses();
	if (frommark == 0L)
	{
		system(extra);
	}
	else /* pipe lines from the file through the command */
	{
		filter(frommark, tomark, extra);
	}

	/* resume curses quietly for MODE_EX, but noisily otherwise */
	Resume_curses(mode == MODE_EX);
}


/*ARGSUSED*/
void cmd_global(frommark, tomark, cmd, bang, extra)
	MARK	frommark, tomark;
	CMD	cmd;
	int	bang;
	char	*extra;	/* rest of the command line */
{
	char	*cmdptr;	/* the command from the command line */
	char	cmdln[100];	/* copy of the command from the command line */
	char	*line;		/* a line from the file */
	long	l;		/* used as a counter to move through lines */
	long	lqty;		/* quantity of lines to be scanned */
	long	nchanged;	/* number of lines changed */
	regexp	*re;		/* the compiled search expression */

	/* can't nest global commands */
	if (doingglobal)
	{
		msg("Can't nest global commands.");
		rptlines = -1L;
		return;
	}

	/* ":g! ..." is the same as ":v ..." */
	if (bang)
	{
		cmd = CMD_VGLOBAL;
	}

	/* make sure we got a search pattern */
	if (*extra != '/' && *extra != '?')
	{
		msg("Usage: %c /regular expression/ command", cmd == CMD_GLOBAL ? 'g' : 'v');
		return;
	}

	/* parse & compile the search pattern */
	cmdptr = parseptrn(extra);
	if (!extra[1])
	{
		msg("Can't use empty regular expression with '%c' command", cmd == CMD_GLOBAL ? 'g' : 'v');
		return;
	}
	re = regcomp(extra + 1);
	if (!re)
	{
		/* regcomp found & described an error */
		return;
	}

	/* for each line in the range */
	doingglobal = TRUE;
	ChangeText
	{
		/* NOTE: we have to go through the lines in a forward order,
		 * otherwise "g/re/p" would look funny.  *BUT* for "g/re/d"
		 * to work, simply adding 1 to the line# on each loop won't
		 * work.  The solution: count lines relative to the end of
		 * the file.  Think about it.
		 */
		for (l = nlines - markline(frommark),
			lqty = markline(tomark) - markline(frommark) + 1L,
			nchanged = 0L;
		     lqty > 0 && nlines - l >= 0 && nchanged >= 0L;
		     l--, lqty--)
		{
			/* fetch the line */
			line = fetchline(nlines - l);

			/* if it contains the search pattern... */
			if ((!regexec(re, line, 1)) == (cmd != CMD_GLOBAL))
			{
				/* move the cursor to that line */
				cursor = MARK_AT_LINE(nlines - l);

				/* do the ex command (without mucking up
				 * the original copy of the command line)
				 */
				strcpy(cmdln, cmdptr);
				rptlines = 0L;
				doexcmd(cmdln);
				nchanged += rptlines;
			}
		}
	}
	doingglobal = FALSE;

	/* free the regexp */
	free(re);

	/* Reporting...*/
	rptlines = nchanged;
}


/*ARGSUSED*/
void cmd_file(frommark, tomark, cmd, bang, extra)
	MARK	frommark, tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
#ifndef CRUNCH
	/* if we're given a new filename, use it as this file's name */
	if (extra && *extra)
	{
		strcpy(origname, extra);
	}
#endif
	if (cmd == CMD_FILE)
	{
		msg("\"%s\" %s%s %ld lines,  line %ld [%ld%%]",
			*origname ? origname : "[NO FILE]",
			tstflag(file, MODIFIED) ? "[MODIFIED]" : "",
			tstflag(file, READONLY) ? "[READONLY]" : "",
			nlines,
			markline(frommark),
			markline(frommark) * 100 / nlines);
	}
	else if (markline(frommark) == markline(tomark))
	{
		msg("%ld", markline(frommark));
	}
	else
	{
		msg("range \"%ld,%ld\" contains %ld lines",
			markline(frommark),
			markline(tomark),
			markline(tomark) - markline(frommark) + 1L);
	}
}


/*ARGSUSED*/
void cmd_edit(frommark, tomark, cmd, bang, extra)
	MARK	frommark, tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	long	line = 1L;	/* might be set to prevline */

	/* Editing previous file?  Then start at previous line */
	if (!strcmp(extra, prevorig))
	{
		line = prevline;
	}

#ifndef CRUNCH
	/* if we were given an explicit starting line, then start there */
	if (*extra == '+')
	{
		for (extra++, line = 0L; *extra >= '0' && *extra <= '9'; extra++)
		{
			line *= 10L;
			line += (*extra - '0');
		}
		while (isascii(*extra) && isspace(*extra))
		{
			extra++;
		}
	}
#endif /* not CRUNCH */

	/* switch files */
	if (tmpabort(bang))
	{
		Tmpstart(extra);
		if (line <= nlines && line >= 1L)
		{
			cursor = MARK_AT_LINE(line);
		}
	}
	else
	{
		msg("Use edit! to abort changes, or w to save changes");

		/* so we can say ":e!#" next time... */
		strcpy(prevorig, extra);
		prevline = 1L;
	}
}

/* This code is also used for rewind -- GB */

/*ARGSUSED*/
void cmd_next(frommark, tomark, cmd, bang, extra)
	MARK	frommark, tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	int	i, j;
	char	*scan;
	char	*build;

	/* if extra stuff given, use ":args" to define a new args list */
	if (cmd == CMD_NEXT && extra && *extra)
	{
		cmd_args(frommark, tomark, cmd, bang, extra);
	}

	/* move to the next arg */
	if (cmd == CMD_NEXT)
	{
		i = argno + 1;
	}
	else if (cmd == CMD_PREVIOUS)
	{
		i = argno - 1;
	}
	else /* cmd == CMD_REWIND */
	{
		i = 0;
	}	
	if (i < 0 || i >= nargs)
	{
		msg("No %sfiles to edit", cmd == CMD_REWIND ? "" : "more ");
		return;
	}

	/* find & isolate the name of the file to edit */
	for (j = i, scan = args; j > 0; j--)
	{
		while(!isascii(*scan) || !isspace(*scan))
		{
			scan++;
		}
		while (isascii(*scan) && isspace(*scan))
		{
			scan++;
		}
	}
	for (build = tmpblk.c; *scan && (!isascii(*scan) || !isspace(*scan)); )
	{
		*build++ = *scan++;
	}
	*build = '\0';

	/* switch to the next file */
	if (tmpabort(bang))
	{
		Tmpstart(tmpblk.c);
		argno = i;
	}
	else
	{
		msg("Use :%s! to abort changes, or w to save changes",
			cmd == CMD_NEXT ? "next" :
			cmd == CMD_PREVIOUS ? "previous" :
					"rewind");
	}
}

/* also called from :wq -- always writes back in this case */

/*ARGSUSED*/
void cmd_xit(frommark, tomark, cmd, bang, extra)
	MARK	frommark, tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	static long	whenwarned;	/* when the user was last warned of extra files */
	int		oldflag;

	/* if there are more files to edit, then warn user */
	if (argno + 1 < nargs && whenwarned != changes && (!bang || cmd != CMD_QUIT))
	{
		msg("More files to edit -- Use \":n\" to go to next file");
		whenwarned = changes;
		return;
	}

	if (cmd == CMD_QUIT)
	{
		if (tmpabort(bang))
		{
			mode = MODE_QUIT;
		}
		else
		{
			msg("Use q! to abort changes, or wq to save changes");
		}
	}
	else
	{
		/* else try to save this file */
		oldflag = tstflag(file, MODIFIED);
		if (cmd == CMD_WQUIT)
			setflag(file, MODIFIED);
		if (tmpend(bang))
		{
			mode = MODE_QUIT;
		}
		else
		{
			msg("Could not save file -- use quit! to abort changes, or w filename");
		}
		if (!oldflag)
			clrflag(file, MODIFIED);
	}
}


/*ARGSUSED*/
void cmd_args(frommark, tomark, cmd, bang, extra)
	MARK	frommark, tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	char	*scan;
	char	*eow;
	int	col;
	int	arg;
	int	addcols;
	int	scrolled = 0;

	/* if no extra names given, or just current name, then report the args
	 * we have now.
	 */
	if (!extra || !*extra)
	{
		for (scan = args, col=arg=0; *scan; )
		{
			while (*scan && isascii(*scan) && isspace(*scan))
				scan++;
			eow = scan;
			while (*eow && (!isascii(*++eow) || !isspace(*eow)))
				;
			if (arg == argno)
				addcols = 2;
			else
				addcols = 0;	
			if (col+addcols+(int)(eow-scan)+1>=COLS)
			{
				addch('\n');
				scrolled=1;
				col=0;
			}
			else if (arg)
			{	qaddch(' ');
				col++;
			}
			if (arg == argno)
				qaddch('[');
			while (scan < eow)
			{	qaddch(*scan++);
				col++;
			}
			if (arg == argno)
				qaddch(']');	
			arg++;	
			col+=addcols;
		}
		/* write a trailing newline */
		if ((mode == MODE_EX || mode == MODE_COLON || scrolled) && col)
			addch('\n');
		exrefresh();	
	}
	else /* new args list given */
	{
		strcpy(args, extra);
		argno = -1; /* before the first, so :next will go to first */

		/* count the names */
		for (nargs = 0, scan = args; *scan; nargs++)
		{
			while (*scan && (!isascii(*scan) || !isspace(*scan)))
			{
				scan++;
			}
			while (isascii(*scan) && isspace(*scan))
			{
				scan++;
			}
		}
		msg("%d files to edit", nargs);
	}
}


/*ARGSUSED*/
void cmd_cd(frommark, tomark, cmd, bang, extra)
	MARK	frommark, tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	char	*getenv();

	/* default directory name is $HOME */
	if (!*extra)
	{
		extra = getenv("HOME");
		if (!extra)
		{
			msg("environment variable $HOME not set");
			return;
		}
	}

	/* go to the directory */
	if (chdir(extra) < 0)
	{
		perror(extra);
	}
}


/*ARGSUSED*/
void cmd_map(frommark, tomark, cmd, bang, extra)
	MARK	frommark, tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	char	*mapto;

	/* "map" with no extra will dump the map table contents */
	if (!*extra)
	{
		dumpkey(bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD);
	}
	else
	{
		/* "extra" is key to map, followed my what it maps to */
		for (mapto = extra; *mapto && *mapto != ' ' && *mapto!= '\t'; mapto++)
		{
		}
		while (*mapto == ' ' || *mapto == '\t')
		{
			*mapto++ = '\0';
		}

		mapkey(extra, mapto, bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, (char *)0);
	}
}


/*ARGSUSED*/
void cmd_set(frommark, tomark, cmd, bang, extra)
	MARK	frommark, tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	if (!*extra)
	{
		dumpopts(FALSE);/* "FALSE" means "don't dump all" - only set */
	}
	else if (!strcmp(extra, "all"))
	{
		dumpopts(TRUE);	/* "TRUE" means "dump all" - even unset vars */
	}
	else
	{
		setopts(extra);

		/* That option may have affected the appearence of text */
		changes++;
	}
}

/*ARGSUSED*/
void cmd_tag(frommark, tomark, cmd, bang, extra)
	MARK	frommark, tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	char	*scan;	/* used to scan through the tmpblk.c */
	char	*cmp;	/* char of tag name we're comparing, or NULL */
	char	*end;	/* marks the end of chars in tmpblk.c */
	int	fd;	/* file descriptor used to read the file */
#ifndef NO_MAGIC
	char	wasmagic; /* preserves the original state of o_magic */
#endif
	static char prevtag[30];

	/* if no tag is given, use the previous tag */
	if (!extra || !*extra)
	{
		if (!*prevtag)
		{
			msg("No previous tag");
			return;
		}
		extra = prevtag;
	}
	else
	{
		strncpy(prevtag, extra, sizeof prevtag);
	}

	/* open the tags file */
	fd = open(TAGS, O_RDONLY);
	if (fd < 0)
	{
		msg("No tags file");
		return;
	}

	/* Hmmm... this would have been a lot easier with <stdio.h> */

	/* find the line with our tag in it */
	for(scan = end = tmpblk.c, cmp = extra; ; scan++)
	{
		/* read a block, if necessary */
		if (scan >= end)
		{
			end = tmpblk.c + tread(fd, tmpblk.c, BLKSIZE);
			scan = tmpblk.c;
			if (scan >= end)
			{
				msg("tag \"%s\" not found", extra);
				close(fd);
				return;
			}
		}

		/* if we're comparing, compare... */
		if (cmp)
		{
			/* matched??? wow! */
			if (!*cmp && *scan == '\t')
			{
				break;
			}
			if (*cmp++ != *scan)
			{
				/* failed! skip to newline */
				cmp = (char *)0;
			}
		}

		/* if we're skipping to newline, do it fast! */
		if (!cmp)
		{
			while (scan < end && *scan != '\n')
			{
				scan++;
			}
			if (scan < end)
			{
				cmp = extra;
			}
		}
	}

	/* found it! get the rest of the line into memory */
	for (cmp = tmpblk.c, scan++; scan < end && *scan != '\n'; )
	{
		*cmp++ = *scan++;
	}
	if (scan == end)
	{
		tread(fd, cmp, BLKSIZE - (cmp - tmpblk.c));
	}

	/* we can close the tags file now */
	close(fd);

	/* extract the filename from the line, and edit the file */
	for (cmp = tmpblk.c; *cmp != '\t'; cmp++)
	{
	}
	*cmp++ = '\0';
	if (strcmp(origname, tmpblk.c) != 0)
	{
		if (!tmpabort(bang))
		{
			msg("Use :tag! to abort changes, or :w to save changes");
			return;
		}
		Tmpstart(tmpblk.c);
	}

	/* move to the desired line (or to line 1 if that fails) */
#ifndef NO_MAGIC
	wasmagic = *o_magic;
	*o_magic = FALSE;
#endif
	cursor = MARK_FIRST;
	linespec(cmp, &cursor);
	if (cursor == MARK_UNSET)
	{
		cursor = MARK_FIRST;
	}
#ifndef NO_MAGIC
	*o_magic = wasmagic;
#endif
}



/*ARGSUSED*/
void cmd_visual(frommark, tomark, cmd, bang, extra)
	MARK	frommark, tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	mode = MODE_VI;
	msg("");
}





/* describe this version of the program */
/*ARGSUSED*/
void cmd_version(frommark, tomark, cmd, bang, extra)
	MARK	frommark;
	MARK	tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
#ifndef DATE
	msg("%s", VERSION);
#else
	msg("%s  (%s)", VERSION, DATE);
#endif
#ifdef COMPILED_BY
	msg("Compiled by %s", COMPILED_BY);
#endif
#ifdef CREDIT
	msg("%s", CREDIT);
#endif
#ifdef COPYING
	msg("%s", COPYING);
#endif
}


#ifndef NO_MKEXRC
/* make a .exrc file which describes the current configuration */
/*ARGSUSED*/
void cmd_mkexrc(frommark, tomark, cmd, bang, extra)
	MARK	frommark;
	MARK	tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	int	fd;

	/* the default name for the .exrc file EXRC */
	if (!*extra)
	{
		extra = EXRC;
	}

	/* create the .exrc file */
	fd = creat(extra, FILEPERMS);
	if (fd < 0)
	{
		msg("Couldn't create a new \"%s\" file", extra);
		return;
	}

	/* save stuff */
	savekeys(fd);
	saveopts(fd);
#ifndef NO_DIGRAPH
	savedigs(fd);
#endif
#ifndef	NO_ABBR
	saveabbr(fd);
#endif

	/* close the file */
	close(fd);
	msg("Created a new \"%s\" file", extra);
}
#endif

#ifndef NO_DIGRAPH
/*ARGSUSED*/
void cmd_digraph(frommark, tomark, cmd, bang, extra)
	MARK	frommark;
	MARK	tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	do_digraph(bang, extra);
}
#endif


#ifndef NO_ERRLIST 
static char	errfile[256];	/* the name of a file containing an error */
static long	errline;	/* the line number for an error */

/* This static function tries to parse an error message.
 *
 * For most compilers, the first word is taken to be the name of the erroneous
 * file, and the first number after that is taken to be the line number where
 * the error was detected.  The description of the error follows, possibly
 * preceded by an "error ... :" or "warning ... :" label which is skipped.
 *
 * For Coherent, error messages look like "line#: filename: message".
 *
 * For non-error lines, or unparsable error lines, this function returns NULL.
 * Normally, though, it alters errfile and errline, and returns a pointer to
 * the description.
 */
static char *parse_errmsg(text)
	REG char	*text;
{
	REG char	*cpy;
	long		atol();
# if COHERENT || TOS /* any Mark Williams compiler */
	/* Get the line number.  If no line number, then ignore this line. */
	errline = atol(text);
	if (errline == 0L)
		return (char *)0;

	/* Skip to the start of the filename */
	while (*text && *text++ != ':')
	{
	}
	if (!*text++)
		return (char *)0;

	/* copy the filename to errfile */
	for (cpy = errfile; *text && (*cpy++ = *text++) != ':'; )
	{
	}
	if (!*text++)
		return (char *)0;
	cpy[-1] = '\0';

	return text;
# else /* not a Mark Williams compiler */
	char		*errmsg;

	/* the error message is the whole line, by default */
	errmsg = text;

	/* skip leading garbage */
	while (*text && !(isascii(*text) && isalnum(*text)))
	{
		text++;
	}

	/* copy over the filename */
	cpy = errfile;
	while(isascii(*text) && isalnum(*text) || *text == '.')
	{
		*cpy++ = *text++;
	}
	*cpy = '\0';

	/* ignore the name "Error" and filenames that contain a '/' */
	if (*text == '/' || !strcmp(errfile + 1, "rror") || access(errfile, 0) < 0)
	{
		return (char *)0;
	}

	/* skip garbage between filename and line number */
	while (*text && !(isascii(*text) && isdigit(*text)))
	{
		text++;
	}

	/* if the number is part of a larger word, then ignore this line */
	if (*text && isascii(text[-1]) && isalpha(text[-1]))
	{
		return (char *)0;
	}

	/* get the error line */
	errline = 0L;
	while (isascii(*text) && isdigit(*text))
	{
		errline *= 10;
		errline += (*text - '0');
		text++;
	}

	/* any line which lacks a filename or line number should be ignored */
	if (!errfile[0] || !errline)
	{
		return (char *)0;
	}

	/* locate the beginning of the error description */
	while (*text && isascii(*text) && !isspace(*text))
	{
		text++;
	}
	while (*text)
	{
#  ifndef CRUNCH
		/* skip "error #:" and "warning #:" clauses */
		if (!strncmp(text + 1, "rror ", 5)
		 || !strncmp(text + 1, "arning ", 7)
		 || !strncmp(text + 1, "atal error", 10))
		{
			do
			{
				text++;
			} while (*text && *text != ':');
			continue;
		}
#  endif

		/* anything other than whitespace or a colon is important */
		if (!isascii(*text) || (!isspace(*text) && *text != ':'))
		{
			errmsg = text;
			break;
		}

		/* else keep looking... */
		text++;
	}

	return errmsg;
# endif /* not COHERENT */
}

/*ARGSUSED*/
void cmd_errlist(frommark, tomark, cmd, bang, extra)
	MARK	frommark, tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	static long	endline;/* original number of lines in this file */
	static long	offset;	/* offset of the next line in the errlist file */
	static int	fd = -2;/* fd of the errlist file */
	int		i;
	char		*errmsg;

	/* if a new errlist file is named, open it */
	if (extra && extra[0])
	{
		/* close the old one */
		if (fd >= 0)
		{
			close(fd);
		}

		fd = open(extra, O_RDONLY);
		offset = 0L;
	}
	else if (fd < 0)
	{
		fd = open(ERRLIST, O_RDONLY);
		offset = 0L;
	}

	/* do we have an errlist file now? */
	if (fd < 0)
	{
		msg("There is no errlist file");
		beep();
		return;
	}

	/* find the next error message in the file */
	do
	{
		/* read the next line from the errlist */
		lseek(fd, offset, 0);
		if (tread(fd, tmpblk.c, (unsigned)BLKSIZE) <= 0)
		{
			msg("No more errors");
			beep();
			close(fd);
			return;
		}
		for (i = 0; tmpblk.c[i] != '\n'; i++)
		{
		}
		tmpblk.c[i++] = 0;

		/* look for an error message in the line */
		errmsg = parse_errmsg(tmpblk.c);
		if (!errmsg)
		{
			offset += i;
		}

	} while (!errmsg);

	/* switch to the file containing the error, if this isn't it */
	if (strcmp(origname, errfile))
	{
		if (!tmpabort(bang))
		{
			msg("Use :er! to abort changes, or :w to save changes");
			beep();
			return;
		}
		Tmpstart(errfile);
		endline = nlines;
	}
	else if (endline == 0L)
	{
		endline = nlines;
	}

	/* go to the line where the error was detected */
	cursor = MARK_AT_LINE(errline + (nlines - endline));
	if (cursor > MARK_LAST)
	{
		cursor = MARK_LAST;
	}
	if (mode == MODE_VI)
	{
		redraw(cursor, FALSE);
	}

	/* display the error message */
	if (nlines > endline)
	{
		msg("line %ld(+%ld): %.60s", errline, nlines - endline, errmsg);
	}
	else if (nlines < endline)
	{
		msg("line %ld(-%ld): %.60s", errline, endline - nlines, errmsg);
	}
	else
	{
		msg("line %ld: %.65s", errline, errmsg);
	}

	/* remember where the NEXT error line will start */
	offset += i;
}


/*ARGSUSED*/
void cmd_make(frommark, tomark, cmd, bang, extra)
	MARK	frommark, tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	BLK	buf;

	/* if the file hasn't been saved, then complain unless ! */
	if (tstflag(file, MODIFIED) && !bang)
	{
		msg("\"%s\" not saved yet", origname);
		return;
	}

	/* build the command */
	sprintf(buf.c, "%s %s %s%s", (cmd == CMD_CC ? o_cc : o_make), extra, REDIRECT, ERRLIST);
	qaddstr(buf.c);
	addch('\n');

	/* run the command, with curses temporarily disabled */
	Suspend_curses();
	system(buf.c);
	Resume_curses(mode == MODE_EX);
	if (mode == MODE_COLON)
		mode = MODE_VI;

	/* run the "errlist" command */
	cmd_errlist(MARK_UNSET, MARK_UNSET, cmd, bang, ERRLIST);
}
#endif


#ifndef NO_ABBR
/*ARGSUSED*/
void cmd_abbr(frommark, tomark, cmd, bang, extra)
	MARK	frommark, tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	do_abbr(extra);
}
#endif
SHAR_EOF
fi # end of overwriting check
if test -f 'cmd2.c'
then
	echo shar: will not over-write existing file "'cmd2.c'"
else
cat << \SHAR_EOF > 'cmd2.c'
/* cmd2.c */

/* Author:
 *	Steve Kirkendall
 *	14407 SW Teal Blvd. #C
 *	Beaverton, OR 97005
 *	kirkenda@cs.pdx.edu
 */


/* This file contains some of the commands - mostly ones that change text */

#include <ctype.h>
#include "config.h"
#include "vi.h"
#include "regexp.h"
#if TOS
# include <stat.h>
#else
# if OSK
#  include "osk.h"
# else
#  include <sys/stat.h>
# endif
#endif


/*ARGSUSED*/
void cmd_substitute(frommark, tomark, cmd, bang, extra)
	MARK	frommark;
	MARK	tomark;
	CMD	cmd;
	int	bang;
	char	*extra;	/* rest of the command line */
{
	char	*line;	/* a line from the file */
	regexp	*re;	/* the compiled search expression */
	char	*subst;	/* the substitution string */
	char	*opt;	/* substitution options */
	long	l;	/* a line number */
	char	*s, *d;	/* used during subtitutions */
	char	*conf;	/* used during confirmation */
	long	chline;	/* # of lines changed */
	long	chsub;	/* # of substitutions made */
	static	optp;	/* boolean option: print when done? */
	static	optg;	/* boolean option: substitute globally in line? */
	static	optc;	/* boolean option: confirm before subst? */


	/* for now, assume this will fail */
	rptlines = -1L;

	if (cmd == CMD_SUBAGAIN)
	{
#ifndef NO_MAGIC
		if (*o_magic)
			subst = "~";
		else
#endif
		subst = "\\~";
		re = regcomp("");

		/* if visual "&", then turn off the "p" and "c" options */
		if (bang)
		{
			optp = optc = FALSE;
		}
	}
	else
	{
		/* make sure we got a search pattern */
		if (*extra != '/' && *extra != '?')
		{
			msg("Usage: s/regular expression/new text/");
			return;
		}

		/* parse & compile the search pattern */
		subst = parseptrn(extra);
		re = regcomp(extra + 1);
	}

	/* abort if RE error -- error message already given by regcomp() */
	if (!re)
	{
		return;
	}

	if (cmd == CMD_SUBSTITUTE)
	{
		/* parse the substitution string & find the option string */
		for (opt = subst; *opt && *opt != *extra; opt++)
		{
			if (*opt == '\\' && opt[1])
			{
				opt++;
			}
		}
		if (*opt)
		{
			*opt++ = '\0';
		}

		/* analyse the option string */
		if (!*o_edcompatible)
		{
			optp = optg = optc = FALSE;
		}
		while (*opt)
		{
			switch (*opt++)
			{
			  case 'p':	optp = !optp;	break;
			  case 'g':	optg = !optg;	break;
			  case 'c':	optc = !optc;	break;
			  case ' ':
			  case '\t':			break;
			  default:
				msg("Subst options are p, c, and g -- not %c", opt[-1]);
				return;
			}
		}
	}

	/* if "c" or "p" flag was given, and we're in visual mode, then NEWLINE */
	if ((optc || optp) && mode == MODE_VI)
	{
		addch('\n');
		exrefresh();
	}

	ChangeText
	{
		/* reset the change counters */
		chline = chsub = 0L;

		/* for each selected line */
		for (l = markline(frommark); l <= markline(tomark); l++)
		{
			/* fetch the line */
			line = fetchline(l);

			/* if it contains the search pattern... */
			if (regexec(re, line, TRUE))
			{
				/* increment the line change counter */
				chline++;

				/* initialize the pointers */
				s = line;
				d = tmpblk.c;

				/* do once or globally ... */
				do
				{
#ifndef CRUNCH
					/* confirm, if necessary */
					if (optc)
					{
						for (conf = line; conf < re->startp[0]; conf++)
							addch(*conf);
						standout();
						for ( ; conf < re->endp[0]; conf++)
							addch(*conf);
						standend();
						for (; *conf; conf++)
							addch(*conf);
						addch('\n');
						exrefresh();
						if (getkey(0) != 'y')
						{
							/* copy accross the original chars */
							while (s < re->endp[0])
								*d++ = *s++;

							/* skip to next match on this line, if any */
							continue;
						}
					}
#endif /* not CRUNCH */

					/* increment the substitution change counter */
					chsub++;

					/* this may be the first line to redraw */
					redrawrange(l, l + 1L, l + 1L);

					/* copy stuff from before the match */
					while (s < re->startp[0])
					{
						*d++ = *s++;
					}
	
					/* substitute for the matched part */
					regsub(re, subst, d);
					s = re->endp[0];
					d += strlen(d);

				} while (optg && regexec(re, s, FALSE));

				/* copy stuff from after the match */
				while (*d++ = *s++)	/* yes, ASSIGNMENT! */
				{
				}

				/* replace the old version of the line with the new */
				d[-1] = '\n';
				d[0] = '\0';
				change(MARK_AT_LINE(l), MARK_AT_LINE(l + 1), tmpblk.c);

				/* if supposed to print it, do so */
				if (optp)
				{
					addstr(tmpblk.c);
					exrefresh();
				}

				/* move the cursor to that line */
				cursor = MARK_AT_LINE(l);
			}
		}
	}

	/* tweak for redrawing */
	mustredraw = TRUE;

	/* free the regexp */
	free(re);

	/* if done from within a ":g" command, then finish silently */
	if (doingglobal)
	{
		rptlines = chline;
		rptlabel = "changed";
		return;
	}

	/* Reporting */
	if (chsub == 0)
	{
		msg("Substitution failed");
	}
	else if (chline >= *o_report)
	{
		msg("%ld substitutions on %ld lines", chsub, chline);
	}
}




/*ARGSUSED*/
void cmd_delete(frommark, tomark, cmd, bang, extra)
	MARK	frommark;
	MARK	tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	MARK	curs2;	/* an altered form of the cursor */

	/* choose your cut buffer */
	if (*extra == '"')
	{
		extra++;
	}
	if (*extra)
	{
		cutname(*extra);
	}

	/* make sure we're talking about whole lines here */
	frommark = frommark & ~(BLKSIZE - 1);
	tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;

	/* yank the lines */
	cut(frommark, tomark);

	/* if CMD_DELETE then delete the lines */
	if (cmd != CMD_YANK)
	{
		curs2 = cursor;
		ChangeText
		{
			/* delete the lines */
			delete(frommark, tomark);
		}
		if (curs2 > tomark)
		{
			cursor = curs2 - tomark + frommark;
		}
		else if (curs2 > frommark)
		{
			cursor = frommark;
		}
	}
}


/*ARGSUSED*/
void cmd_append(frommark, tomark, cmd, bang, extra)
	MARK	frommark;
	MARK	tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	long	l;	/* line counter */

	ChangeText
	{
		/* if we're doing a change, delete the old version */
		if (cmd == CMD_CHANGE)
		{
			/* delete 'em */
			cmd_delete(frommark, tomark, cmd, bang, extra);
		}

		/* new lines start at the frommark line, or after it */
		l = markline(frommark);
		if (cmd == CMD_APPEND)
		{
 			l++;
		}

		/* get lines until no more lines, or "." line, and insert them */
		while (vgets('\0', tmpblk.c, BLKSIZE) >= 0)
		{
			addch('\n');
			if (!strcmp(tmpblk.c, "."))
			{
				break;
			}

			strcat(tmpblk.c, "\n");
			add(MARK_AT_LINE(l), tmpblk.c);
			l++;
		}
	}

	/* on the odd chance that we're calling this from vi mode ... */
	redraw(MARK_UNSET, FALSE);
}


/*ARGSUSED*/
void cmd_put(frommark, tomark, cmd, bang, extra)
	MARK	frommark;
	MARK	tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	/* choose your cut buffer */
	if (*extra == '"')
	{
		extra++;
	}
	if (*extra)
	{
		cutname(*extra);
	}

	/* paste it */
	ChangeText
	{
		cursor = paste(frommark, TRUE, FALSE);
	}
}


/*ARGSUSED*/
void cmd_join(frommark, tomark, cmd, bang, extra)
	MARK	frommark;
	MARK	tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	long	l;
	char	*scan;
	int	len;	/* length of the new line */

	/* if only one line is specified, assume the following one joins too */
	if (markline(frommark) == nlines)
	{
		msg("Nothing to join with this line");
		return;
	}
	if (markline(frommark) == markline(tomark))
	{
		tomark += BLKSIZE;
	}

	/* get the first line */
	l = markline(frommark);
	strcpy(tmpblk.c, fetchline(l));
	len = strlen(tmpblk.c);

	/* build the longer line */
	while (++l <= markline(tomark))
	{
		/* get the next line */
		scan = fetchline(l);

		/* remove any leading whitespace */
		while (*scan == '\t' || *scan == ' ')
		{
			scan++;
		}

		/* see if the line will fit */
		if (strlen(scan) + len + 3 > BLKSIZE)
		{
			msg("Can't join -- the resulting line would be too long");
			return;
		}

		/* catenate it, with a space (or two) in between */
		if (len >= 1 &&
			(tmpblk.c[len - 1] == '.'
			 || tmpblk.c[len - 1] == '?'
			 || tmpblk.c[len - 1] == '!'))
		{
			 tmpblk.c[len++] = ' ';
		}
		tmpblk.c[len++] = ' ';
		strcpy(tmpblk.c + len, scan);
		len += strlen(scan);
	}
	tmpblk.c[len++] = '\n';
	tmpblk.c[len] = '\0';

	/* make the change */
	ChangeText
	{
		frommark &= ~(BLKSIZE - 1);
		tomark &= ~(BLKSIZE - 1);
		tomark += BLKSIZE;
		change(frommark, tomark, tmpblk.c);
	}

	/* Reporting... */
	rptlines = markline(tomark) - markline(frommark) - 1L;
	rptlabel = "joined";
}



/*ARGSUSED*/
void cmd_shift(frommark, tomark, cmd, bang, extra)
	MARK	frommark;
	MARK	tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	long	l;	/* line number counter */
	int	oldidx;	/* number of chars previously used for indent */
	int	newidx;	/* number of chars in the new indent string */
	int	oldcol;	/* previous indent amount */
	int	newcol;	/* new indent amount */
	char	*text;	/* pointer to the old line's text */

	/* figure out how much of the screen we must redraw (for vi mode) */
	if (markline(frommark) != markline(tomark))
	{
		mustredraw = TRUE;
		redrawrange(markline(frommark), markline(tomark) + 1L, markline(tomark) + 1L);
	}

	ChangeText
	{
		/* for each line to shift... */
		for (l = markline(frommark); l <= markline(tomark); l++)
		{
			/* get the line - ignore empty lines unless ! mode */
			text = fetchline(l);
			if (!*text && !bang)
				continue;

			/* calc oldidx and oldcol */
			for (oldidx = 0, oldcol = 0;
			     text[oldidx] == ' ' || text[oldidx] == '\t';
			     oldidx++)
			{
				if (text[oldidx] == ' ')
				{
					oldcol += 1;
				}
				else
				{
					oldcol += *o_tabstop - (oldcol % *o_tabstop);
				}
			}
	
			/* calc newcol */
			if (cmd == CMD_SHIFTR)
			{
				newcol = oldcol + (*o_shiftwidth & 0xff);
			}
			else
			{
				newcol = oldcol - (*o_shiftwidth & 0xff);
				if (newcol < 0)
					newcol = 0;
			}

			/* if no change, then skip to next line */
			if (oldcol == newcol)
				continue;

			/* build a new indent string */
			newidx = 0;
			while (newcol >= *o_tabstop)
			{
				tmpblk.c[newidx++] = '\t';
				newcol -= *o_tabstop;
			}
			while (newcol > 0)
			{
				tmpblk.c[newidx++] = ' ';
				newcol--;
			}
			tmpblk.c[newidx] = '\0';
			
			/* change the old indent string into the new */
			change(MARK_AT_LINE(l), MARK_AT_LINE(l) + oldidx, tmpblk.c);
		}
	}

	/* Reporting... */
	rptlines = markline(tomark) - markline(frommark) + 1L;
	if (cmd == CMD_SHIFTR)
	{
		rptlabel = ">ed";
	}
	else
	{
		rptlabel = "<ed";
	}
}


/*ARGSUSED*/
void cmd_read(frommark, tomark, cmd, bang, extra)
	MARK	frommark;
	MARK	tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	int	fd, rc;	/* used while reading from the file */
	char	*scan;	/* used for finding NUL characters */
	int	hadnul;	/* boolean: any NULs found? */
	int	addnl;	/* boolean: forced to add newlines? */
	int	len;	/* number of chars in current line */
	long	lines;	/* number of lines in current block */
	struct stat statb;

	/* special case: if ":r !cmd" then let the filter() function do it */
	if (extra[0] == '!')
	{
		filter(frommark, MARK_UNSET, extra + 1);
		return;
	}

	/* open the file */
	fd = open(extra, O_RDONLY);
	if (fd < 0)
	{
		msg("Can't open \"%s\"", extra);
		return;
	}

#ifndef CRUNCH
	if (stat(extra, &statb) < 0)
	{
		msg("Can't stat \"%s\"", extra);
	}
# if TOS
	if (statb.st_mode & S_IJDIR)
# else
#  if OSK
	if (statb.st_mode & S_IFDIR)
#  else
	if ((statb.st_mode & S_IFMT) != S_IFREG)
#  endif
# endif
	{
		msg("\"%s\" is not a regular file", extra);
		return;
	}
#endif /* not CRUNCH */

	/* get blocks from the file, and add them */
	ChangeText
	{
		/* insertion starts at the line following frommark */
		tomark = frommark = (frommark | (BLKSIZE - 1L)) + 1L;
		len = 0;
		hadnul = addnl = FALSE;

		/* add an extra newline, so partial lines at the end of
		 * the file don't trip us up
		 */
		add(tomark, "\n");

		/* for each chunk of text... */
		while ((rc = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
		{
			/* count newlines, convert NULs, etc. ... */
			for (lines = 0, scan = tmpblk.c; rc > 0; rc--, scan++)
			{
				/* break up long lines */
				if (*scan != '\n' && len + 2 > BLKSIZE)
				{
					*scan = '\n';
					addnl = TRUE;
				}

				/* protect against NUL chars in file */
				if (!*scan)
				{
					*scan = 0x80;
					hadnul = TRUE;
				}

				/* starting a new line? */
				if (*scan == '\n')
				{
					/* reset length at newline */
					len = 0;
					lines++;
				}
				else
				{
					len++;
				}
			}

			/* add the text */
			*scan = '\0';
			add(tomark, tmpblk.c);
			tomark += MARK_AT_LINE(lines) + len - markidx(tomark);
		}

		/* if partial last line, then retain that first newline */
		if (len > 0)
		{
			msg("Last line had no newline");
			tomark += BLKSIZE; /* <- for the rptlines calc */
		}
		else /* delete that first newline */
		{
			delete(tomark, (tomark | (BLKSIZE - 1L)) + 1L);
		}
	}

	/* close the file */
	close(fd);

	/* Reporting... */
	rptlines = markline(tomark) - markline(frommark);
	rptlabel = "read";

	if (addnl)
		msg("Newlines were added to break up long lines");
	if (hadnul)
		msg("NULs were converted to 0x80");
}



/*ARGSUSED*/
void cmd_undo(frommark, tomark, cmd, bang, extra)
	MARK	frommark;
	MARK	tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	undo();
}


/* print the selected lines */
/*ARGSUSED*/
void cmd_print(frommark, tomark, cmd, bang, extra)
	MARK	frommark;
	MARK	tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	REG char	*scan;
	REG long	l;
	REG int		col;

	for (l = markline(frommark); l <= markline(tomark); l++)
	{
		/* display a line number, if CMD_NUMBER */
		if (cmd == CMD_NUMBER)
		{
			sprintf(tmpblk.c, "%6ld  ", l);
			qaddstr(tmpblk.c);
			col = 8;
		}
		else
		{
			col = 0;
		}

		/* get the next line & display it */
		for (scan = fetchline(l); *scan; scan++)
		{
			/* expand tabs to the proper width */
			if (*scan == '\t' && cmd != CMD_LIST)
			{
				do
				{
					qaddch(' ');
					col++;
				} while (col % *o_tabstop != 0);
			}
			else if (*scan >= 0 && *scan < ' ' || *scan == '\177')
			{
				qaddch('^');
				qaddch(*scan ^ 0x40);
				col += 2;
			}
			else if ((*scan & 0x80) && cmd == CMD_LIST)
			{
				sprintf(tmpblk.c, "\\%03o", *scan);
				qaddstr(tmpblk.c);
				col += 4;
			}
			else
			{
				qaddch(*scan);
				col++;
			}

			/* wrap at the edge of the screen */
			if (!has_AM && col >= COLS)
			{
				addch('\n');
				col -= COLS;
			}
		}
		if (cmd == CMD_LIST)
		{
			qaddch('$');
		}
		addch('\n');
		exrefresh();
	}
}


/* move or copy selected lines */
/*ARGSUSED*/
void cmd_move(frommark, tomark, cmd, bang, extra)
	MARK	frommark;
	MARK	tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	MARK	destmark;

	/* parse the destination linespec.  No defaults.  Line 0 is okay */
	destmark = cursor;
	if (!strcmp(extra, "0"))
	{
		destmark = 0L;
	}
	else if (linespec(extra, &destmark) == extra || !destmark)
	{
		msg("invalid destination address");
		return;
	}

	/* flesh the marks out to encompass whole lines */
	frommark &= ~(BLKSIZE - 1);
	tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
	destmark = (destmark & ~(BLKSIZE - 1)) + BLKSIZE;

	/* make sure the destination is valid */
	if (cmd == CMD_MOVE && destmark >= frommark && destmark < tomark)
	{
		msg("invalid destination address");
	}

	/* Do it */
	ChangeText
	{
		/* save the text to a cut buffer */
		cutname('\0');
		cut(frommark, tomark);

		/* if we're not copying, delete the old text & adjust destmark */
		if (cmd != CMD_COPY)
		{
			delete(frommark, tomark);
			if (destmark >= frommark)
			{
				destmark -= (tomark - frommark);
			}
		}

		/* add the new text */
		paste(destmark, FALSE, FALSE);
	}

	/* move the cursor to the last line of the moved text */
	cursor = destmark + (tomark - frommark) - BLKSIZE;
	if (cursor < MARK_FIRST || cursor >= MARK_LAST + BLKSIZE)
	{
		cursor = MARK_LAST;
	}

	/* Reporting... */
	rptlabel = ( (cmd == CMD_COPY) ? "copied" : "moved" );
}



/* execute EX commands from a file */
/*ARGSUSED*/
void cmd_source(frommark, tomark, cmd, bang, extra)
	MARK	frommark;
	MARK	tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	/* must have a filename */
	if (!*extra)
	{
		msg("\"source\" requires a filename");
		return;
	}

	doexrc(extra);
}


#ifndef NO_AT
/*ARGSUSED*/
void cmd_at(frommark, tomark, cmd, bang, extra)
	MARK	frommark;
	MARK	tomark;
	CMD	cmd;
	int	bang;
	char	*extra;
{
	static	nest = FALSE;
	int	result;
	char	buf[MAXRCLEN];

	/* don't allow nested macros */
	if (nest)
	{
		msg("@ macros can't be nested");
		return;
	}
	nest = TRUE;

	/* require a buffer name */
	if (*extra == '"')
		extra++;
	if (!*extra || !isascii(*extra) ||!islower(*extra))
	{
		msg("@ requires a cut buffer name (a-z)");
	}

	/* get the contents of the buffer */
	result = cb2str(*extra, buf, (unsigned)(sizeof buf));
	if (result <= 0)
	{
		msg("buffer \"%c is empty", *extra);
	}
	else if (result >= sizeof buf)
	{
		msg("buffer \"%c is too large to execute", *extra);
	}
	else
	{
		/* execute the contents of the buffer as ex commands */
		exstring(buf, result);
	}

	nest = FALSE;
}
#endif
SHAR_EOF
fi # end of overwriting check
if test -f 'ctags.c'
then
	echo shar: will not over-write existing file "'ctags.c'"
else
cat << \SHAR_EOF > 'ctags.c'
/* ctags.c */

/* Author:
 *	Steve Kirkendall
 *	14407 SW Teal Blvd. #C
 *	Beaverton, OR 97005
 *	kirkenda@cs.pdx.edu
 */


/* This file contains the complete source to the ctags program. */

/* Special abilities:
 * Can also make a "refs" file for use by the "ref" program.
 */

/* Limitations:
 * This version of ctags always writes its output to the file "tags".
 * It assumes that every command-line argument (but "-r") is a C source file.
 * It does not sort the list of tags, unless CFLAGS=-DSORT.
 * It does not recognize duplicate definitions.
 * It does not try to handle "static" functions in a clever way.
 * It probably won't scan ANSI-C source code very well.
 */

/* Implementation:
 * Lines are scanned one-at-a-time.
 * The context of lines is tracked via a finite state machine.
 * Contexts are:
 *	EXPECTFN - we're looking for a function name.
 *	ARGS	 - between function name and its opening {
 *	BODY	 - we found a function name, skip to end of body.
 *
 * Function tags are referenced by a search string, so that lines may be
 * inserted or deleted without mucking up the tag search.
 *
 * Macro tags are referenced by their line number, because 1) they usually
 * occur near the top of a file, so their line# won't change much; 2)  They
 * often contain characters that are hard to search for; and 3)  Their #define
 * line is likely to be altered.
 *
 * Each line of the resulting "tags" file describes one tag.  Lines begin with
 * the tag name, then a tab, then the file name, then a tab, and then either
 * a line number or a slash-delimited search string.
 */

#include <ctype.h>
#include <stdio.h>
#include "config.h"

#define REFS	"refs"

#if OSK
#define NUMFMT	"%%.%ds\t%%s\t%%ld\n"
#define SRCHFMT	"%%.%ds\t%%s\t/^%%s$/\n"
#define MAINFMT	"M%%.%ds\t%%s\t/^%%s$/\n"
static char fmt[256];
#else
#define NUMFMT	"%.*s\t%s\t%ld\n"
#define SRCHFMT	"%.*s\t%s\t/^%s$/\n"
#define MAINFMT	"M%.*s\t%s\t/^%s$/\n"
#endif

#ifdef VERBOSE
# define SAY(x)	fprintf(stderr, "%s\n", x);
#else
# define SAY(x)
#endif

#define EXPECTFN 1
#define	ARGS	 2
#define BODY	 3

extern char	*fgets();

char		*progname;	/* argv[0], used for diagnostic output */

main(argc, argv)
	int	argc;
	char	**argv;
{
	FILE	*fp;
	int	i;
	FILE	*refs;	/* used to write to the refs file */

#if MSDOS || TOS
	char **wildexpand();
	argv=wildexpand(&argc, argv);
#endif
	/* notice the program name */
	progname = argv[0];

	/* create the "refs" file if first arg is "-r" */
	if (argc > 1 && !strcmp(argv[1], "-r"))
	{
		/* delete the "-r" flag from the args list */
		argc--;
		argv++;

		/* open the "refs" file for writing */
		refs = fopen(REFS, "w");
		if (!refs)
		{
			fprintf(stderr, "%s: could not create \"%s\"\n", progname, REFS);
			exit(2);
		}
	}
	else
	{
		refs = (FILE *)0;
	}

	/* process each file named on the command line, or complain if none */
	if (argc > 1)
	{
		/* redirect stdout to go to the "tags" file */
		if (!freopen("tags", "w", stdout))
		{
			fprintf(stderr, "%s: could not create \"%s\"\n", progname, TAGS);
			exit(2);
		}

		for (i = 1; i < argc; i++)
		{
			/* process this named file */
			fp = fopen(argv[i], "r");
			if (!fp)
			{
				fprintf(stderr, "%s: could not read \"%s\"\n", progname, argv[i]);
				continue;
			}
			ctags(fp, argv[i], refs);
			fclose(fp);
		}
#ifdef SORT
		/* This is a hack which will sort the tags list.   It should
		 * on UNIX and Minix.  You may have trouble with csh.   Note
		 * that the tags list only has to be sorted if you intend to
		 * use it with the real vi;  elvis permits unsorted tags.
		 */
		fflush(stdout);
#if OSK
		fclose(stdout);
		system("qsort tags >-_tags; -nx; del tags; rename _tags tags");
#else	
		system("sort tags >_tags$$; mv _tags$$ tags");
#endif
#endif
		exit(0);
	}
	else
	{
		fprintf(stderr, "usage: %s *.[ch]\n", progname);
		exit(2);
	}
}


/* this function finds all tags in a given file */
ctags(fp, name, refs)
	FILE	*fp;		/* stream of the file to scan */
	char	*name;		/* name of the file being scanned */
	FILE	*refs;		/* NULL, or where to write refs lines */
{
	int	context;	/* context - either EXPECTFN, ARGS, or BODY */
	long	lnum;		/* line number */
	char	text[1000];	/* a line of text from the file */
	char	*scan;		/* used for searching through text */
	int	len;		/* length of the line */

	/* for each line of the file... */
	for (context = EXPECTFN, lnum = 1; fgets(text, sizeof text, fp); lnum++)
	{
#ifdef VERBOSE
		switch(context)
		{
		  case EXPECTFN:	scan = "EXPECTFN";	break;
		  case ARGS:		scan = "ARGS    ";	break;
		  case BODY:		scan = "BODY    ";	break;
		  default:		scan = "context?";
		}
		fprintf(stderr, "%s:%s", scan, text);
#endif

		/* start of body? */
		if (text[0] == '{')
		{
			context = BODY;
			SAY("Start of BODY");
			continue;
		}

		/* argument line, to be written to "refs" ? */
		if (refs && context == ARGS)
		{
			if (text[0] != '\t')
			{
				putc('\t', refs);
			}
			fputs(text, refs);
			SAY("Argument line");
			continue;
		}

		/* ignore empty or indented lines */
		if (text[0] <= ' ')
		{
			SAY("Empty or indented");
			continue;
		}

		/* end of body? */
		if (text[0] == '}')
		{
			context = EXPECTFN;
			SAY("End of BODY");
			continue;
		}

		/* ignore lines in the body of a function */
		if (context != EXPECTFN)
		{
			SAY("BODY or ARGS");
			continue;
		}

		/* strip the newline */
		len = strlen(text);
		text[--len] = '\0';

		/* a preprocessor line? */
		if (text[0] == '#')
		{
			/* find the preprocessor directive */
			for (scan = &text[1]; isspace(*scan); scan++)
			{
			}

			/* if it's a #define, make a tag out of it */
			if (!strncmp(scan, "define", 6))
			{
				/* find the start of the symbol name */
				for (scan += 6; isspace(*scan); scan++)
				{
				}

				/* find the length of the symbol name */
				for (len = 1;
				     isalnum(scan[len]) || scan[len] == '_';
				     len++)
				{
				}
#if OSK
				sprintf(fmt, NUMFMT, len);
				printf(fmt, scan, name, lnum);
#else			
				printf(NUMFMT, len, scan, name, lnum);
#endif
			}
			SAY("Preprocessor line");
			continue;
		}

		/* an extern or static declaration? */
		if (text[len - 1] == ';'
		 || !strncmp(text, "extern", 6)
		 || !strncmp(text, "EXTERN", 6)
		 || !strncmp(text, "static", 6)
		 || !strncmp(text, "PRIVATE", 7))
		{
			SAY("Extern or static");
			continue;
		}

		/* if we get here & the first punctuation other than "*" is
		 * a "(" which is immediately preceded by a name, then
		 * assume the name is that of a function.
		 */
		for (scan = text; *scan; scan++)
		{
			if (ispunct(*scan)
			 && !isspace(*scan) /* in BSD, spaces are punctuation?*/
			 && *scan != '*' && *scan != '_' && *scan != '(')
			{
				SAY("Funny punctuation");
				goto ContinueContinue;
			}

			if (*scan == '(')
			{
				/* permit 0 or 1 spaces between name & '(' */
				if (scan > text && scan[-1] == ' ')
				{
					scan--;
				}

				/* find the start & length of the name */
				for (len = 0, scan--;
				     scan >= text && (isalnum(*scan) || *scan == '_');
				     scan--, len++)
				{
				}
				scan++;

				/* did we find a function? */
				if (len > 0)
				{
					/* found a function! */
					if (len == 4 && !strncmp(scan, "main", 4))
					{
#if OSK
						sprintf(fmt, MAINFMT, strlen(name) - 2);
						printf(fmt, name, name, text);
#else			
						printf(MAINFMT, strlen(name) - 2, name, name, text);
#endif
					}
#if OSK
					sprintf(fmt, SRCHFMT, len);
					printf(fmt, scan, name, text);
#else				
					printf(SRCHFMT, len, scan, name, text);
#endif
					context = ARGS;

					/* add a line to refs, if needed */
					if (refs)
					{
						fputs(text, refs);
						putc('\n', refs);
					}

					goto ContinueContinue;
				}
			}
			else
			{
				SAY("No parenthesis");
			}
		}
		SAY("No punctuation");

ContinueContinue:;
	}
}

#if MSDOS || TOS
#define		WILDCARD_NO_MAIN
#include	"wildcard.c"
#endif
SHAR_EOF
fi # end of overwriting check
if test -f 'curses.c'
then
	echo shar: will not over-write existing file "'curses.c'"
else
cat << \SHAR_EOF > 'curses.c'
/* curses.c */

/* Author:
 *	Steve Kirkendall
 *	14407 SW Teal Blvd. #C
 *	Beaverton, OR 97005
 *	kirkenda@cs.pdx.edu
 */


/* This file contains the functions & variables needed for a tiny subset of
 * curses.  The principle advantage of this version of curses is its
 * extreme speed.  Disadvantages are potentially larger code, few supported
 * functions, limited compatibility with full curses, and only stdscr.
 */

#include "config.h"
#include "vi.h"

#if ANY_UNIX
# if UNIXV
#  include	<termio.h>
#  undef	TIOCWINSZ	/* we can't handle it correctly yet */
# else
#  include	<sgtty.h>
# endif
#endif

#if TOS
# include	<osbind.h>
#endif

#if OSK
# include	<sgstat.h>
#endif

#include <signal.h>

extern char	*getenv();
void	 starttcap();

/* variables, publicly available & used in the macros */
short	ospeed;		/* speed of the tty, eg B2400 */
#if OSK
char	PC_;	/* Pad char */
#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	*BC = "\b";	/* :bc   backspace character string */
char	*VB;		/* :vb=: visible bell */
char	*UP;		/* :up=: move cursor up */
char	*SO;		/* :so=: standout start */
char	*SE;		/* :se=: standout end */
char	*US = "";	/* :us=: underline start */
char	*UE = "";	/* :ue=: underline end */
char	*MD = "";	/* :md=: bold start */
char	*ME = "";	/* :me=: bold end */
char	*AS;		/* :as=: alternate (italic) start */
char	*AE;		/* :ae=: alternate (italic) end */
char	*CM;		/* :cm=: cursor movement */
char	*CE;		/* :ce=: clear to end of line */
char	*CD;		/* :cd=: clear to end of screen */
char	*AL;		/* :al=: add a line */
char	*DL;		/* :dl=: delete a line */
#if OSK
char	*SR_;		/* :sr=: scroll reverse */
#else
char	*SR;		/* :sr=: scroll reverse */
#endif
char	*KS;		/* :ks=: init string for cursor */
char	*KE;		/* :ke=: restore string for cursor */
char	*KU;		/* :ku=: key sequence sent by up arrow */
char	*KD;		/* :kd=: key sequence sent by down arrow */
char	*KL;		/* :kl=: key sequence sent by left arrow */
char	*KR;		/* :kr=: key sequence sent by right arrow */
char	*HM;		/* :HM=: key sequence sent by the <Home> key */
char	*EN;		/* :EN=: key sequence sent by the <End> key */
char	*PU;		/* :PU=: key sequence sent by the <PgUp> key */
char	*PD;		/* :PD=: key sequence sent by the <PgDn> key */
char	*IM;		/* :im=: insert mode start */
char	*IC = "";	/* :ic=: insert the following character */
char	*EI;		/* :ei=: insert mode end */
char	*DC;		/* :dc=: delete a character */
char	*TI;		/* :ti=: terminal init */	/* GB */
char	*TE;		/* :te=: terminal exit */	/* GB */
#ifndef NO_CURSORSHAPE
char	*CQ = (char *)0;/* :cQ=: normal cursor */
char	*CX = (char *)1;/* :cX=: cursor used for EX command/entry */
char	*CV = (char *)2;/* :cV=: cursor used for VI command mode */
char	*CI = (char *)3;/* :cI=: cursor used for VI input mode */
char	*CR = (char *)4;/* :cR=: cursor used for VI replace mode */
#endif
char	*aend = "";	/* end an attribute -- either UE or ME */
char	ERASEKEY;	/* backspace key taken from ioctl structure */

#if ANY_UNIX
# if UNIXV
static struct termio	oldtermio;	/* original tty mode */
static struct termio	newtermio;	/* cbreak/noecho tty mode */
# else
static struct sgttyb	oldsgttyb;	/* original tty mode */
static struct sgttyb	newsgttyb;	/* cbreak/nl/noecho tty mode */
static int		oldint;		/* ^C or DEL, the "intr" character */
#  ifdef TIOCSLTC
static int		oldswitch;	/* ^Z, the "suspend" character */
static int		oldquote;	/* ^V, the "quote next char" char */
#  endif
# endif
#endif

#if OSK
static struct sgbuf	oldsgttyb;	/* orginal tty mode */
static struct sgbuf	newsgttyb;	/* noecho tty mode */
#endif

static char	*capbuf;	/* capability string buffer */


void initscr()
{
	/* make sure TERM variable is set */
#if MSDOS
	char *val;
	if (! (val = getenv("TERM"))
	|| !strcmp(val, "pcbios"))
#else
	if (!getenv("TERM"))
#endif
	{
#if ANY_UNIX
		write(2, "Environment variable TERM must be set\n", (unsigned)38);
		exit(1);
#endif
#if OSK
		writeln(2, "Environment variable TERM must be set\n", (unsigned)38);
		exit(1);
#endif
#if MSDOS || TOS
		Getsize(0);
#endif
	}
	else
	{
#if MSDOS
		*o_pcbios=0;
#endif
		/* start termcap stuff */
		starttcap();
	}

	/* create stdscr and curscr */
	stdscr = kbuf;

	/* change the terminal mode to cbreak/noecho */
#if ANY_UNIX
# if UNIXV
	ioctl(2, TCGETA, &oldtermio);
# else
	ioctl(2, TIOCGETP, &oldsgttyb);
# endif
#endif

#if OSK
	_gs_opt(0, &oldsgttyb);
#endif
	Resume_curses(TRUE);
}


void endwin()
{
	/* change the terminal mode back the way it was */
	Suspend_curses();
}


static int curses_active = FALSE;

void suspend_curses()
{
#if ANY_UNIX && !UNIXV
	struct tchars	tbuf;
# ifdef TIOCSLTC
	struct ltchars	ltbuf;
# endif
#endif
#ifndef NO_CURSORSHAPE
	if (has_CQ)
	{
		do_CQ();
	}
#endif
	if (has_TE)					/* GB */
	{
		do_TE();
	}
	if (has_KE)
	{
		do_KE();
	}
	refresh();

	/* change the terminal mode back the way it was */
#if ANY_UNIX
# if UNIXV
	ioctl(2, TCSETAW, &oldtermio);
# else
	ioctl(2, TIOCSETP, &oldsgttyb);

	ioctl(2, TIOCGETC, &tbuf);
	tbuf.t_intrc = oldint;
	ioctl(2, TIOCSETC, &tbuf);

#  ifdef TIOCSLTC
	ioctl(2, TIOCGLTC, &ltbuf);
	ltbuf.t_suspc = oldswitch;
	ltbuf.t_lnextc = oldquote;
	ioctl(2, TIOCSLTC, &ltbuf);
#  endif
# endif
#endif
#if OSK
	_ss_opt(0, &oldsgttyb);
#endif
	curses_active = FALSE;
}

void resume_curses(quietly)
	int	quietly;
{
	if (!curses_active)
	{
		/* change the terminal mode to cbreak/noecho */
#if ANY_UNIX
# if UNIXV
		ospeed = (oldtermio.c_cflag & CBAUD);
		ERASEKEY = oldtermio.c_cc[VERASE];
		newtermio = oldtermio;
		newtermio.c_iflag &= (IXON|IXOFF|IXANY|ISTRIP|IGNBRK);
		newtermio.c_oflag &= ~OPOST;
		newtermio.c_lflag &= ISIG;
		newtermio.c_cc[VINTR] = ctrl('C'); /* always use ^C for interrupts */
		newtermio.c_cc[VMIN] = 1;
		newtermio.c_cc[VTIME] = 0;
#  ifdef VSWTCH
		newtermio.c_cc[VSWTCH] = 0;
#  endif
		ioctl(2, TCSETAW, &newtermio);
# else /* BSD or V7 or Coherent or Minix */
		struct tchars	tbuf;
#  ifdef TIOCSLTC
		struct ltchars	ltbuf;
#  endif

		ospeed = oldsgttyb.sg_ospeed;
		ERASEKEY = oldsgttyb.sg_erase;
		newsgttyb = oldsgttyb;
		newsgttyb.sg_flags |= CBREAK;
		newsgttyb.sg_flags &= ~(CRMOD|ECHO|XTABS);
		ioctl(2, TIOCSETP, &newsgttyb);

		ioctl(2, TIOCGETC, &tbuf);
		oldint = tbuf.t_intrc;
		tbuf.t_intrc = ctrl('C');	/* always use ^C for interrupts */
		ioctl(2, TIOCSETC, &tbuf);

#  ifdef TIOCSLTC
		ioctl(2, TIOCGLTC, &ltbuf);
		oldswitch = ltbuf.t_suspc;
		ltbuf.t_suspc = 0;		/* disable ^Z for elvis */
		oldquote = ltbuf.t_lnextc;
		ltbuf.t_lnextc = 0;		/* disable ^V for elvis */
		ioctl(2, TIOCSLTC, &ltbuf);
#  endif

# endif
#endif
#if OSK
		newsgttyb = oldsgttyb;
		newsgttyb.sg_echo = 0;
		newsgttyb.sg_eofch = 0;
		newsgttyb.sg_kbach = 0;
		newsgttyb.sg_kbich = ctrl('C');
		_ss_opt(0, &newsgttyb);
		ospeed = oldsgttyb.sg_baud;
		ERASEKEY = oldsgttyb.sg_bspch;
#endif

		if (has_TI)					/* GB */
		{
			do_TI();
		}
		if (has_KS)
		{
			do_KS();
		}

		curses_active = TRUE;
	}

	/* If we're supposed to quit quietly, then we're done */
	if (quietly)
	{
		return;
	}

	signal(SIGINT, SIG_IGN);

	move(LINES - 1, 0);
	do_SO();
	qaddstr("[Press <RETURN> to continue]");
	do_SE();
	refresh();
	ttyread(kbuf, 20); /* in RAW mode, so <20 is very likely */
	if (kbuf[0] == ':')
	{
		mode = MODE_COLON;
		addch('\n');
		refresh();
	}
	else
	{
		mode = MODE_VI;
		redraw(MARK_UNSET, FALSE);
	}	
	exwrote = FALSE;

#if TURBOC
	signal(SIGINT, (void(*)()) trapint);
#else
	signal(SIGINT, trapint);
#endif
}

static void lacking(s)
	char	*s;
{
	write(2, "This termcap entry lacks the :", (unsigned)30);
	write(2, s, (unsigned)2);
	write(2, "=: capability\n", (unsigned)14);
#if OSK
	write(2, "\l", 1);
#endif
	exit(1);
}

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(BC, "bc");
	MAYHAVE(VB, "vb");
	MUSTHAVE(CM, "cm");
	PAIR(SO, SE, "so", "se");
	PAIR(TI, TE, "ti", "te");
	if (Tgetnum("ug") <= 0)
	{
		PAIR(US, UE, "us", "ue");
		PAIR(MD, ME, "md", "me");

		/* get italics, or have it default to underline */
		PAIR(AS, AE, "as", "ae");
		if (!*AS)
		{
			AS = US;
			AE = UE;
		}
	}
	MAYHAVE(AL, "al");
	MAYHAVE(DL, "dl");
	MUSTHAVE(CE, "ce");
	MAYHAVE(CD, "cd");
#if OSK
	MAYHAVE(SR_, "sr");
#else	
	MAYHAVE(SR, "sr");
#endif
	PAIR(IM, EI, "im", "ei");
	MAYHAVE(IC, "ic");
	MAYHAVE(DC, "dc");

	/* other termcap stuff */
	AM = Tgetflag("am");
	PT = Tgetflag("pt");
	Getsize(0);

	/* Key sequences */
	PAIR(KS, KE, "ks", "ke");
	MAYHAVE(KU, "ku");		/* up */
	MAYHAVE(KD, "kd");		/* down */
	MAYHAVE(KL, "kl");		/* left */
	MAYHAVE(KR, "kr");		/* right */
	MAYHAVE(PU, "kP");		/* PgUp */
	MAYHAVE(PD, "kN");		/* PgDn */
	MAYHAVE(HM, "kh");		/* Home */
	MAYHAVE(EN, "kH");		/* End */
#ifndef CRUNCH
	if (!PU) MAYHAVE(PU, "K2");	/* "3x3 pad" names for PgUp, etc. */
	if (!PD) MAYHAVE(PD, "K5");
	if (!HM) MAYHAVE(HM, "K1");
	if (!EN) MAYHAVE(EN, "K4");

	MAYHAVE(PU, "PU");		/* old XENIX names for PgUp, etc. */
	MAYHAVE(PD, "PD");		/* (overrides others, if used.) */
	MAYHAVE(HM, "HM");
	MAYHAVE(EN, "EN");
#endif

#ifndef NO_CURSORSHAPE
	/* cursor shapes */
	CQ = Tgetstr("cQ", &capbuf);
	if (has_CQ)
	{
		CX = Tgetstr("cX", &capbuf);
		if (!CX) CX = CQ;
		CV = Tgetstr("cV", &capbuf);
		if (!CV) CV = CQ;
		CI = Tgetstr("cI", &capbuf);
		if (!CI) CI = CQ;
		CR = Tgetstr("cR", &capbuf);
		if (!CR) CR = CQ;
	}
# ifndef CRUNCH
	else
	{
		PAIR(CQ, CV, "ve", "vs");
		CX = CI = CR = CQ;
	}
# endif /* !CRUNCH */
#endif /* !NO_CURSORSHAPE */

#undef MUSTHAVE
#undef MAYHAVE
#undef PAIR
}


/* This function gets the window size.  It uses the TIOCGWINSZ ioctl call if
 * your system has it, or Tgetnum("li") and Tgetnum("co") if it doesn't.
 * This function is called once during initialization, and thereafter it is
 * called whenever the SIGWINCH signal is sent to this process.
 */
int getsize(signo)
	int	signo;
{
	int	lines;
	int	cols;
#ifdef TIOCGWINSZ
	struct winsize size;
#endif

#ifdef SIGWINCH
	/* reset the signal vector */
	signal(SIGWINCH, getsize);
#endif

	/* get the window size, one way or another. */
	lines = cols = 0;
#ifdef TIOCGWINSZ
	if (ioctl(2, TIOCGWINSZ, &size) >= 0)
	{
		lines = size.ws_row;
		cols = size.ws_col;
	}
#endif
	if ((lines == 0 || cols == 0) && signo == 0)
	{
		LINES = CHECKBIOS(v_rows(), Tgetnum("li"));
		COLS = CHECKBIOS(v_cols(), Tgetnum("co"));
	}
	if (lines >= 2 && cols >= 30)
	{
		LINES = lines;
		COLS = cols;
	}

	/* Make sure we got values that we can live with */
	if (LINES < 2 || COLS < 30)
	{
		write(2, "Screen too small\n", (unsigned)17);
#if OSK
		write(2, "\l", 1);
#endif
		endwin();
		exit(2);
	}

	/* !!! copy the new values into Elvis' options */
	{
		extern char	o_columns[], o_lines[];

		*o_columns = COLS;
		*o_lines = LINES;
	}

	return 0;
}


/* This is a function version of addch() -- it is used by tputs() */
int faddch(ch)
	int	ch;
{
	addch(ch);

	return 0;
}

/* These functions are equivelent to the macros of the same names... */

void qaddstr(str)
	char	*str;
{
	REG char *s_, *d_;

#if MSDOS
	if (o_pcbios[0])
	{
		while (*str)
			qaddch(*str++);
		return;
	}
#endif
	for (s_=(str), d_=stdscr; *d_++ = *s_++; )
	{
	}
	stdscr = d_ - 1;
}

void attrset(a)
	int	a;
{
	do_aend();
	if (a == A_BOLD)
	{
		do_MD();
		aend = ME;
	}
	else if (a == A_UNDERLINE)
	{
		do_US();
		aend = UE;
	}
	else if (a == A_ALTCHARSET)
	{
		do_AS();
		aend = AE;
	}
	else
	{
		aend = "";
	}
}


void insch(ch)
	int	ch;
{
	if (has_IM)
		do_IM();
	do_IC();
	qaddch(ch);
	if (has_EI)
		do_EI();
}

#if MSDOS

static int alarmtime;

/* raw read - #defined to read (0, ...) on non-MSDOS.
 * With MSDOS, am maximum of 1 byte is read.
 * If more bytes should be read, just change the loop.
 * The following code uses the IBM-PC-System-Timer, so probably wont't work
 * on non-compatibles.
 */
/*ARGSUSED*/
ttyread(buf, len)
	char *buf;
	int len;
{
	volatile char far *biostimer;
	char oldtime;
	int nticks = 0;
	int pos = 0;

	biostimer = (char far *)0x0040006cl;
	oldtime = *biostimer;

	while (!pos && (!alarmtime || nticks<alarmtime))
	{	if (kbhit())
			if ((buf[pos++] = getch()) == 0) /* function key */
				buf[pos-1] = '#';
		if (oldtime != *biostimer)
		{	nticks++;
			oldtime = *biostimer;
		}
	}
	return pos;
}

alarm(time)
	int time;
{
	alarmtime = 2 * time;		/* ticks are 1/18 sec. */
}

sleep(seconds)
	unsigned seconds;
{
	volatile char	far *biostimer = (char far *)0x0040006cl;
	char		stop;

	stop = *biostimer + 18 * seconds;
	while (*biostimer != stop)
	{
	}
}
#endif

#if TOS

static int alarmtime;
static long timer;

static gettime()
{
	timer = *(long *)(0x4ba);
}

/*ARGSUSED*/
ttyread(buf, len)
	char *buf;
	int len;
{
	int	pos=0;
	long	l;
	long	endtime;

	Supexec(gettime);
	endtime = timer+alarmtime;

	while (!pos && (!alarmtime || timer<endtime))
	{
		if (Bconstat(2))
		{
			l = Bconin(2);
			if ((buf[pos++]=l) == '\0')
			{
				buf[pos-1]='#';
				buf[pos++]=l>>16;
			}
		}
		Supexec(gettime);
	}
	return pos;
}

alarm(time)
	int time;
{
	alarmtime = 50 * time;		/* ticks are 1/200 sec. */
}

ttywrite(buf, len)
	char *buf;
	int len;
{
	while (len--)
		Bconout(2, *buf++);
}
#endif

#if OSK
ttyread(buf, len)
	char *buf;
	int len;
{
	REG int i;
	if ((i = _gs_rdy(0)) > 0)
		return read(0, buf, i < len ? i : len);
	else
		return read(0, buf, 1);
}

alarm(time)
	int time;
{
#define TIME(secs) ((secs << 8) | 0x80000000)
	static int alrmid;

	if (time)	
		alrmid = alm_set(SIGQUIT, TIME(time));
	else	
		alm_delete(alrmid);
}
#endif /* OSK */
SHAR_EOF
fi # end of overwriting check
if test -f 'cut.c'
then
	echo shar: will not over-write existing file "'cut.c'"
else
cat << \SHAR_EOF > 'cut.c'
/* cut.c */

/* Author:
 *	Steve Kirkendall
 *	14407 SW Teal Blvd. #C
 *	Beaverton, OR 97005
 *	kirkenda@cs.pdx.edu
 */


/* This file contains function which manipulate the cut buffers. */

#include "config.h"
#include "vi.h"
#if TURBOC
#include <process.h>		/* needed for getpid */
#endif
#if TOS
#include <osbind.h>
#define	rename(a,b)	Frename(0,a,b)
#endif

# define NANNONS	9	/* number of annonymous buffers */

static struct cutbuf
{
	short	*phys;	/* pointer to an array of #s of BLKs containing text */
	int	nblks;	/* number of blocks in phys[] array */
	int	start;	/* offset into first block of start of cut */
	int	end;	/* offset into last block of end of cut */
	int	fd;	/* fd of tmp file, or -1 to use tmpfd */
	char	lnmode;	/* boolean: line-mode cut? (as opposed to char-mode) */
}
	named[27],	/* cut buffers "a through "z and ". */
	annon[NANNONS];	/* annonymous cut buffers */

static char	cbname;	/* name chosen for next cut/paste operation */


#ifndef NO_RECYCLE
/* This function builds a list of all blocks needed in the current tmp file
 * for the contents of cut buffers.
 * !!! WARNING: if you have more than ~450000 bytes of text in all of the
 * cut buffers, then this will fail disastrously, because buffer overflow
 * is *not* allowed for.
 */
int cutneeds(need)
	BLK		*need;	/* this is where we deposit the list */
{
	struct cutbuf	*cb;	/* used to count through cut buffers */
	int		i;	/* used to count through blocks of a cut buffer */
	int		n;	/* total number of blocks in list */

	n = 0;

	/* first the named buffers... */
	for (cb = named; cb < &named[27]; cb++)
	{
		if (cb->fd > 0)
			continue;

		for (i = cb->nblks; i-- > 0; )
		{
			need->n[n++] = cb->phys[i];
		}
	}

	/* then the anonymous buffers */
	for (cb = annon; cb < &annon[NANNONS]; cb++)
	{
		if (cb->fd > 0)
			continue;

		for (i = cb->nblks; i-- > 0; )
		{
			need->n[n++] = cb->phys[i];
		}
	}

	return n;
}
#endif

/* This function frees a cut buffer */
static void cutfree(buf)
	struct cutbuf	*buf;
{
	char	cutfname[50];
	int	i;

	/* return immediately if the buffer is already empty */
	if (buf->nblks <= 0)
	{
		return;
	}

	/* else free up stuff */
	buf->nblks = 0;
#ifdef DEBUG
	if (!buf->phys)
		msg("cutfree() tried to free an NULL buf->phys pointer.");
#endif
	free((char *)buf->phys);

	/* see if anybody else needs this tmp file */
	if (buf->fd >= 0)
	{
		for (i = 0; i < 27; i++)
		{
			if (named[i].nblks > 0 && named[i].fd == buf->fd)
			{
				break;
			}
		}
	}

	/* if nobody else needs it, then discard the tmp file */
	if (buf->fd >= 0 && i == 27)
	{
		close(buf->fd);
#if MSDOS || TOS
		strcpy(cutfname, o_directory);
		if ((i = strlen(cutfname)) && !strchr(":/\\", cutfname[i-1]))
			cutfname[i++]=SLASH;
		sprintf(cutfname+i, CUTNAME+3, getpid(), buf->fd);
#else
		sprintf(cutfname, CUTNAME, o_directory, getpid(), buf->fd);
#endif
		unlink(cutfname);
	}
}

/* This function is called when we are about to abort a tmp file.  If any
 * cut buffers still need the file, then a copy of the file should be
 * created for use by the cut buffers.
 *
 * To minimize the number of extra files lying around, only named cut buffers
 * are preserved in a file switch; the annonymous buffers just go away.
 */
void cutswitch(tmpname)
	char	*tmpname; /* name of the tmp file */
{
	char	cutfname[50];	/* used to build a new name for the tmp file */
	int	fd;		/* a new fd for the current tmp file */
	int	i;
#if MSDOS || TOS
	int	j;
#endif

	/* discard all annonymous cut buffers */
	for (i = 0; i < NANNONS; i++)
	{
		cutfree(&annon[i]);
	}

	/* find the first named buffer that uses this tmp file */
	for (i = 0; i < 27; i++)
	{
		if (named[i].nblks > 0 && named[i].fd < 0)
		{
			break;
		}
	}

	/* if none of them use this tmp file, then we're done */
	if (i == 27)
	{
		return;
	}

	/* else we'll need this file and an fd a little longer */
#if MSDOS || TOS
	strcpy(cutfname, o_directory);
	if ((j = strlen(cutfname)) && !strchr(":/\\", cutfname[j-1]))
		cutfname[j++]=SLASH;
	close(tmpfd);
	fd = open(tmpname, O_RDONLY|O_BINARY);
	close(fd);
	sprintf(cutfname+j, CUTNAME+3, getpid(), fd);
	rename(tmpname, cutfname);
	fd = open(cutfname, O_RDONLY|O_BINARY);
	tmpfd = -1; /* we'll try to close this in tmp.c, but who cares? */
#else
	fd = dup(tmpfd);
# if OSK
	sprintf(cutfname, CUTNAME, "", getpid(), fd);
	if (!link(tmpname, &cutfname[1])) /* skip slash */
		unlink(tmpname);
# else	
	sprintf(cutfname, CUTNAME, o_directory, getpid(), fd);
	link(tmpname, cutfname) || unlink(tmpname);
# endif
#endif

	/* have all cut buffers use the new fd instead */
	for (; i < 27; i++)
	{
		if (named[i].nblks > 0 && named[i].fd < 0)
		{
			named[i].fd = fd;
		}
	}
}

/* This function should be called just before termination of vi */
void cutend()
{
	int	i;

	/* free all named cut buffers, since they might be forcing an older
	 * tmp file to be retained.
	 */
	for (i = 0; i < 27; i++)
	{
		cutfree(&named[i]);
	}
}


/* This function is used to select the cut buffer to be used next */
void cutname(name)
	int	name;	/* a single character */
{
	cbname = name;
}




/* This function copies a selected segment of text to a cut buffer */
void cut(from, to)
	MARK	from;		/* start of text to cut */
	MARK	to;		/* end of text to cut */
{
	int		first;	/* logical number of first block in cut */
	int		last;	/* logical number of last block used in cut */
	long		line;	/* a line number */
	int		lnmode;	/* boolean: will this be a line-mode cut? */
	MARK		delthru;/* end of text temporarily inserted for apnd */
	REG struct cutbuf *cb;
	REG long	l;
	REG int		i;
	REG char	*scan;
	char		*blkc;

	/* detect whether this must be a line-mode cut or char-mode cut */
	if (markidx(from) == 0 && markidx(to) == 0)
		lnmode = TRUE;
	else
		lnmode = FALSE;

	/* by default, we don't "delthru" anything */
	delthru = MARK_UNSET;

	/* decide which cut buffer to use */
	if (!cbname)
	{
		/* free up the last annonymous cut buffer */
		cutfree(&annon[NANNONS - 1]);

		/* shift the annonymous cut buffers */
		for (i = NANNONS - 1; i > 0; i--)
		{
			annon[i] = annon[i - 1];
		}

		/* use the first annonymous cut buffer */
		cb = annon;
		cb->nblks = 0;
	}
	else if (cbname >= 'a' && cbname <= 'z')
	{
		cb = &named[cbname - 'a'];
		cutfree(cb);
	}
#ifndef CRUNCH
	else if (cbname >= 'A' && cbname <= 'Z')
	{
		cb = &named[cbname - 'A'];
		if (cb->nblks > 0)
		{
			/* resolve linemode/charmode differences */
			if (!lnmode && cb->lnmode)
			{
				from &= ~(BLKSIZE - 1);
				if (markidx(to) != 0 || to == from)
				{
					to = to + BLKSIZE - markidx(to);
				}
				lnmode = TRUE;
			}

			/* insert the old cut-buffer before the new text */
			mark[28] = to;
			delthru = paste(from, FALSE, TRUE);
			if (delthru == MARK_UNSET)
			{
				return;
			}
			delthru++;
			to = mark[28];
		}
		cutfree(cb);
	}
#endif /* not CRUNCH */
	else if (cbname == '.')
	{
		cb = &named[26];
		cutfree(cb);
	}
	else
	{
		msg("Invalid cut buffer name: \"%c", cbname);
		cbname = '\0';
		return;
	}
	cbname = '\0';
	cb->fd = -1;

	/* detect whether we're doing a line mode cut */
	cb->lnmode = lnmode;

	/* ---------- */

	/* Reporting... */	
	if (markidx(from) == 0 && markidx(to) == 0)
	{
		rptlines = markline(to) - markline(from);
		rptlabel = "yanked";
	}

	/* ---------- */

	/* make sure each block has a physical disk address */
	blksync();

	/* find the first block in the cut */
	line = markline(from);
	for (first = 1; line > lnum[first]; first++)
	{
	}

	/* fetch text of the block containing that line */
	blkc = scan = blkget(first)->c;

	/* find the mark in the block */
	for (l = lnum[first - 1]; ++l < line; )
	{
		while (*scan++ != '\n')
		{
		}
	}
	scan += markidx(from);

	/* remember the offset of the start */
	cb->start = scan - blkc;

	/* ---------- */

	/* find the last block in the cut */
	line = markline(to);
	for (last = first; line > lnum[last]; last++)
	{
	}

	/* fetch text of the block containing that line */
	if (last != first)
	{
		blkc = scan = blkget(last)->c;
	}
	else
	{
		scan = blkc;
	}

	/* find the mark in the block */
	for (l = lnum[last - 1]; ++l < line; )
	{
		while (*scan++ != '\n')
		{
		}
	}
	if (markline(to) <= nlines)
	{
		scan += markidx(to);
	}

	/* remember the offset of the end */
	cb->end = scan - blkc;

	/* ------- */

	/* remember the physical block numbers of all included blocks */
	cb->nblks = last - first;
	if (cb->end > 0)
	{
		cb->nblks++;
	}
#ifdef lint
	cb->phys = (short *)0;
#else
	cb->phys = (short *)malloc((unsigned)(cb->nblks * sizeof(short)));
#endif
	for (i = 0; i < cb->nblks; i++)
	{
		cb->phys[i] = hdr.n[first++];
	}

#ifndef CRUNCH
	/* if we temporarily inserted text for appending, then delete that
	 * text now -- before the user sees it.
	 */
	if (delthru)
	{
		line = rptlines;
		delete(from, delthru);
		rptlines = line;
		rptlabel = "yanked";
	}
#endif /* not CRUNCH */
}


static void readcutblk(cb, blkno)
	struct cutbuf	*cb;
	int		blkno;
{
	int		fd;	/* either tmpfd or cb->fd */

	/* decide which fd to use */
	if (cb->fd >= 0)
	{
		fd = cb->fd;
	}
	else
	{
		fd = tmpfd;
	}

	/* get the block */
	lseek(fd, (long)cb->phys[blkno] * (long)BLKSIZE, 0);
	if (read(fd, tmpblk.c, (unsigned)BLKSIZE) != BLKSIZE)
	{
		msg("Error reading back from tmp file for pasting!");
	}
}


/* This function inserts text from a cut buffer, and returns the MARK where
 * insertion ended.  Return MARK_UNSET on errors.
 */
MARK paste(at, after, retend)
	MARK	at;	/* where to insert the text */
	int	after;	/* boolean: insert after mark? (rather than before) */
	int	retend;	/* boolean: return end of text? (rather than start) */
{
	REG struct cutbuf	*cb;
	REG int			i;

	/* decide which cut buffer to use */
	if (cbname >= 'A' && cbname <= 'Z')
	{
		cb = &named[cbname - 'A'];
	}
	else if (cbname >= 'a' && cbname <= 'z')
	{
		cb = &named[cbname - 'a'];
	}
	else if (cbname >= '1' && cbname <= '9')
	{
		cb = &annon[cbname - '1'];
	}
	else if (cbname == '.')
	{
		cb = &named[26];
	}
	else if (!cbname)
	{
		cb = annon;
	}
	else
	{
		msg("Invalid cut buffer name: \"%c", cbname);
		cbname = '\0';
		return MARK_UNSET;
	}

	/* make sure it isn't empty */
	if (cb->nblks == 0)
	{
		if (cbname)
			msg("Cut buffer \"%c is empty", cbname);
		else
			msg("Cut buffer is empty");
		cbname = '\0';
		return MARK_UNSET;
	}
	cbname = '\0';

	/* adjust the insertion MARK for "after" and line-mode cuts */
	if (cb->lnmode)
	{
		at &= ~(BLKSIZE - 1);
		if (after)
		{
			at += BLKSIZE;
		}
	}
	else if (after)
	{
		/* careful! if markidx(at) == 0 we might be pasting into an
		 * empty line -- so we can't blindly increment "at".
		 */
		if (markidx(at) == 0)
		{
			pfetch(markline(at));
			if (plen != 0)
			{
				at++;
			}
		}
		else
		{
			at++;
		}
	}

	/* put a copy of the "at" mark in the mark[] array, so it stays in
	 * sync with changes made via add().
	 */
	mark[27] = at;

	/* simple one-block paste? */
	if (cb->nblks == 1)
	{
		/* get the block */
		readcutblk(cb, 0);

		/* isolate the text we need within it */
		if (cb->end)
		{
			tmpblk.c[cb->end] = '\0';
		}

		/* insert it */
		ChangeText
		{
			add(at, &tmpblk.c[cb->start]);
		}
	}
	else
	{
		/* multi-block paste */

		ChangeText
		{
			i = cb->nblks - 1;

			/* add text from the last block first */
			if (cb->end > 0)
			{
				readcutblk(cb, i);
				tmpblk.c[cb->end] = '\0';
				add(at, tmpblk.c);
				i--;
			}

			/* add intervening blocks */
			while (i > 0)
			{
				readcutblk(cb, i);
				add(at, tmpblk.c);
				i--;
			}

			/* add text from the first cut block */
			readcutblk(cb, 0);
			add(at, &tmpblk.c[cb->start]);
		}
	}

	/* Reporting... */
	rptlines = markline(mark[27]) - markline(at);
	rptlabel = "pasted";

	/* return the mark at the beginning/end of inserted text */
	if (retend)
	{
		return mark[27] - 1L;
	}
	return at;
}




#ifndef NO_AT

/* This function copies characters from a cut buffer into a string.
 * It returns the number of characters in the cut buffer.  If the cut
 * buffer is too large to fit in the string (i.e. if cb2str() returns
 * a number >= size) then the characters will not have been copied.
 * It returns 0 if the cut buffer is empty, and -1 for invalid cut buffers.
 */
int cb2str(name, buf, size)
	int	name;	/* the name of a cut-buffer to get: a-z only! */
	char	*buf;	/* where to put the string */
	unsigned size;	/* size of buf */
{
	REG struct cutbuf	*cb;
	REG char		*src;
	REG char		*dest;

	/* decide which cut buffer to use */
	if (name >= 'a' && name <= 'z')
	{
		cb = &named[name - 'a'];
	}
	else
	{
		return -1;
	}

	/* if the buffer is empty, return 0 */
	if (cb->nblks == 0)
	{
		return 0;
	}

	/* !!! if not a single-block cut, then fail */
	if (cb->nblks != 1)
	{
		return size;
	}

	/* if too big, return the size now, without doing anything */
	if (cb->end - cb->start >= size)
	{
		return cb->end - cb->start;
	}

	/* get the block */
	readcutblk(cb, 0);

	/* isolate the string within that blk */
	if (cb->start == 0)
	{
		tmpblk.c[cb->end] = '\0';
	}
	else
	{
		for (dest = tmpblk.c, src = dest + cb->start; src < tmpblk.c + cb->end; )
		{
			*dest++ = *src++;
		}
		*dest = '\0';
	}

	/* copy the string into the buffer */
	if (buf != tmpblk.c)
	{
		strcpy(buf, tmpblk.c);
	}

	/* return the length */
	return cb->end - cb->start;
}
#endif
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0

mh@roger.imsd.contel.com (Mike H.) (01/11/91)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	date.c
#	ex.c
#	input.c
# This archive created: Thu Jan 10 17:21:08 1991
export PATH; PATH=/bin:$PATH
if test -f 'date.c'
then
	echo shar: will not over-write existing file "'date.c'"
else
cat << \SHAR_EOF > 'date.c'

SHAR_EOF
fi # end of overwriting check
if test -f 'ex.c'
then
	echo shar: will not over-write existing file "'ex.c'"
else
cat << \SHAR_EOF > 'ex.c'
/* ex.c */

/* Author:
 *	Steve Kirkendall
 *	14407 SW Teal Blvd. #C
 *	Beaverton, OR 97005
 *	kirkenda@cs.pdx.edu
 */


/* This file contains the code for reading ex commands. */

#include "config.h"
#include <ctype.h>
#include "vi.h"

#ifndef isascii
# define isascii(c) !((c)&~0x7f)
#endif

/* This data type is used to describe the possible argument combinations */
typedef short ARGT;
#define FROM	1		/* allow a linespec */
#define	TO	2		/* allow a second linespec */
#define BANG	4		/* allow a ! after the command name */
#define EXTRA	8		/* allow extra args after command name */
#define XFILE	16		/* expand wildcards in extra part */
#define NOSPC	32		/* no spaces allowed in the extra part */
#define	DFLALL	64		/* default file range is 1,$ */
#define DFLNONE	128		/* no default file range */
#define NODFL	256		/* do not default to the current file name */
#define EXRCOK	512		/* can be in a .exrc file */
#define NL	1024		/* if mode!=MODE_EX, then write a newline first */
#define PLUS	2048		/* allow a line number, as in ":e +32 foo" */
#define ZERO	4096		/* allow 0 to be given as a line number */
#define FILES	(XFILE + EXTRA)	/* multiple extra files allowed */
#define WORD1	(EXTRA + NOSPC)	/* one extra word allowed */
#define FILE1	(FILES + NOSPC)	/* 1 file allowed, defaults to current file */
#define NAMEDF	(FILE1 + NODFL)	/* 1 file allowed, defaults to "" */
#define NAMEDFS	(FILES + NODFL)	/* multiple files allowed, default is "" */
#define RANGE	(FROM + TO)	/* range of linespecs allowed */
#define NONE	0		/* no args allowed at all */

/* This array maps ex command names to command codes. The order in which
 * command names are listed below is significant -- ambiguous abbreviations
 * are always resolved to be the first possible match.  (e.g. "r" is taken
 * to mean "read", not "rewind", because "read" comes before "rewind")
 */
static struct
{
	char	*name;	/* name of the command */
	CMD	code;	/* enum code of the command */
	void	(*fn)();/* function which executes the command */
	ARGT	argt;	/* command line arguments permitted/needed/used */
}
	cmdnames[] =
{   /*	cmd name	cmd code	function	arguments */
	{"append",	CMD_APPEND,	cmd_append,	FROM+ZERO	},
#ifdef DEBUG
	{"bug",		CMD_DEBUG,	cmd_debug,	RANGE+BANG+EXTRA+NL},
#endif
	{"change",	CMD_CHANGE,	cmd_append,	RANGE		},
	{"delete",	CMD_DELETE,	cmd_delete,	RANGE+WORD1	},
	{"edit",	CMD_EDIT,	cmd_edit,	BANG+FILE1+PLUS	},
	{"file",	CMD_FILE,	cmd_file,	NAMEDF		},
	{"global",	CMD_GLOBAL,	cmd_global,	RANGE+BANG+EXTRA+DFLALL},
	{"insert",	CMD_INSERT,	cmd_append,	FROM		},
	{"join",	CMD_INSERT,	cmd_join,	RANGE		},
	{"k",		CMD_MARK,	cmd_mark,	FROM+WORD1	},
	{"list",	CMD_LIST,	cmd_print,	RANGE+NL	},
	{"move",	CMD_MOVE,	cmd_move,	RANGE+EXTRA	},
	{"next",	CMD_NEXT,	cmd_next,	BANG+NAMEDFS	},
	{"Next",	CMD_PREVIOUS,	cmd_next,	BANG		},
	{"print",	CMD_PRINT,	cmd_print,	RANGE+NL	},
	{"quit",	CMD_QUIT,	cmd_xit,	BANG		},
	{"read",	CMD_READ,	cmd_read,	FROM+ZERO+NAMEDF},
	{"substitute",	CMD_SUBSTITUTE,	cmd_substitute,	RANGE+EXTRA	},
	{"to",		CMD_COPY,	cmd_move,	RANGE+EXTRA	},
	{"undo",	CMD_UNDO,	cmd_undo,	NONE		},
	{"vglobal",	CMD_VGLOBAL,	cmd_global,	RANGE+EXTRA+DFLALL},
	{"write",	CMD_WRITE,	cmd_write,	RANGE+BANG+FILE1+DFLALL},
	{"xit",		CMD_XIT,	cmd_xit,	BANG+NL		},
	{"yank",	CMD_YANK,	cmd_delete,	RANGE+WORD1	},

	{"!",		CMD_BANG,	cmd_shell,	EXRCOK+RANGE+NAMEDFS+DFLNONE+NL},
	{"<",		CMD_SHIFTL,	cmd_shift,	RANGE		},
	{">",		CMD_SHIFTR,	cmd_shift,	RANGE		},
	{"=",		CMD_EQUAL,	cmd_file,	RANGE		},
	{"&",		CMD_SUBAGAIN,	cmd_substitute,	RANGE		},
#ifndef NO_AT
	{"@",		CMD_AT,		cmd_at,		EXTRA		},
#endif

#ifndef NO_ABBR
	{"abbreviate",	CMD_ABBR,	cmd_abbr,	EXRCOK+EXTRA	},
#endif
	{"args",	CMD_ARGS,	cmd_args,	EXRCOK+NAMEDFS	},
#ifndef NO_ERRLIST
	{"cc",		CMD_CC,		cmd_make,	BANG+FILES	},
#endif
	{"cd",		CMD_CD,		cmd_cd,		EXRCOK+NAMEDF	},
	{"copy",	CMD_COPY,	cmd_move,	RANGE+EXTRA	},
#ifndef NO_DIGRAPH
	{"digraph",	CMD_DIGRAPH,	cmd_digraph,	EXRCOK+BANG+EXTRA},
#endif
#ifndef NO_ERRLIST
	{"errlist",	CMD_ERRLIST,	cmd_errlist,	BANG+NAMEDF	},
#endif
	{"ex",		CMD_EDIT,	cmd_edit,	BANG+FILE1	},
	{"map",		CMD_MAP,	cmd_map,	EXRCOK+BANG+EXTRA},
#ifndef NO_MKEXRC
	{"mkexrc",	CMD_MKEXRC,	cmd_mkexrc,	NAMEDF		},
#endif
	{"number",	CMD_NUMBER,	cmd_print,	RANGE+NL	},
	{"put",		CMD_PUT,	cmd_put,	FROM+ZERO+WORD1	},
	{"set",		CMD_SET,	cmd_set,	EXRCOK+EXTRA	},
	{"shell",	CMD_SHELL,	cmd_shell,	NL		},
	{"source",	CMD_SOURCE,	cmd_source,	EXRCOK+NAMEDF	},
	{"tag",		CMD_TAG,	cmd_tag,	BANG+WORD1	},
	{"version",	CMD_VERSION,	cmd_version,	EXRCOK+NONE	},
	{"visual",	CMD_VISUAL,	cmd_visual,	NONE		},
	{"wq",		CMD_WQUIT,	cmd_xit,	NL		},

#ifdef DEBUG
	{"debug",	CMD_DEBUG,	cmd_debug,	RANGE+BANG+EXTRA+NL},
	{"validate",	CMD_VALIDATE,	cmd_validate,	BANG+NL		},
#endif
	{"chdir",	CMD_CD,		cmd_cd,		EXRCOK+NAMEDF	},
#ifndef NO_ERRLIST
	{"make",	CMD_MAKE,	cmd_make,	BANG+NAMEDFS	},
#endif
	{"mark",	CMD_MARK,	cmd_mark,	FROM+WORD1	},
	{"previous",	CMD_PREVIOUS,	cmd_next,	BANG		},
	{"rewind",	CMD_REWIND,	cmd_next,	BANG		},
	{"unmap",	CMD_UNMAP,	cmd_map,	EXRCOK+BANG+EXTRA},
#ifndef NO_ABBR
	{"unabbreviate",CMD_UNABBR,	cmd_abbr,	EXRCOK+WORD1	},
#endif

	{(char *)0}
};


/* This function parses a search pattern - given a pointer to a / or ?,
 * it replaces the ending / or ? with a \0, and returns a pointer to the
 * stuff that came after the pattern.
 */
char	*parseptrn(ptrn)
	REG char	*ptrn;
{
	REG char 	*scan;

	for (scan = ptrn + 1;
	     *scan && *scan != *ptrn;
	     scan++)
	{
		/* allow backslashed versions of / and ? in the pattern */
		if (*scan == '\\' && scan[1] != '\0')
		{
			scan++;
		}
	}
	if (*scan)
	{
		*scan++ = '\0';
	}

	return scan;
}


/* This function parses a line specifier for ex commands */
char *linespec(s, markptr)
	REG char	*s;		/* start of the line specifier */
	MARK		*markptr;	/* where to store the mark's value */
{
	long		num;
	REG char	*t;

	/* parse each ;-delimited clause of this linespec */
	do
	{
		/* skip an initial ';', if any */
		if (*s == ';')
		{
			s++;
		}

		/* skip leading spaces */
		while (isascii(*s) && isspace(*s))
		{
			s++;
		}
	
		/* dot means current position */
		if (*s == '.')
		{
			s++;
			*markptr = cursor;
		}
		/* '$' means the last line */
		else if (*s == '$')
		{
			s++;
			*markptr = MARK_LAST;
		}
		/* digit means an absolute line number */
		else if (isascii(*s) && isdigit(*s))
		{
			for (num = 0; isascii(*s) && isdigit(*s); s++)
			{
				num = num * 10 + *s - '0';
			}
			*markptr = MARK_AT_LINE(num);
		}
		/* appostrophe means go to a set mark */
		else if (*s == '\'')
		{
			s++;
			*markptr = m_tomark(cursor, 1L, (int)*s);
			s++;
		}
		/* slash means do a search */
		else if (*s == '/' || *s == '?')
		{
			/* put a '\0' at the end of the search pattern */
			t = parseptrn(s);
	
			/* search for the pattern */
			*markptr &= ~(BLKSIZE - 1);
			if (*s == '/')
			{
				pfetch(markline(*markptr));
				if (plen > 0)
					*markptr += plen - 1;
				*markptr = m_fsrch(*markptr, s);
			}
			else
			{
				*markptr = m_bsrch(*markptr, s);
			}
	
			/* adjust command string pointer */
			s = t;
		}
	
		/* if linespec was faulty, quit now */
		if (!*markptr)
		{
			return s;
		}
	
		/* maybe add an offset */
		t = s;
		if (*t == '-' || *t == '+')
		{
			s++;
			for (num = 0; *s >= '0' && *s <= '9'; s++)
			{
				num = num * 10 + *s - '0';
			}
			if (num == 0)
			{
				num = 1;
			}
			*markptr = m_updnto(*markptr, num, *t);
		}
	} while (*s == ';' || *s == '+' || *s == '-');

	return s;
}



/* This function reads an ex command and executes it. */
void ex()
{
	char		cmdbuf[80];
	REG int		cmdlen;
	static long	oldline;
	
	significant = FALSE;
	oldline = markline(cursor);

	while (mode == MODE_EX)
	{
		/* read a line */
		cmdlen = vgets(':', cmdbuf, sizeof cmdbuf);
		if (cmdlen < 0)
		{
			return;
		}

		/* if empty line, assume ".+1" */
		if (cmdlen == 0)
		{
			strcpy(cmdbuf, ".+1");
			qaddch('\r');
			clrtoeol();
		}
		else
		{
			addch('\n');
		}
		refresh();

		/* parse & execute the command */
		doexcmd(cmdbuf);

		/* handle autoprint */
		if (significant || markline(cursor) != oldline)
		{
			significant = FALSE;
			oldline = markline(cursor);
			if (*o_autoprint && mode == MODE_EX)
			{
				cmd_print(cursor, cursor, CMD_PRINT, FALSE, "");
			}
		}
	}
}

void doexcmd(cmdbuf)
	char		*cmdbuf;	/* string containing an ex command */
{
	REG char	*scan;		/* used to scan thru cmdbuf */
	MARK		frommark;	/* first linespec */
	MARK		tomark;		/* second linespec */
	REG int		cmdlen;		/* length of the command name given */
	CMD		cmd;		/* what command is this? */
	ARGT		argt;		/* argument types for this command */
	short		forceit;	/* bang version of a command? */
	REG int		cmdidx;		/* index of command */
	REG char	*build;		/* used while copying filenames */
	int		iswild;		/* boolean: filenames use wildcards? */
	int		isdfl;		/* using default line ranges? */
	int		didsub;		/* did we substitute file names for % or # */


	/* ex commands can't be undone via the shift-U command */
	U_line = 0L;

	/* ignore command lines that start with a double-quote */
	if (*cmdbuf == '"')
	{
		return;
	}

	/* permit extra colons at the start of the line */
	while (*cmdbuf == ':')
	{
		cmdbuf++;
	}

	/* parse the line specifier */
	scan = cmdbuf;
	if (nlines < 1)
	{
		/* no file, so don't allow addresses */
	}
	else if (*scan == '%')
	{
		/* '%' means all lines */
		frommark = MARK_FIRST;
		tomark = MARK_LAST;
		scan++;
	}
	else if (*scan == '0')
	{
		frommark = tomark = MARK_UNSET;
		scan++;
	}
	else
	{
		frommark = cursor;
		scan = linespec(scan, &frommark);
		tomark = frommark;
		if (frommark && *scan == ',')
		{
			scan++;
			scan = linespec(scan, &tomark);
		}
		if (!tomark)
		{
			/* faulty line spec -- fault already described */
			return;
		}
		if (frommark > tomark)
		{
			msg("first address exceeds the second");
			return;
		}
	}
	isdfl = (scan == cmdbuf);

	/* skip whitespace */
	while (isascii(*scan) && isspace(*scan))
	{
		scan++;
	}

	/* if no command, then just move the cursor to the mark */
	if (!*scan)
	{
		cursor = tomark;
		return;
	}

	/* figure out how long the command name is */
	if (isascii(*scan) && !isalpha(*scan))
	{
		cmdlen = 1;
	}
	else
	{
		for (cmdlen = 1;
		     !isascii(scan[cmdlen]) || isalpha(scan[cmdlen]);
		     cmdlen++)
		{
		}
	}

	/* lookup the command code */
	for (cmdidx = 0;
	     cmdnames[cmdidx].name && strncmp(scan, cmdnames[cmdidx].name, cmdlen);
	     cmdidx++)
	{
	}
	argt = cmdnames[cmdidx].argt;
	cmd = cmdnames[cmdidx].code;
	if (cmd == CMD_NULL)
	{
#if OSK
		msg("Unknown command \"%s\"", scan);
#else
		msg("Unknown command \"%.*s\"", cmdlen, scan);
#endif
		return;
	}

	/* if the command ended with a bang, set the forceit flag */
	scan += cmdlen;
	if ((argt & BANG) && *scan == '!')
	{
		scan++;
		forceit = 1;
	}
	else
	{
		forceit = 0;
	}

	/* skip any more whitespace, to leave scan pointing to arguments */
	while (isascii(*scan) && isspace(*scan))
	{
		scan++;
	}

	/* a couple of special cases for filenames */
	if (argt & XFILE)
	{
		/* if names were given, process them */
		if (*scan)
		{
			for (build = tmpblk.c, iswild = didsub = FALSE; *scan; scan++)
			{
				switch (*scan)
				{
				  case '%':
					if (!*origname)
					{
						msg("No filename to substitute for %%");
						return;
					}
					strcpy(build, origname);
					while (*build)
					{
						build++;
					}
					didsub = TRUE;
					break;
	
				  case '#':
					if (!*prevorig)
					{
						msg("No filename to substitute for #");
						return;
					}
					strcpy(build, prevorig);
					while (*build)
					{
						build++;
					}
					didsub = TRUE;
					break;
	
				  case '*':
				  case '?':
#if !(MSDOS || TOS)
				  case '[':
				  case '`':
				  case '{': /* } */
				  case '$':
				  case '~':
#endif
					*build++ = *scan;
					iswild = TRUE;
					break;

				  default:
					*build++ = *scan;
				}
			}
			*build = '\0';
	
			if (cmd == CMD_BANG
			 || cmd == CMD_READ && tmpblk.c[0] == '!'
			 || cmd == CMD_WRITE && tmpblk.c[0] == '!')
			{
				if (didsub)
				{
					if (mode != MODE_EX)
					{
						addch('\n');
					}
					addstr(tmpblk.c);
					addch('\n');
					exrefresh();
				}
			}
			else
			{
				if (iswild && tmpblk.c[0] != '>')
				{
					scan = wildcard(tmpblk.c);
				}
			}
		}
		else /* no names given, maybe assume origname */
		{
			if (!(argt & NODFL))
			{
				strcpy(tmpblk.c, origname);
			}
			else
			{
				*tmpblk.c = '\0';
			}
		}

		scan = tmpblk.c;
	}

	/* bad arguments? */
	if (!(argt & EXRCOK) && nlines < 1L)
	{
		msg("Can't use the \"%s\" command in a %s file", cmdnames[cmdidx].name, EXRC);
		return;
	}
	if (!(argt & (ZERO | EXRCOK)) && frommark == MARK_UNSET)
	{
		msg("Can't use address 0 with \"%s\" command.", cmdnames[cmdidx].name);
		return;
	}
	if (!(argt & FROM) && frommark != cursor && nlines >= 1L)
	{
		msg("Can't use address with \"%s\" command.", cmdnames[cmdidx].name);
		return;
	}
	if (!(argt & TO) && tomark != frommark && nlines >= 1L)
	{
		msg("Can't use a range with \"%s\" command.", cmdnames[cmdidx].name);
		return;
	}
	if (!(argt & EXTRA) && *scan)
	{
		msg("Extra characters after \"%s\" command.", cmdnames[cmdidx].name);
		return;
	}
	if ((argt & NOSPC) && !(cmd == CMD_READ && (forceit || *scan == '!')))
	{
		build = scan;
#ifndef CRUNCH
		if ((argt & PLUS) && *build == '+')
		{
			while (*build && !(isascii(*build) && isspace(*build)))
			{
				build++;
			}
			while (*build && isascii(*build) && isspace(*build))
			{
				build++;
			}
		}
#endif /* not CRUNCH */
		for (; *build; build++)
		{
			if (isspace(*build))
			{
				msg("Too many %s to \"%s\" command.",
					(argt & XFILE) ? "filenames" : "arguments",
					cmdnames[cmdidx].name);
				return;
			}
		}
	}

	/* some commands have special default ranges */
	if (isdfl && (argt & DFLALL))
	{
		frommark = MARK_FIRST;
		tomark = MARK_LAST;
	}
	else if (isdfl && (argt & DFLNONE))
	{
		frommark = tomark = 0L;
	}

	/* write a newline if called from visual mode */
	if ((argt & NL) && mode != MODE_EX && !exwrote)
	{
		addch('\n');
		exrefresh();
	}

	/* act on the command */
	(*cmdnames[cmdidx].fn)(frommark, tomark, cmd, forceit, scan);
}


/* This function executes EX commands from a file.  It returns 1 normally, or
 * 0 if the file could not be opened for reading.
 */
int doexrc(filename)
	char	*filename;	/* name of a ".exrc" file */
{
	int	fd;		/* file descriptor */
	int	len;		/* length of the ".exrc" file */
	char	buf[MAXRCLEN];	/* buffer, holds the entire .exrc file */

	/* open the file, read it, and close */
	fd = open(filename, O_RDONLY);
	if (fd < 0)
	{
		return 0;
	}
	len = tread(fd, buf, MAXRCLEN);
	close(fd);

	/* execute the string */
	exstring(buf, len);

	return 1;
}

void exstring(buf, len)
	char	*buf;	/* the commands to execute */
	int	len;	/* the length of the string */
{
	char	*cmd;		/* start of a command */
	char	*end;		/* used to search for the end of cmd */

	/* find & do each command */
	for (cmd = buf; cmd < &buf[len]; cmd = end + 1)
	{
		/* find the end of the command */
		for (end = cmd; end < &buf[len] && *end != '\n' && *end != '|'; end++)
		{
		}
		*end = '\0';

		/* do it */
		doexcmd(cmd);
	}
}
SHAR_EOF
fi # end of overwriting check
if test -f 'input.c'
then
	echo shar: will not over-write existing file "'input.c'"
else
cat << \SHAR_EOF > 'input.c'
/* input.c */

/* Author:
 *	Steve Kirkendall
 *	14407 SW Teal Blvd. #C
 *	Beaverton, OR 97005
 *	kirkenda@cs.pdx.edu
 */


/* This file contains the input() function, which implements vi's INPUT mode.
 * It also contains the code that supports digraphs.
 */

#include <ctype.h>
#include "config.h"
#include "vi.h"


#ifndef NO_DIGRAPH
static struct _DIG
{
	struct _DIG	*next;
	char		key1;
	char		key2;
	char		dig;
	char		save;
} *digs;

char digraph(key1, key2)
	char	key1;	/* the underlying character */
	char	key2;	/* the second character */
{
	int		newkey;
	REG struct _DIG	*dp;

	/* if digraphs are disabled, then just return the new char */
	if (!*o_digraph)
	{
		return key2;
	}

	/* remember the new key, so we can return it if this isn't a digraph */
	newkey = key2;

	/* sort key1 and key2, so that their original order won't matter */
	if (key1 > key2)
	{
		key2 = key1;
		key1 = newkey;
	}

	/* scan through the digraph chart */
	for (dp = digs;
	     dp && (dp->key1 != key1 || dp->key2 != key2);
	     dp = dp->next)
	{
	}

	/* if this combination isn't in there, just use the new key */
	if (!dp)
	{
		return newkey;
	}

	/* else use the digraph key */
	return dp->dig;
}

/* this function lists or defines digraphs */
void do_digraph(bang, extra)
	int	bang;
	char	extra[];
{
	int		dig;
	REG struct _DIG	*dp;
	struct _DIG	*prev;
	static int	user_defined = FALSE; /* boolean: are all later digraphs user-defined? */
	char		listbuf[8];

	/* if "extra" is NULL, then we've reached the end of the built-ins */
	if (!extra)
	{
		user_defined = TRUE;
		return;
	}

	/* if no args, then display the existing digraphs */
	if (*extra < ' ')
	{
		listbuf[0] = listbuf[1] = listbuf[2] = listbuf[5] = ' ';
		listbuf[7] = '\0';
		for (dig = 0, dp = digs; dp; dp = dp->next)
		{
			if (dp->save || bang)
			{
				dig += 7;
				if (dig >= COLS)
				{
					addch('\n');
					exrefresh();
					dig = 7;
				}
				listbuf[3] = dp->key1;
				listbuf[4] = dp->key2;
				listbuf[6] = dp->dig;
				qaddstr(listbuf);
			}
		}
		addch('\n');
		exrefresh();
		return;
	}

	/* make sure we have at least two characters */
	if (!extra[1])
	{
		msg("Digraphs must be composed of two characters");
		return;
	}

	/* sort key1 and key2, so that their original order won't matter */
	if (extra[0] > extra[1])
	{
		dig = extra[0];
		extra[0] = extra[1];
		extra[1] = dig;
	}

	/* locate the new digraph character */
	for (dig = 2; extra[dig] == ' ' || extra[dig] == '\t'; dig++)
	{
	}
	dig = extra[dig];
	if (!bang && dig)
	{
		dig |= 0x80;
	}

	/* search for the digraph */
	for (prev = (struct _DIG *)0, dp = digs;
	     dp && (dp->key1 != extra[0] || dp->key2 != extra[1]);
	     prev = dp, dp = dp->next)
	{
	}

	/* deleting the digraph? */
	if (!dig)
	{
		if (!dp)
		{
#ifndef CRUNCH
			msg("%c%c not a digraph", extra[0], extra[1]);
#endif
			return;
		}
		if (prev)
			prev->next = dp->next;
		else
			digs = dp->next;
		free(dp);
		return;
	}

	/* if necessary, create a new digraph struct for the new digraph */
	if (dig && !dp)
	{
		dp = (struct _DIG *)malloc(sizeof *dp);
		if (!dp)
		{
			msg("Out of space in the digraph table");
			return;
		}
		if (prev)
			prev->next = dp;
		else
			digs = dp;
		dp->next = (struct _DIG *)0;
	}

	/* assign it the new digraph value */
	dp->key1 = extra[0];
	dp->key2 = extra[1];
	dp->dig = dig;
	dp->save = user_defined;
}

# ifndef NO_MKEXRC
void savedigs(fd)
	int		fd;
{
	static char	buf[] = "digraph! XX Y\n";
	REG struct _DIG	*dp;

	for (dp = digs; dp; dp = dp->next)
	{
		if (dp->save)
		{
			buf[9] = dp->key1;
			buf[10] = dp->key2;
			buf[12] = dp->dig;
			write(fd, buf, (unsigned)14);
		}
	}
}
# endif
#endif


#ifndef NO_ABBR
static struct _AB
{
	struct _AB	*next;
	char		*large;		/* the expanded form */
	char		small[1];	/* the abbreviated form (appended to struct) */
}
	*abbrev;

/* This functions lists or defines abbreviations */
void do_abbr(extra)
	char	*extra;
{
	int		smlen;	/* length of the small form */
	int		lrg;	/* index of the start of the large form */
	REG struct _AB	*ab;	/* used to move through the abbrev list */
	struct _AB	*prev;

	/* no arguments? */
	if (!*extra)
	{
		/* list all current abbreviations */
		for (ab = abbrev; ab; ab = ab->next)
		{
			qaddstr("abbr ");
			qaddstr(ab->small);
			qaddch(' ');
			qaddstr(ab->large);
			addch('\n');
			exrefresh();
		}
		return;
	}

	/* else one or more arguments.  Parse the first & look up in abbrev[] */
	for (smlen = 0; extra[smlen] && isalnum(extra[smlen]); smlen++)
	{
	}
	for (prev = (struct _AB *)0, ab = abbrev; ab; prev = ab, ab = ab->next)
	{
		if (!strncmp(extra, ab->small, smlen) && !ab->small[smlen])
		{
			break;
		}
	}

	/* locate the start of the large form, if any */
	for (lrg = smlen; extra[lrg] && isascii(extra[lrg]) && isspace(extra[lrg]); lrg++)
	{
	}

	/* only one arg? */
	if (!extra[lrg])
	{
		/* trying to undo an abbreviation which doesn't exist? */
		if (!ab)
		{
#ifndef CRUNCH
			msg("\"%s\" not an abbreviation", extra);
#endif
			return;
		}

		/* undo the abbreviation */
		if (prev)
			prev->next = ab->next;
		else
			abbrev = ab->next;
		free(ab->large);
		free(ab);

		return;
	}

	/* multiple args - [re]define an abbreviation */
	if (ab)
	{
		/* redefining - free the old large form */
		free(ab->large);
	}
	else
	{
		/* adding a new definition - make a new struct */
		ab = (struct _AB *)malloc((unsigned)(smlen + sizeof *ab));
#ifndef CRUNCH
		if (!ab)
		{
			msg("Out of memory -- Sorry");
			return;
		}
#endif
		strncpy(ab->small, extra, smlen);
		ab->small[smlen] = '\0';
		ab->next = (struct _AB *)0;
		if (prev)
			prev->next = ab;
		else
			abbrev = ab;
	}

	/* store the new form */
	ab->large = (char *)malloc((unsigned)(strlen(&extra[lrg]) + 1));
	strcpy(ab->large, &extra[lrg]);
}


# ifndef NO_MKEXRC
/* This function is called from cmd_mkexrc() to save the abbreviations */
void saveabbr(fd)
	int	fd;	/* fd to which the :abbr commands should be written */
{
	REG struct _AB	*ab;

	for (ab = abbrev; ab; ab = ab->next)
	{
		twrite(fd, "abbr ", 5);
		twrite(fd, ab->small, strlen(ab->small));
		twrite(fd, " ", 1);
		twrite(fd, ab->large, strlen(ab->large));
		twrite(fd, "\n", 1);
	}
}
# endif

/* This function should be called before each char is inserted.  If the next
 * char is non-alphanumeric and we're at the end of a word, then that word
 * is checked against the abbrev[] array and expanded, if appropriate.  Upon
 * returning from this function, the new char still must be inserted.
 */
static MARK expandabbr(m, ch)
	MARK		m;	/* the cursor position */
	int		ch;	/* the character to insert */
{
	char		*word;	/* where the word starts */
	int		len;	/* length of the word */
	REG struct _AB	*ab;

	/* if no abbreviations are in effect, or ch is aphanumeric, then
	 * don't do anything
	 */
	if (!abbrev || !isascii(ch) || isalnum(ch))
	{
		return m;
	}

	/* see where the preceding word starts */
	pfetch(markline(m));
	for (word = ptext + markidx(m), len = 0;
	     --word >= ptext && (!isascii(*word) || isalnum(*word));
	     len++)
	{
	}
	word++;

	/* if zero-length, then it isn't a word, really -- so nothing */
	if (len == 0)
	{
		return m;
	}

	/* look it up in the abbrev list */
	for (ab = abbrev; ab; ab = ab->next)
	{
		if (!strncmp(ab->small, word, len) && !ab->small[len])
		{
			break;
		}
	}

	/* not an abbreviation? then do nothing */
	if (!ab)
	{
		return m;
	}

	/* else replace the small form with the large form */
	add(m, ab->large);
	delete(m - len, m);

	/* return with the cursor after the end of the large form */
	return m - len + strlen(ab->large);
}
#endif

		
/* This function allows the user to replace an existing (possibly zero-length)
 * chunk of text with typed-in text.  It returns the MARK of the last character
 * that the user typed in.
 */
MARK input(from, to, when, indentref)
	MARK	from;	/* where to start inserting text */
	MARK	to;	/* extent of text to delete */
	int	when;	/* either WHEN_VIINP or WHEN_VIREP */
	long	indentref; /* line rel to current mark to use for autoindent
							reference - MCH */
{
	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) + indentref);
					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 */

			/* this includes any whitespace on a whitespace
			only line if we are in autoindent mode - MCH */

			if (*o_autoindent)
			{
				/* remove indent from this line, if blank */
				pfetch(markline(cursor));
				for (scan = ptext; *scan == ' ' || *scan == '\t'; )
					*scan++;
				if (!*scan && plen > 0)
				{
					to = cursor &= ~(BLKSIZE - 1);
					delete(cursor, cursor + plen);
				}
			}
			
			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;
}
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0

mh@roger.imsd.contel.com (Mike H.) (01/11/91)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	main.c
#	misc.c
#	modify.c
#	move1.c
#	move2.c
#	move3.c
#	move4.c
#	move5.c
# This archive created: Thu Jan 10 17:21:08 1991
export PATH; PATH=/bin:$PATH
if test -f 'main.c'
then
	echo shar: will not over-write existing file "'main.c'"
else
cat << \SHAR_EOF > 'main.c'
/* main.c */

/* Author:
 *	Steve Kirkendall
 *	14407 SW Teal Blvd. #C
 *	Beaverton, OR 97005
 *	kirkenda@cs.pdx.edu
 */


/* This file contains the main() function of vi */

#include "config.h"
#include <signal.h>
#include <setjmp.h>
#include "vi.h"

extern		trapint(); /* defined below */
extern char	*getenv();
jmp_buf		jmpenv;

#ifndef NO_DIGRAPH
static init_digraphs();
#endif

/*---------------------------------------------------------------------*/

void main(argc, argv)
	int	argc;
	char	*argv[];
{
	int	i;
	char	*cmd = (char *)0;
	char	*tag = (char *)0;
	char	*err = (char *)0;
	char	*str;
#if MSDOS || TOS
	char firstarg[256];
#else
	char *firstarg;
#endif

	/* set mode to MODE_VI or MODE_EX depending on program name */
	switch (argv[0][strlen(argv[0]) - 1])
	{
	  case 'x':			/* "ex" */
		mode = MODE_EX;
		break;

	  case 'w':			/* "view" */
		mode = MODE_VI;
		*o_readonly = TRUE;
		break;
#ifndef NO_EXTENSIONS
	  case 't':			/* "edit" or "input" */
		mode = MODE_VI;
		*o_inputmode = TRUE;
		break;
#endif
	  default:			/* "vi" or "elvis" */
		mode = MODE_VI;
	}

#ifndef DEBUG
# ifdef	SIGQUIT
	/* normally, we ignore SIGQUIT.  SIGINT is trapped later */
	signal(SIGQUIT, SIG_IGN);
# endif
#endif

	/* temporarily ignore SIGINT */
	signal(SIGINT, SIG_IGN);

	/* Before we fire up whatever form of curses we are doing let
	special version of elvis get any flags it needs Customflags
	removes any custom flags from argv so the standard elvis
	does'nt have to know about them */

	Customflags(argv, &argc);
	
	/* start curses */
	Initscr();
	cbreak();
	noecho();
	scrollok(stdscr, TRUE);

	/* initialize the options */
	initopts();

	/* map the arrow keys.  The KU,KD,KL,and KR variables correspond to
	 * the :ku=: (etc.) termcap capabilities.  The variables are defined
	 * as part of the curses package.
	 */
	if (has_KU) mapkey(has_KU, "k",    WHEN_VICMD|WHEN_INMV, "<Up>");
	if (has_KD) mapkey(has_KD, "j",    WHEN_VICMD|WHEN_INMV, "<Down>");
	if (has_KL) mapkey(has_KL, "h",    WHEN_VICMD|WHEN_INMV, "<Left>");
	if (has_KR) mapkey(has_KR, "l",    WHEN_VICMD|WHEN_INMV, "<Right>");
	if (has_HM) mapkey(has_HM, "^",    WHEN_VICMD|WHEN_INMV, "<Home>");
	if (has_EN) mapkey(has_EN, "$",    WHEN_VICMD|WHEN_INMV, "<End>");
	if (has_PU) mapkey(has_PU, "\002", WHEN_VICMD|WHEN_INMV, "<PgUp>");
	if (has_PD) mapkey(has_PD, "\006", WHEN_VICMD|WHEN_INMV, "<PgDn>");
#if MSDOS
	if (*o_pcbios)
	{
		mapkey("#R", "i", WHEN_VICMD|WHEN_INMV,	"<Insrt>");
		mapkey("#S", "x", WHEN_VICMD|WHEN_INMV,	"<Del>");
		mapkey("#s", "B", WHEN_VICMD|WHEN_INMV,	"^<left>");
		mapkey("#t", "W", WHEN_VICMD|WHEN_INMV,	"^<right>");
	}
#else
	if (ERASEKEY != '\177')
	{
		mapkey("\177", "x", WHEN_VICMD|WHEN_INMV, "<Del>");
	}
#endif

#ifndef NO_DIGRAPH
	init_digraphs();
#endif /* NO_DIGRAPH */

	/* process any flags */
	for (i = 1; i < argc && *argv[i] == '-'; i++)
	{
		switch (argv[i][1])
		{
		  case 'R':	/* readonly */
			*o_readonly = TRUE;
			break;

		  case 'r':	/* recover */
			msg("Use the `virec` program to recover lost files");
			endmsgs();
			refresh();
			endwin();
			exit(0);
			break;

		  case 't':	/* tag */
			if (argv[i][2])
			{
				tag = argv[i] + 2;
			}
			else
			{
				i++;
				tag = argv[i];
			}
			break;

		  case 'v':	/* vi mode */
			mode = MODE_VI;
			break;

		  case 'e':	/* ex mode */
			mode = MODE_EX;
			break;
#ifndef NO_EXTENSIONS
		  case 'i':	/* input mode */
			*o_inputmode = TRUE;
			break;
#endif
#ifndef NO_ERRLIST
		  case 'm':	/* use "errlist" as the errlist */
			if (argv[i][2])
			{
				err = argv[i] + 2;
			}
			else if (i + 1 < argc)
			{
				i++;
				err = argv[i];
			}
			else
			{
				err = "";
			}
			break;
#endif
		  default:
			msg("Ignoring unknown flag \"%s\"", argv[i]);
		}
	}

	/* if we were given an initial ex command, save it... */
	if (i < argc && *argv[i] == '+')
	{
		if (argv[i][1])
		{
			cmd = argv[i++] + 1;
		}
		else
		{
			cmd = "$"; /* "vi + file" means start at EOF */
			i++;
		}
	}

	/* the remaining args are file names. */
	nargs = argc - i;
	if (nargs > 0)
	{
#if ! ( MSDOS || TOS )
		firstarg = argv[i];
#endif
		strcpy(args, argv[i]);
		while (++i < argc && strlen(args) + 1 + strlen(argv[i]) < sizeof args)
		{
			strcat(args, " ");
			strcat(args, argv[i]);
		}
	}
#if ! ( MSDOS || TOS )
	else
	{
		firstarg = "";
	}
#endif
	argno = 0;

#if MSDOS || TOS
	if (nargs > 0)
	{
		strcpy(args, wildcard(args));
		nargs = 1;
		for (i = 0; args[i]; i++)
		{
			if (args[i] == ' ')
			{
				nargs++;
			}
		}
		for (i = 0; args[i] && args[i] != ' '; i++)
		{
			firstarg[i] = args[i];
		}
		firstarg[i] = '\0';
	}
	else
	{
		firstarg[0] = '\0';
	}
#endif

	/* perform the .exrc files and EXINIT environment variable */
#ifdef SYSEXRC
	doexrc(SYSEXRC);
#endif
#ifdef HMEXRC
	str = getenv("HOME");
	if (str)
	{
		sprintf(tmpblk.c, "%s%c%s", str, SLASH, HMEXRC);
		doexrc(tmpblk.c);
	}
#endif
	doexrc(EXRC);
#ifdef EXINIT
	str = getenv(EXINIT);
	if (str)
	{
		exstring(str, strlen(str));
	}
#endif

	/* search for a tag (or an error) now, if desired */
	blkinit();
	if (tag)
	{
		cmd_tag(MARK_FIRST, MARK_FIRST, CMD_TAG, 0, tag);
	}
#ifndef NO_ERRLIST
	else if (err)
	{
		cmd_errlist(MARK_FIRST, MARK_FIRST, CMD_ERRLIST, 0, err);
	}
#endif

	/* if no tag/err, or tag failed, then start with first arg */
	if (tmpfd < 0 && Tmpstart(firstarg) == 0 && *origname)
	{
		ChangeText
		{
		}
		clrflag(file, MODIFIED);
	}

	/* now we do the immediate ex command that we noticed before */
	if (cmd)
	{
		doexcmd(cmd);
	}

	/* repeatedly call ex() or vi() (depending on the mode) until the
	 * mode is set to MODE_QUIT
	 */
	while (mode != MODE_QUIT)
	{
		if (setjmp(jmpenv))
		{
			/* Maybe we just aborted a change? */
			abortdo();
		}
#if TURBOC
		signal(SIGINT, (void(*)()) trapint);
#else
		signal(SIGINT, trapint);
#endif

		switch (mode)
		{
		  case MODE_VI:
			Vi();
			break;

		  case MODE_EX:
			Ex();
			break;
#ifdef DEBUG
		  default:
			msg("mode = %d?", mode);
			mode = MODE_QUIT;
#endif
		}
	}

	/* free up the cut buffers */
	cutend();

	/* end curses */
#ifndef	NO_CURSORSHAPE
	if (has_CQ)
		do_CQ();
#endif
	endmsgs();
	move(LINES - 1, 0);
	clrtoeol();
	refresh();
	endwin();

	exit(0);
	/*NOTREACHED*/
}


/*ARGSUSED*/
int trapint(signo)
	int	signo;
{
	Resume_curses(FALSE);
	abortdo();
#if OSK
	sigmask(-1);
#endif
#if TURBO_C
	signal(signo, (void (*)())trapint);
#else
	signal(signo, trapint);
#endif
	longjmp(jmpenv, 1);

	return 0;
}


#ifndef NO_DIGRAPH

/* This stuff us used to build the default digraphs table. */
static char	digtable[][4] =
{
# if CS_IBMPC
	"C,\200",	"u\"\1",	"e'\2",		"a^\3",
	"a\"\4",	"a`\5",		"a@\6",		"c,\7",
	"e^\10",	"e\"\211",	"e`\12",	"i\"\13",
	"i^\14",	"i`\15",	"A\"\16",	"A@\17",
	"E'\20",	"ae\21",	"AE\22",	"o^\23",
	"o\"\24",	"o`\25",	"u^\26",	"u`\27",
	"y\"\30",	"O\"\31",	"U\"\32",	"a'\240",
	"i'!",		"o'\"",		"u'#",		"n~$",
	"N~%",		"a-&",		"o-'",		"~?(",
	"~!-",		"\"<.",		"\">/",
#  if CS_SPECIAL
	"2/+",		"4/,",		"^+;",		"^q<",
	"^c=",		"^r>",		"^t?",		"pp]",
	"^^^",		"oo_",		"*a`",		"*ba",
	"*pc",		"*Sd",		"*se",		"*uf",
	"*tg",		"*Ph",		"*Ti",		"*Oj",
	"*dk",		"*Hl",		"*hm",		"*En",
	"*No",		"eqp",		"pmq",		"ger",
	"les",		"*It",		"*iu",		"*/v",
	"*=w",		"sq{",		"^n|",		"^2}",
	"^3~",		"^_\377",
#  endif /* CS_SPECIAL */
# endif /* CS_IBMPC */
# if CS_LATIN1
	"~!!",		"a-*",		"\">+",		"o-:",
	"\"<>",		"~??",

	"A`@",		"A'A",		"A^B",		"A~C",
	"A\"D",		"A@E",		"AEF",		"C,G",
	"E`H",		"E'I",		"E^J",		"E\"K",
	"I`L",		"I'M",		"I^N",		"I\"O",
	"-DP",		"N~Q",		"O`R",		"O'S",
	"O^T",		"O~U",		"O\"V",		"O/X",
	"U`Y",		"U'Z",		"U^[",		"U\"\\",
	"Y'_",

	"a``",		"a'a",		"a^b",		"a~c",
	"a\"d",		"a@e",		"aef",		"c,g",
	"e`h",		"e'i",		"e^j",		"e\"k",
	"i`l",		"i'm",		"i^n",		"i\"o",
	"-dp",		"n~q",		"o`r",		"o's",
	"o^t",		"o~u",		"o\"v",		"o/x",
	"u`y",		"u'z",		"u^{",		"u\"|",
	"y'~",
# endif /* CS_LATIN1 */
	""
};

static init_digraphs()
{
	int	i;

	for (i = 0; *digtable[i]; i++)
	{
		do_digraph(FALSE, digtable[i]);
	}
	do_digraph(FALSE, (char *)0);
}
#endif /* NO_DIGRAPH */
SHAR_EOF
fi # end of overwriting check
if test -f 'misc.c'
then
	echo shar: will not over-write existing file "'misc.c'"
else
cat << \SHAR_EOF > 'misc.c'
/* 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;
	}
}
SHAR_EOF
fi # end of overwriting check
if test -f 'modify.c'
then
	echo shar: will not over-write existing file "'modify.c'"
else
cat << \SHAR_EOF > 'modify.c'
/* modify.c */

/* This file contains the low-level file modification functions:
 *	delete(frommark, tomark)	- removes line or portions of lines
 *	add(frommark, text)		- inserts new text
 *	change(frommark, tomark, text)	- delete, then add
 */

#include "config.h"
#include "vi.h"

#ifdef DEBUG
# include <stdio.h>
static FILE *dbg;

/*VARARGS1*/
debout(msg, arg1, arg2, arg3, arg4, arg5)
	char	*msg, *arg1, *arg2, *arg3, *arg4, *arg5;
{
	if (!dbg)
	{
		dbg = fopen("debug.out", "w");
		setbuf(dbg, (FILE *)0);
	}
	fprintf(dbg, msg, arg1, arg2, arg3, arg4, arg5);
}
#endif /* DEBUG */

/* delete a range of text from the file */
void delete(frommark, tomark)
	MARK		frommark;	/* first char to be deleted */
	MARK		tomark;		/* AFTER last char to be deleted */
{
	int		i;		/* used to move thru logical blocks */
	REG char	*scan;		/* used to scan thru text of the blk */
	REG char	*cpy;		/* used when copying chars */
	BLK		*blk;		/* a text block */
	long		l;		/* a line number */
	MARK		m;		/* a traveling version of frommark */

#ifdef DEBUG
	debout("delete(%ld.%d, %ld.%d)\n", markline(frommark), markidx(frommark), markline(tomark), markidx(tomark));
#endif

	/* if not deleting anything, quit now */
	if (frommark == tomark)
	{
		return;
	}

	/* This is a change */
	changes++;
	significant = TRUE;

	/* if this is a multi-line change, then we'll have to redraw */
	if (markline(frommark) != markline(tomark))
	{
		mustredraw = TRUE;
		redrawrange(markline(frommark), markline(tomark), markline(frommark));
	}

	/* adjust marks 'a through 'z and '' as needed */
	l = markline(tomark);
	for (i = 0; i < NMARKS; i++)
	{
		if (mark[i] < frommark)
		{
			continue;
		}
		else if (mark[i] < tomark)
		{
			mark[i] = MARK_UNSET;
		}
		else if (markline(mark[i]) == l)
		{
			if (markline(frommark) == l)
			{
				mark[i] -= markidx(tomark) - markidx(frommark);
			}
			else
			{
				mark[i] -= markidx(tomark);
			}
		}
		else
		{
			mark[i] -= MARK_AT_LINE(l - markline(frommark));
		}
	}

	/* Reporting... */
	if (markidx(frommark) == 0 && markidx(tomark) == 0)
	{
		rptlines = markline(tomark) - markline(frommark);
		rptlabel = "deleted";
	}

	/* find the block containing frommark */
	l = markline(frommark);
	for (i = 1; lnum[i] < l; i++)
	{
	}

	/* process each affected block... */
	for (m = frommark;
	     m < tomark && lnum[i] < INFINITY;
	     m = MARK_AT_LINE(lnum[i - 1] + 1))
	{
		/* fetch the block */
		blk = blkget(i);

		/* find the mark in the block */
		scan = blk->c;
		for (l = markline(m) - lnum[i - 1] - 1; l > 0; l--)
		{
			while (*scan++ != '\n')
			{
			}
		}
		scan += markidx(m);

		/* figure out where the changes to this block end */
		if (markline(tomark) > lnum[i])
		{
			cpy = blk->c + BLKSIZE;
		}
		else if (markline(tomark) == markline(m))
		{
			cpy = scan - markidx(m) + markidx(tomark);
		}
		else
		{
			cpy = scan;
			for (l = markline(tomark) - markline(m);
			     l > 0;
			     l--)
			{
				while (*cpy++ != '\n')
				{
				}
			}
			cpy += markidx(tomark);
		}

		/* delete the stuff by moving chars within this block */
		while (cpy < blk->c + BLKSIZE)
		{
			*scan++ = *cpy++;
		}
		while (scan < blk->c + BLKSIZE)
		{
			*scan++ = '\0';
		}

		/* adjust tomark to allow for lines deleted from this block */
		tomark -= MARK_AT_LINE(lnum[i] + 1 - markline(m));

		/* if this block isn't empty now, then advance i */
		if (*blk->c)
		{
			i++;
		}

		/* the buffer has changed.  Update hdr and lnum. */
		blkdirty(blk);
	}

	/* must have at least 1 line */
	if (nlines == 0)
	{
		blk = blkadd(1);
		blk->c[0] = '\n';
		blkdirty(blk);
		cursor = MARK_FIRST;
	}
}


/* add some text at a specific place in the file */
void add(atmark, newtext)
	MARK		atmark;		/* where to insert the new text */
	char		*newtext;	/* NUL-terminated string to insert */
{
	REG char	*scan;		/* used to move through string */
	REG char	*build;		/* used while copying chars */
	int		addlines;	/* number of lines we're adding */
	int		lastpart;	/* size of last partial line */
	BLK		*blk;		/* the block to be modified */
	int		blkno;		/* the logical block# of (*blk) */
	REG char	*newptr;	/* where new text starts in blk */
	BLK		buf;		/* holds chars from orig blk */
	BLK		linebuf;	/* holds part of line that didn't fit */
	BLK		*following;	/* the BLK following the last BLK */
	int		i;
	long		l;

#ifdef DEBUG
	debout("add(%ld.%d, \"%s\")\n", markline(atmark), markidx(atmark), newtext);
#endif
#ifdef lint
	buf.c[0] = 0;
#endif
	/* if not adding anything, return now */
	if (!*newtext)
	{
		return;
	}

	/* This is a change */
	changes++;
	significant = TRUE;

	/* count the number of lines in the new text */
	for (scan = newtext, lastpart = addlines = 0; *scan; )
	{
		if (*scan++ == '\n')
		{
			addlines++;
			lastpart = 0;
		}
		else
		{
			lastpart++;
		}
	}

	/* Reporting... */
	if (lastpart == 0 && markidx(atmark) == 0)
	{
		rptlines = addlines;
		rptlabel = "added";
	}

	/* extract the line# from atmark */
	l = markline(atmark);

	/* if more than 0 lines, then we'll have to redraw the screen */
	if (addlines > 0)
	{
		mustredraw = TRUE;
		if (markidx(atmark) == 0 && lastpart == 0)
		{
			redrawrange(l, l, l + addlines);
		}
		else
		{
			/* make sure the last line gets redrawn -- it was
			 * split, so its appearance has changed
			 */
			redrawrange(l, l + 1L, l + addlines + 1L);
		}
	}

	/* adjust marks 'a through 'z and '' as needed */
	for (i = 0; i < NMARKS; i++)
	{
		if (mark[i] < atmark)
		{
			/* earlier line, or earlier in same line: no change */
			continue;
		}
		else if (markline(mark[i]) > l)
		{
			/* later line: move down a whole number of lines */
			mark[i] += MARK_AT_LINE(addlines);
		}
		else
		{
			/* later in same line */
			if (addlines > 0)
			{
				/* multi-line add, which split this line:
				 * move down, and possibly left or right,
				 * depending on where the split was and how
				 * much text was inserted after the last \n
				 */
				mark[i] += MARK_AT_LINE(addlines) + lastpart - markidx(atmark);
			}
			else
			{
				/* totally within this line: move right */
				mark[i] += lastpart;
			}
		}
	}

	/* get the block to be modified */
	for (blkno = 1; lnum[blkno] < l && lnum[blkno + 1] < INFINITY; blkno++)
	{
	}
	blk = blkget(blkno);
	buf = *blk;

	/* figure out where the new text starts */
	for (newptr = buf.c, l = markline(atmark) - lnum[blkno - 1] - 1;
	     l > 0;
	     l--)
	{
		while (*newptr++ != '\n')
		{
		}
	}
	newptr += markidx(atmark);

	/* keep start of old block */
	build = blk->c + (newptr - buf.c);

	/* fill this block (or blocks) from the newtext string */
	while (*newtext)
	{
		while (*newtext && build < blk->c + BLKSIZE - 1)
		{
			*build++ = *newtext++;
		}
		if (*newtext)
		{
			/* save the excess */
			for (scan = linebuf.c + BLKSIZE;
			     build > blk->c && build[-1] != '\n';
			     )
			{
				*--scan = *--build;
			}

			/* write the block */
			while (build < blk->c + BLKSIZE)
			{
				*build++ = '\0';
			}
			blkdirty(blk);

			/* add another block */
			blkno++;
			blk = blkadd(blkno);

			/* copy in the excess from last time */
			for (build = blk->c; scan < linebuf.c + BLKSIZE; )
			{
				*build++ = *scan++;
			}
		}
	}

	/* fill this block(s) from remainder of orig block */
	while (newptr < buf.c + BLKSIZE && *newptr)
	{
		while (newptr < buf.c + BLKSIZE
		    && *newptr
		    && build < blk->c + BLKSIZE - 1)
		{
			*build++ = *newptr++;
		}
		if (newptr < buf.c + BLKSIZE && *newptr)
		{
			/* save the excess */
			for (scan = linebuf.c + BLKSIZE;
			     build > blk->c && build[-1] != '\n';
			     )
			{
				*--scan = *--build;
			}

			/* write the block */
			while (build < blk->c + BLKSIZE)
			{
				*build++ = '\0';
			}
			blkdirty(blk);

			/* add another block */
			blkno++;
			blk = blkadd(blkno);

			/* copy in the excess from last time */
			for (build = blk->c; scan < linebuf.c + BLKSIZE; )
			{
				*build++ = *scan++;
			}
		}
	}

	/* see if we can combine our last block with the following block */
	if (lnum[blkno] < nlines && lnum[blkno + 1] - lnum[blkno] < (BLKSIZE >> 6))
	{
		/* hey, we probably can!  Get the following block & see... */
		following = blkget(blkno + 1);
		if (strlen(following->c) + (build - blk->c) < BLKSIZE - 1)
		{
			/* we can!  Copy text from following to blk */
			for (scan = following->c; *scan; )
			{
				*build++ = *scan++;
			}
			while (build < blk->c + BLKSIZE)
			{
				*build++ = '\0';
			}
			blkdirty(blk);

			/* pretend the following was the last blk */
			blk = following;
			build = blk->c;
		}
	}

	/* that last block is dirty by now */
	while (build < blk->c + BLKSIZE)
	{
		*build++ = '\0';
	}
	blkdirty(blk);
}


/* change the text of a file */
void change(frommark, tomark, newtext)
	MARK	frommark, tomark;
	char	*newtext;
{
	int	i;
	long	l;
	char	*text;
	BLK	*blk;

#ifdef DEBUG
	debout("change(%ld.%d, %ld.%d, \"%s\")\n", markline(frommark), markidx(frommark), markline(tomark), markidx(tomark), newtext);
#endif

	/* optimize for single-character replacement */
	if (frommark + 1 == tomark && newtext[0] && !newtext[1] && newtext[0] != '\n')
	{
		/* find the block containing frommark */
		l = markline(frommark);
		for (i = 1; lnum[i] < l; i++)
		{
		}

		/* get the block */
		blk = blkget(i);

		/* find the line within the block */
		for (text = blk->c, i = l - lnum[i - 1] - 1; i > 0; text++)
		{
			if (*text == '\n')
			{
				i--;
			}
		}

		/* replace the char */
		text += markidx(frommark);
		if (*text == newtext[0])
		{
			/* no change was needed - same char */
			return;
		}
		else if (*text != '\n')
		{
			/* This is a change */
			changes++;
			significant = TRUE;
			ChangeText
			{
				*text = newtext[0];
				blkdirty(blk);
			}
			return;
		}
		/* else it is a complex change involving newline... */
	}

	/* couldn't optimize, so do delete & add */
	ChangeText
	{
		delete(frommark, tomark);
		add(frommark, newtext);
		rptlabel = "changed";
	}
}
SHAR_EOF
fi # end of overwriting check
if test -f 'move1.c'
then
	echo shar: will not over-write existing file "'move1.c'"
else
cat << \SHAR_EOF > 'move1.c'
/* move1.c */

/* Author:
 *	Steve Kirkendall
 *	14407 SW Teal Blvd. #C
 *	Beaverton, OR 97005
 *	kirkenda@cs.pdx.edu
 */


/* This file contains most movement functions */

#include "config.h"
#include <ctype.h>
#include "vi.h"

#ifndef isascii
# define isascii(c)	!((c) & ~0x7f)
#endif

MARK	m_updnto(m, cnt, cmd)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	DEFAULT(cmd == 'G' ? nlines : 1L);

	/* move up or down 'cnt' lines */
	switch (cmd)
	{
	  case ('P'&0x1f):
	  case '-':
	  case 'k':
		m -= MARK_AT_LINE(cnt);
		break;

	  case 'G':
		if (cnt < 1L || cnt > nlines)
		{
			msg("Only %ld lines", nlines);
			return MARK_UNSET;
		}
		m = MARK_AT_LINE(cnt);
		break;

	  default:
		m += MARK_AT_LINE(cnt);
	}

	/* if that left us screwed up, then fail */
	if (m < MARK_FIRST || markline(m) > nlines)
	{
		return MARK_UNSET;
	}

	return m;
}

/*ARGSUSED*/
MARK	m_right(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	int		idx;	/* index of the new cursor position */

	DEFAULT(1);

	/* move to right, if that's OK */
	pfetch(markline(m));
	idx = markidx(m) + cnt;
	if (idx < plen)
	{
		m += cnt;
	}
	else
	{
		return MARK_UNSET;
	}

	return m;
}

/*ARGSUSED*/
MARK	m_left(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	DEFAULT(1);

	/* move to the left, if that's OK */
	if (markidx(m) >= cnt)
	{
		m -= cnt;
	}
	else
	{
		return MARK_UNSET;
	}

	return m;
}

/*ARGSUSED*/
MARK	m_tocol(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	char	*text;	/* text of the line */
	int	col;	/* column number */
	int	idx;	/* index into the line */

	DEFAULT(1);

	/* internally, columns are numbered 0..COLS-1, not 1..COLS */
	cnt--;

	/* if 0, that's easy */
	if (cnt == 0)
	{
		m &= ~(BLKSIZE - 1);
		return m;
	}

	/* find that column within the line */
	pfetch(markline(m));
	text = ptext;
	for (col = idx = 0; col < cnt && *text; text++, idx++)
	{
		if (*text == '\t' && !*o_list)
		{
			col += *o_tabstop;
			col -= col % *o_tabstop;
		}
		else if (UCHAR(*text) < ' ' || *text == '\177')
		{
			col += 2;
		}
#ifndef NO_CHARATTR
		else if (text[0] == '\\' && text[1] == 'f' && text[2] && *o_charattr)
		{
			text += 2; /* plus one more as part of for loop */
		}
#endif
		else
		{
			col++;
		}
	}
	if (!*text)
	{
		return MARK_UNSET;
	}
	else
	{
		m = (m & ~(BLKSIZE - 1)) + idx;
	}
	return m;
}

/*ARGSUSED*/
MARK	m_front(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument (ignored) */
{
	char	*scan;

	/* move to the first non-whitespace character */
	pfetch(markline(m));
	scan = ptext;
	m &= ~(BLKSIZE - 1);
	while (*scan == ' ' || *scan == '\t')
	{
		scan++;
		m++;
	}

	return m;
}

/*ARGSUSED*/
MARK	m_rear(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument (ignored) */
{
	/* Try to move *EXTREMELY* far to the right.  It is fervently hoped
	 * that other code will convert this to a more reasonable MARK before
	 * anything tries to actually use it.  (See adjmove() in vi.c)
	 */
	return m | (BLKSIZE - 1);
}

#ifndef NO_SENTENCE
/*ARGSUSED*/
MARK	m_fsentence(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	REG char	*text;
	REG long	l;

	DEFAULT(1);

	/* get the current line */
	l = markline(m);
	pfetch(l);
	text = ptext + markidx(m);

	/* for each requested sentence... */
	while (cnt-- > 0)
	{
		/* search forward for one of [.?!] followed by spaces or EOL */
		do
		{
			/* wrap at end of line */
			if (!text[0])
			{
				if (l >= nlines)
				{
					return MARK_UNSET;
				}
				l++;
				pfetch(l);
				text = ptext;
			}
			else
			{
				text++;
			}
		} while (text[0] != '.' && text[0] != '?' && text[0] != '!'
			|| text[1] && (text[1] != ' ' || text[2] && text[2] != ' '));
	}

	/* construct a mark for this location */
	m = buildmark(text);

	/* move forward to the first word of the next sentence */
	m = m_fword(m, 1L);

	return m;
}

/*ARGSUSED*/
MARK	m_bsentence(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	REG char	*text;	/* used to scan thru text */
	REG long	l;	/* current line number */
	int		flag;	/* have we passed at least one word? */

	DEFAULT(1);

	/* get the current line */
	l = markline(m);
	pfetch(l);
	text = ptext + markidx(m);

	/* for each requested sentence... */
	flag = TRUE;
	while (cnt-- > 0)
	{
		/* search backward for one of [.?!] followed by spaces or EOL */
		do
		{
			/* wrap at beginning of line */
			if (text == ptext)
			{
				do
				{
					if (l <= 1)
					{
						return MARK_UNSET;
					}
					l--;
					pfetch(l);
				} while (!*ptext);
				text = ptext + plen - 1;
			}
			else
			{
				text--;
			}

			/* are we moving past a "word"? */
			if (text[0] >= '0')
			{
				flag = FALSE;
			}
		} while (flag || text[0] != '.' && text[0] != '?' && text[0] != '!'
			|| text[1] && (text[1] != ' ' || text[2] && text[2] != ' '));
	}

	/* construct a mark for this location */
	m = buildmark(text);

	/* move to the front of the following sentence */
	m = m_fword(m, 1L);

	return m;
}
#endif

/*ARGSUSED*/
MARK	m_fparagraph(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	char	*text;
	char	*pscn;	/* used to scan thru value of "paragraphs" option */
	long	l;

	DEFAULT(1);

	for (l = markline(m); cnt > 0 && l++ < nlines; )
	{
		text = fetchline(l);
		if (!*text)
		{
			cnt--;
		}
#ifndef NO_SENTENCE
		else if (*text == '.')
		{
			for (pscn = o_paragraphs; pscn[0] && pscn[1]; pscn += 2)
			{
				if (pscn[0] == text[1] && pscn[1] == text[2])
				{
					cnt--;
					break;
				}
			}
		}
#endif
	}
	if (l <= nlines)
	{
		m = MARK_AT_LINE(l);
	}
	else
	{
		m = MARK_LAST;
	}
	return m;
}

/*ARGSUSED*/
MARK	m_bparagraph(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	char	*text;
	char	*pscn;	/* used to scan thru value of "paragraph" option */
	long	l;

	DEFAULT(1);

	for (l = markline(m); cnt > 0 && l-- > 1; )
	{
		text = fetchline(l);
		if (!*text)
		{
			cnt--;
		}
#ifndef NO_SENTENCE
		else if (*text == '.')
		{
			for (pscn = o_paragraphs; pscn[0] && pscn[1]; pscn += 2)
			{
				if (pscn[0] == text[1] && pscn[1] == text[2])
				{
					cnt--;
					break;
				}
			}
		}
#endif
	}
	if (l >= 1)
	{
		m = MARK_AT_LINE(l);
	}
	else
	{
		m = MARK_FIRST;
	}
	return m;
}

/*ARGSUSED*/
MARK	m_fsection(m, cnt, key)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* (ignored) */
	int	key;	/* second key stroke - must be ']' */
{
	char	*text;
	char	*sscn;	/* used to scan thru value of "sections" option */
	long	l;

	/* make sure second key was ']' */
	if (key != ']')
	{
		return MARK_UNSET;
	}

	for (l = markline(m); l++ < nlines; )
	{
		text = fetchline(l);
		if (*text == '{')
		{
			break;
		}
#ifndef NO_SENTENCE
		else if (*text == '.')
		{
			for (sscn = o_sections; sscn[0] && sscn[1]; sscn += 2)
			{
				if (sscn[0] == text[1] && sscn[1] == text[2])
				{
					goto BreakBreak;
				}
			}
		}
#endif
	}
BreakBreak:
	if (l <= nlines)
	{
		m = MARK_AT_LINE(l);
	}
	else
	{
		m = MARK_LAST;
	}
	return m;
}

/*ARGSUSED*/
MARK	m_bsection(m, cnt, key)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* (ignored) */
	int	key;	/* second key stroke - must be '[' */
{
	char	*text;
	char	*sscn;	/* used to scan thru value of "sections" option */
	long	l;

	/* make sure second key was '[' */
	if (key != '[')
	{
		return MARK_UNSET;
	}

	for (l = markline(m); l-- > 1; )
	{
		text = fetchline(l);
		if (*text == '{')
		{
			break;
		}
#ifndef NO_SENTENCE
		else if (*text == '.')
		{
			for (sscn = o_sections; sscn[0] && sscn[1]; sscn += 2)
			{
				if (sscn[0] == text[1] && sscn[1] == text[2])
				{
					goto BreakBreak;
				}
			}
		}
#endif
	}
BreakBreak:
	if (l >= 1)
	{
		m = MARK_AT_LINE(l);
	}
	else
	{
		m = MARK_FIRST;
	}
	return m;
}


/*ARGSUSED*/
MARK	m_match(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument (ignored) */
{
	long	l;
	REG char	*text;
	REG char	match;
	REG char	nest;
	REG int		count;

	/* get the current line */
	l = markline(m);
	pfetch(l);
	text = ptext + markidx(m);

	/* search forward within line for one of "[](){}" */
	for (match = '\0'; !match && *text; text++)
	{
		/* tricky way to recognize 'em in ASCII */
		nest = *text;
		if ((nest & 0xdf) == ']' || (nest & 0xdf) == '[')
		{
			match = nest ^ ('[' ^ ']');
		}
		else if ((nest & 0xfe) == '(')
		{
			match = nest ^ ('(' ^ ')');
		}
		else
		{
			match = 0;
		}
	}
	if (!match)
	{
		return MARK_UNSET;
	}
	text--;

	/* search forward or backward for match */
	if (match == '(' || match == '[' || match == '{')
	{
		/* search backward */
		for (count = 1; count > 0; )
		{
			/* wrap at beginning of line */
			if (text == ptext)
			{
				do
				{
					if (l <= 1L)
					{
						return MARK_UNSET;
					}
					l--;
					pfetch(l);
				} while (!*ptext);
				text = ptext + plen - 1;
			}
			else
			{
				text--;
			}

			/* check the char */
			if (*text == match)
				count--;
			else if (*text == nest)
				count++;
		}
	}
	else
	{
		/* search forward */
		for (count = 1; count > 0; )
		{
			/* wrap at end of line */
			if (!*text)
			{
				if (l >= nlines)
				{
					return MARK_UNSET;
				}
				l++;
				pfetch(l);
				text = ptext;
			}
			else
			{
				text++;
			}

			/* check the char */
			if (*text == match)
				count--;
			else if (*text == nest)
				count++;
		}
	}

	/* construct a mark for this place */
	m = buildmark(text);
	return m;
}

/*ARGSUSED*/
MARK	m_tomark(m, cnt, key)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* (ignored) */
	int	key;	/* keystroke - the mark to move to */
{
	/* mark '' is a special case */
	if (key == '\'' || key == '`')
	{
		if (mark[26] == MARK_UNSET)
		{
			return MARK_FIRST;
		}
		else
		{
			return mark[26];
		}
	}

	/* if not a valid mark number, don't move */
	if (key < 'a' || key > 'z')
	{
		return MARK_UNSET;
	}

	/* return the selected mark -- may be MARK_UNSET */
	if (!mark[key - 'a'])
	{
		msg("mark '%c is unset", key);
	}
	return mark[key - 'a'];
}

SHAR_EOF
fi # end of overwriting check
if test -f 'move2.c'
then
	echo shar: will not over-write existing file "'move2.c'"
else
cat << \SHAR_EOF > 'move2.c'
/* 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;
}

SHAR_EOF
fi # end of overwriting check
if test -f 'move3.c'
then
	echo shar: will not over-write existing file "'move3.c'"
else
cat << \SHAR_EOF > 'move3.c'
/* 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
SHAR_EOF
fi # end of overwriting check
if test -f 'move4.c'
then
	echo shar: will not over-write existing file "'move4.c'"
else
cat << \SHAR_EOF > 'move4.c'
/* 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;
}
SHAR_EOF
fi # end of overwriting check
if test -f 'move5.c'
then
	echo shar: will not over-write existing file "'move5.c'"
else
cat << \SHAR_EOF > 'move5.c'
/* move5.c */

/* Author:
 *	Steve Kirkendall
 *	14407 SW Teal Blvd. #C
 *	Beaverton, OR 97005
 *	kirkenda@cs.pdx.edu
 */


/* This file contains the word-oriented movement functions */

#include <ctype.h>
#include "config.h"
#include "vi.h"

#ifndef isascii
# define isascii(c)	!((c) & ~0x7f)
#endif


MARK	m_fword(m, cnt, cmd)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
	int	cmd;	/* either 'w' or 'W' */
{
	REG long	l;
	REG char	*text;
	REG int		i;

	DEFAULT(1);

	l = markline(m);
	pfetch(l);
	text = ptext + markidx(m);
	while (cnt-- > 0) /* yes, ASSIGNMENT! */
	{
		i = *text++;

		if (cmd == 'W')
		{
			/* include any non-whitespace */
			while (i && (!isascii(i) || !isspace(i)))
			{
				i = *text++;
			}
		}
		else if (!isascii(i) || isalnum(i) || i == '_')
		{
			/* include an alphanumeric word */
			while (i && (!isascii(i) || isalnum(i) || i == '_'))
			{
				i = *text++;
			}
		}
		else
		{
			/* include contiguous punctuation */
			while (i && isascii(i) && !isalnum(i) && !isspace(i))
			{
				i = *text++;
			}
		}

		/* include trailing whitespace */
		while (!i || isascii(i) && isspace(i))
		{
			/* did we hit the end of this line? */
			if (!i)
			{
				/* move to next line, if there is one */
				l++;
				if (l > nlines)
				{
					return MARK_UNSET;
				}
				pfetch(l);
				text = ptext;
			}

			i = *text++;
		}
		text--;
	}

	/* construct a MARK for this place */
	m = buildmark(text);
	return m;
}


MARK	m_bword(m, cnt, cmd)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
	int	cmd;	/* either 'b' or 'B' */
{
	REG long	l;
	REG char	*text;

	DEFAULT(1);

	l = markline(m);
	pfetch(l);
	text = ptext + markidx(m);
	while (cnt-- > 0) /* yes, ASSIGNMENT! */
	{
		text--;

		/* include preceding whitespace */
		while (text < ptext || isascii(*text) && isspace(*text))
		{
			/* did we hit the end of this line? */
			if (text < ptext)
			{
				/* move to preceding line, if there is one */
				l--;
				if (l <= 0)
				{
					return MARK_UNSET;
				}
				pfetch(l);
				text = ptext + plen - 1;
			}
			else
			{
				text--;
			}
		}

		if (cmd == 'B')
		{
			/* include any non-whitespace */
			while (text >= ptext && (!isascii(*text) || !isspace(*text)))
			{
				text--;
			}
		}
		else if (!isascii(*text) || isalnum(*text) || *text == '_')
		{
			/* include an alphanumeric word */
			while (text >= ptext && (!isascii(*text) || isalnum(*text) || *text == '_'))
			{
				text--;
			}
		}
		else
		{
			/* include contiguous punctuation */
			while (text >= ptext && isascii(*text) && !isalnum(*text) && !isspace(*text))
			{
				text--;
			}
		}
		text++;
	}

	/* construct a MARK for this place */
	m = buildmark(text);
	return m;
}

MARK	m_eword(m, cnt, cmd)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
	int	cmd;	/* either 'e' or 'E' */
{
	REG long	l;
	REG char	*text;
	REG int		i;

	DEFAULT(1);

	l = markline(m);
	pfetch(l);
	text = ptext + markidx(m);
	while (cnt-- > 0) /* yes, ASSIGNMENT! */
	{
		text++;
		i = *text++;

		/* include preceding whitespace */
		while (!i || isascii(i) && isspace(i))
		{
			/* did we hit the end of this line? */
			if (!i)
			{
				/* move to next line, if there is one */
				l++;
				if (l > nlines)
				{
					return MARK_UNSET;
				}
				pfetch(l);
				text = ptext;
			}

			i = *text++;
		}

		if (cmd == 'E')
		{
			/* include any non-whitespace */
			while (i && (!isascii(i) || !isspace(i)))
			{
				i = *text++;
			}
		}
		else if (!isascii(i) || isalnum(i) || i == '_')
		{
			/* include an alphanumeric word */
			while (i && (!isascii(i) || isalnum(i) || i == '_'))
			{
				i = *text++;
			}
		}
		else
		{
			/* include contiguous punctuation */
			while (i && isascii(i) && !isalnum(i) && !isspace(i))
			{
				i = *text++;
			}
		}
		text -= 2;
	}

	/* construct a MARK for this place */
	m = buildmark(text);
	return m;
}
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0

mh@roger.imsd.contel.com (Mike H.) (01/11/91)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	opts.c
#	osk.c
#	ow.c
#	ow_tcap.c
# This archive created: Thu Jan 10 17:21:09 1991
export PATH; PATH=/bin:$PATH
if test -f 'opts.c'
then
	echo shar: will not over-write existing file "'opts.c'"
else
cat << \SHAR_EOF > 'opts.c'
/* 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);
	}
}
SHAR_EOF
fi # end of overwriting check
if test -f 'osk.c'
then
	echo shar: will not over-write existing file "'osk.c'"
else
cat << \SHAR_EOF > 'osk.c'
/* osk.c */

/* ------------------------------------------------------------------- *
 |
 | OS9Lib:  stat(), fstat()
 |
 |
 |     Copyright (c) 1988 by Wolfgang Ocker, Puchheim,
 |                           Ulli Dessauer, Germering and
 |                           Reimer Mellin, Muenchen
 |                           (W-Germany)
 |
 |  This  programm can  be  copied and  distributed freely  for any
 |  non-commercial  purposes.   It can only  be  incorporated  into
 |  commercial software with the written permission of the authors.
 |
 |  If you should modify this program, the authors would appreciate
 |  a notice about the changes. Please send a (context) diff or the
 |  complete source to:
 |
 |  address:     Wolfgang Ocker
 |               Lochhauserstrasse 35a
 |               D-8039 Puchheim
 |               West Germany
 |
 |  e-mail:      weo@altger.UUCP, ud@altger.UUCP, ram@altger.UUCP
 |               pyramid!tmpmbx!recco!weo
 |               pyramid!tmpmbx!nitmar!ud
 |               pyramid!tmpmbx!ramsys!ram
 |
 * ----------------------------------------------------------------- */

#ifdef OSK

#define PATCHLEVEL 1

#ifndef VIREC
#include <stdio.h>
#include "osk.h"
#include <modes.h>
#include <errno.h>
#endif
#include <signal.h>
#include <sgstat.h>
#include <sg_codes.h>
#include <direct.h>

/*
 * f s t a t
 */
int fstat(fd, buff)
  int         fd;
  struct stat *buff;
{
  struct fildes ftmp;
  struct tm     ttmp;
  struct _sgr   fopt;

  if (_gs_gfd(fd, &ftmp, 16) < 0) /* 16 insteat of sizeof(struct fildes)   */
    return(-1);                   /* used due to a bug in stupid os9net */

  if (_gs_opt(fd, &fopt) < 0)
    return(-1);

  ttmp.tm_year  = (int) ftmp.fd_date[0];
  ttmp.tm_mon   = (int) ftmp.fd_date[1] - 1;
  ttmp.tm_mday  = (int) ftmp.fd_date[2];	
  ttmp.tm_hour  = (int) ftmp.fd_date[3];
  ttmp.tm_min   = (int) ftmp.fd_date[4];
  ttmp.tm_sec   = 0;
  ttmp.tm_isdst = -1;

  buff->st_atime = buff->st_mtime = mktime(&ttmp);

  ttmp.tm_year  = (int) ftmp.fd_dcr[0];
  ttmp.tm_mon   = (int) ftmp.fd_dcr[1] - 1;
  ttmp.tm_mday  = (int) ftmp.fd_dcr[2];	
  ttmp.tm_hour  = ttmp.tm_min = ttmp.tm_sec = 0;
  ttmp.tm_isdst = -1;
  
  buff->st_ctime = mktime(&ttmp);

  memcpy(&(buff->st_size), ftmp.fd_fsize, sizeof(long));  /* misalignment! */
  buff->st_uid   = ftmp.fd_own[1];
  buff->st_gid   = ftmp.fd_own[0];
  buff->st_mode  = ftmp.fd_att;
  buff->st_nlink = ftmp.fd_link;

  buff->st_ino   = fopt._sgr_fdpsn;
  buff->st_dev   = fopt._sgr_dvt;

  return(0);
}

/*
 * s t a t
 */	
int stat(filename, buff)
  char        *filename;
  struct stat *buff;
{
  register int i, ret;

  if ((i = open(filename, S_IREAD)) < 0)
    if ((i = open(filename, S_IFDIR | S_IREAD)) < 0)
      return(-1);

  ret = fstat(i, buff);
  close(i);

  return(ret);
}

/*
	unix library functions mist in OSK
	Author: Peter Reinig
*/

/* NOTE: this version of link() is only capable of renaming files, not true
 * UNIX-style linking.  That's okay, though, because elvis only uses it for
 * renaming.
 */
link(from,to)
char *from,*to;
{
	char *buffer;
	int status;
	char *malloc();

	if ((buffer = malloc(strlen(from) + strlen(to) + 12)) == NULL)
		return -1;
	sprintf(buffer,"rename %s %s\n",from,to);
	status = system(buffer);
	free(buffer);
	return status;
}

typedef (*procref)();
#define MAX_SIGNAL 10

extern exit();

static int (*sig_table[MAX_SIGNAL])();
static int _sig_install = 0;

sig_handler(sig)
int sig;
{
	if ((int) sig_table[sig] > MAX_SIGNAL)
		sig_table[sig](sig);
}

procref signal(sig,func)
int sig;
int (*func)();
{
	int i, (*sav)();

	if (!_sig_install) {
		for (i=0; i < MAX_SIGNAL; i++)
			sig_table[i] = exit;
		_sig_install = 1;
		intercept(sig_handler);
	}	
	sav = sig_table[sig];
	switch ((int) func) {
		case SIG_DFL : sig_table[sig] = exit;
					   break;
		case SIG_IGN : sig_table[sig] = 0;
					   break;
		default      : sig_table[sig] = func;
					   break;
	}
	return sav;
}

perror(str)
char *str;
{
	static int path = 0;
	if (!path && (path = open("/dd/sys/Errmsg", S_IREAD)) == -1) {
		fprintf(stderr,"Can\'t open error message file\n");
		path = 0;
	}
	if (str && *str) {
		fprintf(stderr,"%s: ",str);
		fflush(stderr);
	}
	prerr(path,(short) errno);
}

isatty(fd)
int fd;
{
	struct sgbuf buffer;
	char type;

	_gs_opt(fd,&buffer);
	type = buffer.sg_class;
	if (type == DT_SCF)
		return 1;
	else
		return 0;
}
#endif /* OSK */
SHAR_EOF
fi # end of overwriting check
if test -f 'ow.c'
then
	echo shar: will not over-write existing file "'ow.c'"
else
cat << \SHAR_EOF > 'ow.c'
#if OPENWINDOWS
#include <stdio.h>
#include <sys/time.h>
#include <signal.h>
#include <assert.h>

#include "config.h"
#include "vi.h"
#include "curses.h"
#include "elvis_cps.h"

#define PS_ESC '\200'
#define TTY 1 /* conditional for compiling in dumb tty support */

#ifndef RUNFILE
#define RUNFILE "/usr/local/lib/elvis.ps" /* server side PostScript code */
#endif

/* openwindows related elvis stuff */
/* author: mike hoegeman , mh@wlv.imsd.contel.com*/

struct ow_data Ow;

int     ElvisDestroyTag;
extern void destroyProc();

int     ElvisKeyTag;
extern void keyProc();

int     ElvisMiscTag;
extern void miscProc();

int     ElvisResizeTag;
extern void resizeProc();

int     ElvisDamageTag;
extern void damageProc();

int    *NewsTags[] =
{
    &ElvisDestroyTag,
    &ElvisKeyTag,
    &ElvisMiscTag,
    &ElvisResizeTag,
    &ElvisDamageTag,
    (int *) 0
};

void
destroyProc(tag, data)
    int     tag;
    caddr_t *data;
{
    ow_beep();
    wire_ReadTag();
    return;
}

void
damageProc(tag, data)
    int     tag;
    caddr_t *data;
{
    int     i;
    if (tag >= 0)
	i = wire_ReadTag();
    redraw(MARK_UNSET, FALSE);
    redraw(cursor, 1);
    refresh();
    return;
}

void
resizeProc(tag, data)
    int     tag;
    caddr_t *data;
{
    int     i;
    i = wire_ReadTag();
    LINES = Ow.wLINES = wire_ReadInt();
    COLS = Ow.wCOLS = wire_ReadInt();
    return;
}

void
keyProc(tag, data)
    int     tag;
    caddr_t *data;
{
    int     t;
    t = wire_ReadTag();
    Ow.keyFromNews = wire_ReadInt();
    wire_ExitNotifier();
    return;
}

int
ow_tmpstart(fname)
    char   *fname;
{
    int     x;
    x = tmpstart(fname);
    if (!Ow.tty)
    {
	ps_set_title(*fname == '\0' ? "Elvis" : fname);
	ps_flush_PostScript();
    }
    return x;
}

int
ow_beep()
{
#ifdef TTY
    if (Ow.tty)
    {
	beep();
	return 0;
    }
#   endif
    ps_beep();
    return 0;
}

int ow_rendermsg(msgtxt)
	char *msgtxt;
{
#ifdef TTY
	if (Ow.tty) return(rendermsg(msgtxt));
#endif
	ps_rendermsg(msgtxt);
	return 0;
}

int ow_appendmsg(old, new)
	char *old, *new;
{
	/* we could get 'old' from the window but since it's handy
	here on the c side we might as well use it */

#ifdef TTY
	if (Ow.tty) return(appendmsg(old, new));
#endif
	ps_appendmsg(old, new);
	return 0;
}

#define DBG
#ifdef DBG
static int SHOW_OUTPUT = -1;
sh(o, s, l, c)
    int     o;
    char   *s;
    int     l, c;
{
    putchar(o);
    while (l > 0)
    {
	putchar(*s++);
	l--;
    }
    putchar(c);
    return (0);
}

#define Print if (SHOW_OUTPUT) printf
#define SH if (SHOW_OUTPUT) sh
#else
#define Print
#define SH
#endif

int
ttywrite(buf, len)
    register char *buf;
    register int len;
{

#define binc() buf++,len--
#define flushstr() {\
    if (string) {\
	SH('(', string, buf-string, ')');\
	ps_ST(string, buf-string);\
	string = (char *)0;\
    }\
}

#ifdef DBG
    if (SHOW_OUTPUT < 0)
    {
	extern char *getenv();
	SHOW_OUTPUT = (getenv("SHOW_OUTPUT") == (char *) 0) ? 0 : 1;
    }
#endif

    /* this is where we bury all the smarts for display */
    if (!Ow.tty)
    {

#define Return(x) {\
	    if (cursor_disabled)\
	    {\
		ps_enable_cursor();\
		Print("{{true /EC C S }}\n");\
	    }\
	    ps_flush_PostScript();\
	    Print("------------------------------------------\n");\
	    return(x);\
	}

#define ErrReturn(x) {\
	    msg("Garbled output, is this a binary file?");\
	    Print("Garbled output, is this a binary file?");\
	    Return(x);\
	}

	char   *string = (char *) 0;
	int     rval = len;
	int     cursor_disabled = 0;

	wire_SetCurrent(Ow.mainWire);

	/*
	   if we have a fair chunk of stuff in our buffer, then just turn off
	   the cursor completely till we are all done then turn it back on at
	   the end. The turning on is done in 'Return'
	*/

	if (len > 30)
	{
	    cursor_disabled++;
	    Print("{{false /EC C S }}\n");
	    ps_disable_cursor();
	}

	while (len > 0)
	{
	    switch (*buf)
	    {
	    case PS_ESC:
		flushstr();
		{
		    register char *p;
		    binc();
		    p = buf;
		    while (*buf != PS_ESC)
		    {
			if (!*buf)
			    ErrReturn(rval);
			binc();
		    }
		    ps_pswrite(p, buf - p);
		    Print("\n");
		    SH('{', p, buf - p, '}');
		    Print("\n");
		}
		binc();
		break;

	    default:
		switch (*buf)
		{
		case '\r':
		    flushstr();
		    if (*(buf + 1) == '\n')
		    {
			Print("<crnl>\n");
			ps_CrNl();
			binc();
		    }
		    else
		    {
			Print("<cr>");
			ps_Cr();
		    }
		    break;
		case '\n':
		    flushstr();
		    if (*(buf + 1) == '\r')
		    {
			Print("<crnl>\n");
			ps_CrNl();
			binc();
		    }
		    else
		    {
			Print("<nl>\n");
			ps_Nl();
		    }
		    break;

		    /* move up one line */
		case '\0':
		    /* some binary file ?? , punt */
		    ErrReturn(rval);

		default:
		    if (!string)
			string = buf;
		    break;
		}
		binc();
		break;
	    }
	}
	flushstr();
	Return(rval);

#undef Return
#undef ErrReturn
    }
#ifdef TTY
    return (write(1, buf, len));
#else
    return -1;
#endif
}

int
ttyread(buf, len)
    char   *buf;
    int     len;
{
    int     rlen = 0;

#ifdef TTY
    if (Ow.tty) return read(0, buf, len);
#endif

    Ow.keyFromNews = -1;
    wire_EnterNotifier();
    if (Ow.keyFromNews < 0)
	return -1;
    else
    {
	*buf++ = Ow.keyFromNews;
	rlen++;
    }
    return rlen;
}

void
miscProc(tag, data)
    int     tag;
    caddr_t *data;
{
    return;
}

ow_vi()
{
    extern char *getenv();
    if (Ow.tty)
    {
	vi();
	return 0;
    }
    if (getenv("ELVIS_DEBUG") == (char *) 0 && Ow.background)
	background();
    vi();
    return;
}

ow_ex()
{
    ex();
    return 0;
}

int
ow_suspend_curses()
{
#ifdef TTY
    if (Ow.tty)
    {
	suspend_curses();
	return 0;
    }
#endif
#ifndef NO_CURSORSHAPE
    if (has_CQ)
    {
	do_CQ();
    }
#endif
    return 0;
}

int
ow_resume_curses(quietly)
    int     quietly;
{
    /* If we're supposed to quit quietly, then we're done */
    void    (*proc) ();

# ifdef TTY
    if (Ow.tty)
    {
	resume_curses(quietly);
	return 0;
    }
# endif

    if (quietly)
	return;
    proc = signal(SIGINT, SIG_IGN);
    {
	move(LINES - 1, 0);
	do_SO();
	qaddstr("[Press <RETURN> to continue]");
	do_SE();
	refresh();
	ttyread(kbuf, 20);	/* in RAW mode, so <20 is very likely */
	if (kbuf[0] == ':')
	{
	    mode = MODE_COLON;
	    addch('\n');
	    refresh();
	}
	else
	{
	    mode = MODE_VI;
	    redraw(MARK_UNSET, FALSE);
	}
	exwrote = FALSE;
    }
    signal(SIGINT, proc);
    return 0;
}

#define register_tag(tag_name, tag_value, tag_proc, proc_data)\
    wire_RegisterTag(tag_value, tag_proc, proc_data);\
    ps_RegisterTag(tag_name, tag_value);

int
ow_initscr()
{
    extern char *getenv();
    extern void starttcap();
    char *server;

#ifdef TTY
    if (Ow.tty)
    {
	initscr();
	return 0;
    }
#endif

    if (!(server = getenv("NEWSSERVER"))) 
    {
	server = getenv("DISPLAY");
	if (!server || 
	    !strncmp(server, "unix", strlen("unix")) || 
	    !strncmp(server, "unix", strlen("UNIX")))
	    server = "localhost";
    }

    Ow.mainWire = wire_Open(server);
    if (Ow.mainWire == wire_INVALID_WIRE)
    {
	/* wire_Perror("elvis"); */
	Ow.tty = 1;
	initscr();
	return 0;
    }
    else
    {
	static int firstime = 1;
	if (firstime)
	{
	    wire_ReserveTags(100);
	    wire_AllocateNamedTags(NewsTags);
	    register_tag("ElvisDestroyTag", ElvisDestroyTag, destroyProc, NULL);
	    register_tag("ElvisKeyTag", ElvisKeyTag, keyProc, NULL);
	    register_tag("ElvisMiscTag", ElvisMiscTag, miscProc, NULL);
	    register_tag("ElvisResizeTag", ElvisResizeTag, resizeProc, NULL);
	    register_tag("ElvisDamageTag", ElvisDamageTag, damageProc, NULL);

	    wire_SetCurrent(Ow.mainWire);
	    ps_RunFile(RUNFILE);
	    ps_flush_PostScript();
	}
	else
	    firstime = !firstime;
	stdscr = kbuf;
	starttcap();
	return 0;
    }
}

int
ow_getsize(sig)
    int     sig;
{
    extern char o_columns[], o_lines[];

#ifdef TTY
    if (Ow.tty)
	return getsize(sig);
#endif

    /* copy the new values into Elvis' options */
    *o_columns = COLS = Ow.wCOLS;
    *o_lines = LINES = Ow.wLINES;
    return 0;
}

/* termcap */

/*ARGSUSED*/
int
ow_tgetent(bp, name)
    char   *bp;	/* buffer for entry */
    char   *name;	/* name of the entry */
{
#ifdef TTY
    if (Ow.tty) return(tgetent(bp,name));
#endif

    *bp = '\0';
    return 1;
}

#define CAP(str) CAP2((str)[0], (str)[1])
#define CAP2(a,b) (((a) << 8) + ((b) & 0xff))

int
ow_tgetnum(id)
    char   *id;
{
#ifdef TTY
    if (Ow.tty) return(tgetnum(id));
#endif

    switch (CAP(id))
    {
	/* # lines of lines on screen or page */
    case CAP2('l', 'i'):
	return 44;
	/* # of columns in a line */
    case CAP2('c', 'o'):
	return 80;
	/* # of garbage chars left by so or se */
    case CAP2('s', 'g'):
	return 0;
	/* # of garbage chars left by us or ue */
    case CAP2('u', 'g'):
	return 0;
    default:
	return -1;
    }
}

int
ow_tgetflag(id)
    char   *id;
{
#ifdef TTY
    if (Ow.tty) return(tgetflag(id));
#endif

    switch (CAP(id))
    {
	/* terminal has **no** automatic margins */
    case CAP2('a', 'm'):
	return 0;
	/* terminal has backspace capability */
    case CAP2('b', 's'):
	return 1;
	/* safe to move while in insert mode */
    case CAP2('m', 'i'):
	return 1;
    default:
	return 0;
    }
}

#define VAL2(v,a)      (a)
#define VAL3(a,b,c) (a)

char   *
ow_tgetstr(id, bp)
    char   *id;
    char  **bp;	/* pointer to pointer to buffer - ignored */
{
#ifdef TTY
    extern char *tgetstr();
    if (Ow.tty) return(tgetstr(id,bp));
#endif

/* send method id to 'C', 'S' is a shorthand for 'send' */
#define RETURN(FIRSTLETTER,SECONDLETTER) {\
    static char tp[] = {\
	PS_ESC,\
	    '/', FIRSTLETTER, SECONDLETTER, ' ',\
	    'C',' ','S',' ',\
	PS_ESC\
    };\
    return tp;\
}

    switch (CAP(id))
    {
	/* clear to end of line */
    case CAP2('c', 'e'):
	RETURN('c', 'e');

	/* clear to end of screen and home cursor */
    case CAP2('c', 'l'):
	RETURN('c', 'l');

	/* scroll text down */
    case CAP2('s', 'r'):
	RETURN('s', 'r');

	/* cursor invisible */
    case CAP2('v', 'i'):
	RETURN('v', 'i');

	/* cursor visible */
    case CAP2('v', 'e'):
	RETURN('v', 'e');

/*-
    case CAP2('a', 'l'):
	RETURN('a', 'l');
    case CAP2('d', 'l'):
       RETURN('d', 'l');
*/
	/* start bold */
    case CAP2('s', 'o'):
	RETURN('s', 'o');

	/* end bold */
    case CAP2('s', 'e'):
	RETURN('s', 'e');

	/* start underline */
    case CAP2('u', 's'):
	RETURN('u', 's');
	/* end underline */
    case CAP2('u', 'e'):
	RETURN('u', 'e');

    /* ------ cursor style change commands --------*/
    /* change to normal cursor */
    case CAP2('c', 'Q'):
	RETURN('c', 'Q');
    /* change to ex command entry cursor */
    case CAP2('c', 'X'):
	RETURN('c', 'X');
    /* change to vi command mode cursor */
    case CAP2('c', 'V'):
	RETURN('c', 'V');
    /* change to vi input mode cursor */
    case CAP2('c', 'I'):
	RETURN('c', 'I');
    /* change to vi replace mode cursor */
    case CAP2('c', 'R'):
	RETURN('c', 'R');

#if 0
    case CAP2('V', 'B'):
	RETURN('V', 'B');
    case CAP2('V', 'b'):
	RETURN('V', 'b');

    case CAP2('d', 'o'):
	RETURN('d', 'o');
    case CAP2('n', 'd'):
	RETURN('n', 'd');

    case CAP2('t', 'i'):
	RETURN('t', 'i');
    case CAP2('t', 'e'):
	return "";

    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('H', 'M'):
	return "#G";
    case CAP2('E', 'N'):
	return "#O";
    case CAP2('P', 'U'):
	return "#I";
    case CAP2('P', 'D'):
#endif

	/* move up one line */
    case CAP2('u', 'p'):
	RETURN('u', 'p');

	/* backspace */
    case CAP2('b', 'c'):
	RETURN('b', 'c');

	/* cursor motion */
    case CAP2('c', 'm'):
	{
	    static char *tp = "\200%d %d /cm C S \200";
	    return tp;
	}

    default:
	return (char *) 0;
    }
}

/*ARGSUSED*/
char   *
ow_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];
#ifdef TTY
    extern char *tgoto();
    if (Ow.tty) return (tgoto(cm, destcol, destrow));
#endif

    sprintf(buf, "\200%d %d /cm C S \200", destcol, destrow);
    return buf;
}

    /* declaring cp a register makes gcc blow up ?? ! ?? */
void
ow_tputs(cp, affcnt, outfn)
    char *cp;
    int     affcnt;	/* number of affected lines -- ignored */
    int     (*outfn) ();	/* the output function */
{
#ifdef TTY
    if (Ow.tty) { tputs(cp,affcnt,outfn); return; };
#endif

    while (*cp != '\0')
    {
	(*outfn)(*cp);
	cp++;
    }
    return;
}


int
ow_scrolldown(l, y, x)
    long    l;
    int     y, x;
{
    extern void drawtext();
    REG char *text;
    char    buf[80];

#ifdef TTY
    if (Ow.tty) return(scrolldown(l, y, x));
#endif

    /*- 
	do ...
	    - a move(y, x)
	    - topline-l do_SR()'s 
	    - a move(topline-l-1, 0);
    */
    sprintf( buf, "\200 %d %d /cm C S %d /SR C S %d %d /cm C S \200", 
	     y, x,
	     (topline-l),
	     (topline-1)-1, 0 );

    qaddstr(buf);

    move((topline - l) - 1, 0);
    while (l < topline)
    {
	topline--;
	text = fetchline(topline);
	drawtext(text, FALSE);
	do_UP();
	do_UP();
    }
    move(LINES - 1, 0);
    clrtoeol();
    return 0;
}

int 
ow_scrollup(l, y, x)
    long l;
    int y,x;
{
	REG char *text;
	char buf[80];
	
#ifdef TTY
	if (Ow.tty) return(scrollup(l, y, x));
#endif

	move(y,x);
	clrtoeol();
	sprintf(buf, "\200 %d /RSR C S \200", l-(botline));
	qaddstr(buf);
	move(y - (l-botline), 0);
	while (l > botline)
	{
		topline++; /* <-- also adjusts botline */
		text = fetchline(botline);
		drawtext(text, FALSE);
	}
	return 0;
}


/*+
    X11/NeWS specific flags:

    -tty 
	
	Force elvis to use the plain termcap interface and not the
	window interface. elvis will revert to the termcap interface
	automatically if it cannot connect to the X11/NeWS server.

    -bkg
	
	Have elvis run in the background, disassociated from the parent
	process. If "elvis" is the name of this program this is the
	default.

    +bkg

	Force elvis to run in the forground process. If the name of the
	program is not elvis ("vi" for example). then this is the default.

-*/


int
ow_customflags(argv, argc_p)
    char *argv[];
    int *argc_p;
{
    int x, e;
#   if MALLOC_DEBUG
     malloc_debug(MALLOC_DEBUG);
#   endif

#   define eq(a,b) (!strcmp(a,b))
#   define skip() x++
#   define shift() \
    {\
	int i;\
	for (e = *argc_p, i=x; i<e; i++)\
	    argv[i] = argv[i+1];\
	(*argc_p)--;\
    }
    ow_data_init(&Ow);
    
#   ifndef NO_AUTO_BACKGROUND
    {
	extern char *strrchr();
	char *p  = strchr(argv[0], '/');
	p = p ? p : argv[0];
	if (eq(argv[0], "elvis"))
	    Ow.background = 1;
    }
#   endif

    
    for(x=1; x<*argc_p;)
    {
	if (*argv[x] != '-' && *argv[x] != '+')
	    skip();
	else
	{
	    if (eq(argv[x], "-bkg"))
	    {
		Ow.background = 1;
		shift();
	    }
	    else if (eq(argv[x], "+bkg"))
	    {
		Ow.background = 0;
		shift();
	    }
	    else if (eq(argv[x], "-tty"))
	    {
		Ow.tty = 1;
		shift();
	    }
	    else
	    {
		skip();
	    }
	}
    }
    return 0;

#   undef eq
#   undef skip
#   undef shift
}

#endif

#if BSD
#include <sys/file.h>
#include <sgtty.h>
int background()
{
    int     fd, p;
    p = fork();
    switch (p)
    {
    case -1:
	perror("fork");
	exit(1);
    default:
	exit(0);
    case 0:
	break;
    }
    (void) setpgrp(0, getpid());
    if ((fd = open("/dev/tty", O_RDWR)) >= 0)
    {
	ioctl(fd, TIOCNOTTY, 0);
	close(fd);
    }
    return 0;
}
#endif
SHAR_EOF
fi # end of overwriting check
if test -f 'ow_tcap.c'
then
	echo shar: will not over-write existing file "'ow_tcap.c'"
else
cat << \SHAR_EOF > 'ow_tcap.c'
#include <varargs.h>
#include "curses.h"

/* implemenatation of the wprintw curses function */

void ow_printw(va_alist)
	va_dcl
{
    char *fmt;
    WINDOW *win;
    va_list args;
    char tmpbuf[512];

    va_start(args);
	win = va_arg(args, char *);
	fmt = va_arg(args, char *);
	vsprintf(tmpbuf, fmt, args);
    va_end(args);
    waddstr(win, tmpbuf);
    return;
}
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0

mh@roger.imsd.contel.com (Mike H.) (01/11/91)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	pc.c
#	recycle.c
#	redraw.c
#	ref.c
#	refont.c
#	regexp.c
#	regsub.c
# This archive created: Thu Jan 10 17:21:09 1991
export PATH; PATH=/bin:$PATH
if test -f 'pc.c'
then
	echo shar: will not over-write existing file "'pc.c'"
else
cat << \SHAR_EOF > 'pc.c'
/* pc.c */

/* Author:
 *	Guntram Blohm
 *	Buchenstrasse 19
 *	7904 Erbach, West Germany
 *	Tel. ++49-7305-6997
 *	sorry - no regular network connection
 */

/* This file implements the ibm pc bios interface. See IBM documentation
 * for details.
 * If TERM is set upon invocation of elvis, this code is ignored completely,
 * and the standard termcap functions are used, thus, even not-so-close
 * compatibles can run elvis. For close compatibles however, bios output
 * is much faster (and permits reverse scrolling, adding and deleting lines,
 * and much more ansi.sys isn't capable of). GB.
 */

#include "config.h"
#include "vi.h"

#if MSDOS

#include <dos.h>

static void video();

/* vmode contains the screen attribute index and is set by attrset.*/

int vmode;

/* The following array contains attribute definitions for
 * color/monochrome attributes. Screen selects one of the sets.
 * Maybe i'll put them into elvis options one day.
 */

static int screen;
static char attr[2][5] =
{
	/*	:se:	:so:	:VB:	:ul:	:as:	*/
	{	0x1f,	0x1d,	0x1e,	0x1a,	0x1c,	},	/* color */
	{	0x07,	0x70,	0x0f,	0x01,	0x0f,	},	/* monochrome */
};

/*
 * bios interface functions for elvis - pc version
 */

/* cursor up: determine current position, decrement row, set position */

void v_up()
{
	int dx;
	video(0x300,(int *)0,&dx);
	dx-=0x100;
	video(0x200,(int *)0,&dx);
}

#ifndef NO_CURSORSHAPE
/* cursor big: set begin scan to end scan - 4 */
void v_cb()
{
	int cx;
	video(0x300, &cx, (int *)0);
	cx=((cx&0xff)|(((cx&0xff)-4)<<8));
	video(0x100, &cx, (int *)0);
}

/* cursor small: set begin scan to end scan - 1 */
void v_cs()
{
	int cx;
	video(0x300, &cx, (int *)0);
	cx=((cx&0xff)|(((cx&0xff)-1)<<8));
	video(0x100, &cx, (int *)0);
}
#endif

/* clear to end: get cursor position and emit the aproppriate number
 * of spaces, without moving cursor.
 */
 
void v_ce()
{
	int cx, dx;
	video(0x300,(int *)0,&dx);
	cx=COLS-(dx&0xff);
	video(0x920,&cx,(int *)0);
}

/* clear screen: clear all and set cursor home */

void v_cl()
{
	int cx=0, dx=((LINES-1)<<8)+COLS-1;
	video(0x0600,&cx,&dx);
	dx=0;
	video(0x0200,&cx,&dx);
}

/* clear to bottom: get position, clear to eol, clear next line to end */

void v_cd()
{
	int cx, dx, dxtmp;
	video(0x0300,(int *)0,&dx);
	dxtmp=(dx&0xff00)|(COLS-1);
	cx=dx;
	video(0x0600,&cx,&dxtmp);
	cx=(dx&0xff00)+0x100;
	dx=((LINES-1)<<8)+COLS-1;
	video(0x600,&cx,&dx);
}

/* add line: scroll rest of screen down */

void v_al()
{
	int cx,dx;
	video(0x0300,(int *)0,&dx);
	cx=(dx&0xff00);
	dx=((LINES-1)<<8)+COLS-1;
	video(0x701,&cx,&dx);
}

/* delete line: scroll rest up */

void v_dl()
{
	int cx,dx;
	video(0x0300,(int *)0,&dx);
	cx=(dx&0xff00)/*+0x100*/;
	dx=((LINES-1)<<8)+COLS-1;
	video(0x601,&cx,&dx);
}

/* scroll reverse: scroll whole screen */

void v_sr()
{
	int cx=0, dx=((LINES-1)<<8)+COLS-1;
	video(0x0701,&cx,&dx);
}

/* set cursor */

void v_move(x,y)
	int x, y;
{
	int dx=(y<<8)+x;
	video(0x200,(int *)0,&dx);
}

/* put character: set attribute first, then execute char.
 * Also remember if current line has changed.
 */

int v_put(ch)
	int ch;
{
	int cx=1;
	ch&=0xff;
	if (ch>=' ')
		video(0x900|ch,&cx,(int *)0);
	video(0xe00|ch,(int *)0, (int *)0);
	if (ch=='\n')
	{	exwrote = TRUE;
		video(0xe0d, (int *)0, (int *)0);
	}
	return ch;
}

/* determine number of screen columns. Also set attrset according
 * to monochrome/color screen.
 */

int v_cols()
{
	union REGS regs;
	regs.h.ah=0x0f;
	int86(0x10, &regs, &regs);
	if (regs.h.al==7)			/* monochrome mode ? */
		screen=1;
	else
		screen=0;
	return regs.h.ah;
}

/* Getting the number of rows is hard. Most screens support 25 only,
 * EGA/VGA also support 43/50 lines, and some OEM's even more.
 * Unfortunately, there is no standard bios variable for the number
 * of lines, and the bios screen memory size is always rounded up
 * to 0x1000. So, we'll really have to cheat.
 * When using the screen memory size, keep in mind that each character
 * byte has an associated attribute byte.
 *
 * uses:	word at 40:4c contains	memory size
 *		byte at 40:84 		# of rows-1 (sometimes)
 *		byte at	40:4a		# of columns
 */

int v_rows()
{
	int line, oldline;

	/* screen size less then 4K? then we have 25 lines only */

	if (*(int far *)(0x0040004cl)<=4096)
		return 25;

	/* VEGA vga uses the bios byte at 0x40:0x84 for # of rows.
	 * Use that byte, if it makes sense together with memory size.
	 */

	if ((((*(unsigned char far *)(0x0040004aL)*2*
		(*(unsigned char far *)(0x00400084L)+1))+0xfff)&(~0xfff))==
		*(unsigned int far *)(0x0040004cL))
			return *(unsigned char far *)(0x00400084L)+1;

	/* uh oh. Emit '\n's until screen starts scrolling. */

	v_move(oldline=0, 0);
	for (;;)
	{
		video(0xe0a,(int *)0,(int *)0);
		video(0x300,(int *)0,&line);
		line>>=8;
		if (oldline==line)
			return line+1;
		oldline=line;	
	}
}

/* the REAL bios interface -- used internally only. */

static void video(ax, cx, dx)
	int ax, *cx, *dx;
{
	union REGS regs;

	regs.x.ax=ax;
	if ((ax&0xff00)==0x600 || (ax&0xff00)==0x700)
		regs.h.bh=attr[screen][vmode];
	else
	{
		regs.h.bh=0;
		regs.h.bl=attr[screen][vmode];
	}
	if (cx) regs.x.cx=*cx;
	if (dx) regs.x.dx=*dx;
	int86(0x10, &regs, &regs);
	if (dx) *dx=regs.x.dx;
	if (cx) *cx=regs.x.cx;
}

/* The following function determines which character is used for
 * commandline-options by command.com. This system call is undocumented
 * and valid for versions < 4.00 only.
 */
 
int switchar()
{
	union REGS regs;
	regs.x.ax=0x3700;
	int86(0x21, &regs, &regs);
	return regs.h.dl;
}

#endif
SHAR_EOF
fi # end of overwriting check
if test -f 'recycle.c'
then
	echo shar: will not over-write existing file "'recycle.c'"
else
cat << \SHAR_EOF > 'recycle.c'
/* 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
SHAR_EOF
fi # end of overwriting check
if test -f 'redraw.c'
then
	echo shar: will not over-write existing file "'redraw.c'"
else
cat << \SHAR_EOF > 'redraw.c'
/* 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.
 */
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');
	}
}


#if OPENWINDOWS
# define NUDGE_COST (Ow.tty ? 1 : 5) /* move is always cheaper in NeWS */
#else
# define NUDGE_COST 5
#endif

#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 < NUDGE_COST)
		{
			/* 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)
		{
			Scrolldown(l, 0, 0);
		}
		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
		)
		{
			Scrollup(l, LINES-1, 0);
			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 */

			/* smartlno is not always adjusted right. when it is
			fixed take out the stuff around the HACK comments */

			if (l == smartlno /* HACK */ && 0 /* HACK */)
			{
#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;
}

int scrolldown(l,y,x)
    long l;
    int y, x;
{
	char *text;
	move(y,x);
	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();
	return 0;
}

int scrollup(l, y, x)
    long l;
    int y,x;
{
	char *text;
	move(y,x);
	clrtoeol();
	while (l > botline)
	{
		topline++; /* <-- also adjusts botline */
		text = fetchline(botline);
		drawtext(text, FALSE);
	}
	return 0;
}
SHAR_EOF
fi # end of overwriting check
if test -f 'ref.c'
then
	echo shar: will not over-write existing file "'ref.c'"
else
cat << \SHAR_EOF > 'ref.c'
/* ref.c */

/* Author:
 *	Steve Kirkendall
 *	14407 SW Teal Blvd. #C
 *	Beaverton, OR 97005
 *	kirkenda@cs.pdx.edu
 */


/* This program looks up the declarations of library functions. */

#include <stdio.h>

/* This is the list of files that are searched. */
#ifdef OSK
char *refslist[] = {
	"refs",
	"/dd/usr/src/lib/refs",
	"../lib/refs",
	"/dd/usr/local/lib/refs",
};
#else
char *refslist[] = {
	"refs",
	"/usr/src/lib/refs",
	"../lib/refs",
	"/usr/local/lib/refs"
};
#endif
#define NREFS	(sizeof refslist / sizeof(char *))

main(argc, argv)
	int	argc;
	char	**argv;
{
	int	i;	/* used to step through the refslist */

	/* make sure our arguments are OK */
	if (argc != 2)
	{
		fprintf(stderr, "usage: %s function_name\n", *argv);
		exit(2);
	}

	/* check for the function in each database */
	for (i = 0; i < NREFS; i++)
	{
		if (lookinfor(refslist[i], argv[1]))
		{
			exit(0);
		}
	}

	fprintf(stderr, "%s: don't know about %s\n", argv[0], argv[1]);
	exit(2);
}


/* This function checks a single file for the function.  Returns 1 if found */
int lookinfor(filename, func)
	char	*filename;	/* name of file to look in */
	char	*func;		/* name of function to look for */
{
	FILE	*fp;	/* stream used to access the database */
	char	linebuf[300];
		/* NOTE: in actual use, the first character of linebuf is */
		/* set to ' ' and then we use all EXCEPT the 1st character */
		/* everywhere in this function.  This is because the func */
		/* which examines the linebuf could, in some circumstances */
		/* examine the character before the used part of linebuf; */
		/* we need to control what happens then.		   */


	/* open the database file */
	fp = fopen(filename, "r");
	if (!fp)
	{
		return 0;
	}

	/* find the desired entry */
	*linebuf = ' ';
	do
	{
		if (!fgets(linebuf + 1, (sizeof linebuf) - 1, fp))
		{
			fclose(fp);
			return 0;
		}
	} while (!desired(linebuf + 1, func));

	/* print it */
	do
	{
		fputs(linebuf + 1, stdout);
	} while (fgets(linebuf + 1, sizeof linebuf, fp) && linebuf[1] == '\t');

	/* cleanup & exit */
	fclose(fp);
	return 1;
}


/* This function checks to see if a given line is the first line of the */
/* entry the user wants to see.  If it is, return non-0 else return 0   */
desired(line, word)
	char	*line;	/* the line buffer */
	char	*word;	/* the string it should contain */
{
	static	wlen = -1;/* length of the "word" variable's value */
	register char *scan;

	/* if this line starts with a tab, it couldn't be desired */
	if (*line == '\t')
	{
		return 0;
	}

	/* if we haven't found word's length yet, do so */
	if (wlen < 0)
	{
		wlen = strlen(word);
	}

	/* search for the word in the line */
	for (scan = line; *scan != '('; scan++)
	{
	}
	while (*--scan == ' ')
	{
	}
	scan -= wlen;
	if (scan < line - 1 || *scan != ' ' && *scan != '\t' && *scan != '*')
	{
		return 0;
	}
	scan++;
	return !strncmp(scan, word, wlen);
}
SHAR_EOF
fi # end of overwriting check
if test -f 'refont.c'
then
	echo shar: will not over-write existing file "'refont.c'"
else
cat << \SHAR_EOF > 'refont.c'
/* refont.c */

/* Author:
 *	Steve Kirkendall
 *	14407 SW Teal Blvd. #C
 *	Beaverton, OR 97005
 *	kirkenda@cs.pdx.edu
 */


/* This file contains the complete source code for the refont program */

/* The refont program converts font designations to the format of your choice.
 * Known font formats are:
 *	-b	overtype notation, using backspaces
 *	-c	overtype notation, using carriage returns
 *	-d	the "dot" command notation used by nroff (doesn't work well)
 *	-e	Epson-compatible line printer codes
 *	-f	the "\f" notation
 *	-x	none (strip out the font codes)
 *
 * Other flags are:
 *	-I	recognize \f and dot notations in input
 *	-F	output a formfeed character between files
 */

#include <stdio.h>
#include "config.h"

/* the program name, for diagnostics */
char	*progname;

/* remembers which output format to use */
int outfmt = 'f';

/* do we allow "dot" and "backslash-f" on input? */
int infmt = 0;

/* do we insert formfeeds between input files? */
int add_form_feed = 0;

main(argc, argv)
	int	argc;	/* number of command-line args */
	char	**argv;	/* values of the command line args */
{
	FILE	*fp;
	int	i, j;
	int	retcode;

	progname = argv[0];

	/* parse the flags */
	i = 1;
	if (i < argc && argv[i][0] == '-' && argv[i][1])
	{
		for (j = 1; argv[i][j]; j++)
		{
			switch (argv[i][j])
			{
			  case 'b':
#if !OSK
			  case 'c':
#endif
			  case 'd':
			  case 'e':
			  case 'f':
			  case 'x':
				outfmt = argv[i][j];
				break;

			  case 'I':
				infmt = 'I';
				break;

			  case 'F':
				add_form_feed = 1;
				break;

			  default:
				usage();
			}
		}
		i++;
	}

	retcode = 0;
	if (i == argc)
	{
		/* probably shouldn't read from keyboard */
		if (isatty(fileno(stdin)))
		{
			usage();
		}

		/* no files named, so use stdin */
		refont(stdin);
	}
	else
	{
		for (; i < argc; i++)
		{
			fp = fopen(argv[i], "r");
			if (!fp)
			{
				perror(argv[i]);
				retcode = 1;
			}
			else
			{
				refont(fp);
				if (i < argc - 1 && add_form_feed)
				{
					putchar('\f');
				}
				fclose(fp);
			}
		}
	}

	exit(retcode);
}

usage()
{
	fputs("usage: ", stderr);
	fputs(progname, stderr);
	fputs(" [-bcdefxFI] [filename]...\n", stderr);
	exit(2);
}

/* This function does the refont thing to a single file */
/* I apologize for the length of this function.  It is gross. */
refont(fp)
	FILE	*fp;
{
	char	textbuf[1025];	/* chars of a line of text */
	char	fontbuf[1025];	/* fonts of chars in fontbuf */
	int	col;		/* where we are in the line */
	int	font;		/* remembered font */
	int	more;		/* more characters to be output? */
	int	ch;

	/* reset some stuff */
	for (col = sizeof fontbuf; --col >= 0; )
	{
		fontbuf[col] = 'R';
		textbuf[col] = '\0';
	}
	col = 0;
	font = 'R';

	/* get the first char - quit if eof */
	while ((ch = getc(fp)) != EOF)
	{
		/* if "dot" command, read the rest of the command */
		if (infmt == 'I' && ch == '.' && col == 0)
		{

			textbuf[col++] = '.';
			textbuf[col++] = getc(fp);
			textbuf[col++] = getc(fp);
			textbuf[col++] = ch = getc(fp);

			/* is it a font line? */
			font = 0;
			if (textbuf[1] == 'u' && textbuf[2] == 'l')
			{
				font = 'U';
			}
			else if (textbuf[1] == 'b' && textbuf[2] == 'o')
			{
				font = 'B';
			}
			else if (textbuf[1] == 'i' && textbuf[2] == 't')
			{
				font = 'I';
			}

			/* if it is, discard the stuff so far but remember font */
			if (font)
			{
				while (col > 0)
				{
					textbuf[--col] = '\0';
				}
			}
			else /* some other format line - write it literally */
			{
				while (ch != '\n')
				{
					textbuf[col++] = ch = getc(fp);
				}
				fputs(textbuf, fp);
				while (col > 0)
				{
					textbuf[--col] = '\0';
				}
			}
			continue;
		}

		/* is it a "\f" formatted font? */
		if (infmt == 'I' && ch == '\\')
		{
			ch = getc(fp);
			if (ch == 'f')
			{
				font = getc(fp);
			}
			else
			{
				textbuf[col++] = '\\';
				textbuf[col++] = ch;
			}
			continue;
		}

		/* is it an Epson font? */
		if (ch == '\033')
		{
			ch = getc(fp);
			switch (ch)
			{
			  case '4':
				font = 'I';
				break;

			  case 'E':
			  case 'G':
				font = 'B';
				break;

			  case '5':
			  case 'F':
			  case 'H':
				font = 'R';
				break;

			  case '-':
				font = (getc(fp) & 1) ? 'U' : 'R';
				break;
			}
			continue;
		}

		/* control characters? */
		if (ch == '\b')
		{
			if (col > 0)
				col--;
			continue;
		}
		else if (ch == '\t')
		{
			do
			{
				if (textbuf[col] == '\0')
				{
					textbuf[col] = ' ';
				}
				col++;
			} while (col & 7);
			continue;
		}
#if !OSK
		else if (ch == '\r')
		{
			col = 0;
			continue;
		}
#endif
		else if (ch == ' ')
		{
			if (textbuf[col] == '\0')
			{
				textbuf[col] = ' ';
				fontbuf[col] = font;
				col++;
			}
			continue;
		}

		/* newline? */
		if (ch == '\n')
		{
			more = 0;
			for (col = 0, font = 'R'; textbuf[col]; col++)
			{
				if (fontbuf[col] != font)
				{
					switch (outfmt)
					{
					  case 'd':
						putchar('\n');
						switch (fontbuf[col])
						{
						  case 'B':
							fputs(".bo\n", stdout);
							break;

						  case 'I':
							fputs(".it\n", stdout);
							break;

						  case 'U':
							fputs(".ul\n", stdout);
							break;
						}
						while (textbuf[col] == ' ')
						{
							col++;
						}
						break;

					  case 'e':
						switch (fontbuf[col])
						{
						  case 'B':
							fputs("\033E", stdout);
							break;

						  case 'I':
							fputs("\0334", stdout);
							break;

						  case 'U':
							fputs("\033-1", stdout);
							break;

						  default:
							switch (font)
							{
							  case 'B':
								fputs("\033F", stdout);
								break;

							  case 'I':
							  	fputs("\0335", stdout);
							  	break;

							  case 'U':
							  	fputs("\033-0", stdout);
							  	break;
							}
						}
						break;

					  case 'f':
					  	putchar('\\');
					  	putchar('f');
					  	putchar(fontbuf[col]);
					  	break;
					}

					font = fontbuf[col];
				}

				if (fontbuf[col] != 'R' && textbuf[col] != ' ')
				{
					switch (outfmt)
					{
					  case 'b':
						if (fontbuf[col] == 'B')
						{
							putchar(textbuf[col]);
						}
						else
						{
							putchar('_');
						}
						putchar('\b');
						break;
#if !OSK
					  case 'c':
					  	more = col + 1;
					  	break;
#endif
					}
				}

				putchar(textbuf[col]);
			}

#if !OSK
			/* another pass? */
			if (more > 0)
			{
				putchar('\r');
				for (col = 0; col < more; col++)
				{
					switch (fontbuf[col])
					{
					  case 'I':
					  case 'U':
						putchar('_');
						break;

					  case 'B':
						putchar(textbuf[col]);
						break;

					  default:
						putchar(' ');
					}
				}
			}
#endif /* not OSK */

			/* newline */
			if (font != 'R')
			{
				switch (outfmt)
				{
				  case 'f':
					putchar('\\');
					putchar('f');
					putchar('R');
					break;

				  case 'e':
					switch (font)
					{
					  case 'B':
						fputs("\033F", stdout);
						break;

					  case 'I':
					  	fputs("\0335", stdout);
					  	break;

					  case 'U':
					  	fputs("\033-0", stdout);
					  	break;
					}
				}
			}
			putchar('\n');

			/* reset some stuff */
			for (col = sizeof fontbuf; --col >= 0; )
			{
				fontbuf[col] = 'R';
				textbuf[col] = '\0';
			}
			col = 0;
			font = 'R';
			continue;
		}

		/* normal character */
		if (font != 'R')
		{
			textbuf[col] = ch;
			fontbuf[col] = font;
		}
		else if (textbuf[col] == '_')
		{
			textbuf[col] = ch;
			fontbuf[col] = 'U';
		}
		else if (textbuf[col] && textbuf[col] != ' ' && ch == '_')
		{
			fontbuf[col] = 'U';
		}
		else if (textbuf[col] == ch)
		{
			fontbuf[col] = 'B';
		}
		else
		{
			textbuf[col] = ch;
		}
		col++;
	}
}
SHAR_EOF
fi # end of overwriting check
if test -f 'regexp.c'
then
	echo shar: will not over-write existing file "'regexp.c'"
else
cat << \SHAR_EOF > 'regexp.c'
/* regexp.c */

/* This file contains the code that compiles regular expressions and executes
 * them.  It supports the same syntax and features as vi's regular expression
 * code.  Specifically, the meta characters are:
 *	^	matches the beginning of a line
 *	$	matches the end of a line
 *	\<	matches the beginning of a word
 *	\>	matches the end of a word
 *	.	matches any single character
 *	[]	matches any character in a character class
 *	\(	delimits the start of a subexpression
 *	\)	delimits the end of a subexpression
 *	*	repeats the preceding 0 or more times
 * NOTE: You cannot follow a \) with a *.
 *
 * The physical structure of a compiled RE is as follows:
 *	- First, there is a one-byte value that says how many character classes
 *	  are used in this regular expression
 *	- Next, each character class is stored as a bitmap that is 256 bits
 *	  (32 bytes) long.
 *	- A mixture of literal characters and compiled meta characters follows.
 *	  This begins with M_BEGIN(0) and ends with M_END(0).  All meta chars
 *	  are stored as a \n followed by a one-byte code, so they take up two
 *	  bytes apiece.  Literal characters take up one byte apiece.  \n can't
 *	  be used as a literal character.
 *
 * If NO_MAGIC is defined, then a different set of functions is used instead.
 * That right, this file contains TWO versions of the code.
 */

#include <setjmp.h>
#include <ctype.h>
#include "config.h"
#include "vi.h"
#include "regexp.h"



static char	*previous;	/* the previous regexp, used when null regexp is given */


#ifndef NO_MAGIC
/* THE REAL REGEXP PACKAGE IS USED UNLESS "NO_MAGIC" IS DEFINED */

/* These are used to classify or recognize meta-characters */
#define META		'\0'
#define BASE_META(m)	((m) - 256)
#define INT_META(c)	((c) + 256)
#define IS_META(m)	((m) >= 256)
#define IS_CLASS(m)	((m) >= M_CLASS(0) && (m) <= M_CLASS(9))
#define IS_START(m)	((m) >= M_START(0) && (m) <= M_START(9))
#define IS_END(m)	((m) >= M_END(0) && (m) <= M_END(9))
#define IS_CLOSURE(m)	((m) >= M_SPLAT && (m) <= M_QMARK)
#define ADD_META(s,m)	(*(s)++ = META, *(s)++ = BASE_META(m))
#define GET_META(s)	(*(s) == META ? INT_META(*++(s)) : *s)

/* These are the internal codes used for each type of meta-character */
#define M_BEGLINE	256		/* internal code for ^ */
#define M_ENDLINE	257		/* internal code for $ */
#define M_BEGWORD	258		/* internal code for \< */
#define M_ENDWORD	259		/* internal code for \> */
#define M_ANY		260		/* internal code for . */
#define M_SPLAT		261		/* internal code for * */
#define M_PLUS		262		/* internal code for \+ */
#define M_QMARK		263		/* internal code for \? */
#define M_CLASS(n)	(264+(n))	/* internal code for [] */
#define M_START(n)	(274+(n))	/* internal code for \( */
#define M_END(n)	(284+(n))	/* internal code for \) */

/* These are used during compilation */
static int	class_cnt;	/* used to assign class IDs */
static int	start_cnt;	/* used to assign start IDs */
static int	end_stk[NSUBEXP];/* used to assign end IDs */
static int	end_sp;
static char	*retext;	/* points to the text being compiled */

/* error-handling stuff */
jmp_buf	errorhandler;
#define FAIL(why)	regerror(why); longjmp(errorhandler, 1)





/* This function builds a bitmap for a particular class */
static char *makeclass(text, bmap)
	REG char	*text;	/* start of the class */
	REG char	*bmap;	/* the bitmap */
{
	REG int		i;
	int		complement = 0;

# if TRACE
	printf("makeclass(\"%s\", 0x%lx)\n", text, (long)bmap);
# endif

	/* zero the bitmap */
	for (i = 0; bmap && i < 32; i++)
	{
		bmap[i] = 0;
	}

	/* see if we're going to complement this class */
	if (*text == '^')
	{
		text++;
		complement = 1;
	}

	/* add in the characters */
	while (*text && *text != ']')
	{
		/* is this a span of characters? */
		if (text[1] == '-' && text[2])
		{
			/* spans can't be backwards */
			if (text[0] > text[2])
			{
				FAIL("Backwards span in []");
			}

			/* add each character in the span to the bitmap */
			for (i = text[0]; bmap && i <= text[2]; i++)
			{
				bmap[i >> 3] |= (1 << (i & 7));
			}

			/* move past this span */
			text += 3;
		}
		else
		{
			/* add this single character to the span */
			i = *text++;
			if (bmap)
			{
				bmap[i >> 3] |= (1 << (i & 7));
			}
		}
	}

	/* make sure the closing ] is missing */
	if (*text++ != ']')
	{
		FAIL("] missing");
	}

	/* if we're supposed to complement this class, then do so */
	if (complement && bmap)
	{
		for (i = 0; i < 32; i++)
		{
			bmap[i] = ~bmap[i];
		}
	}

	return text;
}




/* This function gets the next character or meta character from a string.
 * The pointer is incremented by 1, or by 2 for \-quoted characters.  For [],
 * a bitmap is generated via makeclass() (if re is given), and the
 * character-class text is skipped.
 */
static int gettoken(sptr, re)
	char	**sptr;
	regexp	*re;
{
	int	c;

	c = **sptr;
	++*sptr;
	if (c == '\\')
	{
		c = **sptr;
		++*sptr;
		switch (c)
		{
		  case '<':
			return M_BEGWORD;

		  case '>':
			return M_ENDWORD;

		  case '(':
			if (start_cnt >= NSUBEXP)
			{
				FAIL("Too many \\(s");
			}
			end_stk[end_sp++] = start_cnt;
			return M_START(start_cnt++);

		  case ')':
			if (end_sp <= 0)
			{
				FAIL("Mismatched \\)");
			}
			return M_END(end_stk[--end_sp]);

		  case '*':
			return (*o_magic ? c : M_SPLAT);

		  case '.':
			return (*o_magic ? c : M_ANY);

		  case '+':
			return M_PLUS;

		  case '?':
			return M_QMARK;

		  default:
			return c;
		}
	}
	else if (*o_magic)
	{
		switch (c)
		{
		  case '^':
			if (*sptr == retext + 1)
			{
				return M_BEGLINE;
			}
			return c;

		  case '$':
			if (!**sptr)
			{
				return M_ENDLINE;
			}
			return c;

		  case '.':
			return M_ANY;

		  case '*':
			return M_SPLAT;

		  case '[':
			/* make sure we don't have too many classes */
			if (class_cnt >= 10)
			{
				FAIL("Too many []s");
			}

			/* process the character list for this class */
			if (re)
			{
				/* generate the bitmap for this class */
				*sptr = makeclass(*sptr, re->program + 1 + 32 * class_cnt);
			}
			else
			{
				/* skip to end of the class */
				*sptr = makeclass(*sptr, (char *)0);
			}
			return M_CLASS(class_cnt++);

		  default:
			return c;
		}
	}
	else	/* unquoted nomagic */
	{
		switch (c)
		{
		  case '^':
			if (*sptr == retext + 1)
			{
				return M_BEGLINE;
			}
			return c;

		  case '$':
			if (!**sptr)
			{
				return M_ENDLINE;
			}
			return c;

		  default:
			return c;
		}
	}
	/*NOTREACHED*/
}




/* This function calculates the number of bytes that will be needed for a
 * compiled RE.  Its argument is the uncompiled version.  It is not clever
 * about catching syntax errors; that is done in a later pass.
 */
static unsigned calcsize(text)
	char		*text;
{
	unsigned	size;
	int		token;

	retext = text;
	class_cnt = 0;
	start_cnt = 1;
	end_sp = 0;
	size = 5;
	while ((token = gettoken(&text, (regexp *)0)) != 0)
	{
		if (IS_CLASS(token))
		{
			size += 34;
		}
		else if (IS_META(token))
		{
			size += 2;
		}
		else
		{
			size++;
		}
	}

	return size;
}



/* This function compiles a regexp. */
regexp *regcomp(text)
	char		*text;
{
	int		needfirst;
	unsigned	size;
	int		token;
	int		peek;
	char		*build;
	regexp		*re;


	/* prepare for error handling */
	re = (regexp *)0;
	if (setjmp(errorhandler))
	{
		if (re)
		{
			free(re);
		}
		return (regexp *)0;
	}

	/* if an empty regexp string was given, use the previous one */
	if (*text == 0)
	{
		if (!previous)
		{
			FAIL("No previous RE");
		}
		text = previous;
	}
	else /* non-empty regexp given, so remember it */
	{
		if (previous)
			free(previous);
		previous = (char *)malloc((unsigned)(strlen(text) + 1));
		if (previous)
			strcpy(previous, text);
	}

	/* allocate memory */
	class_cnt = 0;
	start_cnt = 1;
	end_sp = 0;
	retext = text;
	size = calcsize(text) + sizeof(regexp);
#ifdef lint
	re = ((regexp *)0) + size;
#else
	re = (regexp *)malloc((unsigned)size);
#endif
	if (!re)
	{
		FAIL("Not enough memory for this RE");
	}

	/* compile it */
	build = &re->program[1 + 32 * class_cnt];
	re->program[0] = class_cnt;
	for (token = 0; token < NSUBEXP; token++)
	{
		re->startp[token] = re->endp[token] = (char *)0;
	}
	re->first = 0;
	re->bol = 0;
	re->minlen = 0;
	needfirst = 1;
	class_cnt = 0;
	start_cnt = 1;
	end_sp = 0;
	retext = text;
	for (token = M_START(0), peek = gettoken(&text, re);
	     token;
	     token = peek, peek = gettoken(&text, re))
	{
		/* special processing for the closure operator */
		if (IS_CLOSURE(peek))
		{
			/* detect misuse of closure operator */
			if (IS_START(token))
			{
				FAIL("* or \\+ or \\? follows nothing");
			}
			else if (IS_META(token) && token != M_ANY && !IS_CLASS(token))
			{
				FAIL("* or \\+ or \\? can only follow a normal character or . or []");
			}

			/* it is okay -- make it prefix instead of postfix */
			ADD_META(build, peek);

			/* take care of "needfirst" - is this the first char? */
			if (needfirst && peek == M_PLUS && !IS_META(token))
			{
				re->first = token;
			}
			needfirst = 0;

			/* we used "peek" -- need to refill it */
			peek = gettoken(&text, re);
			if (IS_CLOSURE(peek))
			{
				FAIL("* or \\+ or \\? doubled up");
			}
		}
		else if (!IS_META(token))
		{
			/* normal char is NOT argument of closure */
			if (needfirst)
			{
				re->first = token;
				needfirst = 0;
			}
			re->minlen++;
		}
		else if (token == M_ANY || IS_CLASS(token))
		{
			/* . or [] is NOT argument of closure */
			needfirst = 0;
			re->minlen++;
		}

		/* the "token" character is not closure -- process it normally */
		if (token == M_BEGLINE)
		{
			/* set the BOL flag instead of storing M_BEGLINE */
			re->bol = 1;
		}
		else if (IS_META(token))
		{
			ADD_META(build, token);
		}
		else
		{
			*build++ = token;
		}
	}

	/* end it with a \) which MUST MATCH the opening \( */
	ADD_META(build, M_END(0));
	if (end_sp > 0)
	{
		FAIL("Not enough \\)s");
	}

	return re;
}



/*---------------------------------------------------------------------------*/


/* This function checks for a match between a character and a token which is
 * known to represent a single character.  It returns 0 if they match, or
 * 1 if they don't.
 */
int match1(re, ch, token)
	regexp		*re;
	REG char	ch;
	REG int		token;
{
	if (!ch)
	{
		/* the end of a line can't match any RE of width 1 */
		return 1;
	}
	if (token == M_ANY)
	{
		return 0;
	}
	else if (IS_CLASS(token))
	{
		if (re->program[1 + 32 * (token - M_CLASS(0)) + (ch >> 3)] & (1 << (ch & 7)))
			return 0;
	}
	else if (ch == token
		|| (*o_ignorecase && isupper(ch) && tolower(ch) == token))
	{
		return 0;
	}
	return 1;
}



/* This function checks characters up to and including the next closure, at
 * which point it does a recursive call to check the rest of it.  This function
 * returns 0 if everything matches, or 1 if something doesn't match.
 */
int match(re, str, prog, here)
	regexp		*re;	/* the regular expression */
	char		*str;	/* the string */
	REG char	*prog;	/* a portion of re->program, an compiled RE */
	REG char	*here;	/* a portion of str, the string to compare it to */
{
	REG int		token;
	REG int		nmatched;
	REG int		closure;

	for (token = GET_META(prog); !IS_CLOSURE(token); prog++, token = GET_META(prog))
	{
		switch (token)
		{
		/*case M_BEGLINE: can't happen; re->bol is used instead */
		  case M_ENDLINE:
			if (*here)
				return 1;
			break;

		  case M_BEGWORD:
			if (here != str &&
			   (here[-1] == '_' ||
			     isascii(here[-1]) && isalnum(here[-1])))
				return 1;
			break;

		  case M_ENDWORD:
			if (here[0] == '_' || isascii(here[0]) && isalnum(here[0]))
				return 1;
			break;

		  case M_START(0):
		  case M_START(1):
		  case M_START(2):
		  case M_START(3):
		  case M_START(4):
		  case M_START(5):
		  case M_START(6):
		  case M_START(7):
		  case M_START(8):
		  case M_START(9):
			re->startp[token - M_START(0)] = (char *)here;
			break;

		  case M_END(0):
		  case M_END(1):
		  case M_END(2):
		  case M_END(3):
		  case M_END(4):
		  case M_END(5):
		  case M_END(6):
		  case M_END(7):
		  case M_END(8):
		  case M_END(9):
			re->endp[token - M_END(0)] = (char *)here;
			if (token == M_END(0))
			{
				return 0;
			}
			break;

		  default: /* literal, M_CLASS(n), or M_ANY */
			if (match1(re, *here, token) != 0)
				return 1;
			here++;
		}
	}

	/* C L O S U R E */

	/* step 1: see what we have to match against, and move "prog" to point
	 * the the remainder of the compiled RE.
	 */
	closure = token;
	prog++, token = GET_META(prog);
	prog++;

	/* step 2: see how many times we can match that token against the string */
	for (nmatched = 0;
	     (closure != M_QMARK || nmatched < 1) && *here && match1(re, *here, token) == 0;
	     nmatched++, here++)
	{
	}

	/* step 3: try to match the remainder, and back off if it doesn't */
	while (nmatched >= 0 && match(re, str, prog, here) != 0)
	{
		nmatched--;
		here--;
	}

	/* so how did it work out? */
	if (nmatched >= ((closure == M_PLUS) ? 1 : 0))
		return 0;
	return 1;
}



/* This function searches through a string for text that matches an RE. */
int regexec(re, str, bol)
	regexp	*re;	/* the compiled regexp to search for */
	char	*str;	/* the string to search through */
	int	bol;	/* boolean: does str start at the beginning of a line? */
{
	char	*prog;	/* the entry point of re->program */
	int	len;	/* length of the string */
	REG char	*here;

	/* if must start at the beginning of a line, and this isn't, then fail */
	if (re->bol && !bol)
	{
		return 0;
	}

	len = strlen(str);
	prog = re->program + 1 + 32 * re->program[0];

	/* search for the RE in the string */
	if (re->bol)
	{
		/* must occur at BOL */
		if ((re->first
			&& match1(re, *(char *)str, re->first))/* wrong first letter? */
		 || len < re->minlen			/* not long enough? */
		 || match(re, (char *)str, prog, str))	/* doesn't match? */
			return 0;			/* THEN FAIL! */
	}
#ifndef CRUNCH
	else if (!*o_ignorecase)
	{
		/* can occur anywhere in the line, noignorecase */
		for (here = (char *)str;
		     (re->first && re->first != *here)
			|| match(re, (char *)str, prog, here);
		     here++, len--)
		{
			if (len < re->minlen)
				return 0;
		}
	}
#endif
	else
	{
		/* can occur anywhere in the line, ignorecase */
		for (here = (char *)str;
		     (re->first && match1(re, *here, (int)re->first))
			|| match(re, (char *)str, prog, here);
		     here++, len--)
		{
			if (len < re->minlen)
				return 0;
		}
	}

	/* if we didn't fail, then we must have succeeded */
	return 1;
}

#else /* NO_MAGIC */

regexp *regcomp(exp)
	char	*exp;
{
	char	*src;
	char	*dest;
	regexp	*re;
	int	i;

	/* allocate a big enough regexp structure */
#ifdef lint
	re = (regexp *)0;
#else
	re = (regexp *)malloc((unsigned)(strlen(exp) + 1 + sizeof(struct regexp)));
#endif
	if (!re)
	{
		regerror("Could not malloc a regexp structure");
		return (regexp *)0;
	}

	/* initialize all fields of the structure */
	for (i = 0; i < NSUBEXP; i++)
	{
		re->startp[i] = re->endp[i] = (char *)0;
	}
	re->minlen = 0;
	re->first = 0;
	re->bol = 0;

	/* copy the string into it, translating ^ and $ as needed */
	for (src = exp, dest = re->program + 1; *src; src++)
	{
		switch (*src)
		{
		  case '^':
			if (src == exp)
			{
				re->bol += 1;
			}
			else
			{
				*dest++ = '^';
				re->minlen++;
			}
			break;

		  case '$':
			if (!src[1])
			{
				re->bol += 2;
			}
			else
			{
				*dest++ = '$';
				re->minlen++;
			}
			break;

		  case '\\':
			if (src[1])
			{
				*dest++ = *++src;
				re->minlen++;
			}
			else
			{
				regerror("extra \\ at end of regular expression");
			}
			break;

		  default:
			*dest++ = *src;
			re->minlen++;
		}
	}
	*dest = '\0';

	return re;
}


/* This "helper" function checks for a match at a given location.  It returns
 * 1 if it matches, 0 if it doesn't match here but might match later on in the
 * string, or -1 if it could not possibly match
 */
static int reghelp(prog, string, bolflag)
	struct regexp	*prog;
	char		*string;
	int		bolflag;
{
	char		*scan;
	char		*str;

	/* if ^, then require bolflag */
	if ((prog->bol & 1) && !bolflag)
	{
		return -1;
	}

	/* if it matches, then it will start here */
	prog->startp[0] = string;

	/* compare, possibly ignoring case */
	if (*o_ignorecase)
	{
		for (scan = &prog->program[1]; *scan; scan++, string++)
			if (tolower(*scan) != tolower(*string))
				return *string ? 0 : -1;
	}
	else
	{
		for (scan = &prog->program[1]; *scan; scan++, string++)
			if (*scan != *string)
				return *string ? 0 : -1;
	}

	/* if $, then require string to end here, too */
	if ((prog->bol & 2) && *string)
	{
		return 0;
	}

	/* if we get to here, it matches */
	prog->endp[0] = string;
	return 1;
}



int regexec(prog, string, bolflag)
	struct regexp	*prog;
	char		*string;
	int		bolflag;
{
	int		rc;

	/* keep trying to match it */
	for (rc = reghelp(prog, string, bolflag); rc == 0; rc = reghelp(prog, string, 0))
	{
		string++;
	}

	/* did we match? */
	return rc == 1;
}
#endif
SHAR_EOF
fi # end of overwriting check
if test -f 'regsub.c'
then
	echo shar: will not over-write existing file "'regsub.c'"
else
cat << \SHAR_EOF > 'regsub.c'
/* regsub.c */

/* This file contains the regsub() function, which performs substitutions
 * after a regexp match has been found.
 */

#include <ctype.h>
#include "config.h"
#include "vi.h"
#include "regexp.h"

static char *previous;	/* a copy of the text from the previous substitution */

/* perform substitutions after a regexp match */
void regsub(re, src, dst)
	regexp		*re;
	REG char	*src;
	REG char	*dst;
{
	REG char	*cpy;
	REG char	*end;
	REG char	c;
	char		*start;
#ifndef CRUNCH
	int		mod;

	mod = 0;
#endif

	start = src;
	while ((c = *src++) != '\0')
	{
#ifndef NO_MAGIC
		/* recognize any meta characters */
		if (c == '&' && *o_magic)
		{
			cpy = re->startp[0];
			end = re->endp[0];
		}
		else if (c == '~' && *o_magic)
		{
			cpy = previous;
			if (cpy)
				end = cpy + strlen(cpy);
		}
		else
#endif /* not NO_MAGIC */
		if (c == '\\')
		{
			c = *src++;
			switch (c)
			{
#ifndef NO_MAGIC
			  case '0':
			  case '1':
			  case '2':
			  case '3':
			  case '4':
			  case '5':
			  case '6':
			  case '7':
			  case '8':
			  case '9':
				/* \0 thru \9 mean "copy subexpression" */
				c -= '0';
				cpy = re->startp[c];
				end = re->endp[c];
				break;
# ifndef CRUNCH
			  case 'U':
			  case 'u':
			  case 'L':
			  case 'l':
				/* \U and \L mean "convert to upper/lowercase" */
				mod = c;
				continue;

			  case 'E':
			  case 'e':
				/* \E ends the \U or \L */
				mod = 0;
				continue;
# endif /* not CRUNCH */
			  case '&':
				/* "\&" means "original text" */
				if (*o_magic)
				{
					*dst++ = c;
					continue;
				}
				cpy = re->startp[0];
				end = re->endp[0];
				break;

			  case '~':
				/* "\~" means "previous text, if any" */
				if (*o_magic)
				{
					*dst++ = c;
					continue;
				}
				cpy = previous;
				if (cpy)
					end = cpy + strlen(cpy);
				break;
#else /* NO_MAGIC */
			  case '&':
				/* "\&" means "original text" */
				cpy = re->startp[0];
				end = re->endp[0];
				break;

			  case '~':
				/* "\~" means "previous text, if any" */
				cpy = previous;
				if (cpy)
					end = cpy + strlen(cpy);
				break;
#endif /* NO_MAGIC */
			  default:
				/* ordinary char preceded by backslash */
				*dst++ = c;
				continue;
			}
		}
		else
		{
			/* ordinary character, so just copy it */
			*dst++ = c;
			continue;
		}

		/* Note: to reach this point in the code, we must have evaded
		 * all "continue" statements.  To do that, we must have hit
		 * a metacharacter that involves copying.
		 */

		/* if there is nothing to copy, loop */
		if (!cpy)
			continue;

		/* copy over a portion of the original */
		while (cpy < end)
		{
#ifndef NO_MAGIC
# ifndef CRUNCH
			switch (mod)
			{
			  case 'U':
			  case 'u':
				/* convert to uppercase */
				if (isascii(*cpy) && islower(*cpy))
				{
					*dst++ = toupper(*cpy);
					cpy++;
				}
				else
				{
					*dst++ = *cpy++;
				}
				break;

			  case 'L':
			  case 'l':
				/* convert to lowercase */
				if (isascii(*cpy) && isupper(*cpy))
				{
					*dst++ = tolower(*cpy);
					cpy++;
				}
				else
				{
					*dst++ = *cpy++;
				}
				break;

			  default:
				/* copy without any conversion */
				*dst++ = *cpy++;
			}

			/* \u and \l end automatically after the first char */
			if (mod && (mod == 'u' || mod == 'l'))
			{
				mod = 0;
			}
# else /* CRUNCH */
			*dst++ = *cpy++;
# endif /* CRUNCH */
#else /* NO_MAGIC */
			*dst++ = *cpy++;
#endif /* NO_MAGIC */
		}
	}
	*dst = '\0';

	/* remember what text we inserted this time */
	if (previous)
		free(previous);
	previous = (char *)malloc((unsigned)(strlen(start) + 1));
	if (previous)
		strcpy(previous, start);
}
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0

mh@roger.imsd.contel.com (Mike H.) (01/11/91)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	shell.c
#	sysdos.c
#	system.c
#	tinytcap.c
#	tio.c
#	tmp.c
#	tnamerec.c
# This archive created: Thu Jan 10 17:21:10 1991
export PATH; PATH=/bin:$PATH
if test -f 'shell.c'
then
	echo shar: will not over-write existing file "'shell.c'"
else
cat << \SHAR_EOF > 'shell.c'
/* shell.c */

/* Author:
 *	Guntram Blohm
 *	Buchenstrasse 19
 *	7904 Erbach, West Germany
 *	Tel. ++49-7305-6997
 *	sorry - no regular network connection
 */

/*
 * This file contains a minimal version of a shell for TOS. It allows the
 * setting of an environment, calling programs, and exiting.
 * If you don't have another one, this might be sufficient, but you should 
 * prefer *any* other shell.
 * You may, however, want to set your SHELL environment variable to this
 * shell: it implements the -c switch, which is required by Elvis, and
 * not supported by most other atari shells.
 */
 
#include <stdio.h>
#include <string.h>
#include <osbind.h>
extern char *getenv(), *malloc();
extern char **environ;
long _stksize=16384;

#define	MAXENV	50

struct
{
	char *name;
	char *value;
} myenv[MAXENV];

int cmd_set(), cmd_exit();

struct buildins
{
	char *name;
	int (*func)();
} buildins[]=
{	"exit", cmd_exit,
	"set", cmd_set,
	0,
};

main(argc, argv)
	int argc;
	char **argv;
{
	char buf[128];
	int i;

	for (i=0; environ[i] && strncmp(environ[i],"ARGV=",5); i++)
		cmd_set(environ[i]);
	script("profile.sh");

	if (argc>1 && !strcmp(argv[1], "-c"))
	{
		buf[0]='\0';
		for (i=2; i<argc; i++)
		{	if (i>2)
				strcat(buf, " ");
			strcat(buf, argv[i]);
		}
		execute(buf);
	}
	else
		while (fputs("$ ", stdout), gets(buf))
			execute(buf);
}

execute(buf)
	char *buf;
{
	char *scan=buf;
	char cmd[80];
	char line[128];
	char env[4096], *ep=env;
	int i;

	while (*scan==' ')
		scan++;
	if (!*scan)
		return;
	while (*scan && *scan!=' ')
		scan++;
	if (*scan)
		*scan++='\0';

	for (i=0; buildins[i].name; i++)
		if (!strcmp(buf, buildins[i].name))
			return (*buildins[i].func)(scan);

	if (!searchpath(buf, cmd))
	{	printf("%s: not found\n", buf);
		return -1;
	}

	strcpy(line+1, scan);
	line[0]=strlen(scan);
	for (i=0; i<MAXENV && myenv[i].name; i++)
	{	strcpy(ep, myenv[i].name);
		strcat(ep, "=");
		strcat(ep, myenv[i].value);
		ep+=strlen(ep)+1;
	}
	
	*ep='\0';

	return Pexec(0, cmd, line, env);
}

searchpath(from, to)
	char *from, *to;
{
	char *path="";
	char *scan;
	char *end;
	char *q;
	int i;

	for (i=0; i<MAXENV && myenv[i].name; i++)
		if (!strcmp(myenv[i].name,"PATH"))
			path=myenv[i].value;
	for (scan=from; *scan; scan++)
		if (*scan==':' || *scan=='\\')
		{	path=0;
			break;
		}
	if (!path)
	{	strcpy(to, from);
		end=to+strlen(to);
		strcpy(end, ".prg"); if (try(to)) return 1;
		strcpy(end, ".ttp"); if (try(to)) return 1;
		strcpy(end, ".tos"); if (try(to)) return 1;
		*to='\0'; return 0;
	}
	for (scan=path; *scan; )
	{
		for (q=to; *scan && *scan!=';' && *scan!=','; scan++)
			*q++=*scan;
		if (*scan==';' || *scan==',')
			scan++;
		*q++='\\';
		*q='\0';
		strcpy(q, from);
		end=q+strlen(q);
		strcpy(end, ".prg"); if (try(to)) return 1;
		strcpy(end, ".ttp"); if (try(to)) return 1;
		strcpy(end, ".tos"); if (try(to)) return 1;
	}
	*to='\0';
	return 0;
}

try(name)
	char *name;
{
	if (Fattrib(name, 0, 0) < 0)
		return 0;
	return 1;
}

cmd_exit()
{
	exit(0);
}

cmd_set(line)
	char *line;
{
	char *value;
	int i;

	if (!*line)
	{
		for (i=0; i<MAXENV && myenv[i].name; i++)
			printf("%s=%s\n", myenv[i].name, myenv[i].value);
		return 0;
	}

	for (value=line; *value && *value!='='; value++)
		;
	if (!*value)
	{	printf("Usage: set name=var\n");
		return -1;
	}
	*value++='\0';
	doset(line, value);
}

doset(line, value)
	char *line, *value;
{
	int i;

	for (i=0; i<MAXENV && myenv[i].name && strcmp(myenv[i].name, line); i++)
		;
	if (i==MAXENV)
	{	printf("No Space\n");
		return -1;
	}
	if (!myenv[i].name)
	{	myenv[i].name=malloc(strlen(line)+1);
		strcpy(myenv[i].name, line);
	}
	if (myenv[i].value)
		free(myenv[i].value);
	myenv[i].value=malloc(strlen(value)+1);
	strcpy(myenv[i].value, value);
	return 0;
}

script(name)
	char *name;
{
	FILE *fp;
	char buf[128], *p;

	if ((fp=fopen(name, "r"))==0)
		return;
	while (fgets(buf, sizeof buf, fp))
	{
		if ((p=strchr(buf, '\n'))!=0)
			*p='\0';
		execute(buf);
	}
	fclose(fp);
}
SHAR_EOF
fi # end of overwriting check
if test -f 'sysdos.c'
then
	echo shar: will not over-write existing file "'sysdos.c'"
else
cat << \SHAR_EOF > 'sysdos.c'
/* sysdos.c  -- DOS version of system.c */

/* Author:
 *	Guntram Blohm
 *	Buchenstrasse 19
 *	7904 Erbach, West Germany
 *	Tel. ++49-7305-6997
 *	sorry - no regular network connection
 */


/* This file is derived from Steve Kirkendall's system.c.
 *
 * Entry points are:
 *	system(cmd)	- run a single shell command
 *	wildcard(names)	- expand wildcard characters in filanames
 *
 * This file is for use with DOS and TOS. For OS/2, slight modifications
 * might be sufficient. For UNIX, use system.c. For Amiga, completely
 * rewrite this stuff.
 *
 * Another system function, filter, is the same on DOS and UNIX and thus
 * can be found in the original system.c.
 */

#include "config.h"
#include "vi.h"
extern char **environ;


#if MSDOS
#include <process.h>
extern unsigned char _osmajor;
#endif
#if TOS
#include <osbind.h>
#endif


#if MSDOS || TOS
#include <string.h>

/*
 * Calling command is a bit nasty because of the undocumented yet sometimes
 * used feature to change the option char to something else than /.
 * Versions 2.x and 3.x support this, 4.x doesn't.
 *
 * For Atari, some shells define a shortcut entry which is faster than
 * shell -c. Also, Mark Williams uses a special ARGV environment variable
 * to pass more than 128 chars to a called command.
 * We try to support all of these features here.
 */

int system(cmd)
	const char	*cmd;
{
#if MSDOS
	char *cmdswitch="/c";
	if (_osmajor<4)
		cmdswitch[0]=switchar();
	return spawnle(P_WAIT, o_shell, o_shell, cmdswitch, cmd, (char *)0, environ);
#else
	long	ssp;
	int	(*shell)();
	char	line[130];
	char	env[4096], *ep=env;
	int	i;

/* does our shell have a shortcut, that we can use? */

	ssp = Super(0L);
	shell = *((int (**)())0x4F6);
	Super(ssp);
	if (shell)
		return (*shell)(cmd);

/* else we'll have to call a shell ... */

	for (i=0; environ[i] && strncmp(environ[i], "ARGV=", 5); i++)
	{	strcpy(ep, environ[i]);
		ep+=strlen(ep)+1;
	}
	if (environ[i])
	{
		strcpy(ep, environ[i]); ep+=strlen(ep)+1;
		strcpy(ep, o_shell); ep+=strlen(ep)+1;
		strcpy(ep, "-c"); ep+=3;
		strcpy(ep, cmd); ep+=strlen(ep)+1;
	}
	*ep='\0';
	strcpy(line+1, "-c ");
	strncat(line+1, cmd, 126);
	line[0]=strlen(line+1);
	return Pexec(0, o_shell, line, env);
#endif
}

/* This private function opens a pipe from a filter.  It is similar to the
 * system() function above, and to popen(cmd, "r").
 * sorry - i cant use cmdstate until rpclose, but get it from spawnle.
 */

static int cmdstate;
static char output[80];

int rpipe(cmd, in)
	char	*cmd;	/* the filter command to use */
	int	in;	/* the fd to use for stdin */
{
	int	fd, old0, old1, old2;

	/* create the file that will collect the filter's output */
	strcpy(output, o_directory);
	if ((fd=strlen(output)) && !strchr("/\\:", output[fd-1]))
		output[fd++]=SLASH;
	strcpy(output+fd, SCRATCHIN+3);
	mktemp(output);
	close(creat(output, 0666));
	if ((fd=open(output, O_RDWR))==-1)
	{
		unlink(output);
		return -1;
	}

	/* save and redirect stdin, stdout, and stderr */
	old0=dup(0);
	old1=dup(1);
	if (in)
	{
		dup2(in, 0);
		close(in);
	}
	dup2(fd, 1);

	/* call command */
	cmdstate=system(cmd);

	/* restore old std... */
	dup2(old0, 0); close(old0);
	dup2(old1, 1); close(old1);

	/* rewind command output */
	lseek(fd, 0L, 0);
	return fd;
}

/* This function closes the pipe opened by rpipe(), and returns 0 for success */
int rpclose(fd)
	int	fd;
{
	int	status;

	close(fd);
	unlink(output);
	return cmdstate;
}

#endif
SHAR_EOF
fi # end of overwriting check
if test -f 'system.c'
then
	echo shar: will not over-write existing file "'system.c'"
else
cat << \SHAR_EOF > 'system.c'
/* system.c  -- UNIX version */

/* Author:
 *	Steve Kirkendall
 *	14407 SW Teal Blvd. #C
 *	Beaverton, OR 97005
 *	kirkenda@cs.pdx.edu
 */


/* This file contains a new version of the system() function and related stuff.
 *
 * Entry points are:
 *	system(cmd)	- run a single shell command
 *	wildcard(names)	- expand wildcard characters in filanames
 *	filter(m,n,cmd)	- run text lines through a filter program
 *
 * This is probably the single least portable file in the program.  The code
 * shown here should work correctly if it links at all; it will work on UNIX
 * and any O.S./Compiler combination which adheres to UNIX forking conventions.
 */

#include "config.h"
#include "vi.h"
#include <signal.h>
extern char	**environ;

#if ANY_UNIX

/* This is a new version of the system() function.  The only difference
 * between this one and the library one is: this one uses the o_shell option.
 */
int system(cmd)
	char	*cmd;	/* a command to run */
{
	int	status;	/* exit status of the command */

	/* warn the user if the file hasn't been saved yet */
	if (*o_warn && tstflag(file, MODIFIED))
	{
		if (mode == MODE_VI)
		{
			mode = MODE_COLON;
		}
		msg("Warning: \"%s\" has been modified but not yet saved", origname);
	}

	signal(SIGINT, SIG_IGN);
	switch (fork())
	{
	  case -1:						/* error */
		msg("fork() failed");
		status = -1;
		break;

	  case 0:						/* child */
		/* for the child, close all files except stdin/out/err */
		for (status = 3; status < 60 && (close(status), errno != EINVAL); status++)
		{
		}

		signal(SIGINT, SIG_DFL);
		if (cmd == o_shell)
		{
			execle(o_shell, o_shell, (char *)0, environ);
		}
		else
		{
			execle(o_shell, o_shell, "-c", cmd, (char *)0, environ);
		}
		msg("execle(\"%s\", ...) failed", o_shell);
		exit(1); /* if we get here, the exec failed */

	  default:						/* parent */
		wait(&status);
		signal(SIGINT, trapint);
	}

	return status;
}

/* This private function opens a pipe from a filter.  It is similar to the
 * system() function above, and to popen(cmd, "r").
 */
static int rpipe(cmd, in)
	char	*cmd;	/* the filter command to use */
	int	in;	/* the fd to use for stdin */
{
	int	r0w1[2];/* the pipe fd's */

	/* make the pipe */
	if (pipe(r0w1) < 0)
	{
		return -1;	/* pipe failed */
	}

	/* The parent process (elvis) ignores signals while the filter runs.
	 * The child (the filter program) will reset this, so that it can
	 * catch the signal.
	 */
	signal(SIGINT, SIG_IGN);

	switch (fork())
	{
	  case -1:						/* error */
		return -1;

	  case 0:						/* child */
		/* close the "read" end of the pipe */
		close(r0w1[0]);

		/* redirect stdout to go to the "write" end of the pipe */
		close(1);
		dup(r0w1[1]);
		close(2);
		dup(r0w1[1]);
		close(r0w1[1]);

		/* redirect stdin */
		if (in != 0)
		{
			close(0);
			dup(in);
			close(in);
		}

		/* the filter should accept SIGINT signals */
		signal(SIGINT, SIG_DFL);

		/* exec the shell to run the command */
		execle(o_shell, o_shell, "-c", cmd, (char *)0, environ);
		exit(1); /* if we get here, exec failed */

	  default:						/* parent */
		/* close the "write" end of the pipe */	
		close(r0w1[1]);

		return r0w1[0];
	}
}

#endif /* non-DOS */

#if OSK

/* This private function opens a pipe from a filter.  It is similar to the
 * system() function above, and to popen(cmd, "r").
 */
static int rpipe(cmd, in)
	char	*cmd;	/* the filter command to use */
	int	in;	/* the fd to use for stdin */
{

    char **argblk;
	char *p, *buffer, *command;
	int stdinp, stdoutp;
	unsigned addstack = 0;
	int words, len, loop = 1;
	int fp, pipe_pid;
	extern int os9forkc();
	extern char *index();

	command = cmd;
	words = 0;
	if ((buffer = (char*) malloc(strlen(cmd))) == (char*) 0)
		return 0;

	do {
		if (!(p = index(command, ' '))) {
			loop--;
			len = strlen(command);
		}
		else
			len = p - command;
		words++;	
		while (command[len] && command[len] == ' ')
			len++;
		if (!command[len])
			break;
		command = command + len;
	}	
	while (loop);
	if ((argblk = (char **)malloc((words+1) * sizeof(char*))) == (char **)0)
		return 0;
	command = cmd;
	words = 0;
	do {
		if (!(p = index(command, ' '))) {
			loop--;
			len = strlen(command);
		}
		else
			len = p - command;
		strncpy(buffer, command, len);
		argblk[words++] = buffer;
		buffer += len;
		*buffer++ = '\0';
		while (command[len] && command[len] == ' ')
			len++;
		if (!command[len])
			break;
		command = command + len;
	} while (loop);
	if (argblk[words - 1][0] == '#') 
		addstack = 1024 * atoi(&argblk[--words][1]);
	argblk[words] = 0;

	stdoutp = dup(1);
	close(1);               /* close stdout */
	if ((fp = open("/pipe",S_IREAD)) < 0) {
		dup(stdoutp);
		close(stdoutp);
		return 0;
	}
	if (in != 0) {
		stdinp = dup(0);
		close(0);
		dup(in);
		close(in);
	}
	pipe_pid = os9exec(os9forkc,argblk[0],argblk,environ,addstack,0,3);
	if (pipe_pid == -1) {
		fclose(fp);
		dup(stdoutp);
		close(stdoutp);
		if (in != 0) {
			close(0);
			dup(stdinp);
		}
		return 0;
	}
	fp = (short)dup(1);     /* save pipe */
	close(1);               /* get rid of the pipe */
	dup(stdoutp);           /* restore old stdout */
	close(stdoutp);         /* close path to stdout copy */
	if (in != 0) {
		close(0);
		dup(stdinp);
	}
	return fp;
}	
#endif

#if ANY_UNIX || OSK

/* This function closes the pipe opened by rpipe(), and returns 0 for success */
static int rpclose(fd)
	int	fd;
{
	int	status;

	close(fd);
	wait(&status);
	signal(SIGINT, trapint);
	return status;
}

#endif /* non-DOS */

/* This function expands wildcards in a filename or filenames.  It does this
 * by running the "echo" command on the filenames via the shell; it is assumed
 * that the shell will expand the names for you.  If for any reason it can't
 * run echo, then it returns the names unmodified.
 */

#if MSDOS || TOS
#define	PROG	"wildcard "
#define	PROGLEN	9
#include <string.h>
#else
#define	PROG	"echo "
#define	PROGLEN	5
#endif

char *wildcard(names)
	char	*names;
{
	int	i, j, fd;
	REG char *s, *d;


	/* build the echo command */
	if (names != tmpblk.c)
	{
		/* the names aren't in tmpblk.c, so we can do it the easy way */
		strcpy(tmpblk.c, PROG);
		strcat(tmpblk.c, names);
	}
	else
	{
		/* the names are already in tmpblk.c, so shift them to make
		 * room for the word "echo "
		 */
		for (s = names + strlen(names) + 1, d = s + PROGLEN; s > names; )
		{
			*--d = *--s;
		}
		strncpy(names, PROG, PROGLEN);
	}

	/* run the command & read the resulting names */
	fd = rpipe(tmpblk.c, 0);
	if (fd < 0) return names;
	i = 0;
	do
	{
		j = tread(fd, tmpblk.c + i, BLKSIZE - i);
		i += j;
	} while (j > 0);

	/* successful? */
	if (rpclose(fd) == 0 && j == 0 && i < BLKSIZE && i > 0)
	{
		tmpblk.c[i-1] = '\0'; /* "i-1" so we clip off the newline */
		return tmpblk.c;
	}
	else
	{
		return names;
	}
}

/* This function runs a range of lines through a filter program, and replaces
 * the original text with the filtered version.  As a special case, if "to"
 * is MARK_UNSET, then it runs the filter program with stdin coming from
 * /dev/null, and inserts any output lines.
 */
int filter(from, to, cmd)
	MARK	from, to;	/* the range of lines to filter */
	char	*cmd;		/* the filter command */
{
	int	scratch;	/* fd of the scratch file */
	int	fd;		/* fd of the pipe from the filter */
	char	scrout[50];	/* name of the scratch out file */
	MARK	new;		/* place where new text should go */
	int	i;

	/* write the lines (if specified) to a temp file */
	if (to)
	{
		/* we have lines */
#if MSDOS || TOS
		strcpy(scrout, o_directory);
		if ((i=strlen(scrout)) && strchr("\\/:", scrout[i-1]))
			scrout[i++]=SLASH;
		strcpy(scrout+i, SCRATCHOUT+3);
#else
		sprintf(scrout, SCRATCHOUT, o_directory);
#endif
		mktemp(scrout);
		cmd_write(from, to, CMD_BANG, 0, scrout);

		/* use those lines as stdin */
		scratch = open(scrout, O_RDONLY);
		if (scratch < 0)
		{
			unlink(scrout);
			return -1;
		}
	}
	else
	{
		scratch = 0;
	}

	/* start the filter program */
	fd = rpipe(cmd, scratch);
	if (fd < 0)
	{
		if (to)
		{
			close(scratch);
			unlink(scrout);
		}
		return -1;
	}

	ChangeText
	{
		/* adjust MARKs for whole lines, and set "new" */
		from &= ~(BLKSIZE - 1);
		if (to)
		{
			to &= ~(BLKSIZE - 1);
			to += BLKSIZE;
			new = to;
		}
		else
		{
			new = from + BLKSIZE;
		}

		/* repeatedly read in new text and add it */
		while ((i = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
		{
			tmpblk.c[i] = '\0';
			add(new, tmpblk.c);
			for (i = 0; tmpblk.c[i]; i++)
			{
				if (tmpblk.c[i] == '\n')
				{
					new = (new & ~(BLKSIZE - 1)) + BLKSIZE;
				}
				else
				{
					new++;
				}
			}
		}
	}

	/* delete old text, if any */
	if (to)
	{
		delete(from, to);
	}

	/* Reporting... */
	rptlabel = "more";
	if (rptlines < 0)
	{
		rptlines = -rptlines;
		rptlabel = "less";
	}

	/* cleanup */
	rpclose(fd);
	if (to)
	{
		close(scratch);
		unlink(scrout);
	}
	return 0;
}
SHAR_EOF
fi # end of overwriting check
if test -f 'tinytcap.c'
then
	echo shar: will not over-write existing file "'tinytcap.c'"
else
cat << \SHAR_EOF > 'tinytcap.c'
/* 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
SHAR_EOF
fi # end of overwriting check
if test -f 'tio.c'
then
	echo shar: will not over-write existing file "'tio.c'"
else
cat << \SHAR_EOF > 'tio.c'
/* tio.c */

/* Author:
 *	Steve Kirkendall
 *	14407 SW Teal Blvd. #C
 *	Beaverton, OR 97005
 *	kirkenda@cs.pdx.edu
 */


/* This file contains terminal I/O functions */

#include "config.h"
#if BSD || COHERENT
# include <setjmp.h>
#endif
#include <signal.h>
#include "vi.h"


/* This function reads in a line from the terminal. */
int vgets(prompt, buf, bsize)
	char	prompt;	/* the prompt character, or '\0' for none */
	char	*buf;	/* buffer into which the string is read */
	int	bsize;	/* size of the buffer */
{
	int	len;	/* how much we've read so far */
	int	ch;	/* a character from the user */
	int	quoted;	/* is the next char quoted? */
	int	tab;	/* column position of cursor */
	char	widths[132];	/* widths of characters */
#ifndef NO_DIGRAPH
	int	erased;	/* 0, or first char of a digraph */
#endif

	/* show the prompt */
	move(LINES - 1, 0);
	tab = 0;
	if (prompt)
	{
		addch(prompt);
		tab = 1;
	}
	clrtoeol();
	refresh();

	/* read in the line */
#ifndef NO_DIGRAPH
	erased =
#endif
	quoted = len = 0;
	for (;;)
	{
		ch = getkey(quoted ? 0 : WHEN_EX);

		/* some special conversions */
		if (ch == ctrl('D') && len == 0)
			ch = ctrl('[');
#ifndef NO_DIGRAPH
		if (*o_digraph && erased != 0 && ch != '\b')
		{
			ch = digraph(erased, ch);
			erased = 0;
		}
#endif

		/* inhibit detection of special chars (except ^J) after a ^V */
		if (quoted && ch != '\n')
		{
			ch |= 256;
		}

		/* process the character */
		switch(ch)
		{
		  case ctrl('V'):
			qaddch('^');
			qaddstr("BC");
			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)
			{
				int b;
				len--;
#ifndef NO_DIGRAPH
				erased = buf[len];
#endif
				for (b = 8 - widths[len]; b < 8; b++) 
				{
					qaddstr(BC);
				}

				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(' ');
				qaddstr(BC);
			}
			/* 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) */

/*- 
       The Appendmsg() and Rendermsg() macros exist to isolate the
   mechanics behind displaying something on the msg line. this is to
   accomodate window versions of elvis which may display the message
   line somewhere other than the main editing window. 
       appendmsg() and rendermsg() are the simple tty based versions of
   Rendermsg() and Appendmsg(). See wconfig.h for what Rendermsg and
   Appendmsg are defined as.
       Note that they must stay non-static this is so window versions can
   use them if they must fall back on using the tty they were started
   from 
   
   - MCH
*/

int appendmsg(orig, new)
	char *orig, *new;
{
# ifdef lint
	orig = orig; /* orig param unused by tty version */
# endif
	qaddstr(new);
	refresh();
	return 0;
}
int rendermsg(msgtxt)
	char *msgtxt;
{
	move(LINES - 1, 0);
	if (*msgtxt)
	{
	    standout();
	    qaddch(' ');
		    qaddstr(msgtxt);
	    qaddch(' ');
	    standend();
	}
	clrtoeol();
	return 0;
}

static int showmsg()
{
	/* if there is no message to show, then don't */
	if (!manymsgs)
		return FALSE;
	
	Rendermsg(pmsg); /* does the actual rendering */
	
	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
 */

#ifdef NO_VSPRINTF

/* use this *only* if you do not have vsprintf this is dangerous on
things like a sparc chip  - MCH */

/*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;
	}
}
#else

#include <varargs.h>

void msg(va_alist)
	va_dcl
{
	char *fmt;
	va_list args;

	va_start(args);
	fmt = va_arg(args, char *);
	if (mode != MODE_VI)
	{
		vsprintf(pmsg, fmt, args);
		qaddstr(pmsg);
		addch('\n');
		exrefresh();
	}
	else
	{
		/* wait for keypress between consecutive msgs */
		if (manymsgs)
		{
			getkey(WHEN_MSG);
		}

		/* real message */
		vsprintf(pmsg, fmt, args);
		manymsgs = TRUE;
	}
	va_end(args);
	return;
}
#endif


/* This function calls refresh() if the option exrefresh is set */
void exrefresh()
{
	char	*scan;

	/* If this ex command wrote ANYTHING set exwrote so vi's  :  command
	 * can tell that it must wait for a user keystroke before redrawing.
	 */
	for (scan=kbuf; scan<stdscr; scan++)
		if (*scan == '\n')
			exwrote = TRUE;

#if MICROSOFT			/* avoid compiler bug */
	scan = stdscr;
#define	stdscr	scan
#endif
	/* now we do the refresh thing */
	if (*o_exrefresh)
	{
		refresh();
	}
	else
	{
		wqrefresh(stdscr);
	}
#if MICROSOFT
#undef	stdscr
	stdscr = scan;
#endif
	if (mode != MODE_VI)
	{
		manymsgs = FALSE;
	}
}



/* These are used for typeahead, and also for fudging the visual @ command */
static char	keybuf[100];	/* array of already-read keys */
static int	nkeys;		/* total number of keys in keybuf */
static int	next;		/* index of next key to return */
#ifndef NO_AT
static int	atnext;		/* index of next key for "@", or 0 normally */
int fromcutbuf(cbname)
	int	cbname;
{
	int	len;

	/* fail if we're already doing an @ macro */
	if (atnext > 0 && keybuf[atnext])
	{
		msg("Can't nest @ commands");
		return FALSE;
	}

	/* use the empty portion of keybuf[] to get chars from the cut buffer */
	len = cb2str(cbname, keybuf + nkeys, sizeof keybuf - nkeys);
	if (len < 0)
	{
		msg("Invalid cut buffer name.  Must be a-z");
		return FALSE;
	}
	if (len == 0)
	{
		msg("Cut buffer \"%c is empty", cbname);
		return FALSE;
	}
	else if (len >= sizeof keybuf - nkeys)
	{
		msg("Cut buffer \"%c is too large to execute", cbname);
		return FALSE;
	}

	/* prepare to "read" those keys on subsequent getkey() calls */
	atnext = nkeys;
	return TRUE;
}
#endif

/* This array describes mapped key sequences */
static struct _keymap
{
	char	*name;		/* name of the key, or NULL */
	char	rawin[LONGKEY];	/* the unmapped version of input */
	char	cooked[80];	/* the mapped version of input */
	int	len;		/* length of the unmapped version */
	int	when;		/* when is this key mapped? */
}
	mapped[MAXMAPS];

#if !MSDOS && !TOS
# if BSD || COHERENT
static jmp_buf env_timeout;
static int dummy()
{
	longjmp(env_timeout, 1);
	return 0;
}
# else 
static int dummy()
{
}
# endif
#endif

/* This function reads in a keystroke for VI mode.  It automatically handles
 * key mapping.
 */
int getkey(when)
	int		when;		/* which bits must be ON? */
{
	static char	*cooked;	/* rawin, or pointer to converted key */ 
	static int	oldwhen;	/* "when" from last time */
	static int	oldleft;
	static long	oldtop;
	static long	oldnlines;
	static char	*cshape;	/* current cursor shape */
	REG char	*kptr;		/* &keybuf[next] */
	REG struct _keymap *km;	/* used to count through keymap */
	REG int		i, j, k;

#ifdef DEBUG
	watch();
#endif

	/* if this key is needed for delay between multiple error messages,
	 * then reset the manymsgs flag and abort any mapped key sequence.
	 */
	if (showmsg())
	{
		if (when == WHEN_MSG)
		{
			Appendmsg(pmsg, "[More...]");
			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
SHAR_EOF
fi # end of overwriting check
if test -f 'tmp.c'
then
	echo shar: will not over-write existing file "'tmp.c'"
else
cat << \SHAR_EOF > 'tmp.c'
/* tmpfile.c */

/* Author:
 *	Steve Kirkendall
 *	14407 SW Teal Blvd. #C
 *	Beaverton, OR 97005
 *	kirkenda@cs.pdx.edu
 */


/* This file contains functions which create & readback a TMPFILE */


#include "config.h"
#include <ctype.h>
#include "vi.h"
#if TOS
# include <stat.h>
#else
# if OSK
#  include "osk.h"
# else
#  include <sys/stat.h>
# endif
#endif

#ifndef NO_MODELINE
static void do_modeline(l, stop)
	long	l;	/* line number to start at */
	long	stop;	/* line number to stop at */
{
	char	*str;	/* used to scan through the line */
	char	*start;	/* points to the start of the line */
	char	buf[80];

	/* if modelines are disabled, then do nothing */
	if (!*o_modeline)
	{
		return;
	}

	/* for each line... */
	for (l = 1; l <= stop; l++)
	{
		/* for each position in the line.. */
		for (str = fetchline(l); *str; str++)
		{
			/* if it is the start of a modeline command... */
			if ((str[0] == 'e' && str[1] == 'x'
			  || str[0] == 'v' && str[1] == 'i')
			  && str[2] == ':')
			{
				start = str += 3;

				/* find the end */
				while (*str && *str != ':')
				{
					str++;
				}

				/* if it is a well-formed modeline, execute it */
				if (*str && str - start < sizeof buf)
				{
					strncpy(buf, start, (int)(str - start));
					buf[str - start] = '\0';
					doexcmd(buf);
					break;
				}
			}
		}
	}
}
#endif


/* The FAIL() macro prints an error message and then exits. */
#define FAIL(why,arg)	mode = MODE_EX; msg(why, arg); endwin(); exit(9)

/* This is the name of the temp file */
static char	tmpname[MAXTMPNAMELEN];

/* 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
	/* throw the pid into the name so we can edit the same file more
	 * than once MCH */
	sprintf(tmpname, TMPNAME, o_directory, sum, statb.st_ino, statb.st_dev,
		getpid());
#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
SHAR_EOF
fi # end of overwriting check
if test -f 'tnamerec.c'
then
	echo shar: will not over-write existing file "'tnamerec.c'"
else
cat << \SHAR_EOF > 'tnamerec.c'
#include "config.h"

# if SUNOS || UNIXV /* systems using X/OPEN type directory routines */
#  define USING_DIRENT 1
#  define USING_DIRECT 0
#  include <dirent.h>
# endif

/* not implemented yet, should be real easy to do though just
hack the X/OPEN style version */
# if 0 /* put systems using struct direct here, 4.2 etc.. */
#  define USING_DIRENT 0 
#  define USING_DIRECT 1 # endif
# endif

#if MSDOS || TOS

# define NO_PID_IN_TMP_NAME 1
# define USING_DIRENT 0
# define USING_DIRECT 0

#endif

/* int tnamerec(template)
 *     char *template;
 * ====
 * Find the true name of a temp file given a template which has nonsense
 * in the pid portion (the part after the dash)
 * This is mainly for use by virec program
 * ====
 * -2 indicates the argument supplied is malformed
 * -1 indicates tmp file with the supplied template was not found
 * 0 indicates the argument has been converted properly
 * >0 indicates an argument has been converted OK but there is more
 * than one tmp file which matches the template
 * ====
 * As a side effect , template is modified to contain the last filename
 * that matched template.
 * ====
 * note: this module is highly dependent on TMPNAME #define in config.h
 */

#ifdef NO_PID_IN_TMPNAME
int tnamerec(template)
char *template;
{
	return 0;
}
#endif

#if USING_DIRENT || USING_DIRECT

int tnamerec(template)
    char *template;
{
# if USING_DIRECT
    {{{{{{{{ to be done
# endif
# if USING_DIRENT
    DIR *dirp;
    struct dirent *entry;
# endif
    
    extern char *strrchr();
    int c;
    char *p;
    char *suff;
    int count = -1;
    int cmplen;
    if ( !(p = strrchr(template, '/')))
	return -2;
    if (!(suff = strrchr(p, '-')))
	return -2;
    cmplen = suff - p;
	
    c = *p; *p = '\0'; /* temporarily truncate off the filename */
    if (!(dirp = opendir(template)))
    {
	*p = c;
	return -1;
    }
    *p++ = c; /* put filename back on */
	
    /* scan the directory looking for a file which matches 'template'
    if the two are the same length and the stuff up to the dash is the
    same and the suffix is a number it's a match */

# if USING_DIRECT
    {{{{{to_be_done{{{{
# endif

# if USING_DIRENT
    for (entry = readdir(dirp); entry; entry = readdir(dirp))
    {
	if (  !strncmp(entry->d_name, p, cmplen) &&
	      (strlen(p) == strlen(entry->d_name)) )
	{
	    char *ext_suff = strchr(entry->d_name, '-');
	    if (ext_suff && atoi((ext_suff+1)) > 0)
	    {
		strcpy(p, entry->d_name);
		count++;
	    }
	}
    }
    return count;
# endif
}

#endif
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0

mh@roger.imsd.contel.com (Mike H.) (01/11/91)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	vars.c
#	vcmd.c
#	vi.c
#	virec.c
#	wildcard.c
# This archive created: Thu Jan 10 17:21:11 1991
export PATH; PATH=/bin:$PATH
if test -f 'vars.c'
then
	echo shar: will not over-write existing file "'vars.c'"
else
cat << \SHAR_EOF > 'vars.c'
/* 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
SHAR_EOF
fi # end of overwriting check
if test -f 'vcmd.c'
then
	echo shar: will not over-write existing file "'vcmd.c'"
else
cat << \SHAR_EOF > 'vcmd.c'
/* vcmd.c */

/* Author:
 *	Steve Kirkendall
 *	14407 SW Teal Blvd. #C
 *	Beaverton, OR 97005
 *	kirkenda@cs.pdx.edu
 */


/* This file contains the functions that handle VI commands */


#include "config.h"
#include "vi.h"
#if MSDOS
#include <process.h>
#include <string.h>
#endif
#if TOS
#include <osbind.h>
#include <string.h>
#endif
#if OSK
# include <stdio.h>
#endif


/* This function puts the editor in EX mode */
MARK v_quit()
{
	move(LINES - 1, 0);
	mode = MODE_EX;
	return cursor;
}

/* This function causes the screen to be redrawn */
MARK v_redraw()
{
	redraw(MARK_UNSET, FALSE);
	return cursor;
}

/* This function executes a single EX command, and waits for a user keystroke
 * before returning to the VI screen.  If that keystroke is another ':', then
 * another EX command is read and executed.
 */
/*ARGSUSED*/
MARK v_1ex(m, text)
	MARK	m;	/* the current line */
	char	*text;	/* the first command to execute */
{
	/* run the command.  be careful about modes & output */
	exwrote = (mode == MODE_COLON);
	doexcmd(text);
	exrefresh();

	/* if mode is no longer MODE_VI, then we should quit right away! */
	if (mode != MODE_VI && mode != MODE_COLON)
		return cursor;

	/* The command did some output.  Wait for a keystoke. */
	if (exwrote)
	{
		mode = MODE_VI;	
		msg("[Hit <RETURN> to continue]");
		if (getkey(0) == ':')
		{	mode = MODE_COLON;
			addch('\n');
		}
		else
			redraw(MARK_UNSET, FALSE);
	}

	return cursor;
}

/* This function undoes the last change */
/*ARGSUSED*/
MARK v_undo(m)
	MARK	m;	/* (ignored) */
{
	if (undo())
	{
		redraw(MARK_UNSET, FALSE);
	}
	return cursor;
}

/* This function deletes the character(s) that the cursor is on */
MARK v_xchar(m, cnt, cmd)
	MARK	m;	/* where to start deletions */
	long	cnt;	/* number of chars to delete */
	int	cmd;	/* either 'x' or 'X' */
{
	DEFAULT(1);

	/* for 'X', adjust so chars are deleted *BEFORE* cursor */
	if (cmd == 'X')
	{
		if (markidx(m) < cnt)
			return MARK_UNSET;
		m -= cnt;
	}

	/* make sure we don't try to delete more thars than there are */
	pfetch(markline(m));
	if (markidx(m + cnt) > plen)
	{
		cnt = plen - markidx(m);
	}
	if (cnt == 0L)
	{
		return MARK_UNSET;
	}

	/* do it */
	ChangeText
	{
		cut(m, m + cnt);
		delete(m, m + cnt);
	}
	return m;
}

/* This function defines a mark */
/*ARGSUSED*/
MARK v_mark(m, count, key)
	MARK	m;	/* where the mark will be */
	long	count;	/* (ignored) */
	int	key;	/* the ASCII label of the mark */
{
	if (key < 'a' || key > 'z')
	{
		msg("Marks must be from a to z");
	}
	else
	{
		mark[key - 'a'] = m;
	}
	return m;
}

/* This function toggles upper & lower case letters */
MARK v_ulcase(m, cnt)
	MARK	m;	/* where to make the change */
	long	cnt;	/* number of chars to flip */
{
	REG char 	*pos;
	REG int		i, j;
	static char	flip[] =
		"aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ[](){}<>";

	DEFAULT(1);

	/* fetch the current version of the line */
	pfetch(markline(m));

	/* for each position in the line */
	for (j = 0, i = markidx(m); j < cnt && ptext[i]; j++, i++)
	{
		tmpblk.c[j] = 0;

		/* one of the standard chars? */
		for (pos = flip; *pos && *pos != ptext[i]; pos++)
		{
		}
		if (*pos)
		{
			tmpblk.c[j] = flip[(int)(pos - flip) ^ 1];
		}
#ifndef NO_DIGRAPH
		else /* one of the non-standard chars? */
		{
			for (pos = o_flipcase; *pos && *pos != ptext[i]; pos++)
			{
			}
			if (*pos)
			{
				tmpblk.c[j] = o_flipcase[(int)(pos - o_flipcase) ^ 1];
			}
		}
#endif

		/* if nothing special, then don't change it */
		if (tmpblk.c[j] == 0)
		{
			tmpblk.c[j] = ptext[i];
		}
	}

	/* if the new text is different from the old, then change it */
	if (strncmp(tmpblk.c, &ptext[markidx(m)], j))
	{
		ChangeText
		{
			tmpblk.c[j] = '\0';
			change(m, m + j, tmpblk.c);
		}
	}

	return m + j;
}


MARK v_replace(m, cnt, key)
	MARK	m;	/* first char to be replaced */
	long	cnt;	/* number of chars to replace */
	int	key;	/* what to replace them with */
{
	REG char	*text;
	REG int		i;

	DEFAULT(1);

	/* map ^M to '\n' */
	if (key == '\r')
	{
		key = '\n';
	}

	/* make sure the resulting line isn't too long */
	if (cnt > BLKSIZE - 2 - markidx(m))
	{
		cnt = BLKSIZE - 2 - markidx(m);
	}

	/* build a string of the desired character with the desired length */
	for (text = tmpblk.c, i = cnt; i > 0; i--)
	{
		*text++ = key;
	}
	*text = '\0';

	/* make sure cnt doesn't extend past EOL */
	pfetch(markline(m));
	key = markidx(m);
	if (key + cnt > plen)
	{
		cnt = plen - key;
	}

	/* do the replacement */
	ChangeText
	{
		change(m, m + cnt, tmpblk.c);
	}

	if (*tmpblk.c == '\n')
	{
		return (m & ~(BLKSIZE - 1)) + cnt * BLKSIZE;
	}
	else
	{
		return m + cnt - 1;
	}
}

MARK v_overtype(m)
	MARK		m;	/* where to start overtyping */
{
	MARK		end;	/* end of a substitution */
	static long	width;	/* width of a single-line replace */

	/* the "doingdot" version of replace is really a substitution */
	if (doingdot)
	{
		/* was the last one really repeatable? */
		if (width < 0)
		{
			msg("Can't repeat a multi-line overtype command");
			return MARK_UNSET;
		}

		/* replacing nothing by nothing?  Don't bother */
		if (width == 0)
		{
			return m;
		}

		/* replace some chars by repeated text */
		return v_subst(m, width);
	}

	/* Normally, we input starting here, in replace mode */
	ChangeText
	{
		end = input(m, m, WHEN_VIREP, -1L);
	}

	/* 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? */
	long	indentref = -1L;

	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;
			indentref = 1L;
			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, indentref);
			if (after)
			{
				m++;
			}
		}
		if (after)
		{
			m--;
		}

		doingdot = wasdot;
	}

#ifndef CRUNCH
# ifndef NO_EXTENSIONS
	if (key == 'i' && *o_inputmode && mode == MODE_VI)
	{
		msg("Now in visual command mode!  To return to input mode, hit <i>.");
	}
# endif
#endif

	return m;
}

/* This starts input mode with some text deleted */
MARK v_change(m, n)
	MARK	m, n;	/* the range of text to change */
{
	int	lnmode;	/* is this a line-mode change? */

	/* swap them if they're in reverse order */
	if (m > n)
	{
		MARK	tmp;
		tmp = m;
		m = n;
		n = tmp;
	}

	/* for line mode, retain the last newline char */
	lnmode = (markidx(m) == 0 && markidx(n) == 0 && m != n);
	if (lnmode)
	{
		n -= BLKSIZE;
		pfetch(markline(n));
		n = (n & ~(BLKSIZE - 1)) + plen;
	}

	ChangeText
	{
		cut(m, n);
		m = input(m, n, WHEN_VIINP, -1L);
	}

	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, -1L);
	}
	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
SHAR_EOF
fi # end of overwriting check
if test -f 'vi.c'
then
	echo shar: will not over-write existing file "'vi.c'"
else
cat << \SHAR_EOF > 'vi.c'
/* vi.c */

/* Author:
 *	Steve Kirkendall
 *	14407 SW Teal Blvd. #C
 *	Beaverton, OR 97005
 *	kirkenda@cs.pdx.edu
 */


#include "config.h"
#include <ctype.h>
#include "vi.h"



/* This array describes what each key does */
#define NO_FUNC		(MARK (*)())0
#define NO_ARGS		0
#define CURSOR_COUNT	1
#define CURSOR		2
#define CURSOR_CNT_KEY	3
#define CURSOR_MOVED	4
#define CURSOR_EOL	5
#define ZERO		6
#define DIGIT		7
#define CURSOR_TEXT	8
#define CURSOR_CNT_CMD	9
#define KEYWORD		10
#define NO_FLAGS	0x00
#define	MVMT		0x01	/* this is a movement command */
#define PTMV		0x02	/* this can be *part* of a movement command */
#define FRNT		0x04	/* after move, go to front of line */
#define INCL		0x08	/* include last char when used with c/d/y */
#define LNMD		0x10	/* use line mode of c/d/y */
#define NCOL		0x20	/* this command can't change the column# */
#define NREL		0x40	/* this is "non-relative" -- set the '' mark */
#define SDOT		0x80	/* set the "dot" variables, for the "." cmd */
static struct keystru
{
	MARK	(*func)();	/* the function to run */
	uchar	args;		/* description of the args needed */
	uchar	flags;		/* other stuff */
}
	vikeys[] =
{
/* NUL not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^A  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^B  page backward	*/	{m_scroll,	CURSOR_CNT_CMD,	FRNT},
/* ^C  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^D  scroll dn 1/2page*/	{m_scroll,	CURSOR_CNT_CMD,	NCOL},
/* ^E  scroll up	*/	{m_scroll,	CURSOR_CNT_CMD,	NCOL},
/* ^F  page forward	*/	{m_scroll,	CURSOR_CNT_CMD,	FRNT},
/* ^G  show file status	*/	{v_status,	NO_ARGS, 	NO_FLAGS},
/* ^H  move left, like h*/	{m_left,	CURSOR_COUNT,	MVMT},
/* ^I  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^J  move down	*/	{m_updnto,	CURSOR_CNT_CMD,	MVMT|LNMD},
/* ^K  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^L  redraw screen	*/	{v_redraw,	NO_ARGS,	NO_FLAGS},
/* ^M  mv front next ln */	{m_updnto,	CURSOR_CNT_CMD,	MVMT|FRNT|LNMD},
/* ^N  move down	*/	{m_updnto,	CURSOR_CNT_CMD,	MVMT|LNMD},
/* ^O  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^P  move up		*/	{m_updnto,	CURSOR_CNT_CMD,	MVMT|LNMD},
/* ^Q  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^R  redraw screen	*/	{v_redraw,	NO_ARGS,	NO_FLAGS},
/* ^S  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^T  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^U  scroll up 1/2page*/	{m_scroll,	CURSOR_CNT_CMD,	NCOL},
/* ^V  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^W  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^X  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^Y  scroll down	*/	{m_scroll,	CURSOR_CNT_CMD,	NCOL},
/* ^Z  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ESC not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^\  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^]  keyword is tag	*/	{v_tag,		KEYWORD,	NO_FLAGS},
/* ^^  previous file	*/	{v_switch,	CURSOR,		NO_FLAGS},
/* ^_  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* SPC move right,like l*/	{m_right,	CURSOR_COUNT,	MVMT},
/*  !  run thru filter	*/	{v_filter,	CURSOR_MOVED,	FRNT|LNMD|INCL},
/*  "  select cut buffer*/	{v_selcut,	CURSOR_CNT_KEY,	PTMV},
#ifndef NO_EXTENSIONS
/*  #  increment number	*/	{v_increment,	KEYWORD,	SDOT},
#else
/*  #  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
#endif
/*  $  move to rear	*/	{m_rear,	CURSOR,		MVMT|INCL},
/*  %  move to match	*/	{m_match,	CURSOR,		MVMT|INCL},
/*  &  repeat subst	*/	{v_again,	CURSOR_MOVED,	SDOT|NCOL|LNMD|INCL},
/*  '  move to a mark	*/	{m_tomark,	CURSOR_CNT_KEY,	MVMT|FRNT|NREL|LNMD|INCL},
#ifndef NO_SENTENCE
/*  (  mv back sentence	*/	{m_bsentence,	CURSOR_COUNT,	MVMT},
/*  )  mv fwd sentence	*/	{m_fsentence,	CURSOR_COUNT,	MVMT},
#else
/*  (  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  )  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
#endif
#ifndef NO_ERRLIST
/*  *  errlist		*/	{v_errlist,	CURSOR,		FRNT|NREL},
#else
/*  *  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
#endif
/*  +  mv front next ln */	{m_updnto,	CURSOR_CNT_CMD,	MVMT|FRNT|LNMD},
#ifndef NO_CHARSEARCH
/*  ,  reverse [fFtT] cmd*/	{m__ch,		CURSOR_CNT_CMD,	MVMT|INCL},
#else
/*  ,  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
#endif
/*  -  mv front prev ln	*/	{m_updnto,	CURSOR_CNT_CMD,	MVMT|FRNT|LNMD},
/*  .  special...	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  /  forward search	*/	{m_fsrch,	CURSOR_TEXT,	MVMT|NREL},
/*  0  part of count?	*/	{NO_FUNC,	ZERO,		MVMT|PTMV},
/*  1  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  2  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  3  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  4  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  5  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  6  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  7  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  8  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  9  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  :  run single EX cmd*/	{v_1ex,		CURSOR_TEXT,	NO_FLAGS},
#ifndef NO_CHARSEARCH
/*  ;  repeat [fFtT] cmd*/	{m__ch,		CURSOR_CNT_CMD,	MVMT|INCL},
#else
/*  ;  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
#endif
/*  <  shift text left	*/	{v_lshift,	CURSOR_MOVED,	SDOT|FRNT|LNMD|INCL},
/*  =  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  >  shift text right	*/	{v_rshift,	CURSOR_MOVED,	SDOT|FRNT|LNMD|INCL},
/*  ?  backward search	*/	{m_bsrch,	CURSOR_TEXT,	MVMT|NREL},
#ifndef NO_AT
/*  @  execute a cutbuf */	{v_at,		CURSOR_CNT_KEY,	NO_FLAGS},
#else
/*  @  undefined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
#endif
/*  A  append at EOL	*/	{v_insert,	CURSOR_CNT_CMD,	SDOT},
/*  B  move back Word	*/	{m_bword,	CURSOR_CNT_CMD,	MVMT},
/*  C  change to EOL	*/	{v_change,	CURSOR_EOL,	SDOT},
/*  D  delete to EOL	*/	{v_delete,	CURSOR_EOL,	SDOT},
/*  E  move end of Word	*/	{m_eword,	CURSOR_CNT_CMD,	MVMT|INCL},
#ifndef NO_CHARSEARCH
/*  F  move bk to char	*/	{m_Fch,		CURSOR_CNT_KEY,	MVMT|INCL},
#else
/*  F  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
#endif
/*  G  move to line #	*/	{m_updnto,	CURSOR_CNT_CMD,	MVMT|NREL|LNMD|FRNT|INCL},
/*  H  move to row	*/	{m_row,		CURSOR_CNT_CMD,	MVMT|FRNT},
/*  I  insert at front	*/	{v_insert,	CURSOR_CNT_CMD,	SDOT},
/*  J  join lines	*/	{v_join,	CURSOR_COUNT,	SDOT},
#ifndef NO_EXTENSIONS
/*  K  look up keyword	*/	{v_keyword,	KEYWORD,	NO_FLAGS},
#else
/*  K  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
#endif
/*  L  move to last row	*/	{m_row,		CURSOR_CNT_CMD,	MVMT|FRNT},
/*  M  move to mid row	*/	{m_row,		CURSOR_CNT_CMD,	MVMT|FRNT},
/*  N  reverse prev srch*/	{m_Nsrch,	CURSOR,		MVMT},
/*  O  insert above line*/	{v_insert,	CURSOR_CNT_CMD,	SDOT},
/*  P  paste before	*/	{v_paste,	CURSOR_CNT_CMD,	NO_FLAGS},
/*  Q  quit to EX mode	*/	{v_quit,	NO_ARGS,	NO_FLAGS},
/*  R  overtype		*/	{v_overtype,	CURSOR,		SDOT},
/*  S  change line	*/	{v_change,	CURSOR_MOVED,	SDOT},
#ifndef NO_CHARSEARCH
/*  T  move bk to char	*/	{m_Tch,		CURSOR_CNT_KEY,	MVMT|INCL},
#else
/*  T  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
#endif
/*  U  undo whole line	*/	{v_undoline,	CURSOR,		FRNT},
/*  V  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  W  move forward Word*/	{m_fword,	CURSOR_CNT_CMD,	MVMT},
/*  X  delete to left	*/	{v_xchar,	CURSOR_CNT_CMD,	SDOT},
/*  Y  yank text	*/	{v_yank,	CURSOR_MOVED,	NCOL},
/*  Z  save file & exit	*/	{v_xit,		CURSOR_CNT_KEY,	NO_FLAGS},
/*  [  move back section*/	{m_bsection,	CURSOR_CNT_KEY,	MVMT|LNMD|NREL},
/*  \  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  ]  move fwd section */	{m_fsection,	CURSOR_CNT_KEY,	MVMT|LNMD|NREL},
/*  ^  move to front	*/	{m_front,	CURSOR,		MVMT},
/*  _  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  `  move to mark	*/	{m_tomark,	CURSOR_CNT_KEY,	MVMT|NREL},
/*  a  append at cursor	*/	{v_insert,	CURSOR_CNT_CMD,	SDOT},
/*  b  move back word	*/	{m_bword,	CURSOR_CNT_CMD,	MVMT},
/*  c  change text	*/	{v_change,	CURSOR_MOVED,	SDOT},
/*  d  delete op	*/	{v_delete,	CURSOR_MOVED,	SDOT|NCOL},
/*  e  move end word	*/	{m_eword,	CURSOR_CNT_CMD,	MVMT|INCL},
#ifndef NO_CHARSEARCH
/*  f  move fwd for char*/	{m_fch,		CURSOR_CNT_KEY,	MVMT|INCL},
#else
/*  f  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
#endif
/*  g  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  h  move left	*/	{m_left,	CURSOR_COUNT,	MVMT},
/*  i  insert at cursor	*/	{v_insert,	CURSOR_CNT_CMD,	SDOT},
/*  j  move down	*/	{m_updnto,	CURSOR_CNT_CMD,	MVMT|NCOL|LNMD},
/*  k  move up		*/	{m_updnto,	CURSOR_CNT_CMD,	MVMT|NCOL|LNMD},
/*  l  move right	*/	{m_right,	CURSOR_COUNT,	MVMT},
/*  m  define a mark	*/	{v_mark,	CURSOR_CNT_KEY,	NO_FLAGS},
/*  n  repeat prev srch	*/	{m_nsrch,	CURSOR, 	MVMT},
/*  o  insert below line*/	{v_insert,	CURSOR_CNT_CMD,	SDOT},
/*  p  paste after	*/	{v_paste,	CURSOR_CNT_CMD,	NO_FLAGS},
/*  q  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  r  replace chars	*/	{v_replace,	CURSOR_CNT_KEY,	SDOT},
/*  s  subst N chars	*/	{v_subst,	CURSOR_COUNT,	SDOT},
#ifndef NO_CHARSEARCH
/*  t  move fwd to char	*/	{m_tch,		CURSOR_CNT_KEY,	MVMT|INCL},
#else
/*  t  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
#endif
/*  u  undo		*/	{v_undo,	CURSOR,		NO_FLAGS},
/*  v  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  w  move fwd word	*/	{m_fword,	CURSOR_CNT_CMD,	MVMT},
/*  x  delete character	*/	{v_xchar,	CURSOR_CNT_CMD,	SDOT},
/*  y  yank text	*/	{v_yank,	CURSOR_MOVED,	NCOL},
/*  z  adjust scrn row	*/	{m_z, 		CURSOR_CNT_KEY,	NCOL},
/*  {  back paragraph	*/	{m_bparagraph,	CURSOR_COUNT,	MVMT|LNMD},
/*  |  move to column	*/	{m_tocol,	CURSOR_COUNT,	NREL},
/*  }  fwd paragraph	*/	{m_fparagraph,	CURSOR_COUNT,	MVMT|LNMD},
/*  ~  upper/lowercase	*/	{v_ulcase,	CURSOR_COUNT,	SDOT},
/* DEL not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS}
};



void vi()
{
	REG int			key;	/* keystroke from user */
	long			count;	/* numeric argument to some functions */
	REG struct keystru	*keyptr;/* pointer to vikeys[] element */
	MARK			tcurs;	/* temporary cursor */
	int			prevkey;/* previous key, if d/c/y/</>/! */
	MARK			range;	/* start of range for d/c/y/</>/! */
	char			text[100];
	int			dotkey;	/* last "key" of a change */
	int			dotpkey;/* last "prevkey" of a change */
	int			dotkey2;/* last extra "getkey()" of a change */
	int			dotcnt;	/* last "count" of a change */
	int			firstkey;
	REG int			i;

	/* tell the redraw() function to start from scratch */
	redraw(MARK_UNSET, FALSE);

#ifdef lint
	/* lint says that "range" might be used before it is set.  This
	 * can't really happen due to the way "range" and "prevkey" are used,
	 * but lint doesn't know that.  This line is here ONLY to keep lint
	 * happy.
	 */
	range = 0L;
#endif

	/* safeguard against '.' with no previous command */
	dotkey = 0;

	/* go immediately into insert mode, if ":set inputmode" */
	firstkey = 0;
#ifndef NO_EXTENSIONS
	if (*o_inputmode)
	{
		firstkey = 'i';
	}
#endif

	/* Repeatedly handle VI commands */
	for (count = 0, prevkey = '\0'; mode == MODE_VI; )
	{
		/* if we've moved off the undoable line, then we can't undo it at all */
		if (markline(cursor) != U_line)
		{
			U_line = 0L;
		}

		/* report any changes from the previous command */
		if (rptlines >= *o_report)
		{
			redraw(cursor, FALSE);
			msg("%ld lines %s", rptlines, rptlabel);
		}
		rptlines = 0L;

		/* get the next command key.  It must be ASCII */
		if (firstkey)
		{
			key = firstkey;
			firstkey = 0;
		}
		else
		{
			do
			{
				key = getkey(WHEN_VICMD);
			} while (key < 0 || key > 127);
		}

		/* change cw and cW commands to ce and cE, respectively */
		/* (Why?  because the real vi does it that way!) */
		if (prevkey == 'c')
		{
			if (key == 'w')
				key = 'e';
			else if (key == 'W')
				key = 'E';

			/* wouldn't work right at the end of a word unless we
			 * backspace one character before doing the move.  This
			 * will fix most cases.  !!! but not all.
			 */
			if (markidx(cursor) > 0 && (key == 'e' || key == 'E'))
			{
				cursor--;
			}
		}

		/* look up the structure describing this command */
		keyptr = &vikeys[key];

		/* if we're in the middle of a d/c/y/</>/! command, reject
		 * anything but movement or a doubled version like "dd".
		 */
		if (prevkey && key != prevkey && !(keyptr->flags & (MVMT|PTMV)))
		{
			Beep();
			prevkey = 0;
			count = 0;
			continue;
		}

		/* set the "dot" variables, if we're supposed to */
		if ((keyptr->flags & SDOT)
		 || (prevkey && vikeys[prevkey].flags & SDOT))
		{
			dotkey = key;
			dotpkey = prevkey;
			dotkey2 = '\0';
			dotcnt = count;

			/* remember the line before any changes are made */
			if (U_line != markline(cursor))
			{
				U_line = markline(cursor);
				strcpy(U_text, fetchline(U_line));
			}
		}

		/* if this is "." then set other vars from the "dot" vars */
		if (key == '.')
		{
			key = dotkey;
			keyptr = &vikeys[key];
			prevkey = dotpkey;
			if (prevkey)
			{
				range = cursor;
			}
			if (count == 0)
			{
				count = dotcnt;
			}
			doingdot = TRUE;

			/* remember the line before any changes are made */
			if (U_line != markline(cursor))
			{
				U_line = markline(cursor);
				strcpy(U_text, fetchline(U_line));
			}
		}
		else
		{
			doingdot = FALSE;
		}

		/* process the key as a command */
		tcurs = cursor;
		switch (keyptr->args)
		{
		  case ZERO:
			if (count == 0)
			{
				tcurs = cursor & ~(BLKSIZE - 1);
				break;
			}
			/* else fall through & treat like other digits... */

		  case DIGIT:
			count = count * 10 + key - '0';
			break;

		  case KEYWORD:
			/* if not on a keyword, fail */
			pfetch(markline(cursor));
			key = markidx(cursor);
			if (isascii(ptext[key])
				&& !isalnum(ptext[key]) && ptext[key] != '_')
			{
				tcurs = MARK_UNSET;
				break;
			}

			/* find the start of the keyword */
			while (key > 0 && (!isascii(ptext[key-1]) ||
			isalnum(ptext[key - 1]) || ptext[key - 1] == '_'))
			{
				key--;
			}
			tcurs = (cursor & ~(BLKSIZE - 1)) + key;

			/* copy it into a buffer, and NUL-terminate it */
			i = 0;
			do
			{
				text[i++] = ptext[key++];
			} while (!isascii(ptext[key]) || isalnum(ptext[key]) || ptext[key] == '_');
			text[i] = '\0';

			/* call the function */
			tcurs = (*keyptr->func)(text, tcurs, count);
			count = 0L;
			break;

		  case NO_ARGS:
			if (keyptr->func)
			{
				(*keyptr->func)();
			}
			else
			{
				Beep();
			}
			count = 0L;
			break;
	
		  case CURSOR_COUNT:
			tcurs = (*keyptr->func)(cursor, count);
			count = 0L;
			break;
	
		  case CURSOR:
			tcurs = (*keyptr->func)(cursor);
			count = 0L;
			break;

		  case CURSOR_CNT_KEY:
			if (doingdot)
			{
				tcurs = (*keyptr->func)(cursor, count, dotkey2);
			}
			else
			{
				/* get a key */
				i = getkey(0);
				if (i == '\033') /* ESC */
				{
					count = 0;
					tcurs = MARK_UNSET;
					break; /* exit from "case CURSOR_CNT_KEY" */
				}
				else if (i == ('V' & 0x1f))
				{
					i = getkey(0);
				}

				/* if part of an SDOT command, remember it */
				 if (keyptr->flags & SDOT
				 || (prevkey && vikeys[prevkey].flags & SDOT))
				{
					dotkey2 = i;
				}

				/* do it */
				tcurs = (*keyptr->func)(cursor, count, i);
			}
			count = 0L;
			break;
	
		  case CURSOR_MOVED:
			/* '&' and uppercase keys always act like doubled */
			if (key == '&' || isascii(key) && isupper(key))
			{
				prevkey = key;
			}

			if (prevkey)
			{
				/* doubling up a command */
				if (!count) count = 1L;
				range = cursor;
				tcurs = range + MARK_AT_LINE(count - 1L);
				count = 0L;
			}
			else
			{
				prevkey = key;
				range = cursor;
				key = -1; /* so we don't think we doubled yet */
			}
			break;

		  case CURSOR_EOL:
			prevkey = key;
			/* a zero-length line needs special treatment */
			pfetch(markline(cursor));
			if (plen == 0)
			{
				/* act on a zero-length section of text */
				range = tcurs = cursor;
				key = ' ';
			}
			else
			{
				/* act like CURSOR_MOVED with '$' movement */
				range = cursor;
				tcurs = m_rear(cursor, 1L);
				key = '$';
			}
			count = 0L;
			keyptr = &vikeys[key];
			break;

		  case CURSOR_TEXT:
		  	do
		  	{	
				text[0] = key;
				if (vgets(key, text + 1, sizeof text - 1) >= 0)
				{
					/* reassure user that <CR> was hit */
					qaddch('\r');
					refresh();

					/* call the function with the text */
					tcurs = (*keyptr->func)(cursor, text);
				}
				else
				{
					if (exwrote || mode == MODE_COLON)
					{
						redraw(MARK_UNSET, FALSE);
					}
					mode = MODE_VI;
				}
			} while (mode == MODE_COLON);
			count = 0L;
			break;

		  case CURSOR_CNT_CMD:
			tcurs = (*keyptr->func)(cursor, count, key);
			count = 0L;
			break;
		}

		/* if that command took us out of vi mode, then exit the loop
		 * NOW, without tweaking the cursor or anything.  This is very
		 * important when mode == MODE_QUIT.
		 */
		if (mode != MODE_VI)
		{
			break;
		}

		/* now move the cursor, as appropriate */
		if (keyptr->args == CURSOR_MOVED)
		{
			/* the < and > keys have FRNT,
			 * but it shouldn't be applied yet
			 */
			tcurs = adjmove(cursor, tcurs, 0);
		}
		else
		{
			tcurs = adjmove(cursor, tcurs, (int)keyptr->flags);
		}

		/* was that the end of a d/c/y/</>/! command? */
		if (prevkey && (prevkey == key || (keyptr->flags & MVMT)) && count == 0L)
		{
			/* if the movement command failed, cancel operation */
			if (tcurs == MARK_UNSET)
			{
				prevkey = 0;
				count = 0;
				continue;
			}

			/* make sure range=front and tcurs=rear.  Either way,
			 * leave cursor=range since that's where we started.
			 */
			cursor = range;
			if (tcurs < range)
			{
				range = tcurs;
				tcurs = cursor;
			}


			/* adjust for line mode & inclusion of last char/line */
			i = (keyptr->flags | vikeys[prevkey].flags);
			if (key == prevkey)
			{
				i |= (INCL|LNMD);
			}
			switch (i & (INCL|LNMD))
			{
			  case INCL:
				tcurs++;
				break;

			  case INCL|LNMD:
				tcurs += BLKSIZE;
				/* fall through... */

			  case LNMD:
				range &= ~(BLKSIZE - 1);
				tcurs &= ~(BLKSIZE - 1);
				break;
			}

			/* run the function */
			tcurs = (*vikeys[prevkey].func)(range, tcurs);
			(void)adjmove(cursor, cursor, 0);
			cursor = adjmove(cursor, tcurs, (int)vikeys[prevkey].flags);

			/* cleanup */
			prevkey = 0;
		}
		else if (!prevkey)
		{
			cursor = tcurs;
		}
	}
}

/* This function adjusts the MARK value that they return; here we make sure
 * it isn't past the end of the line, and that the column hasn't been
 * *accidentally* changed.
 */
MARK adjmove(old, new, flags)
	MARK		old;	/* the cursor position before the command */
	REG MARK	new;	/* the cursor position after the command */
	int		flags;	/* various flags regarding cursor mvmt */
{
	static int	colno;	/* the column number that we want */
	REG char	*text;	/* used to scan through the line's text */
	REG int		i;

#ifdef DEBUG
	watch();
#endif

	/* if the command failed, bag it! */
	if (new == MARK_UNSET)
	{
		Beep();
		return old;
	}

	/* if this is a non-relative movement, set the '' mark */
	if (flags & NREL)
	{
		mark[26] = old;
	}

	/* make sure it isn't past the end of the file */
	if (markline(new) < 1)
	{
		new = MARK_FIRST;
	}
	else if (markline(new) > nlines)
	{
		new = MARK_LAST;
	}

	/* fetch the new line */
	pfetch(markline(new));

	/* move to the front, if we're supposed to */
	if (flags & FRNT)
	{
		new = m_front(new, 1L);
	}

	/* change the column#, or change the mark to suit the column# */
	if (!(flags & NCOL))
	{
		/* change the column# */
		i = markidx(new);
		if (i == BLKSIZE - 1)
		{
			new &= ~(BLKSIZE - 1);
			if (plen > 0)
			{
				new += plen - 1;
			}
			colno = BLKSIZE * 8; /* one heck of a big colno */
		}
		else if (plen > 0)
		{
			if (i >= plen)
			{
				new = (new & ~(BLKSIZE - 1)) + plen - 1;
			}
			colno = idx2col(new, ptext, FALSE);
		}
		else
		{
			new &= ~(BLKSIZE - 1);
			colno = 0;
		}
	}
	else
	{
		/* adjust the mark to get as close as possible to column# */
		for (i = 0, text = ptext; i <= colno && *text; text++)
		{
			if (*text == '\t' && !*o_list)
			{
				i += *o_tabstop - (i % *o_tabstop);
			}
			else if (UCHAR(*text) < ' ' || *text == 127)
			{
				i += 2;
			}
#ifndef NO_CHARATTR
			else if (*o_charattr && text[0] == '\\' && text[1] == 'f' && text[2])
			{
				text += 2; /* plus one more in "for()" stmt */
			}
#endif
			else
			{
				i++;
			}
		}
		if (text > ptext)
		{
			text--;
		}
		new = (new & ~(BLKSIZE - 1)) + (int)(text - ptext);
	}

	return new;
}


#ifdef DEBUG
watch()
{
	static wasset;

	if (*origname)
	{
		wasset = TRUE;
	}
	else if (wasset)
	{
		msg("origname was clobbered");
		endwin();
		abort();
	}

	if (nlines == 0)
	{
		msg("nlines=0");
		endwin();
		abort();
	}
}
#endif
SHAR_EOF
fi # end of overwriting check
if test -f 'virec.c'
then
	echo shar: will not over-write existing file "'virec.c'"
else
cat << \SHAR_EOF > 'virec.c'
/* virec.c */

/* Author:
 *	Steve Kirkendall
 *	14407 SW Teal Blvd. #C
 *	Beaverton, OR 97005
 *	kirkenda@cs.pdx.edu
 */

/* This file contains the file recovery program */


#include "config.h"
#include <stdio.h>
#include <ctype.h>
#include "vi.h"
#if TOS
# include <stat.h>
#else
# if OSK
#  include "osk.h"
# else
#  include <sys/stat.h>
# endif
#endif

extern char	*getenv();
struct stat	stbuf;
BLK		hdr;
BLK		text;

/* the name of the directory where tmp files are stored. */
char o_directory[30] = TMPDIR;

char	*progname;

main(argc, argv)
	int	argc;
	char	**argv;
{
	char	*tmp;
	void recover();
#if MSDOS || TOS
	char **wildexpand();
	argv = wildexpand(&argc, argv);
#endif
	progname = argv[0];
	/* set the o_directory variable */
	if ((tmp = getenv("TMP"))	/* yes, ASSIGNMENT! */
	 || (tmp = getenv("TEMP")))	/* yes, ASSIGNMENT! */
	{
		strcpy(o_directory, tmp);
	}
	if (argc >= 3 && !strcmp(argv[1], "-d"))
	{
		strcpy(o_directory, argv[2]);
		argc -= 2;
		argv += 2;
	}
	/* process the arguments */
	if (argc < 2)
	{
		/* maybe stdin comes from a file? */
		if (isatty(0))
		{
			fprintf(stderr, "usage: %s [-d tmpdir] lostfile...\n", progname);
		}
		else if (read(0, &hdr, (unsigned)BLKSIZE) != BLKSIZE)
		{
			fprintf(stderr, "couldn't get header\n");
		}
		else
		{
			copytext(0, stdout);
		}
	}
	else
	{
		while (--argc > 0)
		{
			recover(*++argv);
		}
	}
	exit(0);
}


/* This function recovers a single file */
void recover(filename)
	char	*filename;
{
	char		tmpname[100];
	int		tmpfd;
	int 		count;
	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,
		0);
#endif
	count = tnamerec(tmpname, TMPNAME);
	tmpfd = open(tmpname, O_RDONLY | O_BINARY);
	if (tmpfd < 0)
	{
		char buf[512];
		sprintf(buf, "recoverable version of %s", filename);
		perror(buf);
		fprintf(stderr, 
		"make sure your TMP or TEMP environment variable is correct\n");
		return;
	}

	/* make sure the file hasn't been modified more recently */
	mtime = stbuf.st_mtime;
	fstat(tmpfd, &stbuf);
	if (stbuf.st_mtime < mtime || count > 0)
	{
		if (count > 0)
		{
		    printf("%d temp files are recoverable\n", count+1);
		    printf("\tyou may want to do more than one recover\n");
		    printf("\tsince the one that *is* recovered is picked\n");
		    printf("\tat random.\n");
		}
		
		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
SHAR_EOF
fi # end of overwriting check
if test -f 'wildcard.c'
then
	echo shar: will not over-write existing file "'wildcard.c'"
else
cat << \SHAR_EOF > 'wildcard.c'
/* wildcard.c */

/* Author:
 *	Guntram Blohm
 *	Buchenstrasse 19
 *	7904 Erbach, West Germany
 *	Tel. ++49-7305-6997
 *	sorry - no regular network connection
 */

/* this program implements wildcard expansion for elvis/dos. It works
 * like UNIX echo, but uses the dos wildcard conventions
 * (*.* matches all files, * matches files without extension only,
 * filespecs may contain drive letters, wildcards not allowed in directory
 * names).
 *
 * It is also #included into ctags.c, ref.c, ...; in this case,
 * we don't want a main function here.
 */

#include <stdio.h>
#include <ctype.h>
#ifdef	__TURBOC__
#include <dir.h>
#endif
#ifdef	M_I86
#define	findfirst(a,b,c)	_dos_findfirst(a,c,b)
#define	findnext		_dos_findnext
#define	ffblk			find_t
#define	ff_name			name
#include <dos.h>
#endif
#ifdef	M68000
#include <stat.h>
#include <osbind.h>
#define	findfirst(a,b,c)	(Fsetdta(b), (Fsfirst(a,c)))
#define	findnext(x)		(Fsnext())
#define	ff_name	d_fname
#endif
#define	MAXFILES	1000

int pstrcmp();
extern char *calloc();

char *files[MAXFILES];
int nfiles;

#ifndef	WILDCARD_NO_MAIN

main(argc, argv)
	char **argv;
{
	int i;

	for (i=1; i<argc; i++)
		expand(argv[i]);
	if (nfiles)
		printf("%s", files[0]);
	for (i=1; i<nfiles; i++)
	{
		printf(" %s", files[i]);
	}
	putchar('\n');
	return 0;
}

#else
char **wildexpand(argc, argv)
	int *argc;
	char **argv;
{
	int i;
	
	for (i=0; i<*argc; i++)
		expand(argv[i]);
	*argc=nfiles;
	return files;
}	
#endif

expand(name)
	char *name;
{
	char *filespec;
	int wildcard=0;
#ifdef	M68000
	DMABUFFER findbuf;
#else
	struct ffblk findbuf;
#endif
	int err;
	char buf[80];
	int lastn;

	strcpy(buf, name);
	for (filespec=buf; *filespec; filespec++)
		;

	while (--filespec>=buf)
	{	if (*filespec=='?' || *filespec=='*')
			wildcard=1;
		if (*filespec=='/' || *filespec=='\\' || *filespec==':')
			break;
	}
	if (!wildcard)
		addfile(buf);
	else
	{
		lastn=nfiles;
		filespec++;
		if ((err=findfirst(buf, &findbuf, 0))!=0)
			addfile(buf);
		while (!err)
		{
			strcpy(filespec, findbuf.ff_name);
			addfile(buf);
			err=findnext(&findbuf);
		}
		if (lastn!=nfiles)
			qsort(files+lastn, nfiles-lastn, sizeof(char *), pstrcmp);
	}
}

addfile(buf)
	char *buf;
{
	char *p;

	for (p=buf; *p; p++)
		*p=tolower(*p);

	if (nfiles<MAXFILES && (files[nfiles]=calloc(strlen(buf)+1, 1))!=0)
		strcpy(files[nfiles++], buf);
}

int pstrcmp(a, b)
	char **a, **b;
{
	return strcmp(*a, *b);
}
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0