echo x - Doc.sh
sed '/^X/s///' > Doc.sh << '/'
Xecho x - cflags.ms
Xsed '/^X/s///' > cflags.ms << '/'
XX.Go 9 "CFLAGS"
XX.PP
XX\*E uses many preprocessor symbols to control compilation.
XXSome of these control the sizes of buffers and such.
XXThe "-DNO_XXXX" options remove small sets of related features.
XX.PP
XXMost \*E users will probably want to keep all features available.
XXMinix-PC users, though, will have to sacrifice some sets because otherwise
XX\*E would be too bulky to compile.
XXThe "asld" phase of the compiler craps out.
XX.IP "-DM_SYSV, -Dbsd, -DTOS, -DCOHERENT, -Damiga"
XXThese flags tell the compiler that \*E is being compiled for
XXSystem-V UNIX, BSD UNIX, Atari TOS, Coherent, or AmigaDos, respectively.
XXFor other systems, the config.h file can generally figure it out automatically.
XX.IP -DRAINBOW
XXFor MS-DOS systems, this causes support for the DEC Rainbow to be compiled
XXinto \*E.
XX.IP -DS5WINSIZE
XXSome versions of SysV UNIX don't support support the "winsize"
XXstyle of screen-size testing,
XXso elvis ignores window size changes by default.
XX.IP
XXHowever, many of the newer SysV systems defines "winsize" in the
XXfile "/usr/include/sys/ptem.h".
XXIf your SysV system has "winsize" then you should add
XX-DS5SWINSIZE to the CFLAGS setting.
XX.IP -DTERMIOS
XXPOSIX is a SysV-derived specification which uses a terminal control
XXpackage called "termios", instead of "termio".
XXSome other SysV systems may also use termios.
XXYou can make elvis uses termios instead of the more common termio
XXby adding -DTERMIOS to CFLAGS.
XX(Note: This hasn't been tested very well.)
XX.IP -DNBUFS=\fInumber\fP
XX\*E keeps most of your text in a temporary file;
XXonly a small amount is actually stored in RAM.
XXThis flag allows you to control how much of the file can be in RAM at any time.
XXThe default is 5 blocks, and the minimum is 3 blocks.
XX(See the -DBLKSIZE flag, below.)
XX.IP
XXMore RAM allows global changes to happen a little faster.
XX f you're just making many small changes in one section of a file, though,
XXextra RAM won't help much.
XX.IP -DBLKSIZE=\fInumber\fP
XXThis controls the size of blocks that \*E uses internally.
XXThe value of BLKSIZE must be a power of two.
XXEvery time you double BLKSIZE, you quadruple the size of a text file that
XX\*E can handle, but you also cause the temporary file to grow faster.
XXFor MS-DOS, Coherent, and Minix-PC, the default value is 1024, which allows
XXyou to edit files up to almost 512K bytes long.
XXFor all other systems, the default value is 2048, which allows you to edit
XXfiles that are nearly 2 megabytes long.
XX.IP
XXThe BLKSIZE also determines the maximum line length, and a few other limits.
XXBLKSIZE should be either 256, 512, 1024, or 2048.
XXValues other than these can lead to strange behaviour.
XX.IP -DTMPDIR=\fIstring\fP
XXThis sets the default value of the "directory" option, which specifies where
XXthe temporary files should reside.
XXThe value of TMPDIR must be a string, so be sure your value includes the
XXquote characters on each end.
XX.IP "-DEXRC=\fIstr\fP, -DHMEXRC=\fIstr\fP, -DSYSEXRC=\fIstr\fP, -DEXINIT=\fIstr\fP"
XXThis lets you control the names of the initialization files.
XXTheir values must be strings, so be careful about quoting.
XX.IP
XXEXRC is the name of the initialization file in the current directory.
XXIts default value is ".exrc" on UNIX systems -- the same as the real vi.
XXSince that isn't a legal DOS filename, under DOS the default is "elvis.rc".
XXFor other systems, check the config.h file.
XX.IP
XXHMEXRC is the name of the initialization file in your home directory.
XXBy default, it is the same as EXRC.
XX\*E will automatically prepend the name of your home directory to HMEXRC
XXat run time, so don't give a full path name.
XX.IP
XXSYSEXRC is the name of a system-wide initialization file.
XXIt has no default value;
XXif you don't define a value for it, then
XXthe code that supports SYSEXRC just isn't compiled.
XXThe value of SYSEXRC should be a full pathname, in quotes.
XX.IP
XXEXINIT is the name of an environment variable that can contain initialization
XXcommands.
XXNormally, its value is "EXINIT".
XX.IP -DKEYWORDPRG=\fIstring\fP
XXThis flag determines the default value of the "keywordprg" option.
XXIts value must be a string, so be careful about quoting.
XXThe default value of this flag is "ref", which is a C reference program.
XX.IP "-DCC_COMMAND=\fIstring\fP -DMAKE_COMMAND=\fIstring\fP -DERRLIST=\fIstring\fP"
XXThese control the names of the C compiler, the "make" utility, and the
XXerror output file, respectively.
XXThey are only used if -DNO_ERRLIST is not given.
XX.IP
XXThe default value of CC_COMMAND depends on the Operating System and compiler
XXthat you use to compile elvis;
XXfor UNIX, the default is "cc".
XXThe default values of MAKE_COMMAND and ERRLIST are "make" and "errlist",
XXrespectively.
XX.IP -DMAXRCLEN=\fInumber\fP
XXThis determines how large a :@ macro command can be (measured in bytes).
XXThe default is 1000 bytes.
XXIf you increase this value significantly,
XXthen you may need to allocate extra memory for the stack.
XXSee the "CHMEM" setting in the Makefile.
XX.IP -DSHELL=\fIstring\fP
XXThis is the default value of the "shell" option, and hence
XXthe default shell used from within \*E.
XXThis only controls the default;
XXthe value you give here may be overridden at run-time by setting
XXan environment variable named SHELL (or COMSPEC for MS-DOS).
XXIts value must be a string constant, so be careful about quoting.
XX.IP -DTAGS=\fIstring\fP
XXThis sets the name of the "tags" file,
XXwhich is used by the :tag command.
XXIts value must be a string constant, so be careful about quoting.
XX.IP "-DCS_IBMPC -DCS_LATIN1 -DCS_SPECIAL"
XXThe digraph table and flipcase option will normally start out empty.
XXHowever, if you add -DCS_IBMPC or -DCS_LATIN1 to your CFLAGS,
XXthen they will start out filled with values that are appropriate for the
XXIBM PC character set or the ISO Latin-1 character set, respectively.
XX.IP
XXYou can also use -DCS_IBMPC and -DCS_SPECIAL together to get digraphs
XXthat produce the PC's graphic characters.
XX.IP "-DDEBUG -DEBUG2"
XX-DDEBUG adds the ":debug" and ":validate" commands,
XXand also adds many internal consistency checks.
XXIt increases the size of the ".text" segment by about 6K.
XX.IP
XX-DDEBUG2 causes a line to be appended to a file called "debug.out"
XXeverytime any change is made to the edit buffer.
XX.IP -DCRUNCH
XXThis flag removes some non-critical code, so that \*E is smaller.
XXFor example, it removes a short-cut from the regexp package, so that
XXtext searches are slower.
XXAlso, screen updates are not as efficient.
XXA couple of obscure features are disabled by this, too.
XX.IP -DNO_MKEXRC
XXThis removes the ":mkexrc" command,
XXso you have to create any .exrc files manually.
XXThe size of the .text segment will be reduced by about 600 bytes.
XX.IP -DNO_CHARATTR
XXPermanently disables the charattr option.
XXThis reduces the size of your ".text" segment by about 850 bytes.
XX.IP -DNO_RECYCLE
XXNormally, \*E will recycle space (from the temporary file) which contains
XXtotally obsolete text.
XXThis flag disables this recycling.
XXWithout recycling, the ".text" segment is about 1K smaller
XXthan it would otherwise be,
XXbut the tmp file grows much faster.
XXIf you have a lot of free space on your hard disk,
XXbut \*E is too bulky to run with recycling,
XXthen try it without recycling.
XX.IP
XXWhen using a version of \*E that has been compiled with -DNO_RECYCLE,
XXyou should be careful to avoid making many small changes to a file
XXbecause each individual change will cause the tmp file to grow by at least 1k.
XXHitting "x" thirty times counts as thirty changes,
XXbut typing "30x" counts as one change.
XXAlso, you should occasionally do a ":w" followed by a ":e" to start with a
XXfresh tmp file.
XX.IP
XXInterestingly, the real vi never recycles space from its temporary file.
XX.IP -DNO_SENTENCE
XXLeaves out the "(" and ")" visual mode commands.
XXAlso, the "[[", "]]", "{", and "}" commands will not recognize *roff macros.
XXThe sections and paragraphs options go away.
XXThis saves about 650 bytes in the ".text" segment.
XX.IP -DNO_CHARSEARCH
XXLeaves out the visual commands which locate a given character
XXin the current line:
XX"f", "t", "F", "T", "," and ";".
XXThis saves about 900 bytes.
XX.IP -DNO_EXTENSIONS
XXLeaves out the "K" and "#" visual commands.
XXAlso, the arrow keys will no longer work in input mode.
XXRegular expressions will no longer recognize the \\{\\} operator.
XX(Other extensions are either inherent in the design of \*E,
XXor are controlled by more specific flags,
XXor are too tiny to be worth removing.)
XXThis saves about 250 bytes.
XX.IP -DNO_MAGIC
XXPermanently disables the "magic" option, so that most meta-characters
XXin a regular expression are *NOT* recognized.
XXThis saves about 3k of space in the ".text" segment, because
XXthe complex regular expression code can be replaced by much simpler code.
XX.IP -DNO_SHOWMODE
XXPermanently disables the "showmode" option, saving about 250 bytes.
XX.IP -DNO_CURSORSHAPE
XXNormally, \*E tries to adjust the shape of the cursor as a reminder
XXof which mode you're in.
XXThe -DNO_CURSORSHAPE flag disables this, saving about 150 bytes.
XX.IP -DNO_DIGRAPH
XXTo allow entry of non-ASCII characters, \*E supports digraphs.
XXA digraph is a single (non-ASCII) character which is entered as a
XXcombination of two other (ASCII) characters.
XXIf you don't need to input non-ASCII characters,
XXor if your keyboard supports a better way of entering non-ASCII characters,
XXthen you can disable the digraph code and save about 450 bytes.
XX.IP -DNO_ERRLIST
XX\*E adds a ":errlist" command, which is useful to programmers.
XXIf you don't need this feature, you can disable it via the -DNO_ERRLIST flag.
XXThis will reduce the .text segment by about 900 bytes, and the .bss segment
XXby about 300 bytes.
XX.IP -DNO_ABBR
XXThe -DNO_ABBR flag disables the ":abbr" command,
XXand reduces the size of \*E by about 250 bytes.
XX.IP -DNO_OPTCOLS
XXWhen \*E displays the current options settings via the ":set" command,
XXthe options are normally sorted into columns.
XXThe -DNO_OPTCOLS flag causes the options to be sorted across the rows,
XXwhich is much simpler for the computer.
XXThe -DNO_OPTCOLS flag will reduce the size of your .text segment by about
XX500 bytes.
XX.IP -DNO_MODELINES
XXThis removes all support for modelines.
XX.IP -DNO_TAG
XXThis disables tag lookup.
XXIt reduces the size of the .text segment by about 750 bytes.
XX.IP "-DNO_ALT_FKEY -DNO_CTRL_FKEY -DNO_SHIFT_FKEY -DNO_FKEY"
XXThese remove explicit support of function keys.
XX-DNO_ALT_FKEY removes support for the <alternate> versions function keys.
XX-DNO_CTRL_FKEY removes support for the <control> and <alternate> versions function keys.
XX-DNO_SHIFT_FKEY removes support for the <shift>, <control>, and <alternate> versions function keys.
XX-DNO_FKEY removes all support of function keys.
XX.IP
XX\*E's ":map" command normally allows you to use the special sequence "#<n>"
XXto map function key <n>.
XXFor example, ":map #1 {!}fmt^M" will cause the <F1> key to reformat a paragraph.
XX\*E checks the :k1=: field in the termcap description of your terminal
XXto figure out what code is sent by the <F1> key.
XXThis is handy because it allows you to create a .exrc file which maps function
XXkeys the same way regardless of what type of terminal you use.
XX.IP
XXThat behaviour is standard; most implementations of the real vi supports it too.
XX\*E extends this to allow you to use "#1s" to refer to <shift>+<F1>,
XX"#1c" to refer to <control>+<F1>, and
XX"#1a" to refer to <alt>+<F1>.
XXThe termcap description for the terminal should have fields named
XX:s1=:c1=:a1=: respectively, to define the code sent by these key conbinations.
XX(You should also have :k2=:s2=:c2=:a2=: for the <F2> key, and so on.)
XX.IP
XXBut there may be problems.
XXThe terminfo database doesn't support :s1=:c1=:a1=:, so no terminfo terminal
XXdescription could ever support shift/control/alt function keys;
XXso you might as well add -DNO_SHIFT_FKEY to CFLAGS if you're using terminfo.
XX.IP
XXNote that, even if you have -DNO_FKEYS, you can still configure \*E to use
XXyour function keys my mapping the literal character codes sent by the key.
XXYou just couldn't do it in a terminal-independent way.
XXTERM_925
XX.IP "-DTERM_AMIGA -DTERM_VT100 -DTERM_VT52 etc."
XXThe tinytcap.c file contains descriptions of several terminal types.
XXFor each system that uses tinytcap, a reasonable subset of the available
XXdescriptions is actually compiled into \*E.
XXIf you wish to enlarge this subset, then you can add the appropriate -DTERM_XXX
XXflag to your CFLAGS settings.
XX.IP
XXFor a list of the available terminal types, check the tinytcap.c file.
XX.IP -DINTERNAL_TAGS
XXNormally, \*E uses the "ref" program to perform tag lookup.
XXThis is more powerful than the real vi's tag lookup,
XXbut it can be much slower.
XX.IP
XXIf you add -DINTERNAL_TAGS to your CFLAGS setting,
XXthen \* will use its own internal tag lookup code, which is faster.
XX.IP -DPRSVDIR=\fIdirectory\fR
XXThis controls where preserved files will be placed.
XXAn appropriate default has been chosen for each Operating System,
XXso you probably don't need to worry about it.
XX.IP -DFILEPERMS=\fInumber\fR
XXThis affects the attributes of files that are created by \*E;
XXit is used as the second argument to the creat() function.
XXThe default is 0666 which (on UNIX systems at least) means that
XXanybody can read or write the new file, but nobody can execute it.
XXOn UNIX systems, the creat() call modifies this via the umask setting.
XX.IP -DKEYBUFSIZE=\fInumber\fR
XXThis determines the size of the type-ahead buffer that elvis uses.
XXIt also limits the size of keymaps that it can handle.
XXThe default is 1000 characters, which should be plenty.
X/
Xecho x - cutbufs.ms
Xsed '/^X/s///' > cutbufs.ms << '/'
XX.Go 6 "CUT BUFFERS"
XX.PP
XXWhen \*E deletes text, it stores that text in a cut buffer.
XXThis happens in both visual mode and EX mode.
XXThere is no practical limit to how much text a cut buffer can hold.
XX.PP
XXThere are 36 cut buffers:
XX26 named buffers ("a through "z),
XX9 anonymous buffers ("1 through "9),
XXand 1 extra cut buffer (".).
XX.PP
XXIn EX mode, the :move and :copy commands use a cut buffer to temporarily
XXhold the text to be moved/copied.
XX.NH 2
XXPutting text into a Cut Buffer
XX.PP
XXIn visual mode, text is copied into a cut buffer when you use the
XXd, y, c, C, s, or x commands.
XXThere are also a few others.
XX.PP
XXBy default, the text goes into the "1 buffer.
XXThe text that used to be in "1 gets shifted into "2,
XX"2 gets shifted into "3, and so on.
XXThe text that used to be in "9 is lost.
XXThis way, the last 9 things you deleted are still accessible.
XX.PP
XXYou can also put the text into a named buffer -- "a through "z.
XXTo do this, you should type the buffer's name
XX(two keystrokes: a double-quote and a lowercase letter)
XXbefore the command that will cut the text.
XXWhen you do this, "1 through "9 are not affected by the cut.
XX.PP
XXYou can append text to one of the named buffers.
XXTo do this, type the buffer's name in uppercase
XX(a double-quote and an uppercase letter)
XXbefore the d/y/c/C/s/x command.
XX.PP
XXThe ". buffer is special.
XXIt isn't affected by the d/y/c/C/s/x command.
XXInstead, it stores the text that you typed in
XXthe last time you were in input mode.
XXIt is used to implement the . visual command,
XXand ^A in input mode.
XX.PP
XXIn EX mode (also known as colon mode),
XXthe :delete, :change, and :yank commands all copy text into a cut buffer.
XXLike the visual commands, these EX commands normally use the "1 buffer,
XXbut you can use one of the named buffers by giving its name after the command.
XXFor example,
XX.sp 1
XX.ti +0.5i
XX:20,30y a
XX.sp
XX.LP
XXwill copy lines 20 through 30 into cut buffer "a.
XX.PP
XXYou can't directly put text into the ". buffer, or the "2 through "9 buffers.
XX.NH 2
XXPasting from a Cut Buffer
XX.PP
XXThere are two styles of pasting:
XXline-mode and character-mode.
XXIf a cut buffer contains whole lines (from a command like "dd")
XXthen line-mode pasting is used;
XXif it contains partial lines (from a command like "dw")
XXthen character-mode pasting is used.
XXThe EX commands always cut whole lines.
XX.PP
XXCharacter-mode pasting causes the text to be inserted into the line that
XXthe cursor is on.
XX.PP
XXLine-mode pasting inserts the text on a new line above or below the line
XXthat the cursor is on.
XXIt doesn't affect the cursor's line at all.
XX.PP
XXIn visual mode, the p and P commands insert text from a cut buffer.
XXUppercase P will insert it before the cursor,
XXand lowercase p will insert it after the cursor.
XXNormally, these commands will paste from the "1 buffer, but you can
XXspecify any other buffer to paste from.
XXJust type its name (a double-quote and another character)
XXbefore you type the P or p.
XX.PP
XXIn EX mode, the (pu)t command pastes text after a given line.
XXTo paste from a buffer other that "1,
XXenter its name after the command.
XX.NH 2
XXMacros
XX.PP
XXThe contents of a named cut buffer can be executed as a series of
XXex/vi commands.
XX.PP
XXTo put the instructions into the cut buffer, you must first insert
XXthem into the file, and then delete them into a named cut buffer.
XX.PP
XXTo execute a cut buffer's contents as EX commands,
XXyou should give the EX command "@" and the name of the buffer.
XXFor example, :@z will execute "z as a series of EX commands.
XX.PP
XXTo execute a cut buffer's contents as visual commands,
XXyou should give the visual command "@" and the letter of the buffer's name.
XXThe visual "@" command is different from the EX "@" command.
XXThey interpret the cut buffer's contents differently.
XX.PP
XXThe visual @ command can be rather finicky.
XXEach character in the buffer is interpretted as a keystroke.
XXIf you load the instructions into the cut buffer via a "zdd command,
XXthen the newline character at the end of the line will be executed just
XXlike any other character, so the cursor would be moved down 1 line.
XXIf you don't want the cursor to move down 1 line at the end of each
XX@z command, then you should load the cut buffer by saying 0"zD instead.
XX.PP
XXAlthough cut buffers can hold any amount of text,
XX\*E can only \fIexecute\fR small buffers.
XXThe size limit is roughly 1000 characters, for either EX macros or VI macros.
XXIf a buffer is too large to execute, an error message is displayed.
XX.PP
XXYou can't nest :@ commands.
XXYou can't run :@ commands from your .exrc file,
XXor any other :source file either.
XXSimilarly, you can't run a :source command from within an @ command.
XXHopefully, these restrictions will be lifted in a later version.
XX.NH 2
XXThe Effect of Switching Files
XX.PP
XXWhen \*E first starts up, all cut buffers are empty.
XXWhen you switch to a different file
XX(via the :n or :e commands perhaps)
XXthe 9 anonymous cut buffers are emptied again,
XXbut the other 27 buffers ("a through "z, and ".) retain their text.
X/
Xecho x - differ.ms
Xsed '/^X/s///' > differ.ms << '/'
XX.Go 7 "DIFFERENCES BETWEEN \*E & BSD VI/EX"
XX.PP
XX\*E is not 100% compatible with the real vi/ex.
XX\*E has many small extensions, some omissions, and a few features which
XXare implemented in a slightly different manner.
XX.NH 2
XXExtensions
XX.IP "Save Configuration" 1i
XXThe :mkexrc command saves the current :set and :map configurations in
XXthe ".exrc" file in your current directory.
XX.IP "Previous File" 1i
XXThe :N or :prev command moves backwards through the args list.
XX.IP "Center Current Row" 1i
XXIn visual command mode, the (lowercase) "zz" command will center the current
XXline on the screen, like "z=".
XX.IP "Changing Repeat Count" 1i
XXThe default count value for . is the same as the previous command
XXwhich . is meant to repeat.
XXHowever, you can supply a new count if you wish.
XXFor example, after "3dw", "." will delete 3 words,
XXbut "5." will delete 5 words.
XX.IP "Previous Text" 1i
XXThe text which was most recently input
XX(via a "cw" command, or something similar)
XXis saved in a cut buffer called ". (which
XXis a pretty hard name to write in an English sentence).
XX.IP "Keyword Lookup" 1i
XXIn visual command mode, you can move the cursor onto a word and press
XXshift-K to have \*E run a reference program to look that word up.
XXThis command alone is worth the price of admission!
XXSee the ctags and ref programs.
XX.IP "Increment/Decrement" 1i
XXIn visual command mode, you can move the cursor onto a number and
XXthen hit ## or #+ to increment that number by 1.
XXTo increment it by a larger amount,
XXtype in the increment value before hitting the initial #.
XXThe number can also be decremented or set by hitting #- or #=, respectively.
XX.IP "Input Mode" 1i
XXYou can backspace past the beginning of the line.
XX.IP "" 1i
XXThe arrow keys work in input mode.
XX.IP "" 1i
XXIf you type control-A, then the text that you input last time is inserted.
XXYou will remain in input mode, so you can backspace over part of it,
XXor add more to it.
XX(This is sort of like control-@ on the real vi,
XXexcept that control-A really works.)
XX.IP "" 1i
XXControl-P will insert the contents of the cut buffer.
XX.IP "" 1i
XXReal vi can only remember up to 128 characters of input,
XXbut \*E can remember any amount.
XX.IP "" 1i
XXThe ^T and ^D keys can adjust the indent of a line no matter where
XXthe cursor happens to be in that line.
XX.IP "" 1i
XXYou can save your file and exit \*E directly from input mode by hitting
XXcontrol-Z twice.
XX.IP "" 1i
XX\*E supports digraphs as a way to enter non-ASCII characters.
XX.IP "Start in Input Mode" 1i
XXIf you ":set inputmode" in your .exrc file, then \*E will start up in
XXinput mode instead of visual command mode.
XX.IP "Visible Fonts" 1i
XXWith ":set charattr", \*E can display "backslash-f" style character attributes on the
XXscreen as you edit.
XXThe following example shows the recognized atributes:
XX.sp
XX.ti +0.5i
XXnormal \\fBboldface\\fR \\fIitalics\\fR \\fUunderlined\\fR normal
XX.sp
XXNOTE: you must compile \*E without the -DNO_CHARATTR flag for
XXthis to work.
XX.IP "File Syncing" 1i
XXAfter a crash, you can usually recover the altered form of the file
XXfrom the temporary file that \*E uses -- unless the temporary file was
XXcorrupted.
XX.IP "" 1i
XXUNIX systems use a delayed-write cache, which means that when \*E tries to
XXwrite to the temporary file, the information might still be in RAM instead
XXof on the disk.
XXA power failure at that time would cause the in-RAM information to be lost.
XXUNIX's sync() call will force all such information to disk.
XX.IP "" 1i
XXMS-DOS and Atari TOS don't write a file's length to disk until that file
XXis closed.
XXConsequently, the temporary file would appear to be 0 bytes long if power
XXfailed when we were editing.
XXTo avoid this problem, a sync() function has been written which will close
XXthe temporary file and then immediately reopen it.
XX.IP "Cursor Shape" 1i
XX\*E changes the shape of the cursor to indicate which mode you're in,
XXif your terminal's termcap entry includes the necessary capabilities.
XX.IP "Hide nroff Lines" 1i
XXTh ":set hideformat" option hides nroff format control lines.
XX(They are displayed on the screen as blank lines.)
XX.ne 7
XX.IP "Compiler Interface" 1i
XX\*E is clever enough to parse the error messages emitted by many compilers.
XXTo use this feature,
XXyou should collect your compiler's error messages into a file called "errlist";
XX\*E will read this file,
XXdetermine which source file caused the error messages,
XXstart editing that file,
XXmove the cursor to the line where the error was detected,
XXand display the error message on the status line.
XXNifty!
XX.IP "Visible Text Selection" 1i
XXIn visual command mode, 'v' starts visibly selecting characters and
XX\&'V' starts visibly selecting whole lines.
XXThe character or line where the cursor is located becomes one
XXendpoint of the selection.
XXYou can then use the standard cursor movement commands to move the cursor
XXto the other endpoint, and then press one of the operator commands
XX(c/d/y/</>/!/=/\\).
XXThe operator will then immediately be applied to the selected text.
XX.IP "Pop-up Menu Operator" 1i
XXThe '\\' key is a new operator,
XXsimilar in operation to the c/d/y/</>/! operators
XXIt conjures up a menu, from which you can select any of the other
XXoperators plus a few other common commands.
XX.IP "Preset Filter Operator" 1i
XXThe '=' key is another new operator.
XXIt is similar to the '!' operator, except that while
XX\&'!' asks you to type in a filter command each time,
XX\&'=' assumes it should always run the command stored in the \fIequalprg\fR option.
XX.IP "Move to a Given Percentage" 1i
XXThe '%' movement key can now accept an optional count.
XXWithout a count, the '%' key still moves to a matching parenthesis
XXlike it always did.
XXWith a count somewhere between 1 and 100, though, it moves the cursor to
XXapproximately a given percentage of the way through the file.
XXFor example, typing "50%" will move the cursor to the middle of the file.
XX.IP "Regular Expressions"
XXIn regular expressions, several new forms of closure operators are supported:
XX\\{\fIn\fR}, \\{\fIn\fR,\fIm\fR}, \\+, and \\?.
XX.NH 2
XXOmissions
XX.PP
XXThe replace mode is a hack.
XXIt doesn't save the text that it overwrites.
XX.PP
XXLong lines are displayed differently -- where the real vi would
XXwrap a long line onto several rows of the screen, \*E simply
XXdisplays part of the line, and allows you to scroll the screen
XXsideways to see the rest of it.
XX.PP
XXThe ":preserve" and ":recover" commands are missing.
XXSo is the -r flag.
XXI've never had a good reason to use ":preserve",
XXand since ":recover" is used so rarely
XXI decided to implement it as a separate program.
XXThere's no need to load the recovery code into memory every
XXtime you edit a file, I figured.
XX.PP
XXLISP support is missing.
XXHowever, the = key is still an operator that reformats lines of text.
XXBy default, it reformats lines by sending them through the \fIfmt\fP filter,
XXbut you could write your own LISP beautifier and configure elvis to use it.
XXKey mappings could take care of most other differences.
XXAuto-indent is the only thing that is irrecoverably lost.
XX.PP
XXAutoindent mode acts a little different from the real vi, anyway.
XXIt doesn't handle ^^D or 0^D correctly.
XXOn the other hand, it \fIdoes\fP allow ^D and ^T to be used anywhere in the
XXline, to adjust the indentation for the whole line.
X/
Xecho x - environ.ms
Xsed '/^X/s///' > environ.ms << '/'
XX.Go 11 "ENVIRONMENT VARIABLES"
XX.PP
XX\*E examines several environment variables when it starts up.
XXThe values of these variables are used internally for a variety
XXof purposes.
XXYou don't need to define all of these;
XXon most systems, \*E only requires TERM to be defined.
XXOn AmigaDOS, MS-DOS or TOS systems, even that is optional.
XX.SH
XXTERM, TERMCAP
XX.PP
XXTERM tells \*E the name of the termcap entry to use.
XXTERMCAP may contain either the entire termcap entry,
XXor the full pathname of the termcap file to search through.
XX.PP
XXIf your version of \*E is using tinytcap instead of the full termcap library,
XXthen the value of TERMCAP \fIcannot\fR contain any backslash escapes (\\E, \\r, etc.)
XXor carat escapes (^[, ^M, etc.), because tinytcap doesn't understand them.
XXInstead, you should embed the actual control character into the string.
XX.SH
XXTMP, TEMP
XX.PP
XXThese only work for AmigaDOS, MS-DOS and Atari TOS.
XXEither of these variables may be used to set the "directory" option,
XXwhich controls where temporary files are stored.
XXIf you define them both, then TMP is used, and TEMP is ignored.
XX.SH
XXLINES, COLUMNS
XX.PP
XXThe termcap entry for your terminal should specify the size of your screen.
XXIf you're using a windowing interface, then there is an ioctl() call which
XXwill provide the size of the window; the ioctl() values will override the
XXvalues in the termcap entry.
XXThe LINES and COLUMNS environment variables (if defined)
XXwill override either of these sources.
XXThey, in turn, can be overridden by a ":set" command.
XX.PP
XXNormally, the LINES and COLUMNS variables shouldn't need to be defined.
XX.SH
XXEXINIT
XX.PP
XXThis variable's value may contain one or more colon-mode commands,
XXwhich will be executed after all of the ".exrc" files
XXbut before interactive editing begins.
XX.PP
XXTo put more than one command in EXINIT, you can separate the commands
XXwith either a newline or a '|' character.
XX.SH
XXSHELL, COMSPEC
XX.PP
XXYou can use COMSPEC in MS-DOS, or SHELL in any other system,
XXto specify which shell should be used for executing commands and
XXexpanding wildcards.
XX.SH
XXHOME
XX.PP
XXThis variable should give the full pathname of your home directory.
XX\*E needs to know the name of your home directory so it can locate
XXthe ".exrc" file there.
XX.SH
XXTAGPATH
XX.PP
XXThis variable is used by the "ref" program.
XXIt contains a list of directories that might contain a relevent "tags" file.
XXUnder AmigaDOS, MS-DOS or Atari TOS, the names of the directories should be separated by
XXsemicolons (";").
XXUnder other operating systems, the names should be separated by colons (":").
XX.PP
XXIf you don't define TAGPATH, then "ref" will use a default list which includes
XXthe current directory and a few other likely places.
XXSee the definition of DEFTAGPATH at the start of ref.c for an accurate list.
X/
Xecho x - ex.ms
Xsed '/^X/s///' > ex.ms << '/'
XX.Go 3 "COLON MODE COMMANDS"
XX.ID
XX.ps
XX.in 0.8i
XX.ta 2i 3.i
XX.\" NOTE: The following macro is used to output a single line of the
XX.\" command chart.  Its usage is:
XX.\"
XX.\"		.Cm <linespecs> <name> <arguments>...
XX.\"
XX.de Cm
XX.if "\\$1"0" \t\\$2\t\\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
XX.if "\\$1"1" \s-2[line]\s+2\t\\$2\t\\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
XX.if "\\$1"2" \s-2[line][,line]\s+2\t\\$2\t\\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
XX..
XX.if t .ds Q ``
XX.if t .ds U ''
XX.if n .ds Q "
XX.if n .ds U "
XX\s+2LINES	COMMAND	ARGUMENTS\s-2
XX.Cm 0 ab[br] [short] [expanded form]
XX.Cm 1 a[ppend][!]
XX.Cm 0 ar[gs] [files]
XX.Cm 0 cc [files]
XX.Cm 0 cd[!] [directory]
XX.Cm 2 c[hange]
XX.Cm 0 chd[ir][!] [directory]
XX.Cm 2 co[py] line
XX.Cm 0 col[or] [when] [[\*Qlight\*U] color] [\*Qon\*U color]
XX.Cm 2 d[elete] [\*Ux]
XX.Cm 0 dig[raph][!] [XX [Y]]
XX.Cm 0 e[dit][!] [file]
XX.Cm 0 er[rlist][!] [errlist]
XX.Cm 0 f[ile] [file]
XX.Cm 2 g[lobal] /regexp/ command
XX.Cm 1 i[nsert]
XX.Cm 2 j[oin][!]
XX.Cm 2 l[ist]
XX.Cm 0 mak[e] [target]
XX.Cm 0 map[!] key mapped_to
XX.Cm 1 ma[rk]  \*Ux
XX.Cm 0 mk[exrc]
XX.Cm 2 m[ove] line
XX.Cm 0 n[ext][!] [files]
XX.Cm 0 N[ext][!]
XX.Cm 2 nu[mber]
XX.Cm 2 p[rint]
XX.Cm 1 pu[t] [\*Ux]
XX.Cm 0 q[uit][!]
XX.Cm 1 r[ead] file
XX.Cm 0 rew[ind][!]
XX.Cm 0 se[t] [options]
XX.Cm 0 so[urce] file
XX.Cm 2 s[ubstitute] /regexp/replacement/[p][g][c]
XX.Cm 0 ta[g][!] tagname
XX.Cm 0 una[bbr] [short]
XX.Cm 0 u[ndo]
XX.Cm 0 unm[ap][!] key
XX.Cm 0 ve[rsion]
XX.Cm 2 v[global] /regexp/ command
XX.Cm 0 vi[sual] [filename]
XX.Cm 0 wq 
XX.Cm 2 w[rite][!] [[>>]file]
XX.Cm 0 x[it][!]
XX.Cm 2 y[ank] [\*Ux]
XX.Cm 2 ! command
XX.Cm 2 < 
XX.Cm 2 = 
XX.Cm 2 > 
XX.Cm 2 & 
XX.Cm 0 @ "" \*Ux
XX.DE
XX.TA
XX.PP
XXTo use colon mode commands, you must switch from visual command
XXmode to colon command mode.
XXThe visual mode commands to do this are ":" for a single colon command,
XXor "Q" for many colon mode commands.
XX.NH 2
XXLine Specifiers
XX.PP
XXLine specifiers are always optional.
XXThe first line specifier of most commands usually defaults to the current line.
XXThe second line specifier usually defaults to be the same
XXas the first line specifier.
XXExceptions are :write, :global, and :vglobal, which act on all lines of the
XXfile by default, and :!, which acts on no lines by default.
XX.PP
XXLine specifiers consist of an absolute part and a relative part.
XXThe absolute part of a line specifier may be either an explicit line number,
XXa mark, a dot to denote the current line, a dollar sign to denote the last
XXline of the file, or a forward or backward search.
XX.PP
XXAn explicit line number is simply a decimal number, expressed as a
XXstring of digits.
XX.PP
XXA mark is typed in as an apostrophe followed by a letter.
XXMarks must be set before they can be used.
XXYou can set a mark in visual command mode by typing "m" and a letter,
XXor you can set it in colon command mode via the "mark" command.
XX.PP
XXA forward search is typed in as a regular expression surrounded by
XXslash characters; searching begins at the default line.
XXA backward search is typed in as a regular expression surrounded by
XXquestion marks; searching begins at the line before the default line.
XX.PP
XXIf you omit the absolute part, then the default line is used.
XX.PP
XXThe relative part of a line specifier is typed as a "+" or "-" character
XXfollowed by a decimal number.
XXThe number is added to or subtracted from the absolute part
XXof the line specifier to produce the final line number.
XX.PP
XXAs a special case, the % character may be used to specify all lines of the file.
XXIt is roughly equivelent to saying 1,$.
XXThis can be a handy shortcut.
XX.PP
XXSome examples:
XX.LD
XX.ps
XX.ta 0.5i 1.8i
XX	:p	print the current line
XX	:37p	print line 37
XX	:'gp	print the line which contains mark g
XX	:/foo/p	print the next line that contains "foo"
XX	:$p	print the last line of the file
XX	:20,30p	print lines 20 through 30
XX	:1,$p	print all lines of the file
XX	:%p	print all lines of the file
XX	:/foo/-2,+4p	print 5 lines around the next "foo"
XX.TA
XX.DE
XX.NH 2
XXText Entry Commands
XX.if n .ul 0
XX.ID
XX.ps
XX[line] append
XX[line][,line] change ["x]
XX[line] insert
XX.DE
XX.PP
XXThe \fBa\fRppend command inserts text after the specified line.
XX.PP
XXThe \fBi\fRnsert command inserts text before the specified line.
XX.PP
XXThe \fBc\fRhange command copies the range of lines into a cut buffer,
XXdeletes them, and inserts new text where the old text used to be.
XX.PP
XXFor all of these commands, you indicate the end of the text you're
XXinserting by hitting ^D or by entering a line which contains only a
XXperiod.
XX.NH 2
XXCut & Paste Commands
XX.if n .ul 0
XX.ID
XX.ps
XX[line][,line] delete ["x]
XX[line][,line] yank ["x]
XX[line] put ["x]
XX[line][,line] copy line
XX[line][,line] to line
XX[line][,line] move line
XX.DE
XX.PP
XXThe \fBd\fRelete command copies the specified range of lines into a
XXcut buffer, and then deletes them.
XX.PP
XXThe \fBy\fRank command copies the specified range of lines into a cut
XXbuffer, but does *not* delete them.
XX.PP
XXThe \fBpu\fRt command inserts text from a cut buffer after the
XXspecified line.
XX.PP
XXThe \fBco\fRpy and \fBt\fRo commands yank the specified range of lines and
XXthen immediately paste them after some other line.
XX.PP
XXThe \fBm\fRove command deletes the specified range of lines and then
XXimmediately pastes them after some other line.
XXIf the destination line comes after the deleted text,
XXthen it will be adjusted automatically to account for the deleted lines.
XX.NH 2
XXDisplay Text Commands
XX.if n .ul 0
XX.ID
XX.ps
XX[line][,line] print
XX[line][,line] list
XX[line][,line] number
XX.DE
XX.PP
XXThe \fBp\fRrint command displays the specified range of lines.
XX.PP
XXThe \fBnu\fRmber command displays the lines, with line numbers.
XX.PP
XXThe \fBl\fRist command also displays them, but it is careful to make
XXcontrol characters visible.
XX.NH 2
XXGlobal Operations Commands
XX.if n .ul 0
XX.ID
XX.ps
XX[line][,line] global /regexp/ command
XX[line][,line] vglobal /regexp/ command
XX.DE
XX.PP
XXThe \fBg\fRlobal command searches through the lines of the specified range
XX(or through the whole file if no range is specified)
XXfor lines that contain a given regular expression.
XXIt then moves the cursor to each of these lines and
XXruns some other command on them.
XX.PP
XXThe \fBv\fRglobal command is similar, but it searches for lines that \fIdon't\fR
XXcontain the regular expression.
XX.NH 2
XXLine Editing Commands
XX.if n .ul 0
XX.ID
XX.ps
XX[line][,line] join[!]
XX[line][,line] ! program
XX[line][,line] <
XX[line][,line] >
XX[line][,line] substitute /regexp/replacement/[p][g][c]
XX[line][,line] &
XX.DE
XX.PP
XXThe \fBj\fRoin command catenates all lines in the specified range together
XXto form one big line.
XXIf only a single line is specified, then the following line is catenated
XXonto it.
XXThe normal ":join" inserts one or two spaces between the lines;
XXthe ":join!" variation (with a '!') doesn't insert spaces.
XX.PP
XXThe \fB!\fR command runs an external filter program,
XXand feeds the specified range of lines to it's stdin.
XXThe lines are then replaced by the output of the filter.
XXA typical example would be ":'a,'z!sort" to sort the lines 'a,'z.
XX.PP
XXThe \fB<\fR and \fB>\fR commands shift the specified range of lines left or right,
XXnormally by the width of 1 tab character.
XXThe "shiftwidth" option determines the shifting amount.
XX.PP
XXThe \fBs\fRubstitute command finds the regular expression in each line,
XXand replaces it with the replacement text.
XXThe "p" option causes the altered lines to be printed.
XXThe "g" option permits all instances of the regular expression
XXto be found & replaced.
XX(Without "g", only the first occurrence in each line is replaced.)
XXThe "c" option asks for confirmation before each substitution.
XX.PP
XXThe \fB&\fR command repeats the previous substitution command.
XXActually, "&" is equivelent to "s//~/" with the same options as last time.
XXIt searches for the last regular expression that you specified for any purpose,
XXand replaces it with the the same text
XXthat was used in the previous substitution.
XX.NH 2
XXUndo Command
XX.if n .ul 0
XX.ID
XX.ps
XXundo
XX.DE
XX.PP
XXThe \fBu\fRndo command restores the file to the state it was in before
XXyour most recent command which changed text.
XX.NH 2
XXConfiguration & Status Commands
XX.if n .ul 0
XX.ID
XX.ps
XXmap[!] [key mapped_to]
XXunmap[!] key
XXabbr [word expanded_form_of_word]
XXunabbr word
XXdigraph[!] [XX [Y]]
XXset [options]
XXmkexrc
XX[line] mark "x
XXvisual
XXversion
XX[line][,line] =
XXfile [file]
XXsource file
XX@ "x
XXcolor [when] [["light"] color] ["on" color]
XX.DE
XX.PP
XXThe \fBma\fRp command allows you to configure \*E to recognize your function keys,
XXand treat them as though they transmitted some other sequence of characters.
XXNormally this mapping is done only when in the visual command mode,
XXbut with the [!] present it will map keys under input and replace modes as well.
XXWhen this command is given with no arguments,
XXit prints a table showing all mappings currently in effect.
XXWhen called with two arguments, the first is the sequence that your
XXfunction key really sends, and the second is the sequence that you want
XX\*E to treat it as having sent.
XXAs a special case, if the first argument is a number then \*E will map the
XXcorresponding function key;
XXfor example, ":map 7 dd" will cause the <F7> key to delete a line.
XX.PP
XXThe \fBunm\fRap command removes key definitions that were made via the map command.
XX.PP
XXThe \fBab\fRbr command is used to define/list a table of abbreviations.
XXThe table contains both the abbreviated form and the fully spelled-out form.
XXWhen you're in visual input mode, and you type in the abbreviated form,
XX\*E will replace the abbreviated form with the fully spelled-out form.
XXWhen this command is called without arguments, it lists the table;
XXwith two or more arguments, the first argument is taken as the abbreviated
XXform, and the rest of the command line is the fully-spelled out form.
XX.PP
XXThe \fBuna\fRbbr command deletes entries from the abbr table.
XX.PP
XXThe \fBdi\fRgraph command allows you to display the set of digraphs that \*E is
XXusing, or add/remove a digraph.
XXTo list the set of digraphs, use the digraph command with no arguments.
XXTo add a digraph, you should give the digraph command two arguments.
XXThe first argument is the two ASCII characters that are to be combined;
XXthe second is the non-ASCII character that they represent.
XXThe non-ASCII character's most significant bit is automatically set by the
XXdigraph command, unless to append a ! to the command name.
XXRemoval of a digraph is similar to adding a digraph, except that you should
XXleave off the second argument.
XX.PP
XXThe \fBse\fRt command allows you examine or set various options.
XXWith no arguments, it displays the values of options that have been changed.
XXWith the single argument "all" it displays the values of all options,
XXregardless of whether they've been explicitly set or not.
XXOtherwise, the arguments are treated as options to be set.
XX.PP
XXThe \fBmk\fRexrc command saves the current configuration to a file
XXcalled ".exrc" in the current directory.
XX.PP
XXThe mar\fBk\fR command defines a named mark to refer to a specific place
XXin the file.
XXThis mark may be used later to specify lines for other commands.
XX.PP
XXThe \fBvi\fRsual command puts the editor into visual mode.
XXInstead of emulating ex, \*E will start emulating vi.
XX.PP
XXThe \fBve\fRrsion command tells you that what version of \*E this is.
XX.PP
XXThe \fB=\fR command tells you what line you specified, or,
XXif you specified a range of lines, it will tell you both endpoints and
XXthe number of lines included in the range.
XX.PP
XXThe \fBf\fRile command tells you the name of the file,
XXwhether it has been modified,
XXthe number of lines in the file,
XXand the current line number.
XXYou can also use it to change the name of the current file.
XX.PP
XXThe \fBso\fRurce command reads a sequence of colon mode commands from a file,
XXand interprets them.
XX.PP
XXThe \fB@\fR command executes the contents of a cut-buffer as EX commands.
XX.PP
XXThe \fBcol\fRor command only works under MS-DOS, or if you have an ANSI-compatible
XXcolor terminal.
XXIt allows you to set the foreground and background colors
XXfor different types of text:
XXnormal, bold, italic, underlined, standout, pop-up menu, and visible selection.
XXBy default, it changes the "normal" colors;
XXto change other colors, the first argument to the :color command should be
XXthe first letter of the type of text you want.
XXThe syntax for the colors themselves is fairly intuitive.
XXFor example, ":color light cyan on blue" causes normal text to be displayed
XXin light cyan on a blue background, and
XX":color b bright white" causes bold text to be displayed in bright white on
XXa blue background.
XXThe background color always defaults to the current background color of
XXnormal text.
XXYour first :color command \fImust\fP specify both the foreground and background
XXfor normal text.
XX.NH 2
XXMultiple File Commands
XX.if n .ul 0
XX.ID
XX.ps
XXargs [files]
XXnext[!] [files]
XXNext[!]
XXprevious[!]
XXrewind[!]
XX.DE
XX.PP
XXWhen you invoke \*E from your shell's command line,
XXany filenames that you give to \*E as arguments are stored in the args list.
XXThe \fBar\fRgs command will display this list, or define a new one.
XX.PP
XXThe \fBn\fRext command switches from the current file to the next one
XXin the args list.
XXYou may specify a new args list here, too.
XX.PP
XXThe \fBN\fRext and \fBpre\fRvious commands
XX(they're really aliases for the same command)
XXswitch from the current file to the preceding file in the args list.
XX.PP
XXThe \fBrew\fRind command switches from the current file to the first file
XXin the args list.
XX.NH 2
XXSwitching Files
XX.if n .ul 0
XX.ID
XX.ps
XXedit[!] [file]
XXtag[!] tagname
XX.DE
XX.PP
XXThe \fBe\fRdit command allows to switch from the current file to some other file.
XXThis has nothing to do with the args list, by the way.
XX.PP
XXThe \fBta\fRg command looks up a given tagname in a file called "tags".
XXThis tells it which file the tag is in, and how to find it in that file.
XX\*E then switches to the tag's file and finds the tag.
XX.NH 2
XXWorking with a Compiler
XX.if n .ul 0
XX.ID
XX.ps
XXcc [files]
XXmake [target]
XXerrlist[!] [errlist]
XX.DE
XX.PP
XXThe \fBcc\fR and \fBmak\fRe commands execute your compiler or "make" utility
XXand redirect any error messages into a file called "errlist".
XXBy default, cc is run on the current file.
XX(You should write it before running cc.)
XXThe contents of the "errlist" file are then scanned for error messages.
XXIf an error message is found, then the cursor is moved to the line where
XXthe error was detected,
XXand the description of the error is displayed on the status line.
XX.PP
XXAfter you've fixed one error, the \fBer\fRrlist command will move
XXthe cursor to the next error.
XXIn visual command mode,
XXhitting `*' will do this, too.
XX.PP
XXYou can also create an "errlist" file from outside of \*E,
XXand use "\*E -m" to start elvis and have the cursor moved to the
XXfirst error.
XXNote that you don't need to supply a filename with "\*E -m" because
XXthe error messages always say which source file an error is in.
XX.PP
XXNote:
XXWhen you use errlist repeatedly to fix several errors in a single file,
XXit will attempt to adjust the reported line numbers to allow for lines
XXthat you have inserted or deleted.
XXThese adjustments are made with the assumption that you will work though
XXthe file from the beginning to the end.
XX.NH 2
XXExit Commands
XX.if n .ul 0
XX.ID
XX.ps
XXquit[!]
XXwq
XXxit
XX.DE
XX.PP
XXThe \fBq\fRuit command exits from the editor without saving your file.
XX.PP
XXThe \fBwq\fR command writes your file out, then then exits.
XX.PP
XXThe \fBx\fRit command is similar to the \fBwq\fR command, except that
XX\fBx\fRit won't bother to write your file if you haven't modified it.
XX.NH 2
XXFile I/O Commands
XX.if n .ul 0
XX.ID
XX.ps
XX[line] read file
XX[line][,line] write[!] [[>>]file]
XX.DE
XX.PP
XXThe \fBr\fRead command gets text from another file and inserts it
XXafter the specified line.
XXIt can also read the output of a program;
XXsimply precede the program name by a '!' and use it in place of the file name.
XX.PP
XXThe \fBw\fRrite command writes the whole file, or just part of it,
XXto some other file.
XXThe !, if present, will permit the lines to be written even if you've set
XXthe readonly option.
XXIf you precede the filename by >> then the lines will be appended to the file.
XXYou can send the lines to the standard input of a program by replacing the
XXfilename with a '!' followed by the command and its arguments.
XX.PP
XXNote: Be careful not to confuse ":w!filename" and ":w !command".
XXTo write to a program, you must have at least one blank before the '!'.
XX.NH 2
XXDirectory Commands
XX.if n .ul 0
XX.ID
XX.ps
XXcd [directory]
XXchdir [directory]
XXshell
XX.DE
XX.PP
XXThe \fBcd\fR and \fBchd\fRir commands
XX(really two names for one command)
XXswitch the current working directory.
XX.PP
XXThe \fBsh\fRell command starts an interactive shell.
XX.NH 2
XXDebugging Commands
XX.if n .ul 0
XX.ID
XX.ps
XX[line][,line] debug[!]
XXvalidate[!]
XX.DE
XX.PP
XXThese commands are only available if you compile \*E with the -DDEBUG flag.
XX.PP
XXThe de\fBb\fRug command lists statistics for the blocks which contain
XXthe specified range of lines.
XXIf the ! is present, then the contents of those blocks is displayed, too.
XX.PP
XXThe \fBva\fRlidate command checks certain variables for internal consistency.
XXNormally it doesn't output anything unless it detects a problem.
XXWith the !, though, it will always produce *some* output.
X/
Xecho x - index.ms
Xsed '/^X/s///' > index.ms << '/'
XX.XS 1
XXINTRODUCTION
XXWhat E\s-2LVIS\s+2 does,
XXCopyright,
XXHow to compile E\s-2LVIS\s+2,
XXOverview
XX.XA 2
XXVISUAL MODE COMMANDS
XXNormal interactive editing,
XXInput mode,
XXArrow keys,
XXDigraphs,
XXAbbreviations,
XXAuto-indentation
XX.XA 3
XXCOLON MODE COMMANDS
XXLine specifiers,
XXText entry,
XXCut & paste,
XXDisplay text,
XXGlobal operations,
XXLine editing,
XXUndo,
XXConfiguration & status,
XXMultiple files,
XXSwitching files,
XXWorking with a compiler,
XXExiting,
XXFile I/O,
XXDirectory & shell,
XXDebugging
XX.XA 4
XXREGULAR EXPRESSIONS
XXSyntax,
XXOptions,
XXSubstitutions,
XXExamples
XX.XA 5
XXOPTIONS
XXAutoindent,
XXAutoprint,
XXetc.
XX.XA 6
XXCUT BUFFERS
XXPutting text into a cut buffer,
XXPasting from a cut buffer,
XXMacros,
XXThe effect of switching files
XX.XA 7
XXDIFFERENCES BETWEEN E\s-2LVIS\s+2 AND THE REAL VI/EX
XXExtensions,
XXOmissions
XX.XA 8
XXINTERNAL
XXFor programmers only,
XXThe temporary file,
XXImplementation of editing,
XXMarks and the cursor,
XXColon command interpretation,
XXScreen control,
XXPortability
XX.XA 9
XXCFLAGS
XX.XA 10
XXTERMCAP
XX.XA 11
XXENVIRONMENT VARIABLES
XX.XA 12
XXVERSIONS
XX.XA 13
XXQUESTIONS & ANSWERS
XX.XE
XX.PX
XX.sp 0.3i
XX.ce 1
XXUNIX-style "man" pages appear at the end of this manual.
X/
Xecho x - internal.ms
Xsed '/^X/s///' > internal.ms << '/'
XX.Go 8 "INTERNAL"
XX.PP
XXYou don't need to know the material in this section to use \*E.
XXYou only need it if you intend to modify \*E.
XX.PP
XXYou should also check out the CFLAGS, TERMCAP, ENVIRONMENT VARIABLES,
XXVERSIONS, and QUIESTIONS & ANSWERS sections of this manual.
XX.NH 2
XXThe temporary file
XX.PP
XXThe temporary file is divided into blocks of 1024 bytes each.
XXThe functions in "blk.c" maintain a cache of the five most recently used blocks,
XXto minimize file I/O.
XX.PP
XXWhen \*E starts up, the file is copied into the temporary file
XXby the function \fBtmpstart()\fR in "tmp.c".
XXSmall amounts of extra space are inserted into the temporary file to
XXinsure that no text lines cross block boundaries.
XXThis speeds up processing and simplifies storage management.
XXThe extra space is filled with NUL characters.
XXthe input file must not contain any NULs, to avoid confusion.
XXThis also limits lines to a length of 1023 characters or less.
XX.PP
XXThe data blocks aren't necessarily stored in sequence.
XXFor example, it is entirely possible that the data block containing
XXthe first lines of text will be stored after the block containing the
XXlast lines of text.
XX.PP
XXIn RAM, \*E maintains two lists: one that describes the "proper"
XXorder of the disk blocks, and another that records the line number of
XXthe last line in each block.
XXWhen \*E needs to fetch a given line of text, it uses these tables
XXto locate the data block which contains that line.
XX.PP
XXBefore each change is made to the file, these lists are copied.
XXThe copies can be used to "undo" the change.
XXAlso, the first list
XX-- the one that lists the data blocks in their proper order --
XXis written to the first data block of the temp file.
XXThis list can be used during file recovery.
XX.PP
XXWhen blocks are altered, they are rewritten to a \fIdifferent\fR block in the file,
XXand the order list is updated accordingly.
XXThe original block is left intact, so that "undo" can be performed easily.
XX\*E will eventually reclaim the original block, when it is no longer needed.
XX.NH 2
XXImplementation of Editing
XX.PP
XXThere are three basic operations which affect text:
XX.ID
XX\(bu delete text	- delete(from, to)
XX\(bu add text	- add(at, text)
XX\(bu yank text	- cut(from, to)
XX.DE
XX.PP
XXTo yank text, all text between two text positions is copied into a cut buffer.
XXThe original text is not changed.
XXTo copy the text into a cut buffer,
XXyou need only remember which physical blocks that contain the cut text,
XXthe offset into the first block of the start of the cut,
XXthe offset into the last block of the end of the cut,
XXand what kind of cut it was.
XX(Cuts may be either character cuts or line cuts;
XXthe kind of a cut affects the way it is later "put".)
XXYanking is implemented in the function \fBcut()\fR,
XXand pasting is implemented in the function \fBpaste()\fR.
XXThese functions are defined in "cut.c".
XX.PP
XXTo delete text, you must modify the first and last blocks, and
XXremove any reference to the intervening blocks in the header's list.
XXThe text to be deleted is specified by two marks.
XXThis is implemented in the function \fBdelete()\fR.
XX.PP
XXTo add text, you must specify
XXthe text to insert (as a NUL-terminated string)
XXand the place to insert it (as a mark).
XXThe block into which the text is to be inserted may need to be split into
XXas many as four blocks, with new intervening blocks needed as well...
XXor it could be as simple as modifying a single block.
XXThis is implemented in the function \fBadd()\fR.
XX.PP
XXThere is also a \fBchange()\fR function,
XXwhich generally just calls delete() and add().
XXFor the special case where a single character is being replaced by another
XXsingle character, though, change() will optimize things somewhat.
XXThe add(), delete(), and change() functions are all defined in "modify.c".
XX.PP
XXThe \fBinput()\fR function reads text from a user and inserts it into the file.
XXIt makes heavy use of the add(), delete(), and change() functions.
XXIt inserts characters one at a time, as they are typed.
XX.PP
XXWhen text is modified, an internal file-revision counter, called \fBchanges\fR,
XXis incremented.
XXThis counter is used to detect when certain caches are out of date.
XX(The "changes" counter is also incremented when we switch to a different file,
XXand also in one or two similar situations -- all related to invalidating caches.)
XX.NH 2
XXMarks and the Cursor
XX.PP
XXMarks are places within the text.
XXThey are represented internally as 32-bit values which are split
XXinto two bitfields:
XXa line number and a character index.
XXLine numbers start with 1, and character indexes start with 0.
XXLines can be up to 1023 characters long, so the character index is 10 bits
XXwide and the line number fills the remaining 22 bits in the long int.
XX.PP
XXSince line numbers start with 1,
XXit is impossible for a valid mark to have a value of 0L.
XX0L is therefore used to represent unset marks.
XX.PP
XXWhen you do the "delete text" change, any marks that were part of
XXthe deleted text are unset, and any marks that were set to points
XXafter it are adjusted.
XXMarks are adjusted similarly after new text is inserted.
XX.PP
XXThe cursor is represented as a mark.
XX.NH 2
XXColon Command Interpretation
XX.PP
XXColon commands are parsed, and the command name is looked up in an array
XXof structures which also contain a pointer to the function that implements
XXthe command, and a description of the arguments that the command can take.
XXIf the command is recognized and its arguments are legal,
XXthen the function is called.
XX.PP
XXEach function performs its task; this may cause the cursor to be
XXmoved to a different line, or whatever.
XX.NH 2
XXScreen Control
XX.PP
XXIn input mode or visual command mode,
XXthe screen is redrawn by a function called \fBredraw()\fR.
XXThis function is called in the getkey() function before each keystroke is
XXread in, if necessary.
XX.PP
XXRedraw() write to the screen via a package which looks like the "curses"
XXlibrary, but isn't.
XXIt is actually much simpler.
XXMost curses operations are implemented as macros which copy characters
XXinto a large I/O buffer, which is then written with a single large
XXwrite() call as part of the refresh() operation.
XX.PP
XX(Note: Under MS-DOS, the pseudo-curses macros check to see whether you're
XXusing the pcbios interface.  If you are, then the macros call functions
XXin "pc.c" to implement screen updates.)
XX.PP
XXThe low-level functions which modify text (namely add(), delete(), and change())
XXsupply redraw() with clues to help redraw() decide which parts of the
XXscreen must be redrawn.
XXThe clues are given via a function called \fBredrawrange()\fR.
XX.PP
XXMost EX commands use the pseudo-curses package to perform their output,
XXlike redraw().
XX.PP
XXThere is also a function called \fBmsg()\fR which uses the same syntax as printf().
XXIn EX mode, msg() writes message to the screen and automatically adds a
XXnewline.
XXIn VI mode, msg() writes the message on the bottom line of the screen
XXwith the "standout" character attribute turned on.
XX.NH 2
XXOptions
XX.PP
XXFor each option available through the ":set" command,
XX\*E contains a character array variable, named "o_\fIoption\fR".
XXFor example, the "lines" option uses a variable called "o_lines".
XX.PP
XXFor boolean options, the array has a dimension of 1.
XXThe first (and only) character of the array will be NUL if the
XXvariable's value is FALSE, and some other value if it is TRUE.
XXTo check the value, just by dereference the array name,
XXas in "if (*o_autoindent)".
XX.PP
XXFor number options, the array has a dimension of 3.
XXThe array is treated as three unsigned one-byte integers.
XXThe first byte is the current value of the option.
XXThe second and third bytes are the lower and upper bounds of that
XXoption.
XX.PP
XXFor string options, the array usually has a dimension of about 60
XXbut this may vary.
XXThe option's value is stored as a normal NUL-terminated string.
XX.PP
XXAll of the options are declared in "opts.c".
XXMost are initialized to their default values;
XXthe \fBinitopts()\fR function is used to perform any environment-specific
XXinitialization.
XX.NH 2
XXPortability
XX.PP
XXTo improve portability, \*E collects as many of the system-dependent
XXdefinitions as possible into the "config.h" file.
XXThis file begins with some preprocessor instructions which attempt to
XXdetermine which compiler and operating system you have.
XXAfter that, it conditionally defines some macros and constants for your system.
XX.PP
XXOne of the more significant macros is \fBttyread()\fR.
XXThis macro is used to read raw characters from the keyboard, possibly
XXwith timeout.
XXFor UNIX systems, this basically reads bytes from stdin.
XXFor MSDOS, TOS, and OS9, ttyread() is a function defined in curses.c.
XXThere is also a \fBttywrite()\fR macro.
XX.PP
XXThe \fBtread()\fR and \fBtwrite()\fR macros are versions of read() and write() that are
XXused for text files.
XXOn UNIX systems, these are equivelent to read() and write().
XXOn MS-DOS, these are also equivelent to read() and write(),
XXsince DOS libraries are generally clever enough to convert newline characters
XXautomatically.
XXFor Atari TOS, though, the MWC library is too stupid to do this,
XXso we had to do the conversion explicitly.
XX.PP
XXOther macros may substitute index() for strchr(), or bcopy() for memcpy(),
XXor map the "void" data type to "int", or whatever.
XX.PP
XXThe file "tinytcap.c" contains a set of functions that emulate the termcap
XXlibrary for a small set of terminal types.
XXThe terminal-specific info is hard-coded into this file.
XXIt is only used for systems that don't support real termcap.
XXAnother alternative for screen control can be seen in
XXthe "curses.h" and "pc.c" files.
XXHere, macros named VOIDBIOS and CHECKBIOS are used to indirectly call
XXfunctions which perform low-level screen manipulation via BIOS calls.
XX.PP
XXThe stat() function must be able to come up with UNIX-style major/minor/inode
XXnumbers that uniquely identify a file or directory.
XX.PP
XXPlease try to keep you changes localized,
XXand wrap them in #if/#endif pairs,
XXso that \*E can still be compiled on other systems.
XXAnd PLEASE let me know about it, so I can incorporate your changes into
XXmy latest-and-greatest version of \*E.
X/
Xecho x - intro.ms
Xsed '/^X/s///' > intro.ms << '/'
XX.Go 1 "INTRODUCTION"
XX.PP
XX\*E is a clone of vi/ex, the standard UNIX editor.
XX\*E supports nearly all of the vi/ex commands,
XXin both visual mode and colon mode.
XX.PP
XXLike vi/ex, \*E stores most of the text in a temporary file, instead of RAM.
XXThis allows it to edit files that are too large to fit
XXin a single process' data space.
XXAlso, the edit buffer can survive a power failure or crash.
XX.PP
XX\*E runs under BSD UNIX, AT&T SysV UNIX, Minix, MS-DOS, Atari TOS,
XXCoherent, OS9/68000, VMS and AmigaDos.
XXThe next version is also expected to add MS-Windows, OS/2 and MacOS.
XXContact me before you start porting it to some other OS,
XXbecause somebody else may have already done it for you.
XX.PP
XX\*E is freely redistributable, in either source form or executable form.
XXThere are no restrictions on how you may use it.
XX.NH 2
XXCompiling
XX.PP
XXSee the "Versions" section of this manual for instructions on how to compile
XX\*E.
XX.PP
XXIf you want to port \*E to another O.S. or compiler, then
XXyou should start be reading the "Portability" part of the "Internal" section.
XX.NH 2
XXOverview of \*E
XX.PP
XXThe user interface of \*E/vi/ex is weird.
XXThere are two major command modes in \*E, and a few text input modes as well.
XXEach command mode has a command which allows you to switch to the other mode.
XX.PP
XXYou will probably use the \fIvisual command mode\fR
XXmost of the time.
XXThis is the mode that \*E normally starts up in.
XX.PP
XXIn visual command mode, the entire screen is filled with lines of text
XXfrom your file.
XXEach keystroke is interpretted as part of a visual command.
XXIf you start typing text, it will \fInot\fR be inserted,
XXit will be treated as part of a command.
XXTo insert text, you must first give an "insert text" command.
XXThis will take some getting used to.
XX(An alternative exists.
XXLookup the "inputmode" option.)
XX.PP
XXThe \fIcolon mode\fR is quite different.
XX\*E displays a ":" character on the bottom line of the screen, as a prompt.
XXYou are then expected to type in a command line and hit the <Return> key.
XXThe set of commands recognized in the colon mode is different
XXfrom visual mode's.
X/
Xecho x - options.ms
Xsed '/^X/s///' > options.ms << '/'
XX.Go 5 "OPTIONS"
XX.PP
XXOptions may be set or examined via the colon command "set".
XXThe values of options will affect the operation of later commands.
XX.PP
XXFor convenience, options have both a long descriptive name and a short name
XXwhich is easy to type.
XXYou may use either name interchangably.
XXI like the short names, myself.
XX.PP
XXThere are three types of options: Boolean, string, and numeric.
XXBoolean options are made TRUE by giving the name of the option as an
XXargument to the "set" command;
XXthey are made FALSE by prefixing the name with "no".
XXFor example, "set autoindent" makes the autoindent option TRUE,
XXand "set noautoindent" makes it FALSE.
XX\*E also allows boolean options to be toggled by prefixing the name with "neg".
XXSo, ":map g :set neglist^M" will cause the <g> key to alternately toggle the
XX"list" option on and off.
XX(The "neg" prefix is an extension; the real vi doesn't support it.)
XX.PP
XXTo change the value of a string or numeric option, pass the "set" command
XXthe name of the option, followed by an "=" sign and the option's new value.
XXFor example, "set tabstop=8" will give the tabstop option a value of 8.
XXFor string options, you may enclose the new value in quotes.
XX.LD
XX.ta 1.9i 2.4i 3.8i
XX.ps +2
XX\fBNAMES	TYPE	DEFAULT	MEANING\fP
XX.ps
XXautoindent, ai	Bool	noai	auto-indent during input
XXautoprint, ap	Bool	ap	in EX, print the current line
XXautotab, at	Bool	at	auto-indent allowed to use tabs?
XXautowrite, aw	Bool	noaw	auto-write when switching files
XXbeautify,  bf	Bool	nobf	strip control chars from file?
XXcharattr, ca	Bool	noca	interpret \\fX sequences?
XXcc, cc	Str	cc="cc -c"	name of the C compiler
XXcolumns, co	Num	co=80	width of the screen
XXdigraph, dig	Bool	nodig	recognize digraphs?
XXdirectory, dir	Str	dir="/usr/tmp"	where tmp files are kept
XXedcompatible, ed	Bool	noed	remember ":s//" options
XXequalprg, ep	Bool	ep="fmt"	program to run for = operator
XXerrorbells, eb	Bool	eb	ring bell on error
XXexrc, exrc	Bool	noexrc	read "./.exrc" file?
XXexrefresh, er	Bool	er	write lines indiviually in EX
XXflash, vbell	Bool	flash	use visible alternative to bell
XXflipcase, fc	Str	fc=""	non-ASCII chars flipped by ~
XXhideformat, hf	Bool	hf	hide text formatter commands
XXignorecase, ic	Bool	noic	upper/lowercase match in search
XXinputmode, im	Bool	noim	start vi in insert mode?
XXkeytime, kt	Num	kt=2	timeout for mapped key entry
XXkeywordprg, kp	Str	kp="ref"	full pathname of shift-K prog
XXlines, ln	Num	ln=25	number of lines on the screen
XXlist, li	Bool	noli	display lines in "list" mode
XXmagic, ma	Bool	ma	use regular expression in search
XXmake, mk	Str	mk="make"	name of the "make" program
XXmesg, ms	Bool	ms	allow messages from other users?
XXmodelines, ml	Bool	noml	are modelines processed?
XXmore, more	Bool	more	pause between messages?
XXnovice, nov	Bool	nonovice	set options for ease of use
XXparagraphs, para	Str	para="PPppIPLPQP"	names of "paragraph" nroff cmd
XXprompt, pr	Bool	pr	show ':' prompt in \fIex\fR mode
XXreadonly, ro	Bool	noro	prevent overwriting of orig file
XXremap, rem	Bool	remap	allow key maps to call key maps
XXreport, re	Num	re=5	report when 5 or more changes
XXruler, ru	Bool	noru	display line/column numbers
XXscroll, sc	Num	sc=12	scroll amount for ^U and ^D
XXsections, sect	Str	sect="NHSHSSSEse"	names of "section" nroff cmd
XXshell, sh	Str	sh="/bin/sh"	full pathname of the shell
XXshowmatch, sm	Bool	nosm	show matching ()[]{}
XXshowmode, smd	Bool	nosmd	say when we're in input mode
XXshiftwidth, sw	Num	sw=8	shift amount for < and >
XXsidescroll, ss	Num	ss=8	amount of sideways scrolling
XXsync, sy	Bool	nosy	call sync() often
XXtabstop, ts	Num	ts=8	width of tab characters
XXtaglength, tl	Num	tl=0	significant chars in tag name
XXterm, te	Str	te="$TERM"	name of the termcap entry
XXterse, tr	Bool	notr	give shorter error messages
XXtimeout, to	Bool	to	distinguish <esc> from <arrow>?
XXwarn, wa	Bool	wa	warn for ! if file modified
XXwindow, wi	Num	wi=24	lines to redraw after long move
XXwrapmargin, wm	Num	wm=0	wrap long lines in input mode
XXwrapscan, ws	Bool	ws	at EOF, searches wrap to line 1
XXwriteany, wr	Bool	nowr	allow :w to clobber files
XX.DE
XX.TA
XX.ne 6
XX.IP "autoindent, ai"
XXDuring input mode, the autoindent option will cause each added line
XXto begin with the same amount of leading whitespace as the line above it.
XXWithout autoindent, added lines are initially empty.
XX.IP "autoprint, ap"
XXThis option only affects EX mode.
XXIf the autoprint option on,
XXand either the cursor has moved to a different line
XXor the previous command modified the file,
XXthen \*E will print the current line.
XX.IP "autotab, at"
XXThis option affects the behaviour of the autoindent mode.
XXIf autoindent is turned off, then autotab has no effect.
XX.IP
XXWhen autotab is turned on, elvis will use a mixture of spaces and tabs
XXto create the proper amount of indentation.
XXThis is the default.
XX.IP
XXWhen autotab is turned off, elvis will only use spaces for auto-indent.
XX\*E will still insert a real tab character when you hit the <Tab> key, though;
XXthe autotab option only affects \fIautomatic\fR indentation.
XX.IP "autowrite, aw"
XXWhen you're editing one file and decide to switch to another
XX\- via the :tag command, or :next command, perhaps \-
XXif your current file has been modified,
XXthen \*E will normally print an error message and refuse to switch.
XX.IP
XXHowever, if the autowrite option is on,
XXthen \*E will write the modified version of the current file
XXand successfully switch to the new file.
XX.IP "beautify, bf"
XXThis option causes all control characters to be deleted from the text file,
XXat the time when you start editing it.
XXIf you're already editing a file when you turn on the beautify option,
XXthen that file won't be affected.
XX.IP cc
XXThe :cc command runs the C compiler.
XXThis option should be set to the name of your compiler.
XX.IP "charattr, ca"
XXMany text formatting programs allow you to designate portions of
XXyour text to be underlined, italicized, or boldface by embedding
XXthe special strings \\fU, \\fI, and \\fB in your text.
XXThe special string \\fP marks the end of underlined or boldface text.
XX.IP
XX\*E normally treats those special strings just like any other text.
XX.IP
XXHowever, if the charattr option is on, then \*E will interpret
XXthose special strings correctly,
XXto display underlined or boldface text on the screen.
XX(This only works, of course, if your terminal can display
XXunderlined and boldface, and if the TERMCAP entry says how to do it.)
XX.IP "columns, co"
XXThis option shows how wide your screen is.
XX.IP "digraph, dig"
XXThis option is used to enable/disable recognition of digraphs.
XXThe default value is nodigraph, which means that digraphs will not be
XXrecognized.
XX.IP "directory, dir"
XX\*E stores text in temporary files.
XXThis option allows you to control which directory those temporary files will
XXappear in.
XXThe default is /usr/tmp.
XX.IP
XXThis option can only be set in a .exrc file;
XXafter that, \*E will have already started making temporary files
XXin some other directory, so it would be too late.
XX.IP "edcompatible, ed"
XXThis option affects the behaviour of the ":s/regexp/text/options" command.
XXIt is normally off (:se noed) which causes all of the substitution options
XXto be off unless explicitly given.
XX.IP
XXHowever, with edcompatible on (:se ed), the substitution command remembers
XXwhich options you used last time.
XXThose same options will continue to be used until you change them.
XXIn edcompatible mode, when you explicitly give the name of a
XXsubstitution option, you will toggle the state of that option.
XX.IP
XXThis all seems very strange to me, but its implementation was almost free
XXwhen I added the ":&" command to repeat the previous substitution,
XXso there it is.
XX.IP "equalprg, ep"
XXThis holds the name & arguments of the external filter program
XXused the the visual = operator.
XXThe defualt value is "fmt",
XXso the = operator will adjust line breaks in text.
XX.IP "errorbells, eb"
XX\*E normally rings a bell when you do something wrong.
XXThis option lets you disable the bell.
XX.IP exrc
XXThis option specifies whether a .exrc file in the current directory
XXshould be executed.
XXBy default, this option is off (":set noexrc") which prevents elvis from
XXexecuting .exrc in the current directory.
XXIf the .exrc file in your home directory turns this option on (":set exrc")
XXthen the \*E will attempt to execute the .exrc file in the current directory.
XX.IP
XXThis option exist mainly for security reasons.
XXA mean-spirited person could do something like
XX.br
XX	echo >/tmp/.exrc '!rm -rf $HOME'
XX.br
XXand then anybody who attempted to edit or view a file in the /tmp directory
XXwould lose most of their files.
XXWith the exrc option turned off, this couldn't happen to you.
XX.IP "exrefresh, er"
XXThe EX mode of \*E writes many lines to the screen.
XXYou can make \*E either write each line to the screen separately,
XXor save up many lines and write them all at once.
XX.IP
XXThe exrefresh option is normally on, so each line is written to the
XXscreen separately.
XX.IP
XXYou may wish to turn the exrefresh option off (:se noer) if the
XX"write" system call is costly on your machine, or if you're using a
XXwindowing environment.
XX(Windowing environments scroll text a lot faster when you write
XXmany lines at once.)
XX.IP
XXThis option has no effect in visual command mode or input mode.
XX.IP "flash, vbell"
XXIf your termcap entry describes a visible alternative to ringing
XXyour terminal's bell, then this option will say whether the visible
XXversion gets used or not.
XXNormally it will be.
XX.IP
XXIf your termcap does NOT include a visible bell capability,
XXthen the flash option will be off, and you can't turn it on.
XX.IP "flipcase, fc"
XXThe flipcase option allows you to control how the non-ASCII characters are
XXaltered by the "~" command.
XX.IP
XXThe string is divided into pairs of characters.
XXWhen "~" is applied to a non-ASCII character,
XX\*E looks up the character in the flipcase string to see which pair it's in,
XXand replaces it by the other character of the pair.
XX.IP "hideformat, hf"
XXMany text formatters require you to embed format commands in your text,
XXon lines that start with a "." character.
XX\*E normally displays these lines like any other text,
XXbut if the hideformat option is on,
XXthen format lines are displayed as blank lines.
XX.IP "ignorecase, ic"
XXNormally, when \*E searches for text, it treats uppercase letters
XXas being different for lowercase letters.
XX.IP
XXWhen the ignorecase option is on, uppercase and lowercase are treated as equal.
XX.IP "inputmode, im"
XXThis option allows you to have \*E start up in insert mode.
XXYou can still exit insert mode at any time by hitting the ESC key, as usual.
XXUsually, this option would be set in your ".exrc" file.
XX.IP "keytime, kt"
XXThe arrow keys of most terminals send a multi-character sequence.
XXIt takes a measurable amount of time for these sequences to be transmitted.
XXThe keytime option allows you to control the maximum amount of time
XXto allow for an arrow key (or other mapped key) to be received in full.
XX.IP
XXOn most systems, the setting is the number of tenths of a second to allow
XXbetween characters.
XXOn some other systems, the setting is in whole seconds.
XX.IP
XXTry to avoid setting keytime=1.
XXMost systems just count clock beats, so if you tried to read a character
XXshortly before a clock beat, you could allow almost no time at all for
XXreading the characters.
XXFor higher keytime settings, the difference is less critical.
XX.IP
XXIf your system's response time is poor, you might want to increase the keytime.
XXIn particular, I've found that when keystrokes must be sent through a network
XX(via X windows, rlogin, or telnet, for example) the keytime should be set to
XXat least 1 second.
XX.IP
XXAs a special case,
XXyou can set keytime to 0 to disable this time limit stuff altogether.
XXThe big problem here is:
XXIf your arrow keys' sequences start with an ESC,
XXthen every time you hit your ESC key \*E will wait... and wait...
XXto see if maybe that ESC was part of an arrow key's sequence.
XX.IP
XXNOTE: this option is a generalization of the timeout option of the real vi.
XX.IP "keywordprg, kp"
XX\*E has a special keyword lookup feature.
XXYou move the cursor onto a word, and hit shift-K,
XXand \*E uses another program to look up the word
XXand display information about it.
XX.IP
XXThis option says which program gets run.
XX.IP
XXThe default value of this option is "ref",
XXwhich is a program that looks up the definition of a function in C.
XXIt looks up the function name in a file called "refs" which is created by ctags.
XX.IP
XXYou can subtitute other programs, such as an English dictionary program
XXor the online manual.
XX\*E runs the program, using the keyword as its only argument.
XXThe program should write information to stdout.
XXThe program's exit status should be 0, unless you want \*E to print
XX"<<< failed >>>".
XX.IP "lines, ln"
XXThis option says how many lines you screen has.
XX.IP "list, li"
XXIn nolist mode (the default), \*E displays text in a "normal" manner
XX-- with tabs expanded to an appropriate number of spaces, etc.
XX.IP
XXHowever, sometimes it is useful to have tab characters displayed differently.
XXIn list mode, tabs are displayed as "^I",
XXand a "$" is displayed at the end of each line.
XX.IP "magic, ma"
XXThe search mechanism in \*E can accept "regular expressions"
XX-- strings in which certain characters have special meaning.
XX.IP
XXThe magic option is normally on, which causes these characters to be treated
XXspecially.
XX.IP
XXIf you turn the magic option off (:se noma),
XXthen all characters except ^ and $ are treated literally.
XX^ and $ retain their special meanings regardless of the setting of magic.
XX.IP "make, mk"
XXThe :make command runs your "make" program.
XXThis option defines the name of your "make" program.
XX.IP mesg
XXWith the real vi, running under real UNIX,
XX":set nomesg" would prevent other users from sending you messages.
XX\*E ignores it, though.
XX.IP "modelines, ml"
XX\*E supports modelines.
XXModelines are lines near the beginning or end of your text file which
XXcontain "ex:yowza:",
XXwhere "yowza" is any EX command.
XXA typical "yowza" would be something like "set ts=5 ca kp=spell wm=15".
XXOther text may also appear on a modeline,
XXso you can place the "ex:yowza:" in a comment:
XX.br
XX.ID
XX/* ex:set sw=4 ai: */
XX.DE
XX.IP
XXNormally these lines are ignored, for security reasons,
XXbut if you have "set modelines" in your .exrc file
XXthen "yowza" is executed.
XX.IP "novice, nov"
XXThe command ":set novice" is equivelent to ":set nomagic report=1 showmode".
XX.IP "paragraphs, pa"
XXThe { and } commands move the cursor forward or backward in increments
XXof one paragraph.
XXParagraphs may be separated by blank lines, or by a "dot" command of
XXa text formatter.
XXDifferent text formatters use different "dot" commands.
XXThis option allows you to configure \*E to work with your text formatter.
XX.IP
XXIt is assumed that your formatter uses commands that start with a
XX"." character at the front of a line,
XXand then have a one- or two-character command name.
XX.IP
XXThe value of the paragraphs option is a string in which each pair
XXof characters is one possible form of your text formatter's paragraph
XXcommand.
XX.IP "more"
XXWhen \*E must display a sequence of messages at the bottom line of the screen
XXin visual mode, it normally pauses after all but the last one, so you have
XXtime to read them all.
XX.IP
XXIf you turn off the "more" option, then \*E will not pause.
XXThis means you can only read the last message, but it is usually the most
XXimportant one anyway.
XX.IP "prompt, pr"
XXIf you ":set noprompt", then \*E will no longer emit a ':' when it
XXexpects you to type in an \fIex\fR command.
XXThis is slightly useful if you're using an astonishingly slow UNIX machine,
XXbut the rest of us can just ignore this one.
XX.IP "readonly, ro"
XXNormally, \*E will let you write back any file to which you have
XXwrite permission.
XXIf you don't have write permission, then you can only write the changed
XXversion of the file to a \fIdifferent\fP file.
XX.IP
XXIf you set the readonly option,
XXthen \*E will pretend you don't have write permission to \fIany\fP file you edit.
XXIt is useful when you really only mean to use \*E to look at a file,
XXnot to change it.
XXThis way you can't change it accidentally.
XX.IP
XXThis option is normally off, unless you use the "view" alias of \*E.
XX"View" is like "vi" except that the readonly option is on.
XX.IP "remap"
XXThe ":map" command allows you to convert one key sequence into another.
XXThe remap option allows you to specify what should happen if portions of
XXthat other sequence are also in the map table.
XXIf remap is on, then those portions will also be mapped, just as if they
XXhad been typed on the keyboard.
XXIf remap is off, then the matching portions will not be mapped.
XX.IP
XXFor example, if you enter the commands ":map A B" and ":map B C",
XXthen when remap is on, A will be converted to C.
XXBut when remap is off, A will be converted only to B.
XX.IP "report, re"
XXCommands in \*E may affect many lines.
XXFor commands that affect a lot of lines, \*E will output a message saying
XXwhat was done and how many lines were affected.
XXThis option allows you to define what "a lot of lines" means.
XXThe default is 5, so any command which affects 5 or more lines will cause
XXa message to be shown.
XX.IP "ruler, ru"
XXThis option is normally off.
XXIf you turn it on, then \*E will constantly display the line/column numbers
XXof the cursor, at the bottom of the screen.
XX.IP "scroll, sc"
XXThe ^U and ^D keys normally scroll backward or forward by half a screenful,
XXbut this is adjustable.
XXThe value of this option says how many lines those keys should scroll by.
XXIf you invoke ^U or ^D with a count argument (for example, "33^D") then
XXthis option's value is set to the count.
XX.IP "sections, se"
XXThe [[ and ]] commands move the cursor backward or forward in increments of
XX1 section.
XXSections may be delimited by a { character in column 1
XX(which is useful for C source code)
XXor by means of a text formatter's "dot" commands.
XX.IP
XXThis option allows you to configure \*E to work with your text formatter's
XX"section" command, in exectly the same way that the paragraphs option makes
XXit work with the formatter's "paragraphs" command.
XX.IP "shell, sh"
XXWhen \*E forks a shell
XX(perhaps for the :! or :shell commands)
XXthis is the program that is uses as a shell.
XXThis is "/bin/sh" by default,
XXunless you have set the SHELL (or COMSPEC, for MS-DOS) environment variable,
XXit which case the default value is copied from the environment.
XX.IP "shiftwidth, sw"
XXThe < and > commands shift text left or right by some uniform number of columns.
XXThe shiftwidth option defines that "uniform number".
XXThe default is 8.
XX.IP "showmatch, sm"
XXWith showmatch set,
XXin input mode every time you hit one of )}],
XX\*E will momentarily move the cursor to the matching ({[.
XX.IP "showmode, smd"
XXIn visual mode, it is easy to forget whether you're in the visual command mode
XXor input/replace mode.
XXNormally, the showmode option is off, and you haven't a clue as to which mode
XXyou're in.
XXIf you turn the showmode option on, though, a little message will appear in the
XXlower right-hand corner of your screen, telling you which mode you're in.
XX.IP "sidescroll, ss"
XXFor long lines, \*E scrolls sideways.
XX(This is different from the real vi,
XXwhich wraps a single long line onto several rows of the screen.)
XX.IP
XXTo minimize the number of scrolls needed,
XX\*E moves the screen sideways by several characters at a time.
XXThe value of this option says how many characters' widths to scroll at a time.
XX.IP
XXGenerally, the faster your screen can be redrawn,
XXthe lower the value you will want in this option.
XX.IP "sync, sy"
XXIf the system crashes during an edit session, then most of your work
XXcan be recovered from the temporary file that \*E uses to store
XXchanges.
XXHowever, sometimes the OS will not copy changes to the
XXhard disk immediately, so recovery might not be possible.
XXThe [no]sync option lets you control this.
XX.IP
XXIn nosync mode (which is the default, for UNIX), \*E lets the operating system
XXcontrol when data is written to the disk.
XXThis is generally faster.
XX.IP
XXIn sync mode (which is the default for MS-DOS, AmigaDos, and Atari TOS),
XX\*E forces all changes out
XXto disk every time you make a change.
XXThis is generally safer, but slower.
XXIt can also be a rather rude thing to do on a multi-user system.
XX.IP "tabstop, ts"
XXTab characters are normally 8 characters wide,
XXbut you can change their widths by means of this option.
XX.IP "taglength, tl"
XXThis option allows you to specify how many characters of a tag's name
XXmust match when performing tag lookup.
XXAs a special case, ":set taglength=0" means that all characters of a tag's
XXname must match.
XX.IP
XXNote: some configurations of \*E don't support this option.
XX.IP "term, te"
XXThis read-only option shows the name of the termcap entry that
XX\*E is using for your terminal.
XX.IP "terse, tr"
XXThe real vi uses this option to select longer vs. shorter error messages.
XX\*E has only one set of error messages, though, so this option has no effect.
XX.IP "timeout, to"
XXThe command ":set notimeout" is equivelent to ":set keytime=0",
XXand ":set timeout" is equivelent to ":set keytime=1".
XXThis affects the behaviour of the <Esc> key.
XXSee the discussion of the "keytime" option for more information.
XX.IP "warn, wa"
XXIf you have modified a file but not yet written it back to disk, then
XX\*E will normally print a warning before executing a ":!cmd" command.
XXHowever, in nowarn mode, this warning is not given.
XX.IP
XX\*E also normally prints a message after a successful search that
XXwrapped at EOF.
XXThe [no]warn option can also disable this warning.
XX.IP "window, wi"
XXThis option controls how many lines are redrawn after a long move.
XX.IP
XXOn fast terminals, this is usually set to the number of rows that the
XXterminal can display, minus one.
XXThis causes the entire screen to be filled with text around the cursor.
XX.IP
XXOn slow terminals, you may wish to reduce this value to about 7 or so.
XXThat way, if you're doing something like repeatedly hitting 'n' to search
XXfor each occurrence of some string and trying to find a particular occurrence,
XXthen you don't need to wait as long for \*E to redraw the screen after each
XXsearch.
XX.IP "wrapmargin, wm"
XXNormally (with wrapmargin=0) \*E will let you type in extremely long
XXlines, if you wish.
XX.IP
XXHowever, with warpmargin set to something other that 0 (wrapmargin=10
XXis nice), \*E will automatically cause long lines to be "wrapped"
XXon a word break for lines come too close to the right-hand margin.
XXFor example: On an 80-column screen, ":set wm=10" will cause lines to
XXwrap when their length exceeds 70 columns.
XX.IP "wrapscan, ws"
XXNormally, when you search for something, \*E will find it no matter
XXwhere it is in the file.
XX\*E starts at the cursor position, and searches forward.
XXIf \*E hits EOF without finding what you're looking for,
XXthen it wraps around to continue searching from line 1.
XXIf you turn off the wrapscan option (:se nows),
XXthen when \*E hits EOF during a search, it will stop and say so.
XX.IP "writeany, wr"
XXWith "writeany" turned off, elvis will prevent you from accidentally
XXoverwriting a file.
XXFor example, if "foo" exists then ":w foo" will fail.
XXIf you turn on the "writeany" option, then ":w foo" will work.
XX.IP
XXRegardless of the setting of "writeany", though, ":w! foo" will work.
XXThe '!' forces the ":w" command to write the file unless the operating system
XXwon't allow it.
X/
Xecho x - question.ms
Xsed '/^X/s///' > question.ms << '/'
XX.nr Qn 0 1
XX.de QQ
XX.sp
XX.IP \fB\\n+(Qn) 0.3i
XX..
XX.de AA
XX.IP \fR 0.75i
XX..
XX.Go 13 "QUESTIONS & ANSWERS"
XX.QQ
XXHow can I make elvis run faster under DOS?
XX.AA
XXThere are several things you can do.
XXThe first thing to do is get a good screen driver such as NANSI.SYS.
XXThis can speed up screen redrawing by as much as a factor of eight!
XXThe DOS-specific part of section 12 tells you how to do this.
XX.AA
XXYou might also consider reducing the size of the blocks that elvis uses.
XXYou'll need to recompile \*E to do this.
XXThe default BLKSIZE is 1024 byte for the DOS version of \*E, which means
XXthat for each keystroke that you insert, elvis must shift an average of
XXabout 500 bytes.
XXThat's a lot to ask from a little old 5MHz 8088.
XXA BLKSIZE of 512 bytes might be more appropriate.
XX.AA
XXIf you're \fIreally\fR desperate for more speed, you might want to make
XX\*E store its temporary files on a RAM disk.
XXHowever, this limits the size of the file you can edit, and it eliminates any
XXchance you may have had to recover your work after a power failure
XXor system crash, but it might be worth it; you decide.
XXTo do this, add ":set dir=R:\\" (or whatever your RAM disk's name is)
XXto the \fIelvis.rc\fP file.
XX.AA
XXNext, consider turning off the "sync" option.
XXWhen the sync option is turned on, \*E will close the temporary file
XXand reopen it after every change, in order to force DOS to update
XXthe file's directory entry.
XXIf you put ":set nosync" into the \fIelvis.rc\fP file, then elvis will
XXonly close the file when you start editing a different text file, or
XXwhen you're exiting \*E.
XXConsequently, there is no chance that you'll be able to recover your
XXchanges after a power failure... so if you're going to this, then you
XXmight as well store the temp files on the RAM disk, too.
XX.QQ
XXWhere's the <Esc> key on a DEC keyboard?
XX.AA
XXI don't know.  Maybe the <F11> key?
XXYou could always use ":map!" to make some other key act like the <Esc> key.
XXIf all else fails, use <Control><[>.
XX.QQ
XXIs there a way to show which keys do what?
XX.AA
XXYes.  The command ":map" will show what each key does in command mode,
XXand ":map!" (with an exclamation mark) shows what each key does in
XXinput mode.
XX.AA
XXThe table is divided into three columns: the key's label, the characters
XXthat it sends, and the characters that \*E pretends you typed.
XX.QQ
XXHow can I make \*E display long lines like the real vi?
XX.AA
XXYou can't yet.
XXThe next version of \*E shouldsupport this, though.
XX.QQ
XXI can't recover my text [under MS-DOS or Atari TOS].
XXAccording to the directory listing, the temporary file is 0 bytes long.
XXWhat went wrong?
XX.AA
XXMS-DOS and TOS only update a file's directory entry when the file is closed.
XXIf the system crashes while the file is still open, then the file's length
XXis stored as 0 bytes.
XXThe ":set sync" option is supposed to prevent this;
XXyou probably turned it off in the interest of speed, right?
XX.AA
XXUnder MS-DOS [I don't know about TOS], you should delete the empty
XXtemporary file, and then run CHKDSK/F.
XXThis \fImight\fP find the data that belonged in the empty file,
XXand place it in a new file with a name like "000001.CHK" -- something like that.
XXYou can then try to extract the text from that temporary file by giving the
XXcommand "elvprsv -R 000001.chk >goodnews.txt".
XXIf you're lucky, then your text might be in GOODNEWS.TXT.
XX.QQ
XXWhat is the most current version of \*E?
XX.AA
XXEach version of \*E that is released to the public has a version number
XXof the form "number point number".
XXAs I write this, the most current version of elvis is 1.5.
XX.AA
XXThe intermediate steps between one release and the next are labeled with
XXthe \fInext\fP version number, with a letter appended.
XXFor example, after 1.4 was released, I started working on 1.5a.
XXI am currently working on 2.0a.
XXWhen \*E reaches a stable state, I'll call it 2.0 and release it.
XX.AA
XXSometimes a beta-test version of elvis will be available via anonymous FTP
XXfrom m2xenix.psg.com, in the directory "pub/elvis/beta".
XX.QQ
XXI only got executables, but now I want the source code.
XXWhere can I get it?
XX.AA
XXIf you have access to the Internet, then you should be able to fetch it
XXfrom one of the public archives such as \fBplains.nodak.edu\fP.
XXIt is accessible via anonymous FTP, or via an email server named
XX"archive-server@plains.nodak.edu".
XXElvis is located in the directory "/pub/Minix/all.contrib".
XX.AA
XXI will also offer it to the C Users' Group.
XXThey sell C source code for us$8 per diskette
XX(or slightly more outside  North  America).
XXTheir phone number is (913) 841-1631,
XXand their address is:
XX.ID
XXThe C Users' Group
XXPO Box 3127
XXLawrence KS 66046-0127
XX.DE
XX.QQ
XXIs this shareware, or public domain, or what?
XX.AA
XXIt is not public domain; it is copyrighted by me, Steve Kirkendall.
XXHowever, this particular version is freely redistributable, in either
XXsource form or executable form.
XX(I would prefer that you give copies away for free, complete with the
XXfull source code... but I'm not going to force you.)
XX.AA
XXIt is not shareware; you aren't expected to send me anything.
XXYou can use it without guilt.
XX.AA
XXIt is not "copylefted."
XXI hold a copyright, but currently I have not added any of the usual restrictions
XXthat you would find on copylefted software.
XXIf people start doing really obnoxious things to \*E, then I will start
XXadding restrictions to subsequent versions, but earlier versions won't
XXbe affected.
XX(So far, everybody has been pretty good about this so no restrictions
XXhave been necessary.)
XX.QQ
XXCan I reuse parts of your source code?
XX.AA
XXYes.  Please be careful, though, to make sure that the code really is mine.
XXSome of the code was contributed by other people, and I don't have the
XXauthority to give you permission to use it.
XXThe author's name can be found near the top of each source file.
XXIf it says "Steve Kirkendall" then you may use it;
XXotherwise, you'd better contact the author first.
XX.AA
XXPlease don't remove my name from the source code.
XXIf you modify the source, please make a note of that fact in a comment
XXnear the top of the source code.
XXAnd, finally, please mention my name in your documentation.
XX.QQ
XXCan \*E work with non-ASCII files?
XX.AA
XX\*E can't edit binary files because it can't handle the NUL character,
XXand because of line-length limitations.
XXHowever, it is 8-bit clean so you should be able to edit any European
XXextended ASCII file without any surprises.
XX.AA
XX\*E has also been modified to work with 16-bit character sets.
XXYongguang Zhang (ygz@cs.purdue.edu) has created a Chinese version of \*E
XXthat uses 16-bit characters and runs under cxterm (Chinese X-term)
XXon X-windows systems.
XXJunichiro Itoh (itojun@foretune.co.jp) has modified \*E to edit Japanese
XXtext under MS-DOS.
X/
Xecho x - regexp.ms
Xsed '/^X/s///' > regexp.ms << '/'
XX.Go 4 "REGULAR EXPRESSIONS"
XX
XX.PP
XX\*E uses regular expressions for searching and substututions.
XXA regular expression is a text string in which some characters have
XXspecial meanings.
XXThis is much more powerful than simple text matching.
XX.SH
XXSyntax
XX.PP
XX\*E' regexp package treats the following one- or two-character
XXstrings (called meta-characters) in special ways:
XX.IP "\\\\\\\\(\fIsubexpression\fP\\\\\\\\)" 0.8i
XXThe \\( and \\) metacharacters are used to delimit subexpressions.
XXWhen the regular expression matches a particular chunk of text,
XX\*E will remember which portion of that chunk matched the \fIsubexpression\fP.
XXThe :s/regexp/newtext/ command makes use of this feature.
XX.IP "^" 0.8i
XXThe ^ metacharacter matches the beginning of a line.
XXIf, for example, you wanted to find "foo" at the beginning of a line,
XXyou would use a regular expression such as /^foo/.
XXNote that ^ is only a metacharacter if it occurs
XXat the beginning of a regular expression;
XXanyplace else, it is treated as a normal character.
XX.IP "$" 0.8i
XXThe $ metacharacter matches the end of a line.
XXIt is only a metacharacter when it occurs at the end of a regular expression;
XXelsewhere, it is treated as a normal character.
XXFor example, the regular expression /$$/ will search for a dollar sign at
XXthe end of a line.
XX.IP "\\\\\\\\<" 0.8i
XXThe \\< metacharacter matches a zero-length string at the beginning of
XXa word.
XXA word is considered to be a string of 1 or more letters and digits.
XXA word can begin at the beginning of a line
XXor after 1 or more non-alphanumeric characters.
XX.IP "\\\\\\\\>" 0.8i
XXThe \\> metacharacter matches a zero-length string at the end of a word.
XXA word can end at the end of the line
XXor before 1 or more non-alphanumeric characters.
XXFor example, /\\<end\\>/ would find any instance of the word "end",
XXbut would ignore any instances of e-n-d inside another word
XXsuch as "calendar".
XX.IP "\&." 0.8i
XXThe . metacharacter matches any single character.
XX.IP "[\fIcharacter-list\fP]" 0.8i
XXThis matches any single character from the \fIcharacter-list\fP.
XXInside the \fIcharacter-list\fP, you can denote a span of characters
XXby writing only the first and last characters, with a hyphen between
XXthem.
XXIf the \fIcharacter-list\fP is preceded by a ^ character, then the
XXlist is inverted -- it will match character that \fIisn't\fP mentioned
XXin the list.
XXFor example, /[a-zA-Z]/ matches any letter, and /[^ ]/ matches anything
XXother than a blank.
XX.IP "\\\\\\\\{\fIn\fP\\\\\\\\}" 0.8i
XXThis is a closure operator,
XXwhich means that it can only be placed after something that matches a
XXsingle character.
XXIt controls the number of times that the single-character expression
XXshould be repeated.
XX.IP "" 0.8i
XXThe \\{\fIn\fP\\} operator, in particular, means that the preceding
XXexpression should be repeated exactly \fIn\fP times.
XXFor example, /^-\\{80\\}$/ matches a line of eighty hyphens, and
XX/\\<[a-zA-Z]\\{4\\}\\>/ matches any four-letter word.
XX.IP "\\\\\\\\{\fIn\fP,\fIm\fP\\\\\\\\}" 0.8i
XXThis is a closure operator which means that the preceding single-character
XXexpression should be repeated between \fIn\fP and \fIm\fP times, inclusive.
XXIf the \fIm\fP is omitted (but the comma is present) then \fIm\fP is
XXtaken to be inifinity.
XXFor example, /"[^"]\\{3,5\\}"/ matches any pair of quotes which contains
XXthree, four, or five non-quote characters.
XX.IP "*" 0.8i
XXThe * metacharacter is a closure operator which means that the preceding
XXsingle-character expression can be repeated zero or more times.
XXIt is equivelent to \\{0,\\}.
XXFor example, /.*/ matches a whole line.
XX.IP "\\\\\\\\+" 0.8i
XXThe \\+ metacharacter is a closure operator which means that the preceding
XXsingle-character expression can be repeated one or more times.
XXIt is equivelent to \\{1,\\}.
XXFor example, /.\\+/ matches a whole line, but only if the line contains
XXat least one character.
XXIt doesn't match empty lines.
XX.IP "\\\\\\\\?" 0.8i
XXThe \\? metacharacter is a closure operator which indicates that the
XXpreceding single-character expression is optional -- that is, that it
XXcan occur 0 or 1 times.
XXIt is equivelent to \\{0,1\\}.
XXFor example, /no[ -]\\?one/ matches "no one", "no-one", or "noone".
XX.PP
XXAnything else is treated as a normal character which must exactly match
XXa character from the scanned text.
XXThe special strings may all be preceded by a backslash to
XXforce them to be treated normally.
XX.SH
XXSubstitutions
XX.PP
XXThe :s command has at least two arguments: a regular expression,
XXand a substitution string.
XXThe text that matched the regular expression is replaced by text
XXwhich is derived from the substitution string.
XX.br
XX.ne 15 \" so we don't mess up the table
XX.PP
XXMost characters in the substitution string are copied into the
XXtext literally but a few have special meaning:
XX.LD
XX.ta 0.75i 1.3i
XX	&	Insert a copy of the original text
XX	~	Insert a copy of the previous replacement text
XX	\\1	Insert a copy of that portion of the original text which
XX		matched the first set of \\( \\) parentheses
XX	\\2-\\9	Do the same for the second (etc.) pair of \\( \\)
XX	\\U	Convert all chars of any later & or \\# to uppercase
XX	\\L	Convert all chars of any later & or \\# to lowercase
XX	\\E	End the effect of \\U or \\L
XX	\\u	Convert the first char of the next & or \\# to uppercase
XX	\\l	Convert the first char of the next & or \\# to lowercase
XX.TA
XX.DE
XX.PP
XXThese may be preceded by a backslash to force them to be treated normally.
XXIf "nomagic" mode is in effect,
XXthen & and ~ will be treated normally,
XXand you must write them as \\& and \\~ for them to have special meaning.
XX.SH
XXOptions
XX.PP
XX\*E has two options which affect the way regular expressions are used.
XXThese options may be examined or set via the :set command.
XX.PP
XXThe first option is called "[no]magic".
XXThis is a boolean option, and it is "magic" (TRUE) by default.
XXWhile in magic mode, all of the meta-characters behave as described above.
XXIn nomagic mode, only ^ and $ retain their special meaning.
XX.PP
XXThe second option is called "[no]ignorecase".
XXThis is a boolean option, and it is "noignorecase" (FALSE) by default.
XXWhile in ignorecase mode, the searching mechanism will not distinguish between
XXan uppercase letter and its lowercase form.
XXIn noignorecase mode, uppercase and lowercase are treated as being different.
XX.PP
XXAlso, the "[no]wrapscan" option affects searches.
XX.SH
XXExamples
XX.PP
XXThis example changes every occurence of "utilize" to "use":
XX.sp
XX.ti +1i
XX:%s/utilize/use/g
XX.PP
XXThis example deletes all whitespace that occurs at the end of a line anywhere
XXin the file.
XX(The brackets contain a single space and a single tab.):
XX.sp
XX.ti +1i
XX:%s/[   ]\\+$//
XX.PP
XXThis example converts the current line to uppercase:
XX.sp
XX.ti +1i
XX:s/.*/\\U&/
XX.PP
XXThis example underlines each letter in the current line,
XXby changing it into an "underscore backspace letter" sequence.
XX(The ^H is entered as "control-V backspace".):
XX.sp
XX.ti +1i
XX:s/[a-zA-Z]/_^H&/g
XX.PP
XXThis example locates the last colon in a line,
XXand swaps the text before the colon with the text after the colon.
XXThe first \\( \\) pair is used to delimit the stuff before the colon,
XXand the second pair delimit the stuff after.
XXIn the substitution text, \\1 and \\2 are given in reverse order
XXto perform the swap:
XX.sp
XX.ti +1i
XX:s/\\(.*\\):\\(.*\\)/\\2:\\1/
X/
Xecho x - termcap.ms
Xsed '/^X/s///' > termcap.ms << '/'
XX.Go 10 "TERMCAP"
XX.PP
XX\*E uses fairly standard termcap fields for most things.
XXI invented the cursor shape names
XXbut other than that there should be few surprises.
XX.SH
XXRequired numeric fields
XX.if n .ul 0
XX.ID
XX:co#:	number of columns on the screen (chars per line)
XX:li#:	number of lines on the screen
XX.DE
XX.SH
XXRequired string fields
XX.ID
XX.if n .ul 0
XX:ce=:	clear to end-of-line
XX:cl=:	home the cursor & clear the screen
XX:cm=:	move the cursor to a given row/column
XX:up=:	move the cursor up one line
XX.DE
XX.SH
XXBoolean fields
XX.if n .ul 0
XX.ID
XX:am:	auto margins - wrap when char is written in last column?
XX:xn:	brain-damaged auto margins - newline ignored after wrap
XX:pt:	physical tabs?
XX.DE
XX.SH
XXOptional string fields
XX.if n .ul 0
XX.ID
XX:al=:	insert a blank row on the screen
XX:dl=:	delete a row from the screen
XX:cd=:	clear to end of display
XX:ei=:	end insert mode
XX:ic=:	insert a blank character
XX:im=:	start insert mode
XX:dc=:	delete a character
XX:sr=:	scroll reverse (insert row at top of screen)
XX:vb=:	visible bell
XX:ti=:	terminal initialization string, to start full-screen mode
XX:te=:	terminal termination, to end full-screen mode
XX:ks=:	enables the cursor keypad
XX:ke=:	disables the cursor keypad
XX.DE
XX.SH
XXOptional strings received from the keyboard
XX.if n .ul 0
XX.ID
XX:kd=:	sequence sent by the <down arrow> key
XX:kl=:	sequence sent by the <left arrow> key
XX:kr=:	sequence sent by the <right arrow> key
XX:ku=:	sequence sent by the <up arrow> key
XX:kP=:	sequence sent by the <PgUp> key
XX:kN=:	sequence sent by the <PgDn> key
XX:kh=:	sequence sent by the <Home> key
XX:kH=:	sequence sent by the <End> key
XX:kI=:	sequence sent by the <Insert> key
XX.DE
XX.PP
XXOriginally, termcap didn't have any names for the <PgUp>, <PgDn>, <Home>,
XXand <End> keys.
XXAlthough the capability names shown in the table above are the most common,
XXthey are \fInot\fR universal.
XXSCO Xenix uses :PU=:PD=:HM=:EN=: for those keys.
XXAlso, if the four arrow keys happen to be part of a 3x3 keypad,
XXthen the five non-arrow keys may be named :K1=: through :K5=:,
XXso an IBM PC keyboard may be described using those names instead.
XX\*E can find any of these names.
XX.SH
XXOptional strings sent by function keys
XX.if n .ul 0
XX.ID
XX:k1=:...:k9=:k0=:	codes sent by <F1> through <F10> keys
XX:s1=:...:s9=:s0=:	codes sent by <Shift F1> ... <Shift F10>
XX:c1=:...:c9=:c0=:	codes sent by <Ctrl F1> ... <Ctrl F10>
XX:a1=:...:a9=:a0=:	codes sent by <Alt F1> ... <Alt F10>
XX.DE
XX.PP
XXNote that :k0=: is used to describe the <F10> key.
XXSome termcap documents recommend :ka=: or even :k;=: for describing
XXthe <F10> key, but \*E doesn't support that.
XX.PP
XXAlso, the :s1=:..., :c1=:..., and :a1=:... codes are very non-standard.
XXThe terminfo library doesn't support them.
XX.SH
XXOptional fields that describe character attributes
XX.if n .ul 0
XX.ID
XX:so=:se=:	start/end standout mode (We don't care about :sg#:)
XX:us=:ue=:	start/end underlined mode
XX:md=:me=:	start/end boldface mode
XX:as=:ae=:	start/end alternate character set (italics)
XX:ug#:		visible gap left by :us=:ue=:md=:me=:as=:ae=:
XX.DE
XX.SH
XXOptional fields that affect the cursor's shape
XX.PP
XXThe :cQ=: string is used by \*E immediately before exiting to undo
XXthe effects of the other cursor shape strings.
XXIf :cQ=: is not given, then all other cursor shape strings are ignored.
XX.ID
XX:cQ=:	normal cursor
XX:cX=:	cursor used for reading EX command
XX:cV=:	cursor used for reading VI commands
XX:cI=:	cursor used during VI input mode
XX:cR=:	cursor used during VI replace mode
XX.DE
XX.PP
XXIf the capabilities above aren't given, then \*E will try to use the
XXfollowing values instead.
XX.ID
XX:ve=:	normal cursor, used as :cQ=:cX=:cI=:cR=:
XX:vs=:	gaudy cursor, used as :cV=:
XX.DE
XX.SH
XXAn example
XX.PP
XXHere's the termcap entry I use on my Minix-ST system.
XXSome of the fields in it have nothing to do with \*E.
XXSome can only work on my system;
XXI have modified my kernel's screen driver.
XX.sp
XX.LD
XX.ne 14
XXmx|minix|minixst|ansi:\\
XX	:is=\\E[0~:co#80:li#25:bs:pt:\\
XX	:cm=\\E[%i%d;%dH:up=\\E[A:do=^J:nd=\\E[C:sr=\\EM:\\
XX	:cd=\\E[J:ce=\\E[K:cl=\\E[H\\E[J:\\
XX	:al=\\E[L:dl=\\E[M:ic=\\E[@:dc=\\E[P:im=:ei=:\\
XX	:so=\\E[7m:se=\\E[m:us=\\E[4m:ue=\\E[m:\\
XX	:md=\\E[1m:me=\\E[m:as=\\E[1;3m:ae=\\E[m:\\
XX	:ku=\\E[A:kd=\\E[B:kr=\\E[C:kl=\\E[D:\\
XX	:k1=\\E[1~:k2=\\E[2~:k3=\\E[3~:k4=\\E[4~:k5=\\E[5~:\\
XX	:k6=\\E[6~:k7=\\E[17~:k8=\\E[18~:k9=\\E[19~:k0=\\E[20~:\\
XX	:kU=\\E[36~:kQ=\\E[32~:kH=\\E[28~:\\
XX	:GV=3:GH=D:G1=?:G2=Z:G3=@:G4=Y:GC=E:GL=4:GR=C:GU=A:GD=B:\\
XX	:cQ=\\E[k:cX=\\E[2;0k:cV=\\E[16;0k:cI=\\E[k:cR=\\E[16;20k:
XX.DE
X/
Xecho x - title.ms
Xsed '/^X/s///' > title.ms << '/'
XX.de tE
XX.ps 80
XX.ce 1
XX\*E
XX..
XX.de nE
XX.ce 7
XX#######                                
XX#        #       #    #     #     #### 
XX#        #       #    #     #    #     
XX#####    #       #    #     #     #### 
XX#        #       #    #     #         #
XX#        #        #  #      #    #    #
XX#######  ######    ##       #     #### 
XX..
XX.sp |2i
XX.if t .tE
XX.if n .nE
XX.ps 10
XX.sp 1
XX.ce 2
XX- a clone of vi/ex -
XXversion \*V
XX.sp |7.5i
XX.IP Author: 0.9i
XXSteve Kirkendall
XX.br
XX14407 SW Teal Blvd., Apt C
XX.br
XXBeaverton, OR 97005
XX.IP E-Mail: 0.9i
XXkirkenda@cs.pdx.edu
XX.IP Phone: 0.9i
XX(503) 643-6980
X/
Xecho x - ver.ms
Xsed '/^X/s///' > ver.ms << '/'
XX.ds V 1.5j-betatest
XX.if t .ds E E\s-2LVIS\s+2
XX.if n .ds E Elvis
XX.\"
XX.\" usage: .Go <section#> <title>
XX.de Go
XX.ds LH "\\$1-\\\\n%
XX.ds RH "\\$1-\\\\n%
XX.ds CH "\\$2
XX.NH S \\$1
XX\\$2
XX.\"if !\\n%=1 .bp 1
XX.if n .ul 0
XX..
X/
Xecho x - versions.ms
Xsed '/^X/s///' > versions.ms << '/'
XX.Go 12 "VERSIONS"
XX.PP
XX\*E currently works under BSD UNIX, AT&T System-V UNIX, SCO XENIX,
XXMinix, Coherent, MS-DOS, Atari TOS, OS9/68k, VAX/VMS, and AmigaDos.
XXThis section of the manual provides special information that applies to each
XXparticular version of \*E.
XX.PP
XXFor all versions except MS-DOS,
XXthe file "Makefile.mix" should be copied to "Makefile",
XXand then edited to select the correct set of options for your system.
XXThere is more information about this embedded in the file itself.
XX.NH 2
XXBSD UNIX
XX.PP
XXTemporary files are stored in /tmp.
XX.PP
XXYou should modify /etc/rc so that
XXthe temp files are preserved when the system is rebooted.
XXFind a line in /etc/rc which reads
XX.br
XX.ti +0.5i
XXex4.3preserve /tmp
XX.PP
XXor something like that, and append the following line after it:
XX.br
XX.ti +0.5i
XXelvprsv /tmp/elv*
XX.PP
XXIf you do not have permission to modify /etc/rc, don't fret.
XXThe above modification is only needed to allow you to recover your changes
XXafter a system crash.
XXYou can still run \*E without that modification,
XXand you can still recover your changes when \*E crashes
XXor when your dialup modem looses the carrier signal, or something like that.
XXOnly a system crash or power failure could hurt you.
XX.PP
XXBoth \*E and the real Vi
XXread initialization commands from a file called ".exrc",
XXbut the commands in that file might work on one but not the other.
XXFor example, "set keywordprg=man" will work for \*E,
XXbut Vi will complain because it doesn't have a "keywordprg" option.
XXIf the warning messages annoy you, then you can edit the config.h file
XXto change the name of the initialization file ".exrc" to something else,
XXsuch as ".elvisrc".
XX.PP
XXIf you use X windows, you may wish to add "-DCS_LATIN1" to CFLAGS.
XXThis will cause the digraph table and the flipcase option to have default
XXvalues that are appropriate for the LATIN-1 character set.
XXThat's the standard character set for X.
XX.PP
XXThe default keyboard macro time-out value is larger for BSD than it is for
XXsome other systems, because I've had trouble running \*E via rlogin or Xterm.
XXI guess it takes a while for those keystokes to squirt through the net.
XX.NH 2
XXSystem-V UNIX
XX.PP
XXMost SysV UNIX systems use terminfo instead of termcap,
XXbut  the  terminfo  library  doesn't seem to have a standard name.
XXAs shipped, Elvis' Makefile.mix  is  configured  with "LIBS=-lterm".
XXYou may need to change it to "LIBS=-ltermcap" or "LIBS=-lterminfo"
XXor even "LIBS=-lcurses".
XX.PP
XXThe /etc/rc file should be modified as described for BSD systems, above.
XXThe only difference is that SysV systems tend to have directories for
XXinitialization, instead of a single large /etc/rc file.
XXEditor recovery is usually done somewhere in the /etc/rc2.d directory.
XX.PP
XXThe potential trouble with ".exrc" described above for BSD UNIX applies
XXto System-V UNIX as well.
XX.PP
XX\*E uses control-C as the interrupt key, not Delete.
XX.NH 2
XXSCO Xenix
XX.PP
XXFor Xenix-386, you can use the generic System-V settings.
XXYou may wish to add "-DCS_IBMPC" to CFLAGS, to have the digraph table and
XXflipcase option start up in a mode that is appropriate for the console.
XX
XXThere is a separate group of settings for use with Xenix-286.
XXIt already has "-DCS_IBMPC" in CFLAGS.
XX.PP
XXBecause Xenix is so similar to System-V, everything I said earlier about
XXSystem-V applies to the Xenix version too, except that editor recovery
XXprobably belongs in a directory called /etc/rc.d/8.
XX.NH 2
XXMinix
XX.PP
XXThere are separate settings in Makefile.mix for Minix-PC and Minix-68k.
XXThe differences between these two are that
XXthe 68k version uses ".o" for the object file extension where
XXthe PC version uses ".s", and
XXthe PC version has some extra flags in CFLAGS to reduce the size of \*E.
XXThe PC version also uses tinytcap (instead of the full termcap) to make it smaller.
XX.PP
XXMinix-PC users should read the CFLAGS section of this manual very carefully.
XXYou have some choices to make...
XX.PP
XXThe temporary files are stored in /usr/tmp.
XXThe /usr/tmp directory must exist before you run \*E,
XXand it must be readable/writable by everybody.
XXWe use /usr/tmp instead of /tmp because
XXafter a system crash or power failure,
XXyou can recover the altered version of a file from the temporary file
XXin /usr/tmp.
XXIf it was stored in /tmp, though, then it would be lost because /tmp is
XXnormally located on the RAM disk.
XX.PP
XX\*E uses control-C as the interrupt key, not Delete.
XX.NH 2
XXCoherent
XX.PP
XX\*E was ported to Coherent by Esa Ahola.
XX.PP
XX\*E is too large to run under Coherent unless you eliminate some
XXfeatures via the CFLAGS setting.
XXThe recommended settings, in Makefile.mix, produce a working version
XXof \*E which emulates Vi faithfully, but lacks most of the extensions.
XXYou should read the CFLAGS section of this manual carefully.
XX.PP
XXYou can probably reduce the size of \*E by using tinytcap.c instead of -lterm.
XXThis would allow you to keep most features of \*E,
XXat the expense of terminal independence.
XX(Tinytcap.c has ANSI escape sequences hard-coded into it.)
XXTo use tinytcap, just add "tinytcap.o" to the "EXTRA=" line in the Makefile,
XXand remove "-lterm" from the "LIBS=" line.
XX.PP
XXThe temporary files are stored in /tmp.
XXYou should modify your /etc/rc file as described for BSD earlier.
XX.NH 2
XXMS-DOS
XX.PP
XX\*E was ported to MS-DOS by Guntram Blohm and Martin Patzel.
XXWillett Kempton added support for the DEC Rainbow.
XX.PP
XXIdeally, \*E should be compiled with Microsoft C 5.10 and the standard
XXMicrosoft Make utility,
XXvia the command "make elvis.mak".
XXThis will compile \*E and all related utilities.
XX.PP
XXWith Microsoft C 6.00, you may have trouble compiling regexp.c.
XXIf so, try compiling it without optimization.
XX.PP
XXThe "Makefile.mix" file contains a set of suggested settings for compiling
XXelvis with Turbo-C or Borland C.
XX(If you have Turbo-C, but not the Make utility,
XXthen you can \fIalmost\fR use the "\*E.prj" file to compile \*E,
XXbut you must explicitly force Turbo-C to compile it with the "medium" memory model.
XXMost of the related programs [ctags, ref, virec, refont, and wildcard] are
XXonly one file long, so you should have no trouble compiling them.)
XXThe "alias.c" file is meant to be compiled once into an executable named
XX"ex.exe".
XXYou should then copy "ex.exe" to "vi.exe" and "view.exe".
XX.PP
XX\*E stores its temporary files in C:\\tmp.
XXIf this is not satisfactory, then you should edit the CFLAGS line of
XXyour Makefile to change TMPDIR to something else before compiling.
XXYou can also control the name of the temp directory via an environment
XXvariable named TMP or TEMP.
XXThe directory must exist before you can run \*E.
XX.PP
XXThe TERM environment variable determines how elvis will write to the screen.
XXIt can be set to any one of the following values:
XX.LD
XX.ta 1.5i 2.5i
XX	pcbios	Use BIOS calls on an IBM-PC clone.
XX	rainbow	Use DEC Rainbow interface.
XX	ansi	Use ANSI.SYS driver.
XX	nansi	User faster NANSI.SYS driver.
XX.DE
XX.PP
XXIf the TERM variable isn't set, then elvis will automatically select either
XXthe "rainbow" interface (when run on a Rainbow) or "pcbios" (on an IBM clone).
XX.PP
XXYou may prefer to use NANSI.SYS for speed;
XXor you may NEED to use ANSI.SYS for a non-clone, such as a lap-top.
XXIf so, you should
XXinstall one of these drivers by adding "driver = nansi.sys" (or whatever)
XXto your CONFIG.SYS file,
XXand then you should define TERM to be "nansi" (or whatever) by adding
XX"set TERM=nansi" to your AUTOEXEC.BAT file.
XXYou must then reboot for these changes to take effect.
XXAfter that, \*E will notice the "TERM" setting and use the driver.
XX.PP
XXSince ".exrc" is not a valid DOS filename,
XXthe name of the initialization file has been changed to "elvis.rc".
XXElvis will look for an "elvis.rc" file first in your home directory,
XXand then in the current directory.
XXNote that you must set an environment variable named "HOME" to the
XXfull pathname of your home directory, for Elvis to check there;
XXif "HOME" isn't set, then Elvis will only look in the current directory.
XXTo set "HOME", you would typically add the following line to your
XXAUTOEXEC.BAT file:
XX.br
XX.ti +0.5i
XXset HOME c:\\
XX.PP
XXAn extra program, called "wildcard", is needed for MS-DOS.
XXIt expands wildcard characters in file names.
XXIf \*E flashes a "Bad command or filename" message when it starts,
XXthen you've probably lost the WILDCARD.EXE program somehow.
XX.PP
XX\*E can run under Windows, but only in full-screen mode.
XXAlso, Windows uses an environment variable called TEMP which interferes with
XXelvis' usage of TEMP;
XXto work around this, you can simply set an environment variable named
XXTMP (with no 'E') to the name of elvis' temporary directory.
XXWhen TEMP and TMP are both set, \*E uses TMP and ignored TEMP.
XX.NH 2
XXAtari TOS
XX.PP
XX\*E was ported to Atari TOS by Guntram Blohm and Martin Patzel.
XXIt is very similar to the MS-DOS version.
XXIt has been tested with the Mark Williams C compiler and also GNU-C.
XX.PP
XXThe TERM environment variable is ignored;
XXthe ST port always assumes that TERM=vt52.
XXThe SHELL (not COMSPEC!) variable should be set to
XXthe name of a line-oriented shell.
XX.PP
XXA simple shell in included with \*E.
XXIts source is in "shell.c", and the name of the executable is "shell.ttp".
XXThe file "profile.sh" should contain a set of instructions to be executed
XXwhen the shell first starts up.
XXAn example of this file is included, but you will almost certainly want to
XXedit it right away to match your configuration.
XX(If you already have a command-line shell,
XXthen you'll probably want to continue using it.
XXThe shell that comes with \*E is very limited.)
XX.PP
XXCurrently, character attributes cannot be displayed on the screen.
XX.PP
XX\*E runs under MiNT (a free multi-tasking extension to TOS)
XXbut it can be a CPU hog because of the way that \*E reads from the
XXkeyboard with timeout.
XXAlso, \*E doesn't use any of the special features of MiNT.
XXI have received a set of patches that optimize \*E for MiNT,
XXbut they arrived too late to integrate into this release.
XX.NH 2
XXOS9/68k
XX.PP
XX\*E was ported to OS9/68k by Peter Reinig.
XX.PP
XXThe Makefile is currently configured to install \*E and the related
XXprograms in /dd/usr/cmds
XXIf this this is unacceptable, then you should change the BIN setting
XXto some other directory.
XXSimilarly, it expects the source code to reside in /dd/usr/src/elvis;
XXthe ODIR setting is used to control this.
XX.PP
XXTemporary files are stored in the /dd/tmp directory.
XXYour /dd/startup file may need to be modified
XXto prevent it from deleting \*E' temporary files;
XXmake /dd/startup run the \fIelvprsv\fR program before it wipes out /dd/tmp.
XX.PP
XXThe program in alias.c is linked repeatedly to produce the
XX"vi", "view", and "input" aliases for \*E.
XXSadly, the "ex" alias is impossible to implement under OS9
XXbecause the shell has a built-in command by that name.
XX.PP
XXFor some purposes,
XXyou must give `make' the "-b" option.
XXSpecifically, you need this for "make -b clean" and "make -b install".
XX.NH 2
XXVAX/VMS
XX.PP
XXJohn Campbell ported \*E to VAX/VMS.
XX.PP
XXA heavily laden VAX can take half an hour to compile elvis.
XXThis is normal.
XXDon't panic.
XX.PP
XXWhile running, elvis will create temporary files in SYS$SCRATCH.
XXEnter SHOW LOGICAL SYS$SCRATCH to see what actual directory you are using.
XXMany sites have SYS$SCRATCH equivalenced to SYS$LOGIN.
XXThe elvis temporary files look like the following on VMS while elvis is running:
XX.br
XX.ti 0.75i
XXELV_1123A.1;1       ELV_1123A.2;1       SO070202.;1
XX.PP
XXAlso, filtering commands (like !!dir and !}fmt) should work on VMS.
XXThis assumes, however, that you can create temporary mailboxes and that
XXyour mailbox quota (a sysgen parameter) is at least 256 bytes for a
XXsingle write to the mailbox.
XXThis is the default sysgen parameter,
XXso there should be few people who experience filter problems.
XX.PP
XXAdditionally, an attempt was made to support the standard terminals on VMS:
XX"vt52", "vt100", "vt200", "vt300", "vt101", "vt102".
XXNon-standard terminals could be supported by setting your terminal type to
XXUNKNOWN (by entering SET TERM/UNKNOWN)
XXand defining the logical name ELVIS_TERM.
XXWhatever ELVIS_TERM translates to, however, will have to be included in
XXtinytcap.c.
XXNote that the upper/lowercase distinctions are significant,
XXand that DCL will upshift characters that are not quoted strings, so
XXenter DEFINE ELVIS_TERM "hp2621a".
XXAs distributed, it would probably not be a good idea to have more than the
XXstandard terminals in tinytcap.c (else it wouldn't be tiny, would it?).
XXChanges here, of course, would require a recompilation to take effect.
XX.PP
XXIf you have a version of the "termcap" library and database on your system,
XXthen you may wish to replace tinytcap with the real termcap.
XX.NH 2
XXAmigaDOS
XX.PP
XXMike Rieser and Dale Rahn ported \*E to AmigaDOS.
XX.PP
XXThe port was done using Manx Aztec C version 5.2b.
XX\*E uses about as much space as it can and still be small code and data.
XX\*E should also compile under DICE, though there may be a little trouble with
XXsigned versus unsigned chars.
XX.PP
XXThe port has been done so the same binary will run under both versions of AmigaDOS.
XXUnder AmigaDOS 2.04, \*E supports all the documented features.
XXIt also uses an external program ref to do tag lookup.
XXSo, the accompanying programs: ref and ctags are recommended.
XXUnder AmigaDOS 1.2/1.3 \*E works, buts lacks the more advanced features.
XX.PP
XXFor the port to AmigaDOS 2.04, we tried to use as many Native AmigaDOS
XXcalls as we could.
XXThis should increase Elvis's chances at being compiled with other compilers.
XXDICE seems to have a different default char type.
XXYou may need to use the UCHAR() macro in tio.c.
XXTo test it, try the :map command; if it looks right, things are cool.
XX.PP
XXFor the port to AmigaDOS 1.3, we tried to make sure the program was at
XXleast usable.
XXMany features are missing, most notably running commands in subshells.
XXAlso, what we could get working, we used Aztec functions to support them,
XXso this part is little more compiler dependent.
XX.PP
XXAztec is compatible with the SAS libcall #pragma.
XXI personally prefer using the includes that come from Commodore over the ones
XXsupplied with Aztec, but for people with a straight Aztec installation,
XXI went with the default names for the Aztec pragmas.
XX.PP
XXOne include you'll need is <sys/types.h>.
XXIts a common include when porting software just make yourself one.
XXIts a two line file that saves a lot of hassle especially in the elvis source.
XXSo, make a directory where your includes are located called `sys'
XXand in a file below that type:
XX.br
XX.ti +0.8i
XX/* sys/types.h */
XX.br
XX.ti +0.8i
XX#include <exec/types.h>
XX.PP
XXWhen setting environment variables (either local or global) for
XXvariables that specify a directory, make sure the variable ends in `:'
XXor `/'.
XXThis saved from having to change much of the way elvis works.
XXThe default temporary directory (if TEMP and TMP aren't specified) is "T:".
XXThe default if HOME directory (if no HOME environment variable is set) is "S:".
XX.PP
XXTo avoid conlict with other uses, \*E uses elvis.rc instead of .exrc or
XXwhere it looks for macros.
XX.NH 2
XXOther Systems
XX.PP
XXFor Sun workstations, use the BSD configuration.
XXEarlier versions of elvis didn't link correctly due to a quirk in Sun's
XXversion of the "make" utility, but this version of elvis has a work-around
XXfor that quirk so you should have no trouble at all.
XX.PP
XXFor Linux, use the SysV settings.
XXYou can probably just remove the "-lterm" from the "LIBS= -lterm" line,
XXsince linux keeps the termcap functions in the standard C library.
XX.PP
XXFor other UNIXoid systems, I suggest you start with the Minix-68k settings
XXand then grow from that.
XXMinix is a nice starting point because it is a clone of Version 7 UNIX,
XXwhich was the last common ancestor of BSD UNIX and SysV UNIX.
XXAny Operating System which claims any UNIX compatibility what so ever
XXwill therefore support V7/Minix code.
XXYou may need to fiddle with #include directives or something, though.
XXMinix-68k is a better starting point than Minix-PC because the PC compiler
XXhas some severe quirks.
X/
Xecho x - visual.ms
Xsed '/^X/s///' > visual.ms << '/'
XX.Go 2 "VISUAL MODE COMMANDS"
XX.PP
XXMost visual mode commands are one keystroke long.
XXThe following table lists the operation performed by each keystroke,
XXand also denotes any options or arguments that it accepts.
XXNotes at the end of the table describe the notation used in this table.
XX.PP
XXIn addition to the keys listed here, your keyboard's "arrow" keys
XXwill be interpretted as the appropriate cursor movement commands.
XXThe same goes for <PgUp> and <PgDn>, if your keyboard has them.
XXThe <Insert> key will toggle between insert mode and replace mode.
XXThere is a colon mode command (":map", to be described later)
XXwhich will allow you to define other keys, such as function keys.
XX.PP
XXA tip: visual command mode looks a lot like text input mode.
XXIf you forget which mode you're in, just hit the <Esc> key.
XXIf \*E beeps, then you're in visual command mode.
XXIf \*E does not beep, then you were in input mode,
XXbut by hitting <Esc> you will have switched to visual command mode.
XXSo, one way or another, after <Esc> \*E will be ready for a command.
XX.LD
XX.ta 0.7i 1.3i
XX\s+2COMMAND	DESCRIPTION\s-2
XX	^A	Search for next occurence of word at cursor (MOVE)(EXT)
XX	^B	Move toward the top of the file by 1 screenful
XX	^C	--- (usually sends SIGINT, to interupt a command)
XXcount	^D	Scroll down <count> lines (default 1/2 screen)
XXcount	^E	Scroll up <count> lines
XX	^F	Move toward the bottom of the file by 1 screenful
XX	^G	Show file status, and the current line #
XXcount	^H	Move left, like h (MOVE)
XX	^I	---
XXcount	^J	Move down (MOVE)
XX	^K	---
XX	^L	Redraw the screen
XXcount	^M	Move to the front of the next line (MOVE)
XXcount	^N	Move down (MOVE)
XX	^O	---
XXcount	^P	Move up (MOVE)
XX	^Q	--- (typically XON, which restarts screen updates)
XX	^R	Redraw the screen
XX	^S	--- (typically XOFF, which stops screen updates)
XX	^T	---
XXcount	^U	Scroll up <count> lines (default 1/2 screen)
XX	^V	---
XX	^W	---
XXcount	^X	Move to a physical column number on the screen (MOVE) (EXT)
XXcount	^Y	Scroll down <count> lines
XX	^Z	--- (sometimes sends SIGSUSP, to suspend execution)
XX	ESC	---
XX	^\\	--- (usually sends SIGQUIT, which is ignored)
XX	^]	If the cursor is on a tag name, go to that tag
XX	^^	Switch to the previous file, like ":e #"
XX	^_	---
XXcount	SPC	Move right,like l (MOVE)
XX	! \s-2mv\s+2	Run the selected lines thru an external filter program
XX	" \s-2key\s+2	Select which cut buffer to use next
XXcount	# \s-2+\s+2	Increment a number (EDIT) (EXT)
XX	$	Move to the rear of the current line (MOVE)
XXcount	%	Move to matching (){}[] or to a given % of file (MOVE) (EXT)
XXcount	&	Repeat the previous ":s//" command here (EDIT)
XX	' \s-2key\s+2	Move to a marked line (MOVE)
XXcount	(	Move backward <count> sentences (MOVE)
XXcount	)	Move forward <count> sentences (MOVE)
XX	*	Go to the next error in the errlist (EXT)
XXcount	+	Move to the front of the next line (MOVE)
XXcount	,	Repeat the previous [fFtT] but in the other direction (MOVE)
XXcount	-	Move to the front of the preceding line (MOVE)
XXcount	.	Repeat the previous "edit" command
XX	/ \s-2text\s+2	Search forward for a given regular expression (MOVE)
XX	0	If not part of count, move to 1st char of this line (MOVE)
XX	1	Part of count
XX	2	Part of count
XX	3	Part of count
XX	4	Part of count
XX	5	Part of count
XX	6	Part of count
XX	7	Part of count
XX	8	Part of count
XX	9	Part of count
XX	: \s-2text\s+2	Run single EX cmd
XXcount	;	Repeat the previous [fFtT] cmd (MOVE)
XX	< \s-2mv\s+2	Shift text left (EDIT)
XX	= \s-2mv\s+2	Reformat
XX	> \s-2mv\s+2	Shift text right (EDIT)
XX	? \s-2text\s+2	Search backward for a given regular expression (MOVE)
XX	@ \s-2key\s+2	Execute the contents of a cut-buffer as VI commands
XXcount	A \s-2inp\s+2	Append at end of the line (EDIT)
XXcount	B	Move back Word (MOVE)
XX	C \s-2inp\s+2	Change text from the cursor through the end of the line (EDIT)
XX	D	Delete text from the cursor through the end of the line (EDIT)
XXcount	E	Move end of Word (MOVE)
XXcount	F \s-2key\s+2	Move leftward to a given character (MOVE)
XXcount	G	Move to line #<count> (default is the bottom line) (MOVE)
XXcount	H	Move to home row (the line at the top of the screen)
XXcount	I \s-2inp\s+2	Insert at the front of the line (after indents) (EDIT)
XXcount	J	Join lines, to form one big line (EDIT)
XX	K	Look up keyword (EXT)
XXcount	L	Move to last row (the line at the bottom of the screen)
XX	M	Move to middle row
XX	N	Repeat previous search, but in the opposite direction (MOVE)
XXcount	O \s-2inp\s+2	Open up a new line above the current line (EDIT)
XX	P	Paste text before the cursor (EDIT)
XX	Q	Quit to EX mode
XX	R \s-2inp\s+2	Overtype (EDIT)
XXcount	S \s-2inp\s+2	Change lines, like <count>cc
XXcount	T \s-2key\s+2	Move leftward *almost* to a given character (MOVE)
XX	U	Undo all recent changes to the current line
XX	V	Start marking lines for c/d/y/</>/!/\\ (EXT)
XXcount	W	Move forward <count> Words (MOVE)
XXcount	X	Delete the character(s) to the left of the cursor (EDIT)
XXcount	Y	Yank text line(s) (copy them into a cut buffer)
XX	Z Z	Save the file & exit
XX	[ [	Move back 1 section (MOVE)
XX	\\ \s-2mv\s+2	Pop-up menu for modifying text (EXT)
XX	] ]	Move forward 1 section (MOVE)
XX	^	Move to the front of the current line (after indent) (MOVE)
XXcount	_	Move to the current line
XX	` \s-2key\s+2	Move to a marked character (MOVE)
XXcount	a \s-2inp\s+2	Insert text after the cursor (EDIT)
XXcount	b	Move back <count> words (MOVE)
XX	c \s-2mv\s+2	Change text (EDIT)
XX	d \s-2mv\s+2	Delete text (EDIT)
XXcount	e	Move forward to the end of the current word (MOVE)
XXcount	f \s-2key\s+2	Move rightward to a given character (MOVE)
XX	g	---
XXcount	h	Move left (MOVE)
XXcount	i \s-2inp\s+2	Insert text at the cursor (EDIT)
XXcount	j	Move down (MOVE)
XXcount	k	Move up (MOVE)
XXcount	l	Move right (MOVE)
XX	m \s-2key\s+2	Mark a line or character
XX	n	Repeat the previous search (MOVE)
XXcount	o \s-2inp\s+2	Open a new line below the current line (EDIT)
XX	p	Paste text after the cursor (EDIT)
XX	q	---
XXcount	r \s-2key\s+2	Replace <count> chars by a given character (EDIT)
XXcount	s \s-2inp\s+2	Replace <count> chars with text from the user (EDIT)
XXcount	t \s-2key\s+2	Move rightward *almost* to a given character (MOVE)
XX	u	Undo the previous edit command
XX	v	Start marking characters for c/d/y/</>/!/\\ (EXT)
XXcount	w	Move forward <count> words (MOVE)
XXcount	x	Delete the character that the cursor's on (EDIT)
XX	y \s-2mv\s+2	Yank text (copy it into a cut buffer)
XX	z \s-2key\s+2	Scroll current line to the screen's +=top -=bottom .=middle
XXcount	{	Move back <count> paragraphs (MOVE)
XXcount	|	Move to column <count> (the leftmost column is 1)
XXcount	}	Move forward <count> paragraphs (MOVE)
XXcount	~	Switch a character between uppercase & lowercase (EDIT)
XX	DEL	--- (usually mapped to shift-X, so it deletes one character)
XX.DE
XX.IP count
XXMany commands may be preceded by a count.  This is a sequence of digits
XXrepresenting a decimal number.  For most commands that use a count,
XXthe command is repeated <count> times.  The count is always optional,
XXand usually defaults to 1.
XX.IP key
XXSome commands require two keystrokes.  The first key always determines
XXwhich command is to be executed.  The second key is used as a parameter
XXto the command.
XX.IP mv
XXSome commands (! < > c d y \\ =) operate on text between the cursor and some
XXother position.
XXThere are three ways that you can specifify that other position.
XX.IP
XXThe first way is to follow the command keystroke with a movement command.
XXFor example, "dw" deletes a single word.
XX"d3w" and "3dw" both delete three words.
XX.IP
XXThe second way is to type the command keystroke twice.
XXThis causes whole lines to be acted upon.
XXFor example, ">>" indents the current line.
XX"3>>" indents the current line and the following two lines.
XX.IP
XXThe last way is to move the cursor to one end of the text,
XXtype 'v' or 'V' to start marking,
XXmove the cursor to the other end,
XXand then type the desired command key.
XX.IP inp
XXMany commands allow the user to interactively enter text.
XXSee the discussion of "input mode" in the following section.
XX.IP (EXT)
XXThese commands are extensions -- the real vi doesn't have them.
XX.IP (EDIT)
XXThese commands affect text, and may be repeated by the "." command.
XX.IP (MOVE)
XXThese commands move the cursor, and may be used to specify the extent
XXof a member of the "mv" class of commands.
XX.NH 2
XXInput Mode
XX.PP
XXYou can't type text into your file directly from visual command mode.
XXInstead, you must first give a command which will put you into input mode.
XXThe commands to do this are A/C/I/O/R/S/a/i/o/s.
XX.PP
XXThe S/s/C/c commands temporarily place a $ at the end of the text that
XXthey are going to change.
XX.PP
XXIn input mode, all keystrokes are inserted into the text at the
XXcursor's position, except for the following:
XX.ID
XX^A	insert a copy of the last input text
XX^D	delete one indent character
XX^H	(backspace) erase the character before the cursor
XX^L	redraw the screen
XX^M	(carriage return) insert a newline (^J, linefeed)
XX^O	execute next key as a visual command (limited!)
XX^P	insert the contents of the cut buffer
XX^R	redraw the screen, like ^L
XX^T	insert an indent character
XX^U	backspace to the beginning of the line
XX^V	insert the following keystroke, even if special
XX^W	backspace to the beginning of the current word
XX^Z^Z	write the file & exit \*E
XX^[	(ESCape) exit from input mode, back to command mode
XX.DE
XX.PP
XXAlso, on some systems, ^S may stop output, ^Q may restart output,
XXand ^C may interupt execution.
XX^@ (the NUL character) cannot be inserted.
XX.PP
XXThe R visual command puts you in overtype mode,
XXwhich is a slightly different form of input mode.
XXIn overtype mode, each time you insert a character,
XXone of the old characters is deleted from the file.
XX.NH 2
XXArrow keys in Input Mode
XX.PP
XXThe arrow keys can be used to move the cursor in input mode.
XX(This is an extension; the real Vi doesn't support arrow keys in input mode.)
XXThe <PgUp>, <PgDn>, <Home>, and <End> keys work in input mode, too.
XXThe <Delete> key deletes a single character in input mode.
XXThe <Insert> key toggles between input mode and replace mode.
XX.PP
XXThe best thing about allowing arrow keys to work in input mode is that
XXas long as you're in input mode,
XX\*E seems to have a fairly ordinary user interface.
XXWith most other text editors, you are always in either insert mode or
XXreplace mode, and you can use the arrow keys at any time to move the cursor.
XXNow, \*E can act like that, too.
XXIn fact, with the new "inputmode" option and the "control-Z control-Z" input
XXcommand, you may never have to go into visual command mode for simple edit
XXsessions.
XX.NH 2
XXDigraphs
XX.PP
XX\*E supports digraphs as a way to enter non-ASCII characters.
XXA digraph is a character which is composed of two other characters.
XXFor example, an apostrophe and the letter i could be defined as a digraph
XXwhich is to be stored & displayed as an accented i.
XX.PP
XXThere is no single standard for extended ASCII character sets.
XX\*E can be compiled to fill the digraph with values appropriate for
XXeither the IBM PC character set, or the LATIN-1 character set used by
XXX windows, or neither.
XX(See the discussions of -DCS_IBMPC and -DCS_LATIN1 in the CFLAGS section
XXof this manual.)
XXYou can view or edit the digraph table via the ":digraph" colon command.
XX.PP
XXDigraphs will not be recognized until you've entered ":set digraph".
XX.PP
XXTo actually use a digraph
XXtype the first character, then hit <Backspace>, and then type the
XXsecond character.
XX\*E will then substitute the non-ASCII character in their place.
XX.NH 2
XXAbbreviations
XX.PP
XX\*E can expand abbreviations for you.
XXYou define an abbreviation with the :abbr command,
XXand then whenever you type in the abbreviated form while in input mode,
XX\*E will immediately replace it with the long form.
XXCOBOL programmers should find this useful. :-)
XX.PP
XX\*E doesn't perform the substitution until you type a non-alphanumeric
XXcharacter to mark the end of the word.
XXIf you type a control-V before that non-alphanumeric character, then
XX\*E will not perform the substitution.
XX.NH 2
XXAuto-Indent
XX.PP
XXWith the ":set autoindent" option turned on,
XX\*E will automatically insert leading whitespace at the beginning of each
XXnew line that you type in.
XXThe leading whitespace is copied from the preceding line.
XX.PP
XXTo add more leading whitespace, type control-T.
XXTo remove some whitespace, type control-D.
XX.PP
XXIf you ":set noautotab", then the whitespace generated by control-T will
XXalways consist of spaces -- never tabs.
XXSome people seem to prefer this.
XX.PP
XX\*E' autoindent mode isn't 100% compatible with vi's.
XXIn \*E, 0^D and ^^D don't work,
XX^U can wipeout all indentation, 
XXand sometimes \*E will use a different amount of indentation than vi would.
X/
/
echo x - Knownbug.txt
sed '/^X/s///' > Knownbug.txt << '/'
XThe following options are missing:
X	[no]optimize	- affects screen redrawing method
X	[no]redraw	- simulate character insertion by redrawing line
X	[no]slowopen	- don't use character insertion
X	tags="tags"	- list of tags, used as TAGPATH
X
XI'd like to improve the versatility of the options whose value is a command:
Xcc, make, kp, and ep.  I'd like to add some notation that allows you to say
Xwhere to insert the current filename or current word.
X-------------------------------------------------------------------------------
XCurrently, elvis is configured to look for | only in .exrc files.  It doesn't
Xlook for | in any interactively entered command lines, yet.
X-------------------------------------------------------------------------------
XThe 'p', '#', and 'l' flags aren't supported.  Also, ex commands don't accept
Xcounts; e.g., ":c5" can't be used to change five lines. 
X-------------------------------------------------------------------------------
XThe following have been reported, but have not been verified.  If you have
Xexperienced any of the following, and haven't reported it yet, then please
Xreport it now!  I need more information about these bugs.
X
X[Bugs that are not in this list should also be reported, of course.]
X
X- Under VMS on an 80-column screen, after scolling sideways to approximately
X  column 110, a ^L will not redraw the part of the line after the cursor.
X- On an Atari ST running under TOS: some ASCII keys seem to send '#' plus
X  another key.  (This is normal for non-ASCII keys like <F1> or <Help>, but
X  ASCII keys should always send a single ASCII character.)
X-------------------------------------------------------------------------------
XBIG JOBS:
X  Desirable extension: merge input mode and visual command mode.
X  Display long lines by wrapping, like the real vi (if ":set sidescroll=0")
X-------------------------------------------------------------------------------
X
X- In the ":w >>filename" command, elvis doesn't allow any whitespace between
X  the ">>" and "filename".
X
X- Elvis doesn't allow "backslash newline" inside a single EX command.
X
X- VMS intercepts the control-T character, which is normally used to increase
X  indentation.  The <Tab> key works, but it doesn't do quite the same thing.
X  (":map! ^I ^T" helps.)
X
X- Under VMS, file I/O is very slow.  Looking over the vmsio.c file, I get the
X  impression that it is rather over-done for elvis.  Its speed could
X  probably be inproved.
X
X- The errlist feature doesn't seem to work with the Borland compilers.  Perhaps
X  they write to stderr instead of stdout?  This will probably be easy to solve
X  once I modify the "cc" and "make" options, as described earlier.
X
X- The command ":0" should move the cursor to line 1.  Currently, it doesn't
X  move the cursor at all.
X
X- File preservation is still flakey.  On DOS/TOS/VMS systems, it is also more
X  complex that it should be.
X
X- The act of appending to a cut buffer (as in "Ayy) sets file modification
X  flag.  It shouldn't!
X
X- The .exrc file is limited to BLKSIZE bytes -- 2048 on most systems, but
X  1024 on Minicx-PC, Coherent, and MS-DOS.
X
X- I *still* haven't quite perfected the screen update code.  If you suspect
X  that the screen doesn't accurately reflect the contents of the edit buffer,
X  then you should try doing a control-L.
X
X  I'll be overhauling the screen update code soon to make it wrap long lines
X  like the real vi.  I expect to fix this bug then.
/
echo x - Makedoc
sed '/^X/s///' > Makedoc << '/'
X# This is the Makefile for Elvis' "doc" directory.  It makes use of a
X# troff-like formatter called mroff.  Since you probably don't have mroff,
X# you'll need to edit this Makefile before you can fully use it.  It can
X# also use nroff, though, so you should be able to get something out of it.
X#
X# make		Use nroff to create an ASCII version of the manual.
X# make foo.doc	Use nroff to create an ASCII version of foo.man or foo.ms
X# make manual	Use MROFF to print a typeset manual on a laser printer
X# make foo.1200	Use MROFF to print a typeset version of foo.man or foo.ms
X# make foo.100	Use MROFF to print a draft-quality version of foo.man or foo.ms
X# make foo.more	Use MROFF to preview foo.man or foo.more on your terminal
X#
X
X###############################################################################
X# Definitions...
X
XMAN=	ctags.man elvis.man elvprsv.man elvrec.man fmt.man ref.man
XMS=	title.ms index.ms intro.ms visual.ms ex.ms regexp.ms options.ms\
X	cutbufs.ms differ.ms internal.ms cflags.ms termcap.ms environ.ms\
X	versions.ms question.ms
XASC=	title.doc index.doc intro.doc visual.doc ex.doc regexp.doc options.doc\
X	cutbufs.doc differ.doc internal.doc cflags.doc termcap.doc environ.doc\
X	versions.doc question.doc\
X	ctags.doc elvis.doc elvprsv.doc elvrec.doc fmt.doc ref.doc
XMANUAL=	title.1200 index.1200 intro.1200 visual.1200 ex.1200 regexp.1200 options.1200\
X	cutbufs.1200 differ.1200 internal.1200 cflags.1200 termcap.1200 environ.1200\
X	versions.1200\
X	ctags.1200 elvis.1200 elvprsv.1200 elvrec.1200 fmt.1200 ref.1200
XVER=	ver.ms
XTROFF=	mroff
XNROFF=	nroff
X
X###############################################################################
X# Rules...
X
X.SUFFIXES: .tmp .100 .1200 .more .doc .man .ms .vga .vgas
X
X.ms.tmp:
X	$(TROFF) -ms $(VER) $< >tmp
X
X.man.tmp:
X	$(TROFF) -man $< >tmp
X
X.ms.more:
X	$(TROFF) -ms $(VER) $< | draft | more
X
X.man.more:
X	$(TROFF) -man $< | draft | more
X
X.ms.1200:
X	$(TROFF) -ms $(VER) $< | hp2 1200 | lp -og $(PRINTER)
X
X.man.1200:
X	$(TROFF) -man $< | hp2 1200 | lp -og $(PRINTER)
X
X.ms.100:
X	$(TROFF) -ms $(VER) $< | hp2 100 | lp -og $(PRINTER)
X
X.man.100:
X	$(TROFF) -man $< | hp2 100 | lp -og $(PRINTER)
X
X.ms.doc:
X	$(NROFF) -ms $(VER) $< >$@
X
X.man.doc:
X	$(NROFF) -man $< >$@
X
X.ms.vga:
X	$(TROFF) -ms $(VER) $< >/tmp/foo
X	-vga /tmp/foo
X	rm /tmp/foo
X
X.ms.vgas:
X	$(TROFF) -ms $(VER) $< >/tmp/foo
X	-vgas /tmp/foo
X	rm /tmp/foo
X
X#############################################################################
X# Targets...
X
Xasc: $(ASC)
X	cat $(ASC) >asc
X
Xmanual: $(MANUAL)
X
Xclean:
X	rm -f *.doc *.sh
/
echo x - Makefile
sed '/^X/s///' > Makefile << '/'
X# Makefile for elvis
X
X# Several groups of Makefile settings are included below.  Choose *ONE* group
X# of settings for your particular system, and leave the others commented out.
X# The meanings of these settings are:
X#	O	the filename extension for unlinked object files -- usually .o
X#	EXTRA	version-specific object files used in elvis
X#	CC	the C compiler command, possibly with "memory model" flags
X#	CFLAGS	compiler flags used to select compile-time options
X#	PROGS	the list of all programs
X#	SORT	if the "tags" file must be sorted, then SORT=-DSORT
X
XO=s
XPROGS=	elvis ctags ref elvrec fmt elvprsv
X
X#---- These settings are recommended for Minix-PC ----
XEXTRA=	tinytcap.$O tinyprnt.$O
XCFLAGS=	-O -w -D_POSIX_SOURCE -D_MINIX -DCRUNCH \
X	-DNO_MKEXRC -DNO_CURSORSHAPE -DNO_CHARATTR -DNO_SHOWMODE \
X	-DNO_MODELINE -DNO_OPTCOLS -DNO_DIGRAPH -DNO_EXTENSIONS \
X	-DNO_ERRLIST -DNO_FKEY -DNO_VISIBLE -DNO_COLOR -DNO_POPUP
X
X#---- These settings are recommended for Minix-ST ----
X#EXTRA=
X#CFLAGS=
X
X###########################################################################
X###     The rest of this Makefile contains no user-serviceable parts    ###
X###########################################################################
X
XOBJ=blk.$O cmd1.$O cmd2.$O ctype.$O curses.$O cut.$O ex.$O input.$O main.$O misc.$O \
X   modify.$O move1.$O move2.$O move3.$O move4.$O move5.$O opts.$O recycle.$O \
X   redraw.$O regexp.$O regsub.$O system.$O tio.$O tmp.$O unix.$O vars.$O vcmd.$O vi.$O
X
Xall:	$(PROGS)
X	@echo all done.
X
Xelvis:	$(OBJ)
X	@rm -rf elvis
X	@echo Start linking elvis
X	$(CC) -i -o elvis $(OBJ) >/dev/null
X	@chmem =35000 elvis
X
Xctags:	ctags.c
X	$(CC) $(CFLAGS) $(SORT) -o ctags ctags.c
X
Xref:	ref.c
X	$(CC) $(CFLAGS) -o ref ref.c
X
Xelvrec:	elvrec.c
X	$(CC) $(CFLAGS) -o elvrec elvrec.c
X
Xfmt:	fmt.c
X	$(CC) $(CFLAGS) -o fmt fmt.c
X
Xelvprsv:	elvprsv.c
X	$(CC) $(CFLAGS) -o elvprsv elvprsv.c ctype.$O
X
X# Dependencies
X$(OBJ):	vi.h curses.h config.h regexp.h ctype.h
X
Xclean:	
X	@rm -f *.o *.s ctags ref elvrec fmt elvprsv elvis
/
echo x - Man.sh
sed '/^X/s///' > Man.sh << '/'
Xecho x - ctags.man
Xsed '/^X/s///' > ctags.man << '/'
XX.TH CTAGS 1
XX.SH NAME
XXctags - Generates "tags" and (optionally) "refs" files
XX.SH SYNOPSIS
XX\fBctags\fP [\fB-stvra\fP] \fIfilesnames\fP...
XX.SH DESCRIPTION
XX\fIctags\fP generates the "tags" and "refs" files
XXfrom a group of C source files.
XXThe "tags" file is used by Elvis' ":tag" command,
XXcontrol-] command,
XXand -t option.
XXThe "refs" file is sometimes used by the \fIref(1)\fP program.
XX.PP
XXEach C source file is scanned for #define statements and
XXglobal function definitions.
XXThe name of the macro or function becomes the name of a tag.
XXFor each tag, a line is added to the "tags" file which contains:
XX.RS
XX.nf
XX       - the name of the tag
XX       - a tab character
XX       - the name of the file containing the tag
XX       - a tab character
XX       - a way to find the particular line within the file.
XX.RE
XX.fi
XX.PP
XXThe filenames list will typically be the names of all C source
XXfiles in the current directory, like this:
XX.RS
XX.nf
XX$ ctags -stv *.[ch]
XX.RE
XX.fi
XX.SH OPTIONS
XX.IP \fB-t\fR
XXInclude typedefs.
XXA tag will be generated for each user-defined type.
XXAlso tags will be generated for struct and enum names.
XXTypes are considered to be global if they are defined in a header file,
XXand static if they are defined in a C source file.
XX.IP \fB-v\fR
XXInclude variable declarations.
XXA tag will be generated for each variable, except for those that are declared
XXinside the body of a function.
XX.IP \fB-s\fR
XXInclude static tags.
XX\fICtags\fR will normally put global tags in the "tags" file, and silently ignore
XXthe static tags.
XXThis flag causes both global and static tags to be added.
XXThe name of a static tag is generated by prefixing the name of the declared
XXitem with the name of the file where it is defined, with a colon in between.
XXFor example, "static foo(){}" in "bar.c" results in a tag named "bar.c:foo".
XX.IP \fB-r\fP
XXThis causes \fIctags\fP to generate both "tags" and "refs".
XXWithout \fB-r\fP, it would only generate "tags".
XX.IP \fB-a\fR
XXAppend to "tags", and maybe "refs".
XXNormally, \fIctags\fR overwrites these files each time it is invoked.
XXThis flag is useful when you have to many files in the current directory
XXfor you to list them on a single command-line;
XXit allows you to split the arguments among several invocations.
XX.SH FILES
XX.IP tags
XXA cross-reference that lists each tag name, the name of the source file that
XXcontains it, and a way to locate a particular line in the source file.
XX.IP refs
XXThe "refs" file contains the definitions for each tag in the "tags" file,
XXand very little else.
XXThis file can be useful, for example, when licensing restrictions prevent
XXyou from making the source code to the standard C library readable by everybody,
XXbut you still everybody to know what arguments the library functions need.
XX.SH BUGS
XX.PP
XX\fIctags\fR is sensitive to indenting and line breaks.
XXConsequently, it might not discover all of the tags in a file that
XXis formatted in an unusual way.
XX.SH "SEE ALSO"
XXelvis(1), refs(1)
XX.SH AUTHOR
XX.nf
XXSteve Kirkendall
XXkirkenda@cs.pdx.edu
XX.fi
X/
Xecho x - elvis.man
Xsed '/^X/s///' > elvis.man << '/'
XX.TH ELVIS 1
XX.SH NAME
XXelvis, ex, vi, view, input - The editor
XX.SH SYNOPSIS
XX\fBelvis\fP [\fIflags\fP] [\fB+\fP\fIcmd\fP] [\fIfiles\fP...]
XX.SH DESCRIPTION
XX\fIElvis\fP is a text editor which emulates \fIvi\fP/\fIex\fP.
XX.PP
XXOn systems which pass the program name as an argument, such as Unix and Minix,
XXyou may also install \fIelvis\fP under the names "ex", "vi", "view", and "input".
XXThese extra names would normally be links to elvis;
XXsee the "ln" shell command.
XX.PP
XXWhen \fIelvis\fP is invoked as "vi",
XXit behaves exactly as though it was invoked as "elvis".
XXHowever, if you invoke \fIelvis\fP as "view",
XXthen the readonly option is set as though you had given it the "-R" flag.
XXIf you invoke \fIelvis\fP as "ex",
XXthen \fIelvis\fP will start up in the colon command mode
XXinstead of the visual command mode,
XXas though you had given it the "-e" flag.
XXIf you invoke \fIelvis\fP as "input" or "edit",
XXthen \fIelvis\fP will start up in input mode,
XXas though the "-i" flag was given.
XX.SH OPTIONS
XX.IP \fB-r\fP
XXTo the real vi, this flag means that a previous edit should be recovered.
XX\fIElvis\fP, though, has a separate program, called \fIelvrec(1)\fP, for recovering
XXfiles.
XXWhen you invoke \fIelvis\fP with -r, \fIelvis\fP will tell you to run \fIelvrec\fP.
XX.IP \fB-R\fP
XXThis sets the "readonly" option,
XXso you won't accidentally overwrite a file.
XX.IP "\fB-t\fP \fItag\fP"
XXThis causes \fIelvis\fP to start editing at the given tag.
XX.IP "\fB-m\fP [\fIfile\fP]"
XX\fIElvis\fP will search through \fIfile\fP for something that looks like
XXan error message from a compiler.
XXIt will then begin editing the source file that caused the error,
XXwith the cursor sitting on the line where the error was detected.
XXIf you don't explicitly name a \fIfile\fP, then "errlist" is assumed.
XX.IP \fB-e\fP
XX\fIElvis\fP will start up in colon command mode.
XX.IP \fB-v\fP
XX\fIElvis\fP will start up in visual command mode.
XX.IP \fB-i\fP
XX\fIElvis\fP will start up in input mode.
XX.IP "\fB-w\fR \fIwinsize\fR"
XXSets the "window" option's value to \fIwinsize\fR.
XX.IP "\fB+\fP\fIcommand\fP or \fB-c\fP \fIcommand\fP"
XXIf you use the +\fIcommand\fP parameter,
XXthen after the first file is loaded
XX\fIcommand\fP is executed as an EX command.
XXA typical example would be "elvis +237 foo",
XXwhich would cause \fIelvis\fP to start editing foo and
XXthen move directly to line 237.
XXThe "-c \fIcommand\fP" variant was added for UNIX SysV compatibility.
XX.SH FILES
XX.IP /tmp/elv*
XXDuring editing,
XX\fIelvis\fP stores text in a temporary file.
XXFor UNIX, this file will usually be stored in the /tmp directory,
XXand the first three characters will be "elv".
XXFor other systems, the temporary files may be stored someplace else;
XXsee the version-specific section of the documentation.
XX.IP tags
XXThis is the database used by the \fI:tags\fP command and the \fB-t\fP option.
XXIt is usually created by the \fIctags(1)\fP program.
XX.IP ".exrc or elvis.rc"
XXOn UNIX-like systems, a file called ".exrc" in your home directory
XXis executed as a series of \fIex\fR commands.
XXA file by the same name may be executed in the current directory, too.
XXOn non-UNIX systems, ".exrc" is usually an invalid file name;
XXthere, the initialization file is called "elvis.rc" instead.
XX.SH "SEE ALSO"
XXctags(1), ref(1), virec(1)
XX.PP
XX\fIElvis - A Clone of Vi/Ex\fP, the complete \fIelvis\fP documentation.
XX.SH BUGS
XXThere is no LISP support.
XXCertain other features are missing, too.
XX.PP
XXAuto-indent mode is not quite compatible with the real vi.
XXAmong other things, 0^D and ^^D don't do what you might expect.
XX.PP
XXLong lines are displayed differently.
XXThe real vi wraps long lines onto multiple rows of the screen,
XXbut \fIelvis\fP scrolls sideways.
XX.SH AUTHOR
XX.nf
XXSteve Kirkendall
XXkirkenda@cs.pdx.edu
XX.fi
XX.PP
XXMany other people have worked to port \fIelvis\fP to various operating systems.
XXTo see who deserves credit, run the \fI:version\fP command from within \fIelvis\fP,
XXor look in the system-specific section of the complete documentation.
X/
Xecho x - elvprsv.man
Xsed '/^X/s///' > elvprsv.man << '/'
XX.TH ELVPRSV 1
XX.SH NAME
XXelvprsv - Preserve the the modified version of a file after a crash.
XX.SH SYNOPSIS
XX.nf
XX\fB\fBelvprsv\fP ["-\fIwhy elvis died\fP"] /tmp/\fIfilename\fP...
XX\fB\fBelvprsv\fP -R /tmp/\fIfilename\fP...
XX.fi
XX.SH DESCRIPTION
XX.PP
XX\fIelvprsv\fP preserves your edited text after \fIelvis\fP dies.
XXThe text can be recovered later, via the \fIelvprsv\fP program.
XX.PP
XXFor UNIX-like systems,
XXyou should never need to run this program from the command line.
XXIt is run automatically when \fIelvis\fP is about to die,
XXand it should be run (via /etc/rc) when the computer is booted.
XXTHAT'S ALL!
XX.PP
XXFor non-UNIX systems such as MS-DOS, you can either use \fIelvprsv\fP
XXthe same way as under UNIX systems (by running it from your AUTOEXEC.BAT file),
XXor you can run it separately with the "-R" flag to recover the files
XXin one step.
XX.PP
XXIf you're editing a file when \fIelvis\fP dies
XX(due to a bug, system crash, power failure, etc.)
XXthen \fIelvprsv\fP will preserve the most recent version of your text.
XXThe preserved text is stored in a special directory; it does NOT overwrite
XXyour text file automatically.
XX.PP
XX\fIelvprsv\fP will send mail to any user whose work it preserves,
XXif your operating system normally supports mail.
XX.SH FILES
XX.IP /tmp/elv*
XXThe temporary file that \fIelvis\fP was using when it died.
XX.IP /usr/preserve/p*
XXThe text that is preserved by \fIelvprsv\fP.
XX.IP /usr/preserve/Index
XXA text file which lists the names of all preserved files, and the names
XXof the /usr/preserve/p* files which contain their preserved text.
XX.SH BUGS
XX.PP
XXDue to the permissions on the /usr/preserve directory, on UNIX systems
XX\fIelvprsv\fP must be run as superuser.
XXThis is accomplished by making the \fIelvprsv\fP executable be owned by "root"
XXand turning on its "set user id" bit.
XX.PP
XXIf you're editing a nameless buffer when \fIelvis\fP dies, then \fIelvprsv\fP will pretend
XXthat the file was named "foo".
XX.SH AUTHOR
XX.nf
XXSteve Kirkendall
XXkirkenda@cs.pdx.edu
XX.fi
X/
Xecho x - elvrec.man
Xsed '/^X/s///' > elvrec.man << '/'
XX.TH ELVREC 1
XX.SH NAME
XXelvrec - Recover the modified version of a file after a crash
XX.SH SYNOPSIS
XX.nf
XX\fBelvrec\fP [\fIpreservedfile\fP [\fInewfile\fR]]
XX.fi
XX.SH DESCRIPTION
XX.PP
XXIf you're editing a file when \fIelvis\fP dies, the system crashes, or power fails,
XXthe most recent version of your text will be preserved.
XXThe preserved text is stored in a special directory; it does NOT overwrite
XXyour text file automatically.
XX.PP
XXThe \fIelvrec\fP program locates the preserved version of a given file,
XXand writes it over the top of your text file -- or to a new file, if you prefer.
XXThe recovered file will have nearly all of your changes.
XX.PP
XXTo see a list of all recoverable files, run \fIelvrec\fP with no arguments.
XX.SH FILES
XX.IP /usr/preserve/p*
XXThe text that was preserved when \fIelvis\fP died.
XX.IP /usr/preserve/Index
XXA text file which lists the names of all preserved files, and the names
XXof the /usr/preserve/p* files which contain their preserved text.
XX.SH BUGS
XX.PP
XX\fIelvrec\fP is very picky about filenames.
XXYou must tell it to recover the file using exactly the same pathname as
XXwhen you were editing it.
XXThe simplest way to do this is to go into the same directory that you were
XXediting, and invoke \fIelvrec\fP with the same filename as \fIelvis\fP.
XXIf that doesn't work, then try running \fIelvrec\fP with no arguments,
XXto see exactly which pathname it is using for the desired file.
XX.PP
XXDue to the permissions on the /usr/preserve directory, on UNIX systems
XX\fIelvrec\fP must be run as superuser.
XXThis is accomplished by making the \fIelvrec\fP executable be owned by "root"
XXand setting its "set user id" bit.
XX.PP
XXIf you're editing a nameless buffer when \fIelvis\fP dies, then \fIelvrec\fP
XXwill pretend that the file was named "foo".
XX.SH AUTHOR
XX.nf
XXSteve Kirkendall
XXkirkenda@cs.pdx.edu
XX.fi
X/
Xecho x - fmt.man
Xsed '/^X/s///' > fmt.man << '/'
XX.TH FMT 1
XX.SH NAME
XXfmt - adjust line-length for paragraphs of text
XX.SH SYNOPSIS
XX\fBfmt\fP [\-\fIwidth\fP] [\fIfiles\fP]...
XX.SH DESCRIPTION
XX\fIfmt\fR is a simple text formatter.
XXIt inserts or deletes newlines, as necessary, to make all lines in a
XXparagraph be approximately the same width.
XXIt preserves indentation and word spacing.
XX.PP
XXThe default line width is 72 characters.
XXYou can override this with the \-\fIwidth\fR flag.
XXIf you don't name any files on the command line,
XXthen \fIfmt\fR will read from stdin.
XX.PP
XXIt is typically used from within \fIvi\fR to adjust the line breaks
XXin a single paragraph.
XXTo do this, move the cursor to the top of the paragraph,
XXtype "!}fmt", and
XXhit <Return>.
XX.SH AUTHOR
XX.nf
XXSteve Kirkendall
XXkirkenda@cs.pdx.edu
XX.fi
X/
Xecho x - ref.man
Xsed '/^X/s///' > ref.man << '/'
XX.TH REF 1
XX.SH NAME
XXref - Display a C function header
XX.SH SYNOPSIS
XX\fBref\fR [-t] [-c \fIclass\fR]... [-f \fIfile\fR]... \fItag\fR
XX.SH DESCRIPTION
XX\fIref\fP quickly locates and displays the header of a function.
XXTo do this, \fIref\fR
XXlooks in the "tags" file for the line that describes the function, and then 
XXscans the source file for the function.
XXWhen it locates the function, it displays an introductory comment
XX(if there is one), the function's declaration, and the declarations of all
XXarguments.
XX.SH "SEARCH METHOD"
XX.PP
XX\fIref\fR uses a fairly sophisticated tag look-up algorithm.
XXIf you supply a filename via \fB-f\fR \fIfile\fR, then elvis first scans
XXthe tags file for a static tag from that file.
XXThis search is limited to the tags file in the current directory.
XX.PP
XXIf you supply a classname via \fB-c\fR \fIclass\fR, then elvis searches
XXfor a tag from that class.
XXThis search is not limited to the current directory;
XXYou can supply a list of directories in the environment variable \fITAGPATH\fR,
XXand \fIref\fR will search through the "tags" file in each directory until it finds
XXa tag in the desired class.
XX.PP
XXIf that fails, \fIref\fR will then try to look up an ordinary global tag.
XXThis search checks all of the directories listed in \fITAGPATH\fR, too.
XX.PP
XXIf you've given the \fB-t\fR flag, then \fIref\fR will simply output the tag line that
XXit found, and then exit.
XXWithout \fB-t\fR, though, \fIref\fR will search for the tag line.
XXIt will try to open the source file, which should be in the same directory
XXas the tags file where the tag was discovered.
XXIf the source file doesn't exist, or is unreadable, then \fIref\fR will try to open
XXa file called "\fIrefs\fR" in that directory.
XXEither way, \fIref\fR will try to locate the tag, and display whatever it finds.
XX.SH "INTERACTION WITH ELVIS"
XX.PP
XX\fIref\fP is used by \fIelvis\fR' shift-K command.
XXIf the cursor is located on a word such as "splat", in the file "foo.c",
XXthen \fIelvis\fR will invoke \fIref\fR with the command "ref -f foo.c splat".
XX.PP
XXIf \fIelvis\fR has been compiled with the -DEXTERNAL_TAGS flag, then \fIelvis\fR will
XXuse \fIref\fR \fB\fRto scan the tags files.
XXThis is slower than the built-in tag searching, but it allows \fIelvis\fR to access
XXthe more sophisticated tag lookup provided by \fIref\fR.
XXOther than that, external tags should act exactly like internal tags.
XX.SH OPTIONS
XX.IP \fB-t\fR
XXOutput tag info, instead of the function header.
XX.IP "\fB-f\fR \fIfile\fR"
XXThe tag might be a static function in \fIfile\fR.
XXYou can use several -f flags to have \fIref\fR consider static tags from more than one file.
XX.IP "\fB-c\fR \fIclass\fR"
XXThe tag might be a member of class \fIclass\fR.
XXYou can use several -c flags to have \fIref\fR consider tags from more than one class.
XX.SH FILES
XX.IP \fBtags\fR
XXList of function names and their locations, generated by \fIctags\fR.
XX.IP \fBrefs\fR
XXFunction headers extracted from source files (optional).
XX.SH ENVIRONMENT
XX.IP \fBTAGPATH\fR
XXList of directories to be searched.
XXThe elements in the list are separated by either
XXsemicolons (for MS-DOS, Atari TOS, and AmigaDos), or
XXby colons (every other operating system).
XXFor each operating system, \fIref\fR has a built-in default which is probably
XXadequate.
XX.SH NOTES
XX.PP
XXYou might want to generate a "tags" file the directory that contains the
XXsource code for standard C library on your system.
XXIf licensing restrictions prevent you from making the library source readable
XXby everybody, then you can have \fIctags\fR generate a "refs" file,
XXand make "refs" readable by everybody.
XX.PP
XXIf your system doesn't come with the library source code, then perhaps you
XXcan produce something workable from the \fIlint\fR libraries.
XX.SH "SEE ALSO"
XXelvis(1), ctags(1)
XX.SH AUTHOR
XX.nf
XXSteve Kirkendall
XXkirkenda@cs.pdx.edu
XX.fi
X/
/
echo x - Readme.txt
sed '/^X/s///' > Readme.txt << '/'
XElvis is a clone of vi/ex, the standard UNIX editor.  Elvis supports
Xnearly all of the vi/ex commands, in both visual mode and colon mode.
X
XElvis runs under BSD UNIX, AT&T SysV UNIX, SCO Xenix, Minix, MS-DOS
X(Turbo-C or MSC 5.1), Atari TOS, OS9/68000, Coherent, VMS, and AmigaDos.
XPorts to other operating systems are in progress; contact me before you
Xstart porting it to some other OS, because somebody else may have
Xalready done it for you.
X
XElvis is freely redistributable, in either source form or executable
Xform.  There are no restrictions on how you may use it.
X
XThe file "elvisman.txt" contains the manual for elvis.  It is a plain
XASCII file with nothing more exotic than a newline character.  It is
Xformatted for 66-line, 80-column pages.  There may also be an archive of
X"*.ms" and "*.man" files, which contain the TROFF source text used to
Xgenerate that manual.
X
XThe file named "Makefile.mix" is used to compile elvis for all systems
Xexcept VMS and possibly MS-DOS.  You should copy "Makefile.mix" to
X"Makefile", and then edit "Makefile" to select the appropriate group of
Xsettings for your system.
X
X
XAuthor: Steve Kirkendall
X	14407 SW Teal Blvd. #C
X	Beaverton, OR   97005
X
XE-mail:	kirkenda@cs.pdx.edu
X
XPhone:	(503) 643-6980
/
echo x - blk.c
sed '/^X/s///' > blk.c << '/'
X/* blk.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains the functions that get/put blocks from the temp file.
X * It also contains the "do" and "undo" functions.
X */
X
X#include "config.h"
X#include "vi.h"
X
X#ifndef NBUFS
X# define NBUFS	5		/* must be at least 3 -- more is better */
X#endif
X
X
X/*------------------------------------------------------------------------*/
X
XBLK		hdr;		/* buffer for the header block */
X
Xstatic int	b4cnt;		/* used to count context of beforedo/afterdo */
Xstatic struct _blkbuf
X{
X	BLK		buf;		/* contents of a text block */
X	unsigned short	logical;	/* logical block number */
X	int		dirty;		/* must the buffer be rewritten? */
X}
X		blk[NBUFS],	/* buffers for text[?] blocks */
X		*toonew,	/* buffer which shouldn't be recycled yet */
X		*newtoo,	/* another buffer which should be recycled */
X		*recycle = blk;	/* next block to be recycled */
X
X
X
X
X
X/* This function wipes out all buffers */
Xvoid blkinit()
X{
X	int	i;
X
X	for (i = 0; i < NBUFS; i++)
X	{
X		blk[i].logical = 0;
X		blk[i].dirty = FALSE;
X	}
X	for (i = 0; i < MAXBLKS; i++)
X	{
X		hdr.n[i] = 0;
X	}
X}
X
X/* This function allocates a buffer and fills it with a given block's text */
XBLK *blkget(logical)
X	int	logical;	/* logical block number to fetch */
X{
X	REG struct _blkbuf	*this;	/* used to step through blk[] */
X	REG int	i;
X
X	/* if logical is 0, just return the hdr buffer */
X	if (logical == 0)
X	{
X		return &hdr;
X	}
X
X	/* see if we have that block in mem already */
X	for (this = blk; this < &blk[NBUFS]; this++)
X	{
X		if (this->logical == logical)
X		{
X			newtoo = toonew;
X			toonew = this;
X			return &this->buf;
X		}
X	}
X
X	/* choose a block to be recycled */
X	do
X	{
X		this = recycle++;
X		if (recycle == &blk[NBUFS])
X		{
X			recycle = blk;
X		}
X	} while (this == toonew || this == newtoo);
X
X	/* if it contains a block, flush that block */
X	blkflush(this);
X
X	/* fill this buffer with the desired block */
X	this->logical = logical;
X	if (hdr.n[logical])
X	{
X		/* it has been used before - fill it from tmp file */
X		lseek(tmpfd, (long)hdr.n[logical] * (long)BLKSIZE, 0);
X		if (read(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE)
X		{
X			msg("Error reading back from tmp file!");
X		}
X	}
X	else
X	{
X		/* it is new - zero it */
X		for (i = 0; i < BLKSIZE; i++)
X		{
X			this->buf.c[i] = 0;
X		}
X	}
X
X	/* This isn't really a change, but it does potentially invalidate
X	 * the kinds of shortcuts that the "changes" variable is supposed
X	 * to protect us from... so count it as a change.
X	 */
X	changes++;
X
X	/* mark it as being "not dirty" */
X	this->dirty = 0;
X
X	/* return it */
X	newtoo = toonew;
X	toonew = this;
X	return &this->buf;
X}
X
X
X
X/* This function writes a block out to the temporary file */
Xvoid blkflush(this)
X	REG struct _blkbuf	*this;	/* the buffer to flush */
X{
X	long		seekpos;	/* seek position of the new block */
X	unsigned short	physical;	/* physical block number */
X
X	/* if its empty (an orphan blkadd() maybe?) then make it dirty */
X	if (this->logical && !*this->buf.c)
X	{
X		blkdirty(&this->buf);
X	}
X
X	/* if it's an empty buffer or a clean version is on disk, quit */
X	if (!this->logical || hdr.n[this->logical] && !this->dirty)
X	{
X		return;
X	}
X
X	/* find a free place in the file */
X#ifndef NO_RECYCLE
X	seekpos = allocate();
X	lseek(tmpfd, seekpos, 0);
X#else
X	seekpos = lseek(tmpfd, 0L, 2);
X#endif
X	physical = seekpos / BLKSIZE;
X
X	/* put the block there */
X	if (write(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE)
X	{
X		msg("Trouble writing to tmp file");
X	}
X	this->dirty = FALSE;
X
X	/* update the header so it knows we put it there */
X	hdr.n[this->logical] = physical;
X}
X
X
X/* This function sets a block's "dirty" flag or deletes empty blocks */
Xvoid blkdirty(bp)
X	BLK	*bp;	/* buffer returned by blkget() */
X{
X	REG int		i, j;
X	REG char	*scan;
X	REG int		k;
X
X	/* find the buffer */
X	for (i = 0; i < NBUFS && bp != &blk[i].buf; i++)
X	{
X	}
X#ifdef DEBUG
X	if (i >= NBUFS)
X	{
X		msg("blkdirty() called with unknown buffer at 0x%lx", bp);
X		return;
X	}
X	if (blk[i].logical == 0)
X	{
X		msg("blkdirty called with freed buffer");
X		return;
X	}
X#endif
X
X	/* if this block ends with line# INFINITY, then it must have been
X	 * allocated unnecessarily during tmpstart().  Forget it.
X	 */
X	if (lnum[blk[i].logical] == INFINITY)
X	{
X#ifdef DEBUG
X		if (blk[i].buf.c[0])
X		{
X			msg("bkldirty called with non-empty extra BLK");
X		}
X#endif
X		blk[i].logical = 0;
X		blk[i].dirty = FALSE;
X		return;
X	}
X
X	/* count lines in this block */
X	for (j = 0, scan = bp->c; *scan && scan < bp->c + BLKSIZE; scan++)
X	{
X		if (*scan == '\n')
X		{
X			j++;
X		}
X	}
X
X	/* adjust lnum, if necessary */
X	k = blk[i].logical;
X	j += (lnum[k - 1] - lnum[k]);
X	if (j != 0)
X	{
X		nlines += j;
X		while (k < MAXBLKS && lnum[k] != INFINITY)
X		{
X			lnum[k++] += j;
X		}
X	}
X
X	/* if it still has text, mark it as dirty */
X	if (*bp->c)
X	{
X		blk[i].dirty = TRUE;
X	}
X	else /* empty block, so delete it */
X	{
X		/* adjust the cache */
X		k = blk[i].logical;
X		for (j = 0; j < NBUFS; j++)
X		{
X			if (blk[j].logical >= k)
X			{
X				blk[j].logical--;
X			}
X		}
X
X		/* delete it from hdr.n[] and lnum[] */
X		blk[i].logical = 0;
X		blk[i].dirty = FALSE;
X		while (k < MAXBLKS - 1)
X		{
X			hdr.n[k] = hdr.n[k + 1];
X			lnum[k] = lnum[k + 1];
X			k++;
X		}
X		hdr.n[MAXBLKS - 1] = 0;
X		lnum[MAXBLKS - 1] = INFINITY;
X	}
X}
X
X
X/* insert a new block into hdr, and adjust the cache */
XBLK *blkadd(logical)
X	int	logical;	/* where to insert the new block */
X{
X	REG int	i;
X
X	/* adjust hdr and lnum[] */
X	for (i = MAXBLKS - 1; i > logical; i--)
X	{
X		hdr.n[i] = hdr.n[i - 1];
X		lnum[i] = lnum[i - 1];
X	}
X	hdr.n[logical] = 0;
X	lnum[logical] = lnum[logical - 1];
X
X	/* adjust the cache */
X	for (i = 0; i < NBUFS; i++)
X	{
X		if (blk[i].logical >= logical)
X		{
X			blk[i].logical++;
X		}
X	}
X
X	/* return the new block, via blkget() */
X	return blkget(logical);
X}
X
X
X/* This function forces all dirty blocks out to disk */
Xvoid blksync()
X{
X	int	i;
X
X	for (i = 0; i < NBUFS; i++)
X	{
X		/* blk[i].dirty = TRUE; */
X		blkflush(&blk[i]);
X	}
X	if (*o_sync)
X	{
X		sync();
X	}
X}
X
X/*------------------------------------------------------------------------*/
X
Xstatic MARK	undocurs;	/* where the cursor should go if undone */
Xstatic long	oldnlines;
Xstatic long	oldlnum[MAXBLKS];
X
X
X/* This function should be called before each command that changes the text.
X * It defines the state that undo() will reset the file to.
X */
Xvoid beforedo(forundo)
X	int		forundo;	/* boolean: is this for an undo? */
X{
X	REG int		i;
X	REG long	l;
X
X	/* if this is a nested call to beforedo, quit! Use larger context */
X	if (b4cnt++ > 0)
X	{
X		return;
X	}
X
X	/* force all block buffers to disk */
X	blksync();
X
X#ifndef NO_RECYCLE
X	/* perform garbage collection on blocks from tmp file */
X	garbage();
X#endif
X
X	/* force the header out to disk */
X	lseek(tmpfd, 0L, 0);
X	if (write(tmpfd, hdr.c, (unsigned)BLKSIZE) != BLKSIZE)
X	{
X		msg("Trouble writing header to tmp file ");
X	}
X
X	/* copy or swap oldnlines <--> nlines, oldlnum <--> lnum */
X	if (forundo)
X	{
X		for (i = 0; i < MAXBLKS; i++)
X		{
X			l = lnum[i];
X			lnum[i] = oldlnum[i];
X			oldlnum[i] = l;
X		}
X		l = nlines;
X		nlines = oldnlines;
X		oldnlines = l;
X	}
X	else
X	{
X		for (i = 0; i < MAXBLKS; i++)
X		{
X			oldlnum[i] = lnum[i];
X		}
X		oldnlines = nlines;
X	}
X
X	/* save the cursor position */
X	undocurs = cursor;
X
X	/* upon return, the calling function continues and makes changes... */
X}
X
X/* This function marks the end of a (nested?) change to the file */
Xvoid afterdo()
X{
X	if (--b4cnt)
X	{
X		/* after abortdo(), b4cnt may decribe nested beforedo/afterdo
X		 * pairs incorrectly.  If it is decremented to often, then
X		 * keep b4cnt sane but don't do anything else.
X		 */
X		if (b4cnt < 0)
X			b4cnt = 0;
X
X		return;
X	}
X
X	/* make sure the cursor wasn't left stranded in deleted text */
X	if (markline(cursor) > nlines)
X	{
X		cursor = MARK_LAST;
X	}
X	/* NOTE: it is still possible that markidx(cursor) is after the
X	 * end of a line, so the Vi mode will have to take care of that
X	 * itself */
X
X	/* if a significant change has been made to this file, then set the
X	 * MODIFIED flag.
X	 */
X	if (significant)
X	{
X		setflag(file, MODIFIED);
X		setflag(file, UNDOABLE);
X	}	
X}
X
X/* This function cuts short the current set of changes.  It is called after
X * a SIGINT.
X */
Xvoid abortdo()
X{
X	/* finish the operation immediately. */
X	if (b4cnt > 0)
X	{
X		b4cnt = 1;
X		afterdo();
X	}
X
X	/* in visual mode, the screen is probably screwed up */
X	if (mode == MODE_COLON)
X	{
X		mode = MODE_VI;
X	}
X	if (mode == MODE_VI)
X	{
X		redraw(MARK_UNSET, FALSE);
X	}
X}
X
X/* This function discards all changes made since the last call to beforedo() */
Xint undo()
X{
X	BLK		oldhdr;
X
X	/* if beforedo() has never been run, fail */
X	if (!tstflag(file, UNDOABLE))
X	{
X		msg("You haven't modified this file yet.");
X		return FALSE;
X	}
X
X	/* read the old header form the tmp file */
X	lseek(tmpfd, 0L, 0);
X	if (read(tmpfd, oldhdr.c, (unsigned)BLKSIZE) != BLKSIZE)
X	{
X		msg("Trouble rereading the old header from tmp file");
X	}
X
X	/* "do" the changed version, so we can undo the "undo" */
X	cursor = undocurs;
X	beforedo(TRUE);
X	afterdo();
X
X	/* wipe out the block buffers - we can't assume they're correct */
X	blkinit();
X
X	/* use the old header -- and therefore the old text blocks */
X	hdr = oldhdr;
X
X	/* This is a change */
X	significant = TRUE;
X	changes++;
X
X	return TRUE;
X}
/
echo x - cmd1.c
sed '/^X/s///' > cmd1.c << '/'
X/* cmd1.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains some of the EX commands - mostly ones that deal with
X * files, options, etc. -- anything except text.
X */
X
X#include "config.h"
X#include "ctype.h"
X#include "vi.h"
X#include "regexp.h"
X
X#ifdef DEBUG
X/* print the selected lines with info on the blocks */
X/*ARGSUSED*/
Xvoid cmd_debug(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	REG char	*scan;
X	REG long	l;
X	REG int		i;
X	int		len;
X
X	/* scan lnum[] to determine which block its in */
X	l = markline(frommark);
X	for (i = 1; l > lnum[i]; i++)
X	{
X	}
X
X	do
X	{
X		/* fetch text of the block containing that line */
X		scan = blkget(i)->c;
X
X		/* calculate its length */
X		if (scan[BLKSIZE - 1])
X		{
X			len = BLKSIZE;
X		}
X		else
X		{
X			len = strlen(scan);
X		}
X
X		/* print block stats */
X		msg("##### hdr[%d]=%d, lnum[%d-1]=%ld, lnum[%d]=%ld (%ld lines)",
X			i, hdr.n[i], i, lnum[i-1], i, lnum[i], lnum[i] - lnum[i - 1]);
X		msg("##### len=%d, buf=0x%lx, %sdirty",
X			len, scan, ((int *)scan)[MAXBLKS + 1] ? "" : "not ");
X		if (bang)
X		{
X			while (--len >= 0)
X			{
X				addch(*scan);
X				scan++;
X			}
X		}
X		exrefresh();
X
X		/* next block */
X		i++;
X	} while (i < MAXBLKS && lnum[i] && lnum[i - 1] < markline(tomark));
X}
X
X
X/* This function checks a lot of conditions to make sure they aren't screwy */
X/*ARGSUSED*/
Xvoid cmd_validate(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	char	*scan;
X	int	i;
X	int	nlcnt;	/* used to count newlines */
X	int	len;	/* counts non-NUL characters */
X
X	/* check lnum[0] */
X	if (lnum[0] != 0L)
X	{
X		msg("lnum[0] = %ld", lnum[0]);
X	}
X
X	/* check each block */
X	for (i = 1; lnum[i] <= nlines; i++)
X	{
X		scan = blkget(i)->c;
X		if (scan[BLKSIZE - 1])
X		{
X			msg("block %d has no NUL at the end", i);
X		}
X		else
X		{
X			for (nlcnt = len = 0; *scan; scan++, len++)
X			{
X				if (*scan == '\n')
X				{
X					nlcnt++;
X				}
X			}
X			if (scan[-1] != '\n')
X			{
X				msg("block %d doesn't end with '\\n' (length %d)", i, len);
X			}
X			if (bang || nlcnt != lnum[i] - lnum[i - 1])
X			{
X				msg("block %d (line %ld?) has %d lines, but should have %ld",
X					i, lnum[i - 1] + 1L, nlcnt, lnum[i] - lnum[i - 1]);
X			}
X		}
X		exrefresh();
X	}
X
X	/* check lnum again */
X	if (lnum[i] != INFINITY)
X	{
X		msg("hdr.n[%d] = %d, but lnum[%d] = %ld",
X			i, hdr.n[i], i, lnum[i]);
X	}
X
X	msg("# = \"%s\", %% = \"%s\"", prevorig, origname);
X	msg("V_from=%ld.%d, cursor=%ld.%d", markline(V_from), markidx(V_from), markline(cursor), markidx(cursor));
X}
X#endif /* DEBUG */
X
X
X/*ARGSUSED*/
Xvoid cmd_mark(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	/* validate the name of the mark */
X	if (*extra == '"')
X	{
X		extra++;
X	}
X	/* valid mark names are lowercase ascii characters */
X	if (!isascii(*extra) || !islower(*extra) || extra[1])
X	{
X		msg("Invalid mark name");
X		return;
X	}
X
X	mark[*extra - 'a'] = tomark;
X}
X
X/*ARGSUSED*/
Xvoid cmd_write(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	int		fd;
X	int		append;	/* boolean: write in "append" mode? */
X	REG long	l;
X	REG char	*scan;
X	REG int		i;
X
X	/* if writing to a filter, then let filter() handle it */
X	if (*extra == '!')
X	{
X		filter(frommark, tomark, extra + 1, FALSE);
X		return;
X	}
X
X	/* if all lines are to be written, use tmpsave() */
X	if (frommark == MARK_FIRST && tomark == MARK_LAST && cmd == CMD_WRITE)
X	{
X		tmpsave(extra, bang);
X		return;
X	}
X
X	/* see if we're going to do this in append mode or not */
X	append = FALSE;
X	if (extra[0] == '>' && extra[1] == '>')
X	{
X		extra += 2;
X		append = TRUE;
X	}
X
X	/* either the file must not exist, or we must have a ! or be appending */
X	if (access(extra, 0) == 0 && !bang && !append)
X	{
X		msg("File already exists - Use :w! to overwrite");
X		return;
X	}
X
X	/* else do it line-by-line, like cmd_print() */
X	if (append)
X	{
X#ifdef O_APPEND
X		fd = open(extra, O_WRONLY|O_APPEND);
X#else
X		fd = open(extra, O_WRONLY);
X		if (fd >= 0)
X		{
X			lseek(fd, 0L, 2);
X		}
X#endif
X	}
X	else
X	{
X		fd = -1; /* so we know the file isn't open yet */
X	}
X
X	if (fd < 0)
X	{
X		fd = creat(extra, FILEPERMS);
X		if (fd < 0)
X		{
X			msg("Can't write to \"%s\"", extra);
X			return;
X		}
X	}
X	for (l = markline(frommark); l <= markline(tomark); l++)
X	{
X		/* get the next line */
X		scan = fetchline(l);
X		i = strlen(scan);
X		scan[i++] = '\n';
X
X		/* print the line */
X		if (twrite(fd, scan, i) < i)
X		{
X			msg("Write failed");
X			break;
X		}
X	}
X	rptlines = markline(tomark) - markline(frommark) + 1;
X	rptlabel = "written";
X	close(fd);
X}	
X
X
X/*ARGSUSED*/
Xvoid cmd_shell(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	static char	prevextra[80];
X
X	/* special case: ":sh" means ":!sh" */
X	if (cmd == CMD_SHELL)
X	{
X		extra = o_shell;
X		frommark = tomark = 0L;
X	}
X
X	/* if extra is "!", substitute previous command */
X	if (*extra == '!')
X	{
X		if (!*prevextra)
X		{
X			msg("No previous shell command to substitute for '!'");
X			return;
X		}
X		extra = prevextra;
X	}
X	else if (cmd == CMD_BANG && strlen(extra) < sizeof(prevextra) - 1)
X	{
X		strcpy(prevextra, extra);
X	}
X
X	/* warn the user if the file hasn't been saved yet */
X	if (*o_warn && tstflag(file, MODIFIED))
X	{
X		if (mode == MODE_VI)
X		{
X			mode = MODE_COLON;
X		}
X		msg("Warning: \"%s\" has been modified but not yet saved", origname);
X	}
X
X	/* if no lines were specified, just run the command */
X	suspend_curses();
X	if (frommark == 0L)
X	{
X		system(extra);
X	}
X	else /* pipe lines from the file through the command */
X	{
X		filter(frommark, tomark, extra, TRUE);
X	}
X
X	/* resume curses quietly for MODE_EX, but noisily otherwise */
X	resume_curses(mode == MODE_EX);
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_global(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;	/* rest of the command line */
X{
X	char	*cmdptr;	/* the command from the command line */
X	char	cmdln[100];	/* copy of the command from the command line */
X	char	*line;		/* a line from the file */
X	long	l;		/* used as a counter to move through lines */
X	long	lqty;		/* quantity of lines to be scanned */
X	long	nchanged;	/* number of lines changed */
X	regexp	*re;		/* the compiled search expression */
X
X	/* can't nest global commands */
X	if (doingglobal)
X	{
X		msg("Can't nest global commands.");
X		rptlines = -1L;
X		return;
X	}
X
X	/* ":g! ..." is the same as ":v ..." */
X	if (bang)
X	{
X		cmd = CMD_VGLOBAL;
X	}
X
X	/* make sure we got a search pattern */
X	if (*extra != '/' && *extra != '?')
X	{
X		msg("Usage: %c /regular expression/ command", cmd == CMD_GLOBAL ? 'g' : 'v');
X		return;
X	}
X
X	/* parse & compile the search pattern */
X	cmdptr = parseptrn(extra);
X	if (!extra[1])
X	{
X		msg("Can't use empty regular expression with '%c' command", cmd == CMD_GLOBAL ? 'g' : 'v');
X		return;
X	}
X	re = regcomp(extra + 1);
X	if (!re)
X	{
X		/* regcomp found & described an error */
X		return;
X	}
X
X	/* for each line in the range */
X	doingglobal = TRUE;
X	ChangeText
X	{
X		/* NOTE: we have to go through the lines in a forward order,
X		 * otherwise "g/re/p" would look funny.  *BUT* for "g/re/d"
X		 * to work, simply adding 1 to the line# on each loop won't
X		 * work.  The solution: count lines relative to the end of
X		 * the file.  Think about it.
X		 */
X		for (l = nlines - markline(frommark),
X			lqty = markline(tomark) - markline(frommark) + 1L,
X			nchanged = 0L;
X		     lqty > 0 && nlines - l >= 0 && nchanged >= 0L;
X		     l--, lqty--)
X		{
X			/* fetch the line */
X			line = fetchline(nlines - l);
X
X			/* if it contains the search pattern... */
X			if ((!regexec(re, line, 1)) == (cmd != CMD_GLOBAL))
X			{
X				/* move the cursor to that line */
X				cursor = MARK_AT_LINE(nlines - l);
X
X				/* do the ex command (without mucking up
X				 * the original copy of the command line)
X				 */
X				strcpy(cmdln, cmdptr);
X				rptlines = 0L;
X				doexcmd(cmdln);
X				nchanged += rptlines;
X			}
X		}
X	}
X	doingglobal = FALSE;
X
X	/* free the regexp */
X	free(re);
X
X	/* Reporting...*/
X	rptlines = nchanged;
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_file(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X#ifndef CRUNCH
X	/* if we're given a new filename, use it as this file's name */
X	if (extra && *extra)
X	{
X		strcpy(origname, extra);
X		storename(origname);
X		setflag(file, NOTEDITED);
X	}
X#endif
X	if (cmd == CMD_FILE)
X	{
X#ifndef CRUNCH
X		msg("\"%s\" %s%s%s %ld lines,  line %ld [%ld%%]",
X#else
X		msg("\"%s\" %s%s %ld lines,  line %ld [%ld%%]",
X#endif
X			*origname ? origname : "[NO FILE]",
X			tstflag(file, MODIFIED) ? "[MODIFIED]" : "",
X#ifndef CRUNCH
X			tstflag(file, NOTEDITED) ?"[NOT EDITED]":"",
X#endif
X			tstflag(file, READONLY) ? "[READONLY]" : "",
X			nlines,
X			markline(frommark),
X			markline(frommark) * 100 / nlines);
X	}
X#ifndef CRUNCH
X	else if (markline(frommark) != markline(tomark))
X	{
X		msg("range \"%ld,%ld\" contains %ld lines",
X			markline(frommark),
X			markline(tomark),
X			markline(tomark) - markline(frommark) + 1L);
X	}
X#endif
X	else
X	{
X		msg("%ld", markline(frommark));
X	}
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_edit(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	long	line = 1L;	/* might be set to prevline */
X#ifndef CRUNCH
X	char	*init = (char *)0;
X#endif
X
X
X	/* if ":vi", then switch to visual mode, and if no file is named
X	 * then don't switch files.
X	 */
X	if (cmd == CMD_VISUAL)
X	{
X		mode = MODE_VI;
X		msg("");
X		if (!*extra)
X		{
X			return;
X		}
X	}
X
X	/* Editing previous file?  Then start at previous line */
X	if (!strcmp(extra, prevorig))
X	{
X		line = prevline;
X	}
X
X#ifndef CRUNCH
X	/* if we were given an explicit starting line, then start there */
X	if (*extra == '+')
X	{
X		for (init = ++extra; !isspace(*extra); extra++)
X		{
X		}
X		while (isspace(*extra))
X		{
X			*extra++ = '\0';
X		}
X		if (!*init)
X		{
X			init = "$";
X		}
X		if (!extra)
X		{
X			extra = origname;
X		}
X	}
X#endif /* not CRUNCH */
X
X	/* switch files */
X	if (tmpabort(bang))
X	{
X		tmpstart(extra);
X		if (line <= nlines && line >= 1L)
X		{
X			cursor = MARK_AT_LINE(line);
X		}
X#ifndef CRUNCH
X		if (init)
X		{
X			doexcmd(init);
X		}
X#endif
X	}
X	else
X	{
X		msg("Use edit! to abort changes, or w to save changes");
X
X		/* so we can say ":e!#" next time... */
X		strcpy(prevorig, extra);
X		prevline = 1L;
X	}
X}
X
X/* This code is also used for rewind -- GB */
X
X/*ARGSUSED*/
Xvoid cmd_next(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	int	i, j;
X	char	*scan;
X
X	/* if extra stuff given, use ":args" to define a new args list */
X	if (cmd == CMD_NEXT && extra && *extra)
X	{
X		cmd_args(frommark, tomark, cmd, bang, extra);
X	}
X
X	/* move to the next arg */
X	if (cmd == CMD_NEXT)
X	{
X		i = argno + 1;
X	}
X	else if (cmd == CMD_PREVIOUS)
X	{
X		i = argno - 1;
X	}
X	else /* cmd == CMD_REWIND */
X	{
X		i = 0;
X	}	
X	if (i < 0 || i >= nargs)
X	{
X		msg("No %sfiles to edit", cmd == CMD_REWIND ? "" : "more ");
X		return;
X	}
X
X	/* find & isolate the name of the file to edit */
X	for (j = i, scan = args; j > 0; j--)
X	{
X		while(*scan++)
X		{
X		}
X	}
X
X	/* switch to the next file */
X	if (tmpabort(bang))
X	{
X		tmpstart(scan);
X		argno = i;
X	}
X	else
X	{
X		msg("Use :%s! to abort changes, or w to save changes",
X			cmd == CMD_NEXT ? "next" :
X			cmd == CMD_PREVIOUS ? "previous" :
X					"rewind");
X	}
X}
X
X/* also called from :wq -- always writes back in this case */
X
X/*ARGSUSED*/
Xvoid cmd_xit(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	static long	whenwarned;	/* when the user was last warned of extra files */
X	int		oldflag;
X
X	/* if there are more files to edit, then warn user */
X	if (argno >= 0 && argno + 1 < nargs && whenwarned != changes && (!bang || cmd != CMD_QUIT))
X	{
X		msg("More files to edit -- Use \":n\" to go to next file");
X		whenwarned = changes;
X		return;
X	}
X
X	if (cmd == CMD_QUIT)
X	{
X		oldflag = *o_autowrite;
X		*o_autowrite = FALSE;
X		if (tmpabort(bang))
X		{
X			mode = MODE_QUIT;
X		}
X		else
X		{
X			msg("Use q! to abort changes, or wq to save changes");
X		}
X		*o_autowrite = oldflag;
X	}
X	else
X	{
X		/* else try to save this file */
X		oldflag = tstflag(file, MODIFIED);
X		if (cmd == CMD_WQUIT)
X			setflag(file, MODIFIED);
X		if (tmpend(bang))
X		{
X			mode = MODE_QUIT;
X		}
X		else
X		{
X			msg("Could not save file -- use quit! to abort changes, or w filename");
X		}
X		if (!oldflag)
X			clrflag(file, MODIFIED);
X	}
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_args(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	char	*scan;
X	int	col;
X	int	arg;
X	int	scrolled = FALSE;
X	int	width;
X
X	/* if no extra names given, or just current name, then report the args
X	 * we have now.
X	 */
X	if (!extra || !*extra)
X	{
X		/* empty args list? */
X		if (nargs == 1 && !*args)
X		{
X			return;
X		}
X
X		/* list the arguments */
X		for (scan = args, col = arg = 0;
X		     arg < nargs;
X		     scan += width + 1, col += width, arg++)
X		{
X			width = strlen(scan);
X			if (col + width >= COLS - 4)
X			{
X				addch('\n');
X				col = 0;
X				scrolled = TRUE;
X			}
X			else if (col > 0)
X			{
X				addch(' ');
X				col++;
X			}
X			if (arg == argno)
X			{
X				addch('[');
X				addstr(scan);
X				addch(']');
X				col += 2;
X			}
X			else
X			{
X				addstr(scan);
X			}
X		}
X
X		/* write a trailing newline */
X		if ((mode == MODE_EX || mode == MODE_COLON || scrolled) && col)
X		{
X			addch('\n');
X		}
X		exrefresh();	
X	}
X	else /* new args list given */
X	{
X		for (scan = args, nargs = 1; *extra; )
X		{
X			if (isspace(*extra))
X			{
X				*scan++ = '\0';
X				while (isspace(*extra))
X				{
X					extra++;
X				}
X				if (*extra)
X				{
X					nargs++;
X				}
X			}
X			else
X			{
X				*scan++ = *extra++;
X			}
X		}
X		*scan = '\0';
X
X		/* reset argno to before the first, so :next will go to first */
X		argno = -1;
X
X		if (nargs != 1)
X		{
X                        msg("%d files to edit", nargs);
X		}
X	}
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_cd(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	char	*getenv();
X
X#ifndef CRUNCH
X	/* if current file is modified, and no '!' was given, then error */
X	if (tstflag(file, MODIFIED) && !bang)
X	{
X		msg("File modified; use \"cd! %s\" to switch anyway", extra);
X	}
X#endif
X
X	/* default directory name is $HOME */
X	if (!*extra)
X	{
X		extra = getenv("HOME");
X		if (!extra)
X		{
X			msg("environment variable $HOME not set");
X			return;
X		}
X	}
X
X	/* go to the directory */
X	if (chdir(extra) < 0)
X	{
X		perror(extra);
X	}
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_map(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	char	*mapto;
X	char	*build, *scan;
X#ifndef NO_FKEY
X	static char *fnames[NFKEYS] =
X	{
X		"#10", "#1", "#2", "#3", "#4",
X		"#5", "#6", "#7", "#8", "#9",
X# ifndef NO_SHIFT_FKEY
X		"#10s", "#1s", "#2s", "#3s", "#4s",
X		"#5s", "#6s", "#7s", "#8s", "#9s",
X#  ifndef NO_CTRL_FKEY
X		"#10c", "#1c", "#2c", "#3c", "#4c",
X		"#5c", "#6c", "#7c", "#8c", "#9c",
X#   ifndef NO_ALT_FKEY
X		"#10a", "#1a", "#2a", "#3a", "#4a",
X		"#5a", "#6a", "#7a", "#8a", "#9a",
X#   endif
X#  endif
X# endif
X	};
X	int	key;
X#endif
X
X	/* "map" with no extra will dump the map table contents */
X	if (!*extra)
X	{
X#ifndef NO_ABBR
X		if (cmd == CMD_ABBR)
X		{
X			dumpkey(bang ? WHEN_EX|WHEN_VIINP|WHEN_VIREP : WHEN_VIINP|WHEN_VIREP, TRUE);
X		}
X		else
X#endif
X		{
X			dumpkey(bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, FALSE);
X		}
X	}
X	else
X	{
X		/* "extra" is key to map, followed by what it maps to */
X
X		/* handle quoting inside the "raw" string */
X		for (build = mapto = extra;
X		     *mapto && (*mapto != ' ' && *mapto != '\t');
X		     *build++ = *mapto++)
X		{
X			if (*mapto == ctrl('V') && mapto[1])
X			{
X				mapto++;
X			}
X		}
X
X		/* skip whitespace, and mark the end of the "raw" string */
X		while ((*mapto == ' ' || *mapto == '\t'))
X		{
X			*mapto++ = '\0';
X		}
X		*build = '\0';
X
X		/* strip ^Vs from the "cooked" string */
X		for (scan = build = mapto; *scan; *build++ = *scan++)
X		{
X			if (*scan == ctrl('V') && scan[1])
X			{
X				scan++;
X			}
X		}
X		*build = '\0';
X
X#ifndef NO_FKEY
X		/* if the mapped string is '#' and a number, then assume
X		 * the user wanted that function key
X		 */
X		if (extra[0] == '#' && isdigit(extra[1]))
X		{
X			key = atoi(extra + 1) % 10;
X# ifndef NO_SHIFT_FKEY
X			build = extra + strlen(extra) - 1;
X			if (*build == 's')
X				key += 10;
X#  ifndef NO_CTRL_FKEY
X			else if (*build == 'c')
X				key += 20;
X#   ifndef NO_ALT_FKEY
X			else if (*build == 'a')
X				key += 30;
X#   endif
X#  endif
X# endif
X			if (FKEY[key])
X				mapkey(FKEY[key], mapto, bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, fnames[key]);
X			else
X				msg("This terminal has no %s key", fnames[key]);
X		}
X		else
X#endif
X#ifndef NO_ABBR
X		if (cmd == CMD_ABBR || cmd == CMD_UNABBR)
X		{
X			mapkey(extra, mapto, bang ? WHEN_EX|WHEN_VIINP|WHEN_VIREP : WHEN_VIINP|WHEN_VIREP, "abbr");
X		}
X		else
X#endif
X		{
X			mapkey(extra, mapto, bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, (char *)0);
X		}
X	}
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_set(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	if (!*extra)
X	{
X		dumpopts(FALSE);/* "FALSE" means "don't dump all" - only set */
X	}
X	else if (!strcmp(extra, "all"))
X	{
X		dumpopts(TRUE);	/* "TRUE" means "dump all" - even unset vars */
X	}
X	else
X	{
X		setopts(extra);
X
X		/* That option may have affected the appearence of text */
X		changes++;
X	}
X}
X
X/*ARGSUSED*/
Xvoid cmd_tag(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	int	fd;	/* file descriptor used to read the file */
X	char	*scan;	/* used to scan through the tmpblk.c */
X#ifdef INTERNAL_TAGS
X	char	*cmp;	/* char of tag name we're comparing, or NULL */
X	char	*end;	/* marks the end of chars in tmpblk.c */
X#else
X	int	i;
X#endif
X#ifndef NO_MAGIC
X	char	wasmagic; /* preserves the original state of o_magic */
X#endif
X	static char prevtag[30];
X
X	/* if no tag is given, use the previous tag */
X	if (!extra || !*extra)
X	{
X		if (!*prevtag)
X		{
X			msg("No previous tag");
X			return;
X		}
X		extra = prevtag;
X	}
X	else
X	{
X		strncpy(prevtag, extra, sizeof prevtag);
X		prevtag[sizeof prevtag - 1] = '\0';
X	}
X
X#ifndef INTERNAL_TAGS
X	/* use "ref" to look up the tag info for this tag */
X	sprintf(tmpblk.c, "ref -t %s%s %s", (*origname ? "-f" : ""),origname, prevtag);
X	fd = rpipe(tmpblk.c, 0);
X	if (fd < 0)
X	{
X		msg("Can't run \"%s\"", tmpblk.c);
X		return;
X	}
X
X	/* try to read the tag info */
X	for (scan = tmpblk.c;
X	     (i = tread(fd, scan, scan - tmpblk.c + BLKSIZE)) > 0;
X	     scan += i)
X	{
X	}
X	*scan = '\0';
X
X	/* close the pipe.  abort if error */
X	if (rpclose(fd) != 0 || scan < tmpblk.c + 3)
X	{
X		msg("tag \"%s\" not found", extra);
X		return;
X	}
X
X#else /* use internal code to look up the tag */
X	/* open the tags file */
X	fd = open(TAGS, O_RDONLY);
X	if (fd < 0)
X	{
X		msg("No tags file");
X		return;
X	}
X
X	/* Hmmm... this would have been a lot easier with <stdio.h> */
X
X	/* find the line with our tag in it */
X	for(scan = end = tmpblk.c, cmp = extra; ; scan++)
X	{
X		/* read a block, if necessary */
X		if (scan >= end)
X		{
X			end = tmpblk.c + tread(fd, tmpblk.c, BLKSIZE);
X			scan = tmpblk.c;
X			if (scan >= end)
X			{
X				msg("tag \"%s\" not found", extra);
X				close(fd);
X				return;
X			}
X		}
X
X		/* if we're comparing, compare... */
X		if (cmp)
X		{
X			/* matched??? wow! */
X			if (!*cmp && *scan == '\t')
X			{
X				break;
X			}
X			if (*cmp++ != *scan)
X			{
X				/* failed! skip to newline */
X				cmp = (char *)0;
X			}
X		}
X
X		/* if we're skipping to newline, do it fast! */
X		if (!cmp)
X		{
X			while (scan < end && *scan != '\n')
X			{
X				scan++;
X			}
X			if (scan < end)
X			{
X				cmp = extra;
X			}
X		}
X	}
X
X	/* found it! get the rest of the line into memory */
X	for (cmp = tmpblk.c, scan++; scan < end && *scan != '\n'; )
X	{
X		*cmp++ = *scan++;
X	}
X	if (scan == end)
X	{
X		tread(fd, cmp, BLKSIZE - (int)(cmp - tmpblk.c));
X	}
X	else
X		*cmp = *scan;
X
X	/* we can close the tags file now */
X	close(fd);
X#endif /* INTERNAL_TAGS */
X
X	/* extract the filename from the line, and edit the file */
X	for (scan = tmpblk.c; *scan != '\t'; scan++)
X	{
X	}
X	*scan++ = '\0';
X	if (strcmp(origname, tmpblk.c) != 0)
X	{
X		if (!tmpabort(bang))
X		{
X			msg("Use :tag! to abort changes, or :w to save changes");
X			return;
X		}
X		tmpstart(tmpblk.c);
X	}
X
X	/* move to the desired line (or to line 1 if that fails) */
X#ifndef NO_MAGIC
X	wasmagic = *o_magic;
X	*o_magic = FALSE;
X#endif
X	cursor = MARK_FIRST;
X	linespec(scan, &cursor);
X	if (cursor == MARK_UNSET)
X	{
X		cursor = MARK_FIRST;
X		msg("Tag's address is out of date");
X	}
X#ifndef NO_MAGIC
X	*o_magic = wasmagic;
X#endif
X}
X
X
X
X
X
X/* describe this version of the program */
X/*ARGSUSED*/
Xvoid cmd_version(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	msg("%s", VERSION);
X#ifdef CREDIT
X	msg("%s", CREDIT);
X#endif
X#ifdef CREDIT2
X	msg("%s", CREDIT2);
X#endif
X#ifdef COMPILED_BY
X	msg("Compiled by %s", COMPILED_BY);
X#endif
X#ifdef COPYING
X	msg("%s", COPYING);
X#endif
X}
X
X
X#ifndef NO_MKEXRC
X/* make a .exrc file which describes the current configuration */
X/*ARGSUSED*/
Xvoid cmd_mkexrc(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	int	fd;
X
X	/* the default name for the .exrc file EXRC */
X	if (!*extra)
X	{
X		extra = EXRC;
X	}
X
X	/* create the .exrc file */
X	fd = creat(extra, FILEPERMS);
X	if (fd < 0)
X	{
X		msg("Couldn't create a new \"%s\" file", extra);
X		return;
X	}
X
X	/* save stuff */
X	saveopts(fd);
X	savemaps(fd, FALSE);
X#ifndef NO_ABBR
X	savemaps(fd, TRUE);
X#endif
X#ifndef NO_DIGRAPH
X	savedigs(fd);
X#endif
X#ifndef NO_COLOR
X	savecolor(fd);
X#endif
X
X	/* close the file */
X	close(fd);
X	msg("Configuration saved");
X}
X#endif
X
X#ifndef NO_DIGRAPH
X/*ARGSUSED*/
Xvoid cmd_digraph(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	do_digraph(bang, extra);
X}
X#endif
X
X
X#ifndef NO_ERRLIST 
Xstatic char	errfile[256];	/* the name of a file containing an error */
Xstatic long	errline;	/* the line number for an error */
Xstatic int	errfd = -2;	/* fd of the errlist file */
X
X/* This static function tries to parse an error message.
X *
X * For most compilers, the first word is taken to be the name of the erroneous
X * file, and the first number after that is taken to be the line number where
X * the error was detected.  The description of the error follows, possibly
X * preceded by an "error ... :" or "warning ... :" label which is skipped.
X *
X * For Coherent, error messages look like "line#: filename: message".
X *
X * For non-error lines, or unparsable error lines, this function returns NULL.
X * Normally, though, it alters errfile and errline, and returns a pointer to
X * the description.
X */
Xstatic char *parse_errmsg(text)
X	REG char	*text;
X{
X	REG char	*cpy;
X	long		atol();
X# if COHERENT || TOS /* any Mark Williams compiler */
X	/* Get the line number.  If no line number, then ignore this line. */
X	errline = atol(text);
X	if (errline == 0L)
X		return (char *)0;
X
X	/* Skip to the start of the filename */
X	while (*text && *text++ != ':')
X	{
X	}
X	if (!*text++)
X		return (char *)0;
X
X	/* copy the filename to errfile */
X	for (cpy = errfile; *text && (*cpy++ = *text++) != ':'; )
X	{
X	}
X	if (!*text++)
X		return (char *)0;
X	cpy[-1] = '\0';
X
X	return text;
X# else /* not a Mark Williams compiler */
X	char		*errmsg;
X
X	/* the error message is the whole line, by default */
X	errmsg = text;
X
X	/* skip leading garbage */
X	while (*text && !isalnum(*text))
X	{
X		text++;
X	}
X
X	/* copy over the filename */
X	cpy = errfile;
X	while(isalnum(*text) || *text == '.')
X	{
X		*cpy++ = *text++;
X	}
X	*cpy = '\0';
X
X	/* ignore the name "Error" and filenames that contain a '/' */
X	if (*text == '/' || !*errfile || !strcmp(errfile + 1, "rror") || access(errfile, 0) < 0)
X	{
X		return (char *)0;
X	}
X
X	/* skip garbage between filename and line number */
X	while (*text && !isdigit(*text))
X	{
X		text++;
X	}
X
X	/* if the number is part of a larger word, then ignore this line */
X	if (*text && isalpha(text[-1]))
X	{
X		return (char *)0;
X	}
X
X	/* get the error line */
X	errline = 0L;
X	while (isdigit(*text))
X	{
X		errline *= 10;
X		errline += (*text - '0');
X		text++;
X	}
X
X	/* any line which lacks a filename or line number should be ignored */
X	if (!errfile[0] || !errline)
X	{
X		return (char *)0;
X	}
X
X	/* locate the beginning of the error description */
X	while (*text && !isspace(*text))
X	{
X		text++;
X	}
X	while (*text)
X	{
X#  ifndef CRUNCH
X		/* skip "error #:" and "warning #:" clauses */
X		if (!strncmp(text + 1, "rror ", 5)
X		 || !strncmp(text + 1, "arning ", 7)
X		 || !strncmp(text + 1, "atal error", 10))
X		{
X			do
X			{
X				text++;
X			} while (*text && *text != ':');
X			continue;
X		}
X#  endif
X
X		/* anything other than whitespace or a colon is important */
X		if (!isspace(*text) && *text != ':')
X		{
X			errmsg = text;
X			break;
X		}
X
X		/* else keep looking... */
X		text++;
X	}
X
X	return errmsg;
X# endif /* not COHERENT */
X}
X
X/*ARGSUSED*/
Xvoid cmd_errlist(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	static long	endline;/* original number of lines in this file */
X	static long	offset;	/* offset of the next line in the errlist file */
X	int		i;
X	char		*errmsg;
X
X	/* if a new errlist file is named, open it */
X	if (extra && extra[0])
X	{
X		/* close the old one */
X		if (errfd >= 0)
X		{
X			close(errfd);
X		}
X
X		/* open the new one */
X		errfd = open(extra, O_RDONLY);
X		offset = 0L;
X		endline = nlines;
X	}
X	else if (errfd < 0)
X	{
X		/* open the default file */
X		errfd = open(ERRLIST, O_RDONLY);
X		offset = 0L;
X		endline = nlines;
X	}
X
X	/* do we have an errlist file now? */
X	if (errfd < 0)
X	{
X		msg("There is no errlist file");
X		beep();
X		return;
X	}
X
X	/* find the next error message in the file */
X	do
X	{
X		/* read the next line from the errlist */
X		lseek(errfd, offset, 0);
X		if (tread(errfd, tmpblk.c, (unsigned)BLKSIZE) <= 0)
X		{
X			msg("No more errors");
X			beep();
X			close(errfd);
X			errfd = -2;
X			return;
X		}
X		for (i = 0; tmpblk.c[i] != '\n'; i++)
X		{
X		}
X		tmpblk.c[i++] = 0;
X
X		/* look for an error message in the line */
X		errmsg = parse_errmsg(tmpblk.c);
X		if (!errmsg)
X		{
X			offset += i;
X		}
X
X	} while (!errmsg);
X
X	/* switch to the file containing the error, if this isn't it */
X	if (strcmp(origname, errfile))
X	{
X		if (!tmpabort(bang))
X		{
X			msg("Use :er! to abort changes, or :w to save changes");
X			beep();
X			return;
X		}
X		tmpstart(errfile);
X		endline = nlines;
X	}
X	else if (endline == 0L)
X	{
X		endline = nlines;
X	}
X
X	/* go to the line where the error was detected */
X	cursor = MARK_AT_LINE(errline + (nlines - endline));
X	if (cursor > MARK_LAST)
X	{
X		cursor = MARK_LAST;
X	}
X	if (mode == MODE_VI)
X	{
X		redraw(cursor, FALSE);
X	}
X
X	/* display the error message */
X#ifdef CRUNCH
X	msg("%.70s", errmsg);
X#else
X	if (nlines > endline)
X	{
X		msg("line %ld(+%ld): %.60s", errline, nlines - endline, errmsg);
X	}
X	else if (nlines < endline)
X	{
X		msg("line %ld(-%ld): %.60s", errline, endline - nlines, errmsg);
X	}
X	else
X	{
X		msg("line %ld: %.65s", errline, errmsg);
X	}
X#endif
X
X	/* remember where the NEXT error line will start */
X	offset += i;
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_make(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	BLK	buf;
X
X	/* if the file hasn't been saved, then complain unless ! */
X	if (tstflag(file, MODIFIED) && !bang)
X	{
X		msg("\"%s\" not saved yet", origname);
X		return;
X	}
X
X	/* build the command */
X	sprintf(buf.c, "%s %s %s%s", (cmd == CMD_CC ? o_cc : o_make), extra, REDIRECT, ERRLIST);
X	qaddstr(buf.c);
X	addch('\n');
X
X	/* close the old errlist file, if any */
X	if (errfd >= 0)
X	{
X		close(errfd);
X		errfd = -3;
X	}
X
X	/* run the command, with curses temporarily disabled */
X	suspend_curses();
X	system(buf.c);
X	resume_curses(mode == MODE_EX);
X	if (mode == MODE_COLON)
X		mode = MODE_VI;
X
X	/* run the "errlist" command */
X	cmd_errlist(MARK_UNSET, MARK_UNSET, cmd, bang, ERRLIST);
X}
X#endif
X
X
X
X#ifndef NO_COLOR
X
X/* figure out the number of text colors we use with this configuration */
X# ifndef NO_POPUP
X#  ifndef NO_VISIBLE
X#   define NCOLORS 7
X#  else
X#   define NCOLORS 6
X#  endif
X# else
X#  ifndef NO_VISIBLE
X#   define NCOLORS 6
X#  else
X#   define NCOLORS 5
X#  endif
X# endif
X
X/* the attribute bytes used in each of "when"s */
Xstatic char bytes[NCOLORS];
X
Xstatic struct
X{
X	char	*word;	/* a legal word */
X	int	type;	/* what type of word this is */
X	int	val;	/* some other value */
X}
X	words[] =
X{
X	{"normal",	1,	A_NORMAL},	/* all "when" names must come */
X	{"standout",	1,	A_STANDOUT},	/* at the top of the list.    */
X	{"bold",	1,	A_BOLD},	/* The first 3 must be normal,*/
X	{"underlined",	1,	A_UNDERLINE},	/* standout, and bold; the    */
X	{"italics",	1,	A_ALTCHARSET},	/* remaining names follow.    */
X#ifndef NO_POPUP
X	{"popup",	1,	A_POPUP},
X#endif
X#ifndef NO_VISIBLE
X	{"visible",	1,	A_VISIBLE},
X#endif
X
X	{"black",	3,	0x00},		/* The color names start right*/
X	{"blue",	3,	0x01},		/* after the "when" names.    */
X	{"green",	3,	0x02},
X	{"cyan",	3,	0x03},
X	{"red",		3,	0x04},
X	{"magenta",	3,	0x05},
X	{"brown",	3,	0x06},
X	{"white",	3,	0x07},
X	{"yellow",	3,	0x0E}, /* bright brown */
X	{"gray",	3,	0x08}, /* bright black?  of course! */
X	{"grey",	3,	0x08},
X
X	{"bright",	2,	0x08},
X	{"light",	2,	0x08},
X	{"blinking",	2,	0x80},
X	{"on",		0,	0},
X	{"n",		1,	A_NORMAL},
X	{"s",		1,	A_STANDOUT},
X	{"b",		1,	A_BOLD},
X	{"u",		1,	A_UNDERLINE},
X	{"i",		1,	A_ALTCHARSET},
X#ifndef NO_POPUP
X	{"p",		1,	A_POPUP},
X	{"menu",	1,	A_POPUP},
X#endif
X#ifndef NO_VISIBLE
X	{"v",		1,	A_VISIBLE},
X#endif
X	{(char *)0,	0,	0}
X};
X
X/*ARGSUSED*/
Xvoid cmd_color(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	int	attrbyte;
X	int	cmode;
X	int	nowbg;	/* BOOLEAN: is the next color background? */
X
X	REG char *scan;
X	REG	i;
X
X
X#ifndef CRUNCH
X	/* if no args are given, then report the current colors */
X	if (!*extra)
X	{
X		/* if no colors are set, then say so */
X		if (!bytes[0])
X		{
X			msg("no colors have been set");
X			return;
X		}
X
X		/* report all five color combinations */
X		for (i = 0; i < NCOLORS; i++)
X		{
X			qaddstr("color ");
X			qaddstr(words[i].word);
X			qaddch(' ');
X			if (bytes[i] & 0x80)
X				qaddstr("blinking ");
X			switch (bytes[i] & 0xf)
X			{
X			  case 0x08:	qaddstr("gray");	break;
X			  case 0x0e:	qaddstr("yellow");	break;
X			  case 0x0f:	qaddstr("bright white");break;
X			  default:
X				if (bytes[i] & 0x08)
X					qaddstr("light ");
X				qaddstr(words[(bytes[i] & 0x07) + NCOLORS].word);
X			}
X			qaddstr(" on ");
X			qaddstr(words[((bytes[i] >> 4) & 0x07) + NCOLORS].word);
X			addch('\n');
X			exrefresh();
X		}
X		return;
X	}
X#endif
X
X	/* The default background color is the same as "normal" chars.
X	 * There is no default foreground color.
X	 */
X	cmode = A_NORMAL;
X	attrbyte = bytes[0] & 0x70;
X	nowbg = FALSE;
X
X	/* parse each word in the "extra" text */
X	for (scan = extra; *extra; extra = scan)
X	{
X		/* locate the end of the word */
X		while (*scan && *scan != ' ')
X		{
X			scan++;
X		}
X
X		/* skip whitespace at the end of the word */
X		while(*scan == ' ')
X		{
X			*scan++ = '\0';
X		}
X
X		/* lookup the word */
X		for (i = 0; words[i].word && strcmp(words[i].word, extra); i++)
X		{
X		}
X
X		/* if not a word, then complain */
X		if (!words[i].word)
X		{
X			msg("Invalid color name: %s", extra);
X			return;
X		}
X
X		/* process the word */
X		switch (words[i].type)
X		{
X		  case 1:
X			cmode = words[i].val;
X			break;
X
X		  case 2:
X			attrbyte |= words[i].val;
X			break;
X
X		  case 3:
X			if (nowbg)
X				attrbyte = ((attrbyte & ~0x70) | ((words[i].val & 0x07) << 4));
X			else
X				attrbyte |= words[i].val;
X			nowbg = TRUE;
X			break;
X		}
X	}
X
X	/* if nowbg isn't set now, then we were never given a foreground color */
X	if (!nowbg)
X	{
X		msg("usage: color [when] [\"bright\"] [\"blinking\"] foreground [background]");
X		return;
X	}
X
X	/* the first ":color" command MUST define the "normal" colors */
X	if (!bytes[0])
X		cmode = A_NORMAL;
X
X	/* we should now have a cmode and an attribute byte... */
X
X	/* set the color */
X	setcolor(cmode, attrbyte);
X
X	/* remember what we just did */
X	bytes[cmode] = attrbyte;
X
X	/* if the other colors haven't been set yet, then set them to defaults */
X	if (!bytes[1])
X	{
X		/* standout is the opposite of normal */
X		bytes[1] = ((attrbyte << 4) & 0x70 | (attrbyte >> 4) & 0x07);
X		setcolor(A_STANDOUT, bytes[1]);
X
X		/* if "normal" isn't bright, then bold defaults to normal+bright
X		 * else bold defaults to bright white.
X		 */
X		bytes[2] = attrbyte | ((attrbyte & 0x08) ? 0x0f : 0x08);
X		setcolor(A_BOLD, bytes[2]);
X
X		/* all others default to the "standout" colors, without blinking */
X		for (i = 3; i < NCOLORS; i++)
X		{
X			bytes[i] = (bytes[1] & 0x7f);
X			setcolor(words[i].val, bytes[i]);
X		}
X	}
X
X	/* force a redraw, so we see the new colors */
X	redraw(MARK_UNSET, FALSE);
X}
X
X
X
Xvoid savecolor(fd)
X	int	fd;	/* file descriptor to write colors to */
X{
X	int	i;
X	char	buf[80];
X
X	/* if no colors are set, then return */
X	if (!bytes[0])
X	{
X		return;
X	}
X
X	/* save all five color combinations */
X	for (i = 0; i < NCOLORS; i++)
X	{
X		strcpy(buf, "color ");
X		strcat(buf, words[i].word);
X		strcat(buf, " ");
X		if (bytes[i] & 0x80)
X			strcat(buf, "blinking ");
X		switch (bytes[i] & 0xf)
X		{
X		  case 0x08:	strcat(buf, "gray");	break;
X		  case 0x0e:	strcat(buf, "yellow");	break;
X		  case 0x0f:	strcat(buf, "bright white");break;
X		  default:
X			if (bytes[i] & 0x08)
X				strcat(buf, "light ");
X			strcat(buf, words[(bytes[i] & 0x07) + NCOLORS].word);
X		}
X		strcat(buf, " on ");
X		strcat(buf, words[((bytes[i] >> 4) & 0x07) + NCOLORS].word);
X		strcat(buf, "\n");
X		twrite(fd, buf, (unsigned)strlen(buf));
X	}
X}
X#endif
X
X#ifdef SIGTSTP
X/* temporarily suspend elvis */
X/*ARGSUSED*/
Xvoid cmd_suspend(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	void	(*func)();	/* stores the previous setting of SIGTSTP */
X
X#if ANY_UNIX
X	/* the Bourne shell can't handle ^Z */
X	if (!strcmp(o_shell, "/bin/sh"))
X	{
X		msg("The /bin/sh shell doesn't support ^Z");
X		return;
X	}
X#endif
X
X	move(LINES - 1, 0);
X	if (tstflag(file, MODIFIED))
X	{
X		addstr("Warning: \"");
X		addstr(origname);
X		addstr("\" modified but not yet saved");
X		clrtoeol();
X	}
X	refresh();
X	suspend_curses();
X	func = signal(SIGTSTP, SIG_DFL);
X	kill (0, SIGTSTP);
X
X	/* the process stops and resumes here */
X
X	signal(SIGTSTP, func);
X	resume_curses(TRUE);
X	if (mode == MODE_VI || mode == MODE_COLON)
X		redraw(MARK_UNSET, FALSE);
X	else
X		refresh ();
X}
X#endif
/
echo x - cmd2.c
sed '/^X/s///' > cmd2.c << '/'
X/* cmd2.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains some of the commands - mostly ones that change text */
X
X#include "config.h"
X#include "ctype.h"
X#include "vi.h"
X#include "regexp.h"
X#if TOS
X# include <stat.h>
X#else
X# if OSK
X#  include "osk.h"
X# else
X#  if AMIGA
X#   include "amistat.h"
X#  else
X#   include <sys/stat.h>
X#  endif
X# endif
X#endif
X
X
X/*ARGSUSED*/
Xvoid cmd_substitute(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;	/* rest of the command line */
X{
X	char	*line;	/* a line from the file */
X	regexp	*re;	/* the compiled search expression */
X	char	*subst;	/* the substitution string */
X	char	*opt;	/* substitution options */
X	long	l;	/* a line number */
X	char	*s, *d;	/* used during subtitutions */
X	char	*conf;	/* used during confirmation */
X	long	chline;	/* # of lines changed */
X	long	chsub;	/* # of substitutions made */
X	static	optp;	/* boolean option: print when done? */
X	static	optg;	/* boolean option: substitute globally in line? */
X	static	optc;	/* boolean option: confirm before subst? */
X#ifndef CRUNCH
X	long	oldnlines;
X#endif
X
X
X	/* for now, assume this will fail */
X	rptlines = -1L;
X
X	if (cmd == CMD_SUBAGAIN)
X	{
X#ifndef NO_MAGIC
X		if (*o_magic)
X			subst = "~";
X		else
X#endif
X		subst = "\\~";
X		re = regcomp("");
X
X		/* if visual "&", then turn off the "p" and "c" options */
X		if (bang)
X		{
X			optp = optc = FALSE;
X		}
X	}
X	else /* CMD_SUBSTITUTE */
X	{
X		/* make sure we got a search pattern */
X		if (*extra != '/' && *extra != '?')
X		{
X			msg("Usage: s/regular expression/new text/");
X			return;
X		}
X
X		/* parse & compile the search pattern */
X		subst = parseptrn(extra);
X		re = regcomp(extra + 1);
X	}
X
X	/* abort if RE error -- error message already given by regcomp() */
X	if (!re)
X	{
X		return;
X	}
X
X	if (cmd == CMD_SUBSTITUTE)
X	{
X		/* parse the substitution string & find the option string */
X		for (opt = subst; *opt && *opt != *extra; opt++)
X		{
X			if (*opt == '\\' && opt[1])
X			{
X				opt++;
X			}
X		}
X		if (*opt)
X		{
X			*opt++ = '\0';
X		}
X
X		/* analyse the option string */
X		if (!*o_edcompatible)
X		{
X			optp = optg = optc = FALSE;
X		}
X		while (*opt)
X		{
X			switch (*opt++)
X			{
X			  case 'p':	optp = !optp;	break;
X			  case 'g':	optg = !optg;	break;
X			  case 'c':	optc = !optc;	break;
X			  case ' ':
X			  case '\t':			break;
X			  default:
X				msg("Subst options are p, c, and g -- not %c", opt[-1]);
X				return;
X			}
X		}
X	}
X
X	/* if "c" or "p" flag was given, and we're in visual mode, then NEWLINE */
X	if ((optc || optp) && mode == MODE_VI)
X	{
X		addch('\n');
X		exrefresh();
X	}
X
X	ChangeText
X	{
X		/* reset the change counters */
X		chline = chsub = 0L;
X
X		/* for each selected line */
X		for (l = markline(frommark); l <= markline(tomark); l++)
X		{
X			/* fetch the line */
X			line = fetchline(l);
X
X			/* if it contains the search pattern... */
X			if (regexec(re, line, TRUE))
X			{
X				/* increment the line change counter */
X				chline++;
X
X				/* initialize the pointers */
X				s = line;
X				d = tmpblk.c;
X
X				/* do once or globally ... */
X				do
X				{
X#ifndef CRUNCH
X					/* confirm, if necessary */
X					if (optc)
X					{
X						for (conf = line; conf < re->startp[0]; conf++)
X							addch(*conf);
X						standout();
X						for ( ; conf < re->endp[0]; conf++)
X							addch(*conf);
X						standend();
X						for (; *conf; conf++)
X							addch(*conf);
X						addch('\n');
X						exrefresh();
X						if (getkey(0) != 'y')
X						{
X							/* copy accross the original chars */
X							while (s < re->endp[0])
X								*d++ = *s++;
X
X							/* skip to next match on this line, if any */
X							goto Continue;
X						}
X					}
X#endif /* not CRUNCH */
X
X					/* increment the substitution change counter */
X					chsub++;
X
X					/* copy stuff from before the match */
X					while (s < re->startp[0])
X					{
X						*d++ = *s++;
X					}
X
X					/* substitute for the matched part */
X					regsub(re, subst, d);
X					s = re->endp[0];
X					d += strlen(d);
X
XContinue:
X					/* if this regexp could conceivably match
X					 * a zero-length string, then require at
X					 * least 1 unmatched character between
X					 * matches.
X					 */
X					if (re->minlen == 0)
X					{
X						if (!*s)
X							break;
X						*d++ = *s++;
X					}
X
X				} while (optg && regexec(re, s, FALSE));
X
X				/* copy stuff from after the match */
X				while (*d++ = *s++)	/* yes, ASSIGNMENT! */
X				{
X				}
X
X#ifndef CRUNCH
X				/* NOTE: since the substitution text is allowed to have ^Ms which are
X				 * translated into newlines, it is possible that the number of lines
X				 * in the file will increase after each line has been substituted.
X				 * we need to adjust for this.
X				 */
X				oldnlines = nlines;
X#endif
X
X				/* replace the old version of the line with the new */
X				d[-1] = '\n';
X				d[0] = '\0';
X				change(MARK_AT_LINE(l), MARK_AT_LINE(l + 1), tmpblk.c);
X
X#ifndef CRUNCH
X				l += nlines - oldnlines;
X				tomark += MARK_AT_LINE(nlines - oldnlines);
X#endif
X
X				/* if supposed to print it, do so */
X				if (optp)
X				{
X					addstr(tmpblk.c);
X					exrefresh();
X				}
X
X				/* move the cursor to that line */
X				cursor = MARK_AT_LINE(l);
X			}
X		}
X	}
X
X	/* free the regexp */
X	free(re);
X
X	/* if done from within a ":g" command, then finish silently */
X	if (doingglobal)
X	{
X		rptlines = chline;
X		rptlabel = "changed";
X		return;
X	}
X
X	/* Reporting */
X	if (chsub == 0)
X	{
X		msg("Substitution failed");
X	}
X	else if (chline >= *o_report)
X	{
X		msg("%ld substitutions on %ld lines", chsub, chline);
X	}
X	rptlines = 0L;
X}
X
X
X
X
X/*ARGSUSED*/
Xvoid cmd_delete(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	MARK	curs2;	/* an altered form of the cursor */
X
X	/* choose your cut buffer */
X	if (*extra == '"')
X	{
X		extra++;
X	}
X	if (*extra)
X	{
X		cutname(*extra);
X	}
X
X	/* make sure we're talking about whole lines here */
X	frommark = frommark & ~(BLKSIZE - 1);
X	tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
X
X	/* yank the lines */
X	cut(frommark, tomark);
X
X	/* if CMD_DELETE then delete the lines */
X	if (cmd != CMD_YANK)
X	{
X		curs2 = cursor;
X		ChangeText
X		{
X			/* delete the lines */
X			delete(frommark, tomark);
X		}
X		if (curs2 > tomark)
X		{
X			cursor = curs2 - tomark + frommark;
X		}
X		else if (curs2 > frommark)
X		{
X			cursor = frommark;
X		}
X	}
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_append(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	long	l;	/* line counter */
X
X#ifndef CRUNCH
X	/* if '!' then toggle auto-indent */
X	if (bang)
X	{
X		*o_autoindent = !*o_autoindent;
X	}
X#endif
X
X	ChangeText
X	{
X		/* if we're doing a change, delete the old version */
X		if (cmd == CMD_CHANGE)
X		{
X			/* delete 'em */
X			cmd_delete(frommark, tomark, cmd, bang, extra);
X		}
X
X		/* new lines start at the frommark line, or after it */
X		l = markline(frommark);
X		if (cmd == CMD_APPEND)
X		{
X 			l++;
X		}
X
X		/* get lines until no more lines, or "." line, and insert them */
X		while (vgets('\0', tmpblk.c, BLKSIZE) >= 0)
X		{
X			addch('\n');
X			if (!strcmp(tmpblk.c, "."))
X			{
X				break;
X			}
X
X			strcat(tmpblk.c, "\n");
X			add(MARK_AT_LINE(l), tmpblk.c);
X			l++;
X		}
X	}
X
X	/* on the odd chance that we're calling this from vi mode ... */
X	redraw(MARK_UNSET, FALSE);
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_put(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	/* choose your cut buffer */
X	if (*extra == '"')
X	{
X		extra++;
X	}
X	if (*extra)
X	{
X		cutname(*extra);
X	}
X
X	/* paste it */
X	ChangeText
X	{
X		cursor = paste(frommark, TRUE, FALSE);
X	}
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_join(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	long	l;
X	char	*scan;
X	int	len;	/* length of the new line */
X
X	/* if only one line is specified, assume the following one joins too */
X	if (markline(frommark) == nlines)
X	{
X		msg("Nothing to join with this line");
X		return;
X	}
X	if (markline(frommark) == markline(tomark))
X	{
X		tomark += BLKSIZE;
X	}
X
X	/* get the first line */
X	l = markline(frommark);
X	strcpy(tmpblk.c, fetchline(l));
X	len = strlen(tmpblk.c);
X
X	/* build the longer line */
X	while (++l <= markline(tomark))
X	{
X		/* get the next line */
X		scan = fetchline(l);
X
X		/* remove any leading whitespace */
X		while (*scan == '\t' || *scan == ' ')
X		{
X			scan++;
X		}
X
X		/* see if the line will fit */
X		if (strlen(scan) + len + 3 > BLKSIZE)
X		{
X			msg("Can't join -- the resulting line would be too long");
X			return;
X		}
X
X		/* catenate it, with a space (or two) in between */
X		if (!bang)
X		{
X			if (len >= 1)
X			{
X				if (tmpblk.c[len - 1] == '.'
X				 || tmpblk.c[len - 1] == '?'
X				 || tmpblk.c[len - 1] == '!')
X				{
X					 tmpblk.c[len++] = ' ';
X				}
X				tmpblk.c[len++] = ' ';
X			}
X		}
X		strcpy(tmpblk.c + len, scan);
X		len += strlen(scan);
X	}
X	tmpblk.c[len++] = '\n';
X	tmpblk.c[len] = '\0';
X
X	/* make the change */
X	ChangeText
X	{
X		frommark &= ~(BLKSIZE - 1);
X		tomark &= ~(BLKSIZE - 1);
X		tomark += BLKSIZE;
X		change(frommark, tomark, tmpblk.c);
X	}
X
X	/* Reporting... */
X	rptlines = markline(tomark) - markline(frommark) - 1L;
X	rptlabel = "joined";
X}
X
X
X
X/*ARGSUSED*/
Xvoid cmd_shift(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	long	l;	/* line number counter */
X	int	oldidx;	/* number of chars previously used for indent */
X	int	newidx;	/* number of chars in the new indent string */
X	int	oldcol;	/* previous indent amount */
X	int	newcol;	/* new indent amount */
X	char	*text;	/* pointer to the old line's text */
X
X	ChangeText
X	{
X		/* for each line to shift... */
X		for (l = markline(frommark); l <= markline(tomark); l++)
X		{
X			/* get the line - ignore empty lines unless ! mode */
X			text = fetchline(l);
X			if (!*text && !bang)
X				continue;
X
X			/* calc oldidx and oldcol */
X			for (oldidx = 0, oldcol = 0;
X			     text[oldidx] == ' ' || text[oldidx] == '\t';
X			     oldidx++)
X			{
X				if (text[oldidx] == ' ')
X				{
X					oldcol += 1;
X				}
X				else
X				{
X					oldcol += *o_tabstop - (oldcol % *o_tabstop);
X				}
X			}
X
X			/* calc newcol */
X			if (cmd == CMD_SHIFTR)
X			{
X				newcol = oldcol + (*o_shiftwidth & 0xff);
X			}
X			else
X			{
X				newcol = oldcol - (*o_shiftwidth & 0xff);
X				if (newcol < 0)
X					newcol = 0;
X			}
X
X			/* if no change, then skip to next line */
X			if (oldcol == newcol)
X				continue;
X
X			/* build a new indent string */
X			newidx = 0;
X			if (*o_autotab)
X			{
X				while (newcol >= *o_tabstop)
X				{
X					tmpblk.c[newidx++] = '\t';
X					newcol -= *o_tabstop;
X				}
X			}
X			while (newcol > 0)
X			{
X				tmpblk.c[newidx++] = ' ';
X				newcol--;
X			}
X			tmpblk.c[newidx] = '\0';
X
X			/* change the old indent string into the new */
X			change(MARK_AT_LINE(l), MARK_AT_LINE(l) + oldidx, tmpblk.c);
X		}
X	}
X
X	/* Reporting... */
X	rptlines = markline(tomark) - markline(frommark) + 1L;
X	if (cmd == CMD_SHIFTR)
X	{
X		rptlabel = ">ed";
X	}
X	else
X	{
X		rptlabel = "<ed";
X	}
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_read(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	int	fd, rc;	/* used while reading from the file */
X	char	*scan;	/* used for finding NUL characters */
X	int	hadnul;	/* boolean: any NULs found? */
X	int	addnl;	/* boolean: forced to add newlines? */
X	int	len;	/* number of chars in current line */
X	long	lines;	/* number of lines in current block */
X	struct stat statb;
X
X	/* special case: if ":r !cmd" then let the filter() function do it */
X	if (extra[0] == '!')
X	{
X		filter(frommark, MARK_UNSET, extra + 1, TRUE);
X		return;
X	}
X
X	/* open the file */
X	fd = open(extra, O_RDONLY);
X	if (fd < 0)
X	{
X		msg("Can't open \"%s\"", extra);
X		return;
X	}
X
X#ifndef CRUNCH
X	if (stat(extra, &statb) < 0)
X	{
X		msg("Can't stat \"%s\"", extra);
X	}
X# if TOS
X	if (statb.st_mode & S_IJDIR)
X# else
X#  if OSK
X	if (statb.st_mode & S_IFDIR)
X#  else
X	if ((statb.st_mode & S_IFMT) != S_IFREG)
X#  endif
X# endif
X	{
X		msg("\"%s\" is not a regular file", extra);
X		return;
X	}
X#endif /* not CRUNCH */
X
X	/* get blocks from the file, and add them */
X	ChangeText
X	{
X		/* insertion starts at the line following frommark */
X		tomark = frommark = (frommark | (BLKSIZE - 1L)) + 1L;
X		len = 0;
X		hadnul = addnl = FALSE;
X
X		/* add an extra newline, so partial lines at the end of
X		 * the file don't trip us up
X		 */
X		add(tomark, "\n");
X
X		/* for each chunk of text... */
X		while ((rc = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
X		{
X			/* count newlines, convert NULs, etc. ... */
X			for (lines = 0, scan = tmpblk.c; rc > 0; rc--, scan++)
X			{
X				/* break up long lines */
X				if (*scan != '\n' && len + 2 > BLKSIZE)
X				{
X					*scan = '\n';
X					addnl = TRUE;
X				}
X
X				/* protect against NUL chars in file */
X				if (!*scan)
X				{
X					*scan = 0x80;
X					hadnul = TRUE;
X				}
X
X				/* starting a new line? */
X				if (*scan == '\n')
X				{
X					/* reset length at newline */
X					len = 0;
X					lines++;
X				}
X				else
X				{
X					len++;
X				}
X			}
X
X			/* add the text */
X			*scan = '\0';
X			add(tomark, tmpblk.c);
X			tomark += MARK_AT_LINE(lines) + len - markidx(tomark);
X		}
X
X		/* if partial last line, then retain that first newline */
X		if (len > 0)
X		{
X			msg("Last line had no newline");
X			tomark += BLKSIZE; /* <- for the rptlines calc */
X		}
X		else /* delete that first newline */
X		{
X			delete(tomark, (tomark | (BLKSIZE - 1L)) + 1L);
X		}
X	}
X
X	/* close the file */
X	close(fd);
X
X	/* Reporting... */
X	rptlines = markline(tomark) - markline(frommark);
X	rptlabel = "read";
X	if (mode == MODE_EX)
X	{
X		cursor = (tomark & ~BLKSIZE) - BLKSIZE;
X	}
X	else
X	{
X		cursor = frommark;
X	}
X
X	if (addnl)
X		msg("Newlines were added to break up long lines");
X	if (hadnul)
X		msg("NULs were converted to 0x80");
X}
X
X
X
X/*ARGSUSED*/
Xvoid cmd_undo(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	undo();
X}
X
X
X/* print the selected lines */
X/*ARGSUSED*/
Xvoid cmd_print(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	REG char	*scan;
X	REG long	l;
X	REG int		col;
X
X	for (l = markline(frommark); l <= markline(tomark); l++)
X	{
X		/* display a line number, if CMD_NUMBER */
X		if (cmd == CMD_NUMBER)
X		{
X			sprintf(tmpblk.c, "%6ld  ", l);
X			qaddstr(tmpblk.c);
X			col = 8;
X		}
X		else
X		{
X			col = 0;
X		}
X
X		/* get the next line & display it */
X		for (scan = fetchline(l); *scan; scan++)
X		{
X			/* expand tabs to the proper width */
X			if (*scan == '\t' && cmd != CMD_LIST)
X			{
X				do
X				{
X					qaddch(' ');
X					col++;
X				} while (col % *o_tabstop != 0);
X			}
X			else if (*scan > 0 && *scan < ' ' || *scan == '\177')
X			{
X				qaddch('^');
X				qaddch(*scan ^ 0x40);
X				col += 2;
X			}
X			else if ((*scan & 0x80) && cmd == CMD_LIST)
X			{
X				sprintf(tmpblk.c, "\\%03o", UCHAR(*scan));
X				qaddstr(tmpblk.c);
X				col += 4;
X			}
X			else
X			{
X				qaddch(*scan);
X				col++;
X			}
X
X			/* wrap at the edge of the screen */
X			if (!has_AM && col >= COLS)
X			{
X				addch('\n');
X				col -= COLS;
X			}
X		}
X		if (cmd == CMD_LIST)
X		{
X			qaddch('$');
X		}
X		addch('\n');
X		exrefresh();
X	}
X}
X
X
X/* move or copy selected lines */
X/*ARGSUSED*/
Xvoid cmd_move(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	MARK	destmark;
X
X	/* parse the destination linespec.  No defaults.  Line 0 is okay */
X	destmark = cursor;
X	if (!strcmp(extra, "0"))
X	{
X		destmark = 0L;
X	}
X	else if (linespec(extra, &destmark) == extra || !destmark)
X	{
X		msg("invalid destination address");
X		return;
X	}
X
X	/* flesh the marks out to encompass whole lines */
X	frommark &= ~(BLKSIZE - 1);
X	tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
X	destmark = (destmark & ~(BLKSIZE - 1)) + BLKSIZE;
X
X	/* make sure the destination is valid */
X	if (cmd == CMD_MOVE && destmark >= frommark && destmark < tomark)
X	{
X		msg("invalid destination address");
X	}
X
X	/* Do it */
X	ChangeText
X	{
X		/* save the text to a cut buffer */
X		cutname('\0');
X		cut(frommark, tomark);
X
X		/* if we're not copying, delete the old text & adjust destmark */
X		if (cmd != CMD_COPY)
X		{
X			delete(frommark, tomark);
X			if (destmark >= frommark)
X			{
X				destmark -= (tomark - frommark);
X			}
X		}
X
X		/* add the new text */
X		paste(destmark, FALSE, FALSE);
X	}
X
X	/* move the cursor to the last line of the moved text */
X	cursor = destmark + (tomark - frommark) - BLKSIZE;
X	if (cursor < MARK_FIRST || cursor >= MARK_LAST + BLKSIZE)
X	{
X		cursor = MARK_LAST;
X	}
X
X	/* Reporting... */
X	rptlabel = ( (cmd == CMD_COPY) ? "copied" : "moved" );
X}
X
X
X
X/* execute EX commands from a file */
X/*ARGSUSED*/
Xvoid cmd_source(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	/* must have a filename */
X	if (!*extra)
X	{
X		msg("\"source\" requires a filename");
X		return;
X	}
X
X	doexrc(extra);
X}
X
X
X#ifndef NO_AT
X/*ARGSUSED*/
Xvoid cmd_at(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	static	nest = FALSE;
X	int	result;
X	char	buf[MAXRCLEN];
X
X	/* don't allow nested macros */
X	if (nest)
X	{
X		msg("@ macros can't be nested");
X		return;
X	}
X	nest = TRUE;
X
X	/* require a buffer name */
X	if (*extra == '"')
X		extra++;
X	if (!*extra || !isascii(*extra) ||!islower(*extra))
X	{
X		msg("@ requires a cut buffer name (a-z)");
X	}
X
X	/* get the contents of the buffer */
X	result = cb2str(*extra, buf, (unsigned)(sizeof buf));
X	if (result <= 0)
X	{
X		msg("buffer \"%c is empty", *extra);
X	}
X	else if (result >= sizeof buf)
X	{
X		msg("buffer \"%c is too large to execute", *extra);
X	}
X	else
X	{
X		/* execute the contents of the buffer as ex commands */
X		exstring(buf, result, '\\');
X	}
X
X	nest = FALSE;
X}
X#endif
/
echo x - config.h
sed '/^X/s///' > config.h << '/'
X/*
X * vi configuration file
X * We try to automatically configure to various compilers and operating
X * systems. Extend the autoconf section as needed.
X */
X
X#ifndef _CONFIG_H
X# define _CONFIG_H
X
X/*************************** autoconf section ************************/
X
X/* Commodore-Amiga */
X#ifdef	amiga
X# define AMIGA		1
X# define COMPILED_BY	"Manx Aztec C 5.2b"
X#endif
X
X/* standard unix V (?) */
X#ifdef	M_SYSV
X# define UNIXV		1
X#endif
X
X/* xelos system, University of Ulm */
X#ifdef	xelos
X# define UNIXV		1
X#endif
X
X/* BSD UNIX? */
X#ifdef bsd
X# define BSD		1
X#else
X# ifdef sun
X#  define BSD		1
X# endif
X#endif
X
X/* Microsoft C: sorry, Watcom does the same thing */
X#ifdef	M_I86
X# ifndef M_SYSV
X#  define MSDOS		1
X#  ifdef IBMC2
X#   define COMPILED_BY	"IBM C/2 1.00"
X#  else
X#   define MICROSOFT	1
X#   define COMPILED_BY	"Microsoft C 5.10"
X#  endif
X# endif
X#endif
X
X/* Borland's Turbo C */
X#ifdef	__TURBOC__
X# define MSDOS		1
X# define TURBOC		1
X# ifdef __BORLANDC__
X# define COMPILED_BY	"Borland C 2.00"
X# else
X# define COMPILED_BY	(__TURBOC__ >= 661 ? "Turbo C++ 1.00" : "Turbo C 2.00")
X# endif
X#endif
X
X/* Tos Mark-Williams */
X#ifdef	M68000
X# define TOS 1
X# define COMPILED_BY	"Mark Williams C"
X#endif
X
X/* Tos GNU-C */
X#if defined(__atarist__) && defined(__gem__)
X# define TOS 1
X# define COMPILED_BY	"GNU-C " __VERSION__
X#endif
X
X/* OS9/68000 */
X#ifdef	OSK
X# define COMPILED_BY	"Microware C V2.3 Edition 40"
X#endif
X
X/* DEC Rainbow, running MS-DOS (handled by earlier MS-DOS tests) */
X/* (would need -DRAINBOW in CFLAGS to compile a Rainbow-compatible .EXE) */
X
X#ifdef VMS
X# define COMPILED_BY    "VAX/VMS VAXC compiler"
X# undef VMS
X# define VMS 1
X#endif
X
X/*************************** end of autoconf section ************************/
X
X/* All undefined symbols are defined to zero here, to allow for older    */
X/* compilers which dont understand #if defined() or #if UNDEFINED_SYMBOL */
X
X/*************************** operating systems *****************************/
X 
X#ifndef	BSD
X# define BSD	0		/* UNIX - Berkeley 4.x */
X#endif
X
X#ifndef	UNIXV
X# define UNIXV	0		/* UNIX - AT&T SYSV */
X#endif
X
X#ifndef	UNIX7
X# define UNIX7	0		/* UNIX - version 7 */
X#endif
X
X#ifndef	MSDOS
X# define MSDOS	0		/* PC		*/
X#endif
X
X#ifndef	TOS
X# define TOS	0		/* Atari ST	*/
X#endif
X
X#ifndef	AMIGA
X# define AMIGA	0		/* Commodore Amiga */
X#endif
X
X#ifndef OSK
X# define OSK	0		/* OS-9 / 68k */
X#endif
X
X#ifndef COHERENT
X# define COHERENT 0		/* Coherent */
X#endif
X
X#ifndef RAINBOW			/* DEC Rainbow support, under MS-DOS */
X# define RAINBOW 0
X#endif
X
X#ifndef VMS
X# define VMS 0                  /* VAX/VMS */
X#endif
X				/* Minix has no predefines */
X#if !BSD && !UNIXV && !UNIX7 && !MSDOS && !TOS && !AMIGA && !OSK && !COHERENT && !VMS
X# define MINIX	1
X#else
X# define MINIX	0
X#endif
X
X				/* generic combination of Unices */
X#if UNIXV || UNIX7 || BSD || MINIX || COHERENT
X# define ANY_UNIX 1
X#else
X# define ANY_UNIX 0
X#endif
X
X/*************************** compilers **************************************/
X 
X#ifndef	AZTEC_C
X# define AZTEC_C	0
X#endif
X
X#ifndef	MICROSOFT
X# define MICROSOFT	0
X#endif
X
X#ifndef	TURBOC
X# define TURBOC		0
X#endif
X
X/******************************* Credit ************************************/
X
X#if MSDOS
X# define CREDIT "Ported to MS-DOS by Guntram Blohm & Martin Patzel"
X# if RAINBOW
X#  define CREDIT2 "Rainbow support added by Willett Kempton"
X# endif
X#endif
X
X#if AMIGA
X# define CREDIT "Ported to AmigaDOS 2.04 by Mike Rieser & Dale Rahn"
X#endif
X
X#if TOS
X# define CREDIT "Ported to Atari/TOS by Guntram Blohm & Martin Patzel"
X#endif
X
X#if OSK
X# define CREDIT	"Ported to Microware OS9/68k by Peter Reinig"
X#endif
X
X#if COHERENT
X# define CREDIT	"Ported to Coherent by Esa Ahola"
X#endif
X
X#if VMS
X# define CREDIT "Ported to VAX/VMS by John Campbell"
X#endif
X/*************************** functions depending on OS *********************/
X
X/* There are two terminal-related functions that we need: ttyread() and
X * ttywrite().  The ttyread() function implements read-with-timeout and is
X * a true function on all systems.  The ttywrite() function is almost always
X * just a macro...
X */
X#if !TOS && !AMIGA
X# define ttywrite(buf, len)	write(1, buf, (unsigned)(len))	/* raw write */
X#endif
X
X/* The strchr() function is an official standard now, so everybody has it
X * except Unix version 7 (which is old) and BSD Unix (which is academic).
X * Those guys use something called index() to do the same thing.
X */
X#if BSD || UNIX7 || OSK
X# define strchr	index
X#endif
Xextern char *strchr();
X
X/* BSD uses bcopy() instead of memcpy() */
X#if BSD
X# define memcpy(dest, src, siz)	bcopy(src, dest, siz)
X#endif
X
X/* BSD uses getwd() instead of getcwd().  The arguments are a little different,
X * but we'll ignore that and hope for the best; adding arguments to the macro
X * would mess up an "extern" declaration of the function.
X */
X#if BSD || COHERENT
X# define getcwd	getwd
X#endif
Xextern char *getcwd();
X
X/* text versa binary mode for read/write */
X#if !TOS
X#define	tread(fd,buf,n)		read(fd,buf,(unsigned)(n))
X#define twrite(fd,buf,n)	write(fd,buf,(unsigned)(n))
X#endif
X
X/**************************** Compiler quirks *********************************/
X
X/* the UNIX version 7 and (some) TOS compilers, don't allow "void" */
X#if UNIX7 || TOS
X# define void int
X#endif
X
X/* as far as I know, all compilers except version 7 support unsigned char */
X/* NEWFLASH: the Minix-ST compiler has subtle problems with unsigned char */
X#if UNIX7 || MINIX
X# define UCHAR(c)	((c) & 0xff)
X# define uchar		char
X#else
X# define UCHAR(c)	((unsigned char)(c))
X# define uchar		unsigned char
X#endif
X
X/* Some compilers prefer to have malloc declared as returning a (void *) */
X#if BSD || AMIGA
Xextern void *malloc();
X#else
Xextern char *malloc();
X#endif
X
X/* everybody but Amiga wants lseek declared here */
X#if !AMIGA
Xextern long lseek();
X#endif
X
X/******************* Names of files and environment vars **********************/
X
X#if ANY_UNIX
X# ifndef TMPDIR
X#  if MINIX
X#   define TMPDIR	"/usr/tmp"	/* Keep elvis' temp files off RAM disk! */
X#  else
X#   define TMPDIR	"/tmp"		/* directory where temp files live */
X#  endif
X# endif
X# ifndef PRSVDIR
X#  define PRSVDIR	"/usr/preserve"	/* directory where preserved file live */
X# endif
X# ifndef PRSVINDEX
X#  define PRSVINDEX	"/usr/preserve/Index" /* index of files in PRSVDIR */
X# endif
X# ifndef EXRC
X#  define EXRC		".exrc"		/* init file in current directory */
X# endif
X# define SCRATCHOUT	"%s/soXXXXXX"	/* temp file used as input to filter */
X# ifndef SHELL
X#  define SHELL		"/bin/sh"	/* default shell */
X# endif
X# if COHERENT
X#  ifndef REDIRECT
X#   define REDIRECT	">"		/* Coherent CC writes errors to stdout */
X#  endif
X# endif
X#endif
X
X#if AMIGA		/* Specify AMIGA environment */
X# ifndef CC_COMMAND
X#  define CC_COMMAND	"cc"		/* generic C compiler */
X# endif
X# ifndef COLON
X#  define COLON		':'		/* Amiga files can also end in `:' */
X# endif
X# ifndef SYSEXRC
X#  define SYSEXRC	"S:" EXRC	/* name of ".exrc" file in system dir */
X# endif
X# ifndef MAXRCLEN
X#  define MAXRCLEN	2048		/* max size of a .exrc file */
X# endif
X# ifndef NBUFS
X#  define NBUFS		10		/* must be at least 3 -- more is better */
X# endif
X# ifndef NEEDSYNC
X#  define NEEDSYNC	TRUE		/* assume ":se sync" by default */
X# endif
X# ifndef PRSVDIR
X#  define PRSVDIR	"Elvis:"	/* directory where preserved file live */
X# endif
X# ifndef PRSVINDEX
X#  define PRSVINDEX	"Elvis:Index"	/* index of files in PRSVDIR */
X# endif
X# ifndef REDIRECT
X#  define REDIRECT	">"		/* Amiga writes errors to stdout */
X# endif
X# ifndef SCRATCHIN
X#  define SCRATCHIN	"%sSIXXXXXX"
X# endif
X# ifndef SCRATCHOUT
X#  define SCRATCHOUT	"%sSOXXXXXX"
X# endif
X# ifndef SHELL
X#  define SHELL		"newshell"	/* default shell */
X# endif
X# ifndef TERMTYPE
X#  define TERMTYPE	"amiga"		/* default termtype */
X# endif
X# ifndef TMPDIR				/* for AMIGA should end in `:' or `/' */
X#  define TMPDIR	"T:"		/* directory where temp files live */
X# endif
X# ifndef TMPNAME
X#  define TMPNAME	"%selv_%x.%x"	/* format of names for temp files */
X# endif
X#endif
X
X#if MSDOS || TOS
X/* do not change TMPNAME and SCRATCH*: they MUST begin with '%s\\'! */
X# ifndef TMPDIR
X#  define TMPDIR	"C:\\tmp"	/* directory where temp files live */
X# endif
X# ifndef PRSVDIR
X#  define PRSVDIR	"C:\\preserve"	/* directory where preserved file live */
X# endif
X# ifndef PRSVINDEX
X#  define PRSVINDEX	"C:\\preserve\\Index" /* index of files in PRSVDIR */
X# endif
X# define TMPNAME	"%s\\elv_%x.%x" /* temp file */
X# if MSDOS
X#  if MICROSOFT
X#   define CC_COMMAND	"cl -c"		/* C compiler */
X#  else
X#   if __BORLANDC__  /* Borland C */
X#    define CC_COMMAND	"bcc"		/* C compiler */
X#   else
X#   if TURBOC        /* Turbo C */
X#    define CC_COMMAND	"tcc"		/* C compiler */
X#   endif	/* TURBOC */
X#   endif	/* BORLANDC */
X#  endif		/* MICROSOFT */
X# endif		/* MSDOS */
X# define SCRATCHIN	"%s\\siXXXXXX"	/* DOS ONLY - output of filter program */
X# define SCRATCHOUT	"%s\\soXXXXXX"	/* temp file used as input to filter */
X# define SLASH		'\\'
X# ifndef SHELL
X#  if TOS
X#   define SHELL	"shell.ttp"	/* default shell */
X#  else
X#   define SHELL	"command.com"	/* default shell */
X#  endif
X# endif
X# define NEEDSYNC	TRUE		/* assume ":se sync" by default */
X# if TOS && __GNUC__			/* probably on other systems, too */
X#  define REDIRECT	"2>"		/* GNUC reports on 2, others on 1 */
X#  define CC_COMMAND	"gcc -c"
X# else
X#  define REDIRECT	">"		/* shell's redirection of stderr */
X# endif
X#endif
X
X#if VMS
X/* do not change TMPNAME, and SCRATCH*: they MUST begin with '%s\\'! */
X# ifndef TMPDIR
X#  define TMPDIR        "sys$scratch:"  /* directory where temp files live */
X# endif
X# define TMPNAME        "%selv_%x.%x;1" /* temp file */
X# define SCRATCHIN      "%ssiXXXXXX"    /* DOS ONLY - output of filter program */
X# define SCRATCHOUT     "%ssoXXXXXX"    /* temp file used as input to filter */
X# define SLASH          '\:'  /* Worry point... jdc */
X# ifndef SHELL
X#   define SHELL        ""      /* default shell */
X# endif
X# define REDIRECT       ">"             /* shell's redirection of stderr */
X# define tread(fd,buf,n)  vms_read(fd,buf,(unsigned)(n))
X# define close vms_close
X# define lseek vms_lseek
X# define unlink vms_delete
X# define delete __delete   /* local routine conflicts w/VMS rtl routine. */
X# define rpipe vms_rpipe
X# define rpclose vms_rpclose
X# define ttyread vms_ttyread
X/* There is no sync() on vms */
X# define sync()
X/* jdc -- seems VMS external symbols are case insensitive */
X# define m_fWord m_fw_ord
X# define m_bWord m_bw_ord
X# define m_eWord m_ew_ord
X# define m_Nsrch m_n_srch
X# define m_Fch   m_f_ch
X# define m_Tch   m_t_ch
X# define v_Xchar v_x_char
X/* jdc -- also, braindead vms curses always found by linker. */
X# define LINES elvis_LINES
X# define COLS  elvis_COLS
X# define curscr elvis_curscr
X# define stdscr elvis_stdscr
X# define initscr elvis_initscr
X# define endwin  elvis_endwin
X# define wrefresh elvis_wrefresh
X#endif
X
X#if OSK
X# ifndef TMPDIR
X#  define TMPDIR	"/dd/tmp"	   /* directory where temp files live */
X# endif
X# ifndef PRSVDIR
X#  define PRSVDIR	"/dd/usr/preserve" /* directory where preserved file live */
X# endif
X# ifndef PRSVINDEX
X#  define PRSVINDEX	"/dd/usr/preserve/Index" /* index of files in PRSVDIR */
X# endif
X# ifndef CC_COMMAND
X#  define CC_COMMAND	"cc -r"		   /* name of the compiler */
X# endif
X# ifndef EXRC
X#  define EXRC		".exrc"		   /* init file in current directory */
X# endif
X# define SCRATCHOUT	"%s/soXXXXXX"	   /* temp file used as input to filter */
X# ifndef SHELL
X#  define SHELL		"shell"		   /* default shell */
X# endif
X# define FILEPERMS	(S_IREAD|S_IWRITE) /* file permissions used for creat() */
X# define REDIRECT	">>-"		   /* shell's redirection of stderr */
X# define sync()				   /* OS9 doesn't need a sync() */
X#endif
X
X#ifndef	TAGS
X# define TAGS		"tags"		/* name of the tags file */
X#endif
X
X#ifndef TMPNAME
X# define TMPNAME	"%s/elv_%x.%x"	/* format of names for temp files */
X#endif
X
X#ifndef EXINIT
X# define EXINIT		"EXINIT"	/* name of EXINIT environment variable */
X#endif
X
X#ifndef	EXRC
X# define EXRC		"elvis.rc"	/* name of ".exrc" file in current dir */
X#endif
X
X#ifndef HMEXRC
X# define HMEXRC		EXRC		/* name of ".exrc" file in home dir */
X#endif
X
X#ifndef	KEYWORDPRG
X# define KEYWORDPRG	"ref"
X#endif
X
X#ifndef	SCRATCHOUT
X# define SCRATCHIN	"%s/SIXXXXXX"
X# define SCRATCHOUT	"%s/SOXXXXXX"
X#endif
X
X#ifndef ERRLIST
X# define ERRLIST	"errlist"
X#endif
X
X#ifndef	SLASH
X# define SLASH		'/'
X#endif
X
X#ifndef SHELL
X# define SHELL		"shell"
X#endif
X
X#ifndef REG
X# define REG		register
X#endif
X
X#ifndef NEEDSYNC
X# define NEEDSYNC	FALSE
X#endif
X
X#ifndef FILEPERMS
X# define FILEPERMS	0666
X#endif
X
X#ifndef PRESERVE
X# define PRESERVE	"elvprsv"	/* name of the "preserve" program */
X#endif
X
X#ifndef CC_COMMAND
X# define CC_COMMAND	"cc -c"
X#endif
X
X#ifndef MAKE_COMMAND
X# define MAKE_COMMAND	"make"
X#endif
X
X#ifndef REDIRECT
X# define REDIRECT	"2>"
X#endif
X
X#ifndef BLKSIZE
X# ifdef CRUNCH
X#  define BLKSIZE	1024
X# else
X#  define BLKSIZE	2048
X# endif
X#endif
X
X#ifndef KEYBUFSIZE
X# define KEYBUFSIZE	1000
X#endif
X
X#endif  /* ndef _CONFIG_H */
/
echo x - ctags.c
sed '/^X/s///' > ctags.c << '/'
X/* ctags.c */
X
X/* This is a reimplementation of the ctags(1) program.  It supports ANSI C,
X * and has heaps o' flags.  It is meant to be distributed with elvis.
X */
X
X#include <stdio.h>
X#include "config.h"
X#ifndef FALSE
X# define FALSE	0
X# define TRUE	1
X#endif
X#ifndef TAGS
X# define TAGS	"tags"
X#endif
X#ifndef REFS
X# define REFS	"refs"
X#endif
X#ifndef BLKSIZE
X# define BLKSIZE 1024
X#endif
X
X#include "ctype.c" /* yes, that really is the .c file, not the .h one. */
X
X/* -------------------------------------------------------------------------- */
X/* Some global variables */
X
X/* The following boolean variables are set according to command line flags */
Xint	incl_static;	/* -s  include static tags */
Xint	incl_types;	/* -t  include typedefs and structs */
Xint	incl_vars;	/* -v  include variables */
Xint	make_refs;	/* -r  generate a "refs" file */
Xint	append_files;	/* -a  append to "tags" [and "refs"] files */
X
X/* The following are used for outputting to the "tags" and "refs" files */
XFILE	*tags;		/* used for writing to the "tags" file */
XFILE	*refs;		/* used for writing to the "refs" file */
X
X/* -------------------------------------------------------------------------- */
X/* These are used for reading a source file.  It keeps track of line numbers */
Xchar	*file_name;	/* name of the current file */
XFILE	*file_fp;	/* stream used for reading the file */
Xlong	file_lnum;	/* line number in the current file */
Xlong	file_seek;	/* fseek() offset to the start of current line */
Xint	file_afternl;	/* boolean: was previous character a newline? */
Xint	file_prevch;	/* a single character that was ungotten */
Xint	file_header;	/* boolean: is the current file a header file? */
X
X/* This function opens a file, and resets the line counter.  If it fails, it
X * it will display an error message and leave the file_fp set to NULL.
X */
Xvoid file_open(name)
X	char	*name;	/* name of file to be opened */
X{
X	/* if another file was already open, then close it */
X	if (file_fp)
X	{
X		fclose(file_fp);
X	}
X
X	/* try to open the file for reading.  The file must be opened in
X	 * "binary" mode because otherwise fseek() would misbehave under DOS.
X	 */
X#if MSDOS || TOS
X	file_fp = fopen(name, "rb");
X#else
X	file_fp = fopen(name, "r");
X#endif
X	if (!file_fp)
X	{
X		perror(name);
X	}
X
X	/* reset the name & line number */
X	file_name = name;
X	file_lnum = 0L;
X	file_seek = 0L;
X	file_afternl = TRUE;
X
X	/* determine whether this is a header file */
X	file_header = FALSE;
X	name += strlen(name) - 2;
X	if (name >= file_name && name[0] == '.' && (name[1] == 'h' || name[1] == 'H'))
X	{
X		file_header = TRUE;
X	}
X}
X
X/* This function reads a single character from the stream.  If the *previous*
X * character was a newline, then it also increments file_lnum and sets
X * file_offset.
X */
Xint file_getc()
X{
X	int	ch;
X
X	/* if there is an ungotten character, then return it.  Don't do any
X	 * other processing on it, though, because we already did that the
X	 * first time it was read.
X	 */
X	if (file_prevch)
X	{
X		ch = file_prevch;
X		file_prevch = 0;
X		return ch;
X	}
X
X	/* if previous character was a newline, then we're starting a line */
X	if (file_afternl)
X	{
X		file_afternl = FALSE;
X		file_seek = ftell(file_fp);
X		file_lnum++;
X	}
X
X	/* Get a character.  If no file is open, then return EOF */
X	ch = (file_fp ? getc(file_fp) : EOF);
X
X	/* if it is a newline, then remember that fact */
X	if (ch == '\n')
X	{
X		file_afternl = TRUE;
X	}
X
X	/* return the character */
X	return ch;
X}
X
X/* This function ungets a character from the current source file */
Xvoid file_ungetc(ch)
X	int	ch;	/* character to be ungotten */
X{
X	file_prevch = ch;
X}
X
X/* This function copies the current line out some other fp.  It has no effect
X * on the file_getc() function.  During copying, any '\' characters are doubled
X * and a leading '^' or trailing '$' is also quoted.  The newline character is
X * not copied.
X *
X * This is meant to be used when generating a tag line.
X */
Xvoid file_copyline(seek, fp)
X	long	seek;	/* where the lines starts in the source file */
X	FILE	*fp;	/* the output stream to copy it to */
X{
X	long	oldseek;/* where the file's pointer was before we messed it up */
X	char	ch;	/* a single character from the file */
X	char	next;	/* the next character from this file */
X
X	/* go to the start of the line */
X	oldseek = ftell(file_fp);
X	fseek(file_fp, seek, 0);
X
X	/* if first character is '^', then emit \^ */
X	ch = getc(file_fp);
X	if (ch == '^')
X	{
X		putc('\\', fp);
X		putc('^', fp);
X		ch = getc(file_fp);
X	}
X
X	/* write everything up to, but not including, the newline */
X	while (ch != '\n')
X	{
X		/* preread the next character from this file */
X		next = getc(file_fp);
X
X		/* if character is '\', or a terminal '$', then quote it */
X		if (ch == '\\' || (ch == '$' && next == '\n'))
X		{
X			putc('\\', fp);
X		}
X		putc(ch, fp);
X
X		/* next character... */
X		ch = next;
X	}
X
X	/* seek back to the old position */
X	fseek(file_fp, oldseek, 0);
X}
X
X/* -------------------------------------------------------------------------- */
X/* This section handles preprocessor directives.  It strips out all of the
X * directives, and may emit a tag for #define directives.
X */
X
Xint	cpp_afternl;	/* boolean: look for '#' character? */
Xint	cpp_prevch;	/* an ungotten character, if any */
Xint	cpp_refsok;	/* boolean: can we echo characters out to "refs"? */
X
X/* This function opens the file & resets variables */
Xvoid cpp_open(name)
X	char	*name;	/* name of source file to be opened */
X{
X	/* use the lower-level file_open function to open the file */
X	file_open(name);
X
X	/* reset variables */
X	cpp_afternl = TRUE;
X	cpp_refsok = TRUE;
X}
X
X/* This function copies a character from the source file to the "refs" file */
Xvoid cpp_echo(ch)
X	int	ch; /* the character to copy */
X{
X	static	wasnl;
X
X	/* echo non-EOF chars, unless not making "ref", or echo turned off */
X	if (ch != EOF && make_refs && cpp_refsok && !file_header)
X	{
X		/* try to avoid blank lines */
X		if (ch == '\n')
X		{
X			if (wasnl)
X			{
X				return;
X			}
X			wasnl = TRUE;
X		}
X		else
X		{
X			wasnl = FALSE;
X		}
X
X		/* add the character */
X		putc(ch, refs);
X	}
X}
X
X/* This function returns the next character which isn't part of a directive */
Xint cpp_getc()
X{
X	static
X	int	ch;	/* the next input character */
X	char	*scan;
X
X	/* if we have an ungotten character, then return it */
X	if (cpp_prevch)
X	{
X		ch = cpp_prevch;
X		cpp_prevch = 0;
X		return ch;
X	}
X
X	/* Get a character from the file.  Return it if not special '#' */
X	ch = file_getc();
X	if (ch == '\n')
X	{
X		cpp_afternl = TRUE;
X		cpp_echo(ch);
X		return ch;
X	}
X	else if (ch != '#' || !cpp_afternl)
X	{
X		/* normal character.  Any non-whitespace should turn off afternl */
X		if (ch != ' ' && ch != '\t')
X		{
X			cpp_afternl = FALSE;
X		}
X		cpp_echo(ch);
X		return ch;
X	}
X
X	/* Yikes!  We found a directive */
X
X	/* see whether this is a #define line */
X	scan = " define ";
X	while (*scan)
X	{
X		if (*scan == ' ')
X		{
X			/* space character matches any whitespace */
X			do
X			{
X				ch = file_getc();
X			} while (ch == ' ' || ch == '\t');
X			file_ungetc(ch);
X		}
X		else
X		{
X			/* other characters should match exactly */
X			ch = file_getc();
X			if (ch != *scan)
X			{
X				file_ungetc(ch);
X				break;
X			}
X		}
X		scan++;
X	}
X
X	/* is this a #define line?  and should we generate a tag for it? */
X	if (!*scan && (file_header || incl_static))
X	{
X		/* if not a header, then this will be a static tag */
X		if (!file_header)
X		{
X			fputs(file_name, tags);
X			putc(':', tags);
X		}
X
X		/* output the tag name */
X		for (ch = file_getc(); isalnum(ch) || ch == '_'; ch = file_getc())
X		{
X			putc(ch, tags);
X		}
X
X		/* output a tab, the filename, another tab, and the line number */
X		fprintf(tags, "\t%s\t%ld\n", file_name, file_lnum);
X	}
X
X	/* skip to the end of the directive -- a newline that isn't preceded
X	 * by a '\' character.
X	 */
X	while (ch != EOF && ch != '\n')
X	{
X		if (ch == '\\')
X		{
X			ch = file_getc();
X		}
X		ch = file_getc();
X	}
X
X	/* return the newline that we found at the end of the directive */
X	cpp_echo(ch);
X	return ch;
X}
X
X/* This puts a character back into the input queue for the source file */
Xcpp_ungetc(ch)
X	int	ch;	/* a character to be ungotten */
X{
X	cpp_prevch = ch;
X}
X
X
X/* -------------------------------------------------------------------------- */
X/* This is the lexical analyser.  It gets characters from the preprocessor,
X * and gives tokens to the parser.  Some special codes are...
X *   (deleted)  /*...* /	(comments)
X *   (deleted)	//...\n	(comments)
X *   (deleted)	(*	(parens used in complex declaration)
X *   (deleted)	[...]	(array subscript, when ... contains no ])
X *   (deleted)	struct	(intro to structure declaration)
X *   BODY	{...}	('{' can occur anywhere, '}' only at BOW if ... has '{')
X *   ARGS	(...{	(args of function, not extern or forward)
X *   ARGS	(...);	(args of an extern/forward function declaration)
X *   COMMA	,	(separate declarations that have same scope)
X *   SEMICOLON	;	(separate declarations that have different scope)
X *   SEMICOLON  =...;	(initializer)
X *   TYPEDEF	typedef	(the "typedef" keyword)
X *   STATIC	static	(the "static" keyword)
X *   STATIC	private	(the "static" keyword)
X *   STATIC	PRIVATE	(the "static" keyword)
X *   NAME	[a-z]+	(really any valid name that isn't reserved word)
X */
X
X/* #define EOF -1 */
X#define DELETED	  0
X#define BODY	  1
X#define ARGS	  2
X#define COMMA	  3
X#define SEMICOLON 4
X#define TYPEDEF   5
X#define STATIC	  6
X#define EXTERN	  7
X#define NAME	  8
X
Xchar	lex_name[BLKSIZE];	/* the name of a "NAME" token */
Xlong	lex_seek;		/* start of line that contains lex_name */
X
Xlex_gettoken()
X{
X	int	ch;		/* a character from the preprocessor */
X	int	next;		/* the next character */
X	int	token;		/* the token that we'll return */
X	int	i;
X
X	/* loop until we get a token that isn't "DELETED" */
X	do
X	{
X		/* get the next character */
X		ch = cpp_getc();
X
X		/* process the character */
X		switch (ch)
X		{
X		  case ',':
X			token = COMMA;
X			break;
X
X		  case ';':
X			token = SEMICOLON;
X			break;
X
X		  case '/':
X			/* get the next character */
X			ch = cpp_getc();
X			switch (ch)
X			{
X			  case '*':	/* start of C comment */
X				ch = cpp_getc();
X				next = cpp_getc();
X				while (next != EOF && (ch != '*' || next != '/'))
X				{
X					ch = next;
X					next = cpp_getc();
X				}
X				break;
X
X			  case '/':	/* start of a C++ comment */
X				do
X				{
X					ch = cpp_getc();
X				} while (ch != '\n' && ch != EOF);
X				break;
X
X			  default:	/* some other slash */
X				cpp_ungetc(ch);
X			}
X			token = DELETED;
X			break;
X
X		  case '(':
X			ch = cpp_getc();
X			if (ch == '*')
X			{
X				token = DELETED;
X			}
X			else
X			{
X				next = cpp_getc();
X				while (ch != '{' && ch != EOF && (ch != ')' || next != ';'))/*}*/
X				{
X					ch = next;
X					next = cpp_getc();
X				}
X				if (ch == '{')/*}*/
X				{
X					cpp_ungetc(ch);
X				}
X				else if (next == ';')
X				{
X					cpp_ungetc(next);
X				}
X				token = ARGS;
X			}
X			break;
X
X		  case '{':/*}*/
X			/* don't send the next characters to "refs" */
X			cpp_refsok = FALSE;
X
X			/* skip ahead to closing '}', or to embedded '{' */
X			do
X			{
X				ch = cpp_getc();
X			} while (ch != '{' && ch != '}' && ch != EOF);
X
X			/* if has embedded '{', then skip to '}' in column 1 */
X			if (ch == '{') /*}*/
X			{
X				ch = cpp_getc();
X				next = cpp_getc();
X				while (ch != EOF && (ch != '\n' || next != '}'))/*{*/
X				{
X					ch = next;
X					next = cpp_getc();
X				}
X			}
X
X			/* resume "refs" processing */
X			cpp_refsok = TRUE;
X			cpp_echo('}');
X
X			token = BODY;
X			break;
X
X		  case '[':
X			/* skip to matching ']' */
X			do
X			{
X				ch = cpp_getc();
X			} while (ch != ']' && ch != EOF);
X			token = DELETED;
X			break;
X
X		  case '=':
X		  	/* skip to next ';' */
X			do
X			{
X				ch = cpp_getc();
X
X				/* leave array initializers out of "refs" */
X				if (ch == '{')
X				{
X					cpp_refsok = FALSE;
X				}
X			} while (ch != ';' && ch != EOF);
X
X			/* resume echoing to "refs" */
X			if (!cpp_refsok)
X			{
X				cpp_refsok = TRUE;
X				cpp_echo('}');
X				cpp_echo(';');
X			}
X			token = SEMICOLON;
X			break;
X
X		  case EOF:
X			token = EOF;
X			break;
X
X		  default:
X			/* is this the start of a name/keyword? */
X			if (isalpha(ch) || ch == '_')
X			{
X				/* collect the whole word */
X				lex_name[0] = ch;
X				for (i = 1, ch = cpp_getc();
X				     i < BLKSIZE - 1 && (isalnum(ch) || ch == '_');
X				     i++, ch = cpp_getc())
X				{
X					lex_name[i] = ch;
X				}
X				lex_name[i] = '\0';
X				cpp_ungetc(ch);
X
X				/* is it a reserved word? */
X				if (!strcmp(lex_name, "typedef"))
X				{
X					token = TYPEDEF;
X					lex_seek = -1L;
X				}
X				else if (!strcmp(lex_name, "static")
X				      || !strcmp(lex_name, "private")
X				      || !strcmp(lex_name, "PRIVATE"))
X				{
X					token = STATIC;
X					lex_seek = -1L;
X				}
X				else if (!strcmp(lex_name, "extern")
X				      || !strcmp(lex_name, "EXTERN")
X				      || !strcmp(lex_name, "FORWARD"))
X				{
X					token = EXTERN;
X					lex_seek = -1L;
X				}
X				else
X				{
X					token = NAME;
X					lex_seek = file_seek;
X				}
X			}
X			else /* not part of a name/keyword */
X			{
X				token = DELETED;
X			}
X
X		} /* end switch(ch) */
X
X	} while (token == DELETED);
X
X	return token;
X}
X
X/* -------------------------------------------------------------------------- */
X/* This is the parser.  It locates tag candidates, and then decides whether to
X * generate a tag for them.
X */
X
X/* This function generates a tag for the object in lex_name, whose tag line is
X * located at a given seek offset.
X */
Xvoid maketag(scope, seek)
X	int	scope;	/* 0 if global, or STATIC if static */
X	long	seek;	/* the seek offset of the line */
X{
X	/* output the tagname and filename fields */
X	if (scope == EXTERN)
X	{
X		/* whoa!  we should *never* output a tag for "extern" decl */
X		return;
X	}
X	else if (scope == STATIC)
X	{
X		fprintf(tags, "%s:%s\t%s\t", file_name, lex_name, file_name);
X	}
X	else
X	{
X		fprintf(tags, "%s\t%s\t", lex_name, file_name);
X	}
X
X	/* output the target line */
X	putc('/', tags);
X	putc('^', tags);
X	file_copyline(seek, tags);
X	putc('$', tags);
X	putc('/', tags);
X	putc('\n', tags);
X}
X
X
X/* This function parses a source file, adding any tags that it finds */
Xvoid ctags(name)
X	char	*name;	/* the name of a source file to be checked */
X{
X	int	prev;	/* the previous token from the source file */
X	int	token;	/* the current token from the source file */
X	int	scope;	/* normally 0, but could be a TYPEDEF or STATIC token */
X	int	gotname;/* boolean: does lex_name contain a tag candidate? */
X	long	tagseek;/* start of line that contains lex_name */
X
X	/* open the file */
X	cpp_open(name);
X
X	/* reset */
X	scope = 0;
X	gotname = FALSE;
X	token = SEMICOLON;
X
X	/* parse until the end of the file */
X	while (prev = token, (token = lex_gettoken()) != EOF)
X	{
X		/* scope keyword? */
X		if (token == TYPEDEF || token == STATIC || token == EXTERN)
X		{
X			scope = token;
X			gotname = FALSE;
X			continue;
X		}
X
X		/* name of a possible tag candidate? */
X		if (token == NAME)
X		{
X			tagseek = file_seek;
X			gotname = TRUE;
X			continue;
X		}
X
X		/* if NAME BODY, without ARGS, then NAME is a struct tag */
X		if (gotname && token == BODY && prev != ARGS)
X		{
X			gotname = FALSE;
X			
X			/* ignore if in typedef -- better name is coming soon */
X			if (scope == TYPEDEF)
X			{
X				continue;
X			}
X
X			/* generate a tag, if -t and maybe -s */
X			if (incl_types && (file_header || incl_static))
X			{
X				maketag(file_header ? 0 : STATIC, tagseek);
X			}
X		}
X
X		/* If NAME ARGS BODY, then NAME is a function */
X		if (gotname && prev == ARGS && token == BODY)
X		{
X			gotname = FALSE;
X			
X			/* generate a tag, maybe checking -s */
X			if (scope != STATIC || incl_static)
X			{
X				maketag(scope, tagseek);
X			}
X		}
X
X		/* If NAME SEMICOLON or NAME COMMA, then NAME is var/typedef */
X		if (gotname && (token == SEMICOLON || token == COMMA))
X		{
X			gotname = FALSE;
X
X			/* generate a tag, if -v/-t and maybe -s */
X			if (scope == TYPEDEF && incl_types && (file_header || incl_static)
X			 || scope == STATIC && incl_vars && incl_static
X			 || incl_vars)
X			{
X				/* a TYPEDEF outside of a header is STATIC */
X				if (scope == TYPEDEF && !file_header)
X				{
X					maketag(STATIC, tagseek);
X				}
X				else /* use whatever scope was declared */
X				{
X					maketag(scope, tagseek);
X				}
X			}
X		}
X
X		/* reset after a semicolon or ARGS BODY pair */
X		if (token == SEMICOLON || (prev == ARGS && token == BODY))
X		{
X			scope = 0;
X			gotname = FALSE;
X		}
X	}
X
X	/* The source file will be automatically closed */
X}
X
X/* -------------------------------------------------------------------------- */
X
Xvoid usage()
X{
X	fprintf(stderr, "usage: ctags [flags] filenames...\n");
X	fprintf(stderr, "\t-s  include static functions\n");
X	fprintf(stderr, "\t-t  include typedefs\n");
X	fprintf(stderr, "\t-v  include variable declarations\n");
X	fprintf(stderr, "\t-r  generate a \"refs\" file, too\n");
X	fprintf(stderr, "\t-a  append to \"tags\", instead of overwriting\n");
X	exit(2);
X}
X
X
X
X#if AMIGA
X# include "amiwild.c"
X#endif
X
X#if VMS
X# include "vmswild.c"
X#endif
X
Xmain(argc, argv)
X	int	argc;
X	char	**argv;
X{
X	int	i, j;
X
X#if MSDOS || TOS
X	char	**wildexpand();
X	argv = wildexpand(&argc, argv);
X#endif
X
X	/* build the tables used by the ctype macros */
X	_ct_init("");
X
X	/* parse the option flags */
X	for (i = 1; i < argc && argv[i][0] == '-'; i++)
X	{
X		for (j = 1; argv[i][j]; j++)
X		{
X			switch (argv[i][j])
X			{
X			  case 's':	incl_static = TRUE;	break;
X			  case 't':	incl_types = TRUE;	break;
X			  case 'v':	incl_vars = TRUE;	break;
X			  case 'r':	make_refs = TRUE;	break;
X			  case 'a':	append_files = TRUE;	break;
X			  default:	usage();
X			}
X		}
X	}
X
X	/* There should always be at least one source file named in args */
X	if (i == argc)
X	{
X		usage();
X	}
X
X	/* open the "tags" and maybe "refs" files */
X	tags = fopen(TAGS, append_files ? "a" : "w");
X	if (!tags)
X	{
X		perror(TAGS);
X		exit(3);
X	}
X	if (make_refs)
X	{
X		refs = fopen(REFS, append_files ? "a" : "w");
X		if (!refs)
X		{
X			perror(REFS);
X			exit(4);
X		}
X	}
X
X	/* parse each source file */
X	for (; i < argc; i++)
X	{
X		ctags(argv[i]);
X	}
X
X	/* close "tags" and maybe "refs" */
X	fclose(tags);
X	if (make_refs)
X	{
X		fclose(refs);
X	}
X
X#ifdef SORT
X		/* This is a hack which will sort the tags list.   It should
X		 * on UNIX and OS-9.  You may have trouble with csh.   Note
X		 * that the tags list only has to be sorted if you intend to
X		 * use it with the real vi;  elvis permits unsorted tags.
X		 */
X# if OSK
X		system("qsort tags >-_tags; -nx; del tags; rename _tags tags");
X# else	
X		system("sort tags >_tags$$; mv _tags$$ tags");
X# endif
X#endif
X
X	exit(0);
X	/*NOTREACHED*/
X}
X
X#if MSDOS || TOS
X# define WILDCARD_NO_MAIN
X# include "wildcard.c"
X#endif
/
echo x - ctype.c
sed '/^X/s///' > ctype.c << '/'
X/* ctype.c */
X
X/* This file contains the tables and initialization function for elvis'
X * version of <ctype.h>.  It should be portable.
X */
X
X#include "config.h"
X#include "ctype.h"
X
Xuchar	_ct_toupper[256];
Xuchar	_ct_tolower[256];
Xuchar	_ct_ctypes[256];
X
X/* This function initializes the tables used by the ctype macros.  It should
X * be called at the start of the program.  It can be called again anytime you
X * wish to change the non-standard "flipcase" list.  The "flipcase" list is
X * a string of characters which are taken to be lowercase/uppercase pairs.
X * If you don't want to use any special flipcase characters, then pass an
X * empty string.
X */
Xvoid _ct_init(flipcase)
X	uchar	*flipcase;	/* list of non-standard lower/upper letter pairs */
X{
X	int	i;
X	uchar	*scan;
X
X	/* reset all of the tables */
X	for (i = 0; i < 256; i++)
X	{
X		_ct_toupper[i] = _ct_tolower[i] = i;
X		_ct_ctypes[i] = 0;
X	}
X
X	/* add the digits */
X	for (scan = (uchar *)"0123456789"; *scan; scan++)
X	{
X		_ct_ctypes[*scan] |= _CT_DIGIT | _CT_ALNUM;
X	}
X
X	/* add the whitespace */
X	for (scan = (uchar *)" \t\n\r\f"; *scan; scan++)
X	{
X		_ct_ctypes[*scan] |= _CT_SPACE;
X	}
X
X	/* add the standard ASCII letters */
X	for (scan = (uchar *)"aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ"; *scan; scan += 2)
X	{
X		_ct_ctypes[scan[0]] |= _CT_LOWER | _CT_ALNUM;
X		_ct_ctypes[scan[1]] |= _CT_UPPER | _CT_ALNUM;
X		_ct_toupper[scan[0]] = scan[1];
X		_ct_tolower[scan[1]] = scan[0];
X	}
X
X	/* add the flipcase letters */
X	for (scan = flipcase; scan[0] && scan[1]; scan += 2)
X	{
X		_ct_ctypes[scan[0]] |= _CT_LOWER | _CT_ALNUM;
X		_ct_ctypes[scan[1]] |= _CT_UPPER | _CT_ALNUM;
X		_ct_toupper[scan[0]] = scan[1];
X		_ct_tolower[scan[1]] = scan[0];
X	}
X
X	/* include '_' in the isalnum() list */
X	_ct_ctypes[UCHAR('_')] |= _CT_ALNUM;
X
X	/* !!! find the control characters in an ASCII-dependent way */
X	for (i = 0; i < ' '; i++)
X	{
X		_ct_ctypes[i] |= _CT_CNTRL;
X	}
X	_ct_ctypes[127] |= _CT_CNTRL;
X	_ct_ctypes[255] |= _CT_CNTRL;
X}
/
echo x - ctype.h
sed '/^X/s///' > ctype.h << '/'
X/* ctype.h */
X
X/* This file contains macros definitions and extern declarations for a
X * version of <ctype.h> which is aware of the o_flipcase letters used in
X * elvis.
X *
X * This file uses the "uchar" data type and "UCHAR" conversion macro which
X * are defined in "config.h".  Consequently, any file that includes this
X * header must include config.h first.
X */
X
X#ifndef _CT_UPPER
X
X#define _CT_UPPER	0x01
X#define _CT_LOWER	0x02
X#define _CT_SPACE	0x04
X#define _CT_DIGIT	0x08
X#define _CT_ALNUM	0x10
X#define _CT_CNTRL	0x20
X
X#define isalnum(c)	(_ct_ctypes[UCHAR(c)] & _CT_ALNUM)
X#define isalpha(c)	(_ct_ctypes[UCHAR(c)] & (_CT_LOWER|_CT_UPPER))
X#define isdigit(c)	(_ct_ctypes[UCHAR(c)] & _CT_DIGIT)
X#define islower(c)	(_ct_ctypes[UCHAR(c)] & _CT_LOWER)
X#define isspace(c)	(_ct_ctypes[UCHAR(c)] & _CT_SPACE)
X#define isupper(c)	(_ct_ctypes[UCHAR(c)] & _CT_UPPER)
X#define iscntrl(c)	(_ct_ctypes[UCHAR(c)] & _CT_CNTRL)
X#define ispunct(c)	(!_ct_ctypes[UCHAR(c)]) /* punct = "none of the above" */
X
X#define isascii(c)	(!((c) & 0x80))
X
X#define toupper(c)	_ct_toupper[UCHAR(c)]
X#define tolower(c)	_ct_tolower[UCHAR(c)]
X
Xextern uchar	_ct_toupper[];
Xextern uchar	_ct_tolower[];
Xextern uchar	_ct_ctypes[];
Xextern void	_ct_init(/* char *flipcase */);
X
X#endif /* ndef _CT_UPPER */
/
echo x - curses.c
sed '/^X/s///' > curses.c << '/'
X/* curses.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains the functions & variables needed for a tiny subset of
X * curses.  The principle advantage of this version of curses is its
X * extreme speed.  Disadvantages are potentially larger code, few supported
X * functions, limited compatibility with full curses, and only stdscr.
X */
X
X#include "config.h"
X#include "vi.h"
X
X#if ANY_UNIX
X# if UNIXV
X#  ifdef TERMIOS
X#   include	<termios.h>
X#  else
X#   include	<termio.h>
X#  endif
X#  ifdef S5WINSIZE
X#   include	<sys/stream.h>	/* winsize struct defined in one of these? */
X#   include	<sys/ptem.h>
X#  else
X#   undef	TIOCGWINSZ	/* we can't handle it correctly yet */
X#  endif
X# else
X#  include	<sgtty.h>
X# endif
X#endif
X
X#if TOS
X# include	<osbind.h>
X#endif
X
X#if OSK
X# include	<sgstat.h>
X#endif
X
X#if VMS
Xextern int VMS_read_raw;  /* Set in initscr() */
X#endif
X
X
Xextern char	*getenv();
Xstatic void	 starttcap();
X
X/* variables, publicly available & used in the macros */
Xchar	*termtype;	/* name of terminal entry */
Xshort	ospeed;		/* speed of the tty, eg B2400 */
X#if OSK
Xchar	PC_;	/* Pad char */
Xchar	*BC;	/* backspace character string */
X#else
Xchar	PC;		/* Pad char */
X#endif
XWINDOW	*stdscr;	/* pointer into kbuf[] */
XWINDOW	kbuf[KBSIZ];	/* a very large output buffer */
Xint	LINES;		/* :li#: number of rows */
Xint	COLS;		/* :co#: number of columns */
Xint	AM;		/* :am:  boolean: auto margins? */
Xint	PT;		/* :pt:  boolean: physical tabs? */
Xchar	*VB;		/* :vb=: visible bell */
Xchar	*UP;		/* :up=: move cursor up */
Xchar	*SO = "";	/* :so=: standout start */
Xchar	*SE = "";	/* :se=: standout end */
Xchar	*US = "";	/* :us=: underline start */
Xchar	*UE = "";	/* :ue=: underline end */
Xchar	*MD = "";	/* :md=: bold start */
Xchar	*ME = "";	/* :me=: bold end */
Xchar	*AS = "";	/* :as=: alternate (italic) start */
Xchar	*AE = "";	/* :ae=: alternate (italic) end */
X#ifndef NO_VISIBLE
Xchar	*MV;		/* :mv=: "visible" selection start */
X#endif
Xchar	*CM;		/* :cm=: cursor movement */
Xchar	*CE;		/* :ce=: clear to end of line */
Xchar	*CD;		/* :cd=: clear to end of screen */
Xchar	*AL;		/* :al=: add a line */
Xchar	*DL;		/* :dl=: delete a line */
X#if OSK
Xchar	*SR_;		/* :sr=: scroll reverse */
X#else
Xchar	*SR;		/* :sr=: scroll reverse */
X#endif
Xchar	*KS = "";	/* :ks=: init string for cursor */
Xchar	*KE = "";	/* :ke=: restore string for cursor */
Xchar	*KU;		/* :ku=: key sequence sent by up arrow */
Xchar	*KD;		/* :kd=: key sequence sent by down arrow */
Xchar	*KL;		/* :kl=: key sequence sent by left arrow */
Xchar	*KR;		/* :kr=: key sequence sent by right arrow */
Xchar	*HM;		/* :HM=: key sequence sent by the <Home> key */
Xchar	*EN;		/* :EN=: key sequence sent by the <End> key */
Xchar	*PU;		/* :PU=: key sequence sent by the <PgUp> key */
Xchar	*PD;		/* :PD=: key sequence sent by the <PgDn> key */
Xchar	*KI;		/* :kI=: key sequence sent by the <Insert> key */
X#ifndef NO_FKEY
Xchar	*FKEY[NFKEYS];	/* :k0=: ... :k9=: sequences sent by function keys */
X#endif
Xchar	*IM = "";	/* :im=: insert mode start */
Xchar	*IC = "";	/* :ic=: insert the following character */
Xchar	*EI = "";	/* :ei=: insert mode end */
Xchar	*DC;		/* :dc=: delete a character */
Xchar	*TI = "";	/* :ti=: terminal init */	/* GB */
Xchar	*TE = "";	/* :te=: terminal exit */	/* GB */
X#ifndef NO_CURSORSHAPE
X#if 1
Xchar	*CQ = (char *)0;/* :cQ=: normal cursor */
Xchar	*CX = (char *)1;/* :cX=: cursor used for EX command/entry */
Xchar	*CV = (char *)2;/* :cV=: cursor used for VI command mode */
Xchar	*CI = (char *)3;/* :cI=: cursor used for VI input mode */
Xchar	*CR = (char *)4;/* :cR=: cursor used for VI replace mode */
X#else
Xchar	*CQ = "";	/* :cQ=: normal cursor */
Xchar	*CX = "";	/* :cX=: cursor used for EX command/entry */
Xchar	*CV = "";	/* :cV=: cursor used for VI command mode */
Xchar	*CI = "";	/* :cI=: cursor used for VI input mode */
Xchar	*CR = "";	/* :cR=: cursor used for VI replace mode */
X#endif
X#endif
Xchar	*aend = "";	/* end an attribute -- either UE or ME */
Xchar	ERASEKEY;	/* backspace key taken from ioctl structure */
X#ifndef NO_COLOR
Xchar	normalcolor[16];
Xchar	SOcolor[16];
Xchar	SEcolor[16];
Xchar	UScolor[16];
Xchar	UEcolor[16];
Xchar	MDcolor[16];
Xchar	MEcolor[16];
Xchar	AScolor[16];
Xchar	AEcolor[16];
X# ifndef NO_POPUP
Xchar	POPUPcolor[16];
X# endif
X# ifndef NO_VISIBLE
Xchar	VISIBLEcolor[16];
X# endif
X#endif
X
X#if ANY_UNIX
X# if UNIXV
X#  ifdef TERMIOS
Xstatic struct termios	oldtermio;	/* original tty mode */
Xstatic struct termios	newtermio;	/* cbreak/noecho tty mode */
X#  else
Xstatic struct termio	oldtermio;	/* original tty mode */
Xstatic struct termio	newtermio;	/* cbreak/noecho tty mode */
X#  endif
X# else
Xstatic struct sgttyb	oldsgttyb;	/* original tty mode */
Xstatic struct sgttyb	newsgttyb;	/* cbreak/nl/noecho tty mode */
Xstatic int		oldint;		/* ^C or DEL, the "intr" character */
X#  ifdef TIOCSLTC
Xstatic int		oldswitch;	/* ^Z, the "suspend" character */
Xstatic int		olddswitch;	/* ^Y, the "delayed suspend" char */
Xstatic int		oldquote;	/* ^V, the "quote next char" char */
X#  endif
X# endif
X#endif
X
X#if OSK
Xstatic struct sgbuf	oldsgttyb;	/* orginal tty mode */
Xstatic struct sgbuf	newsgttyb;	/* noecho tty mode */
X#endif
X
Xstatic char	*capbuf;	/* capability string buffer */
X
X
X/* Initialize the Curses package. */
Xvoid initscr()
X{
X	/* make sure TERM variable is set */
X	termtype = getenv("TERM");
X
X#if VMS
X	/* VMS getenv() handles TERM as a environment setting.  Foreign 
X	 * terminal support can be implemented by setting the ELVIS_TERM
X	 * logical or symbol to match a tinytcap entry.
X	 */
X	if (!strcmp(termtype,"unknown"))
X		termtype = getenv("ELVIS_TERM");
X#endif
X#if MSDOS
X	/* For MS-DOS, if TERM is unset we can default to "pcbios", or
X	 * maybe "rainbow".
X	 */
X	if (!termtype)
X	{
X#ifdef RAINBOW
X		if (*(unsigned char far*)(0xffff000eL) == 6   /* Rainbow 100a */
X		 || *(unsigned char far*)(0xffff000eL) == 148)/* Rainbow 100b */
X		{
X			termtype = "rainbow";
X		}
X		else
X#endif
X			termtype = "pcbios";
X	}
X	if (!strcmp(termtype, "pcbios"))
X#else
X	if (!termtype)
X#endif
X	{
X#if ANY_UNIX
X		write(2, "Environment variable TERM must be set\n", (unsigned)38);
X		exit(1);
X#endif
X#if OSK
X		writeln(2, "Environment variable TERM must be set\n", (unsigned)38);
X		exit(1);
X#endif
X#if AMIGA
X		termtype = TERMTYPE;
X		starttcap(termtype);
X#endif
X#if MSDOS
X		starttcap("pcbios");
X#endif
X#if TOS
X		termtype = "vt52";
X		starttcap(termtype);
X#endif
X#if VMS
X		write(2, "UNKNOWN terminal: define ELVIS_TERM\n", (unsigned)36);
X		exit(1);
X#endif
X	}
X	else
X	{
X#if MSDOS
X		*o_pcbios = 0;
X#endif
X		/* start termcap stuff */
X		starttcap(termtype);
X	}
X
X	/* create stdscr and curscr */
X	stdscr = kbuf;
X
X	/* change the terminal mode to cbreak/noecho */
X#if ANY_UNIX
X# if UNIXV
X#  ifdef TERMIOS
X	tcgetattr(2, &oldtermio);
X#  else
X	ioctl(2, TCGETA, &oldtermio);
X#  endif
X# else
X	ioctl(2, TIOCGETP, &oldsgttyb);
X# endif
X#endif
X
X#if OSK
X	_gs_opt(0, &oldsgttyb);
X#endif
X
X#if VMS
X	VMS_read_raw = 1;   /* cbreak/noecho */
X	vms_open_tty();
X#endif
X	resume_curses(TRUE);
X}
X
X/* Shut down the Curses package. */
Xvoid endwin()
X{
X	/* change the terminal mode back the way it was */
X	suspend_curses();
X#if AMIGA
X	amiclosewin();
X#endif
X}
X
X
Xstatic int curses_active = FALSE;
X
X/* Send any required termination strings.  Turn off "raw" mode. */
Xvoid suspend_curses()
X{
X#if ANY_UNIX && !UNIXV
X	struct tchars	tbuf;
X# ifdef TIOCSLTC
X	struct ltchars	ltbuf;
X# endif
X#endif
X#ifndef NO_CURSORSHAPE
X	if (has_CQ)
X	{
X		do_CQ();
X	}
X#endif
X	if (has_TE)					/* GB */
X	{
X		do_TE();
X	}
X	if (has_KE)
X	{
X		do_KE();
X	}
X#ifndef NO_COLOR
X	quitcolor();
X#endif
X	refresh();
X
X	/* change the terminal mode back the way it was */
X#if ANY_UNIX
X# if UNIXV
X#  if TERMIOS
X	tcsetattr(2, TCSADRAIN, &oldtermio);
X#  else
X	ioctl(2, TCSETAW, &oldtermio);
X#  endif
X# else
X	ioctl(2, TIOCSETP, &oldsgttyb);
X
X	ioctl(2, TIOCGETC, (struct sgttyb *) &tbuf);
X	tbuf.t_intrc = oldint;
X	ioctl(2, TIOCSETC, (struct sgttyb *) &tbuf);
X
X#  ifdef TIOCSLTC
X	ioctl(2, TIOCGLTC, &ltbuf);
X	ltbuf.t_suspc = oldswitch;
X	ltbuf.t_dsuspc = olddswitch;
X	ltbuf.t_lnextc = oldquote;
X	ioctl(2, TIOCSLTC, &ltbuf);
X#  endif
X# endif
X#endif
X#if OSK
X	_ss_opt(0, &oldsgttyb);
X#endif
X#if AMIGA
X	ttyshutdown();
X#endif
X#if MSDOS
X	raw_set_stdio(FALSE);
X#endif
X
X#if VMS
X	VMS_read_raw = 0;
X#endif
X	curses_active = FALSE;
X}
X
X
X/* put the terminal in RAW mode.  If "quietly" is FALSE, then ask the user
X * to hit a key, and wait for keystroke before returning.
X */
Xvoid resume_curses(quietly)
X	int	quietly;
X{
X	if (!curses_active)
X	{
X		/* change the terminal mode to cbreak/noecho */
X#if ANY_UNIX
X# if UNIXV
X		ospeed = (oldtermio.c_cflag & CBAUD);
X		ERASEKEY = oldtermio.c_cc[VERASE];
X		newtermio = oldtermio;
X		newtermio.c_iflag &= (IXON|IXOFF|IXANY|ISTRIP|IGNBRK);
X		newtermio.c_oflag &= ~OPOST;
X		newtermio.c_lflag &= ISIG;
X		newtermio.c_cc[VINTR] = ctrl('C'); /* always use ^C for interrupts */
X		newtermio.c_cc[VMIN] = 1;
X		newtermio.c_cc[VTIME] = 0;
X#  ifdef VSWTCH
X		newtermio.c_cc[VSWTCH] = 0;
X#  endif
X#  ifdef VSUSP
X		newtermio.c_cc[VSUSP] = 0;
X#  endif
X#  ifdef TERMIOS
X		tcsetattr(2, TCSADRAIN, &newtermio);
X#  else
X		ioctl(2, TCSETAW, &newtermio);
X#  endif
X# else /* BSD or V7 or Coherent or Minix */
X		struct tchars	tbuf;
X#  ifdef TIOCSLTC
X		struct ltchars	ltbuf;
X#  endif
X
X		ospeed = oldsgttyb.sg_ospeed;
X		ERASEKEY = oldsgttyb.sg_erase;
X		newsgttyb = oldsgttyb;
X		newsgttyb.sg_flags |= CBREAK;
X		newsgttyb.sg_flags &= ~(CRMOD|ECHO|XTABS);
X		ioctl(2, TIOCSETP, &newsgttyb);
X
X		ioctl(2, TIOCGETC, (struct sgttyb *) &tbuf);
X		oldint = tbuf.t_intrc;
X		tbuf.t_intrc = ctrl('C');	/* always use ^C for interrupts */
X		ioctl(2, TIOCSETC, (struct sgttyb *) &tbuf);
X
X#  ifdef TIOCSLTC
X		ioctl(2, TIOCGLTC, &ltbuf);
X		oldswitch = ltbuf.t_suspc;
X		ltbuf.t_suspc = 0;		/* disable ^Z for elvis */
X		olddswitch = ltbuf.t_dsuspc;
X		ltbuf.t_dsuspc = 0;		/* disable ^Y for elvis */
X		oldquote = ltbuf.t_lnextc;
X		ltbuf.t_lnextc = 0;		/* disable ^V for elvis */
X		ioctl(2, TIOCSLTC, &ltbuf);
X#  endif
X
X# endif
X#endif
X#if OSK
X		newsgttyb = oldsgttyb;
X		newsgttyb.sg_echo = 0;
X		newsgttyb.sg_eofch = 0;
X		newsgttyb.sg_kbach = 0;
X		newsgttyb.sg_kbich = ctrl('C');
X		_ss_opt(0, &newsgttyb);
X		ospeed = oldsgttyb.sg_baud;
X		ERASEKEY = oldsgttyb.sg_bspch;
X#endif
X#if AMIGA
X		/* turn on window resize and RAW */
X		ttysetup();
X#endif
X#if MSDOS
X		raw_set_stdio(TRUE);
X#endif
X
X#if VMS
X		VMS_read_raw = 1;
X		{ int c;
X			read(0,&c,0);   /* Flush the tty buffer. */
X		}
X		ERASEKEY = '\177';  /* Accept <DEL> as <^H> for VMS */
X#endif
X
X		if (has_TI)					/* GB */
X		{
X			do_TI();
X		}
X		if (has_KS)
X		{
X			do_KS();
X		}
X
X		curses_active = TRUE;
X	}
X
X	/* If we're supposed to quit quietly, then we're done */
X	if (quietly)
X	{
X		return;
X	}
X
X	signal(SIGINT, SIG_IGN);
X
X	move(LINES - 1, 0);
X	do_SO();
X#if VMS
X	qaddstr("\n[Press <RETURN> to continue]");
X#else
X	qaddstr("[Press <RETURN> to continue]");
X#endif
X	do_SE();
X	refresh();
X	ttyread(kbuf, 20, 0); /* in RAW mode, so <20 is very likely */
X	if (kbuf[0] == ':')
X	{
X		mode = MODE_COLON;
X		addch('\n');
X		refresh();
X	}
X	else
X	{
X		mode = MODE_VI;
X		redraw(MARK_UNSET, FALSE);
X	}	
X	exwrote = FALSE;
X
X#if TURBOC || __GNUC__ || _ANSI
X	signal(SIGINT, (void(*)()) trapint);
X#else
X	signal(SIGINT, trapint);
X#endif
X}
X
X/* This function fetches an optional string from termcap */
Xstatic void mayhave(T, s)
X	char	**T;	/* where to store the returned pointer */
X	char	*s;	/* name of the capability */
X{
X	char	*val;
X
X	val = tgetstr(s, &capbuf);
X	if (val)
X	{
X		*T = val;
X	}
X}
X
X
X/* This function fetches a required string from termcap */
Xstatic void musthave(T, s)
X	char	**T;	/* where to store the returned pointer */
X	char	*s;	/* name of the capability */
X{
X	mayhave(T, s);
X	if (!*T)
X	{
X		write(2, "This termcap entry lacks the :", (unsigned)30);
X		write(2, s, (unsigned)2);
X		write(2, "=: capability\n", (unsigned)14);
X#if OSK
X		write(2, "\l", 1);
X#endif
X		exit(1);
X	}
X}
X
X
X/* This function fetches a pair of strings from termcap.  If one of them is
X * missing, then the other one is ignored.
X */
Xstatic void pair(T, U, sT, sU)
X	char	**T;	/* where to store the first pointer */
X	char	**U;	/* where to store the second pointer */
X	char	*sT;	/* name of the first capability */
X	char	*sU;	/* name of the second capability */
X{
X	mayhave(T, sT);
X	mayhave(U, sU);
X	if (!**T || !**U)
X	{
X		*T = *U = "";
X	}
X}
X
X
X
X/* Read everything from termcap */
Xstatic void starttcap(term)
X	char	*term;
X{
X	static char	cbmem[800];
X
X	/* allocate memory for capbuf */
X	capbuf = cbmem;
X
X	/* get the termcap entry */
X	switch (tgetent(kbuf, term))
X	{
X	  case -1:
X		write(2, "Can't read /etc/termcap\n", (unsigned)24);
X#if OSK
X		write(2, "\l", 1);
X#endif
X		exit(2);
X
X	  case 0:
X		write(2, "Unrecognized TERM type\n", (unsigned)23);
X#if OSK
X		write(2, "\l", 1);
X#endif
X		exit(3);
X	}
X
X	/* get strings */
X	musthave(&UP, "up");
X	mayhave(&VB, "vb");
X	musthave(&CM, "cm");
X	pair(&SO, &SE, "so", "se");
X	mayhave(&TI, "ti");
X	mayhave(&TE, "te");
X	if (tgetnum("ug") <= 0)
X	{
X		pair(&US, &UE, "us", "ue");
X		pair(&MD, &ME, "md", "me");
X
X		/* get italics, or have it default to underline */
X		pair(&AS, &AE, "as", "ae");
X		if (!*AS)
X		{
X			AS = US;
X			AE = UE;
X		}
X	}
X#ifndef NO_VISIBLE
X	MV = SO; /* by default */
X	mayhave(&MV, "mv");
X#endif
X	mayhave(&AL, "al");
X	mayhave(&DL, "dl");
X	musthave(&CE, "ce");
X	mayhave(&CD, "cd");
X#if OSK
X	mayhave(&SR_, "sr");
X#else	
X	mayhave(&SR, "sr");
X#endif
X	pair(&IM, &EI, "im", "ei");
X	mayhave(&IC, "ic");
X	mayhave(&DC, "dc");
X
X	/* other termcap stuff */
X	AM = (tgetflag("am") && !tgetflag("xn"));
X	PT = tgetflag("pt");
X#if AMIGA
X	amiopenwin(termtype);	/* Must run this before ttysetup(); */
X	ttysetup();	/* Must run this before getsize(0); */
X#endif
X	getsize(0);
X
X	/* Key sequences */
X	pair(&KS, &KE, "ks", "ke");
X	mayhave(&KU, "ku");		/* up */
X	mayhave(&KD, "kd");		/* down */
X	mayhave(&KL, "kl");		/* left */
X	mayhave(&KR, "kr");		/* right */
X	mayhave(&PU, "kP");		/* PgUp */
X	mayhave(&PD, "kN");		/* PgDn */
X	mayhave(&HM, "kh");		/* Home */
X	mayhave(&EN, "kH");		/* End */
X	mayhave(&KI, "kI");		/* Insert */
X#ifndef CRUNCH
X	if (!PU) mayhave(&PU, "K2");	/* "3x3 pad" names for PgUp, etc. */
X	if (!PD) mayhave(&PD, "K5");
X	if (!HM) mayhave(&HM, "K1");
X	if (!EN) mayhave(&EN, "K4");
X
X	mayhave(&PU, "PU");		/* old XENIX names for PgUp, etc. */
X	mayhave(&PD, "PD");		/* (overrides others, if used.) */
X	mayhave(&HM, "HM");
X	mayhave(&EN, "EN");
X#endif
X#ifndef NO_FKEY
X	mayhave(&FKEY[0], "k0");		/* function key codes */
X	mayhave(&FKEY[1], "k1");
X	mayhave(&FKEY[2], "k2");
X	mayhave(&FKEY[3], "k3");
X	mayhave(&FKEY[4], "k4");
X	mayhave(&FKEY[5], "k5");
X	mayhave(&FKEY[6], "k6");
X	mayhave(&FKEY[7], "k7");
X	mayhave(&FKEY[8], "k8");
X	mayhave(&FKEY[9], "k9");
X# ifndef NO_SHIFT_FKEY
X	mayhave(&FKEY[10], "s0");		/* shift function key codes */
X	mayhave(&FKEY[11], "s1");
X	mayhave(&FKEY[12], "s2");
X	mayhave(&FKEY[13], "s3");
X	mayhave(&FKEY[14], "s4");
X	mayhave(&FKEY[15], "s5");
X	mayhave(&FKEY[16], "s6");
X	mayhave(&FKEY[17], "s7");
X	mayhave(&FKEY[18], "s8");
X	mayhave(&FKEY[19], "s9");
X#  ifndef NO_CTRL_FKEY
X	mayhave(&FKEY[20], "c0");		/* control function key codes */
X	mayhave(&FKEY[21], "c1");
X	mayhave(&FKEY[22], "c2");
X	mayhave(&FKEY[23], "c3");
X	mayhave(&FKEY[24], "c4");
X	mayhave(&FKEY[25], "c5");
X	mayhave(&FKEY[26], "c6");
X	mayhave(&FKEY[27], "c7");
X	mayhave(&FKEY[28], "c8");
X	mayhave(&FKEY[29], "c9");
X#   ifndef NO_ALT_FKEY
X	mayhave(&FKEY[30], "a0");		/* alt function key codes */
X	mayhave(&FKEY[31], "a1");
X	mayhave(&FKEY[32], "a2");
X	mayhave(&FKEY[33], "a3");
X	mayhave(&FKEY[34], "a4");
X	mayhave(&FKEY[35], "a5");
X	mayhave(&FKEY[36], "a6");
X	mayhave(&FKEY[37], "a7");
X	mayhave(&FKEY[38], "a8");
X	mayhave(&FKEY[39], "a9");
X#   endif
X#  endif
X# endif
X#endif
X
X#ifndef NO_CURSORSHAPE
X	/* cursor shapes */
X	CQ = tgetstr("cQ", &capbuf);
X	if (has_CQ)
X	{
X		CX = tgetstr("cX", &capbuf);
X		if (!CX) CX = CQ;
X		CV = tgetstr("cV", &capbuf);
X		if (!CV) CV = CQ;
X		CI = tgetstr("cI", &capbuf);
X		if (!CI) CI = CQ;
X		CR = tgetstr("cR", &capbuf);
X		if (!CR) CR = CQ;
X	}
X# ifndef CRUNCH
X	else
X	{
X		CQ = CV = "";
X		pair(&CQ, &CV, "ve", "vs");
X		CX = CI = CR = CQ;
X	}
X# endif /* !CRUNCH */
X#endif /* !NO_CURSORSHAPE */
X
X#ifndef NO_COLOR
X	strcpy(SOcolor, SO);
X	strcpy(SEcolor, SE);
X	strcpy(AScolor, AS);
X	strcpy(AEcolor, AE);
X	strcpy(MDcolor, MD);
X	strcpy(MEcolor, ME);
X	strcpy(UScolor, US);
X	strcpy(UEcolor, UE);
X# ifndef NO_POPUP
X	strcpy(POPUPcolor, SO);
X# endif
X# ifndef NO_VISIBLE
X	strcpy(VISIBLEcolor, MV);
X# endif
X#endif
X
X}
X
X
X/* This function gets the window size.  It uses the TIOCGWINSZ ioctl call if
X * your system has it, or tgetnum("li") and tgetnum("co") if it doesn't.
X * This function is called once during initialization, and thereafter it is
X * called whenever the SIGWINCH signal is sent to this process.
X */
Xint getsize(signo)
X	int	signo;
X{
X	int	lines;
X	int	cols;
X#ifdef TIOCGWINSZ
X	struct winsize size;
X#endif
X
X#ifdef SIGWINCH
X	/* reset the signal vector */
X	signal(SIGWINCH, getsize);
X#endif
X
X	/* get the window size, one way or another. */
X	lines = cols = 0;
X#ifdef TIOCGWINSZ
X	if (ioctl(2, TIOCGWINSZ, &size) >= 0)
X	{
X		lines = size.ws_row;
X		cols = size.ws_col;
X	}
X#endif
X#if AMIGA
X	/* Amiga gets window size by asking the console.device */
X	if (!strcmp(TERMTYPE, termtype))
X	{
X	    auto long len;
X	    auto char buf[30];
X	    
X	    Write(Output(), "\2330 q", 4); /* Ask the console.device */
X	    len = Read(Input(), buf, 29);
X	    buf[len] = '\000';
X	    sscanf(&buf[5], "%d;%d", &lines, &cols);
X	}
X#endif
X	if ((lines == 0 || cols == 0) && signo == 0)
X	{
X		LINES = tgetnum("li");
X		COLS = tgetnum("co");
X	}
X#if MSDOS
X# ifdef RAINBOW
X	if (!strcmp(termtype, "rainbow"))
X	{
X		/* Determine whether Rainbow is in 80-column or 132-column mode */
X		cols = *(unsigned char far *)0xee000f57L;
X	}
X	else
X# endif
X	{
X		lines = v_rows();
X		cols = v_cols();
X	}
X#endif
X	if (lines >= 2 && cols >= 30)
X	{
X		LINES = lines;
X		COLS = cols;
X	}
X
X	/* Make sure we got values that we can live with */
X	if (LINES < 2 || COLS < 30)
X	{
X		write(2, "Screen too small\n", (unsigned)17);
X#if OSK
X		write(2, "\l", 1);
X#endif
X		endwin();
X		exit(2);
X	}
X
X#if AMIGA
X	if (*o_lines != LINES || *o_columns != COLS)
X	{
X		*o_lines = LINES;
X		*o_columns = COLS;
X	}
X#endif
X
X	return 0;
X}
X
X
X/* This is a function version of addch() -- it is used by tputs() */
Xint faddch(ch)
X	int	ch;
X{
X	addch(ch);
X
X	return 0;
X}
X
X/* This function quickly adds a string to the output queue.  It does *NOT*
X * convert \n into <CR><LF>.
X */
Xvoid qaddstr(str)
X	char	*str;
X{
X	REG char *s_, *d_;
X
X#if MSDOS
X	if (o_pcbios[0])
X	{
X		while (*str)
X			qaddch(*str++);
X		return;
X	}
X#endif
X	for (s_=(str), d_=stdscr; *d_++ = *s_++; )
X	{
X	}
X	stdscr = d_ - 1;
X}
X
X/* Output the ESC sequence needed to go into any video mode, if supported */
Xvoid attrset(a)
X	int	a;
X{
X	do_aend();
X	if (a == A_BOLD)
X	{
X		do_MD();
X		aend = ME;
X	}
X	else if (a == A_UNDERLINE)
X	{
X		do_US();
X		aend = UE;
X	}
X	else if (a == A_ALTCHARSET)
X	{
X		do_AS();
X		aend = AE;
X	}
X	else
X	{
X		aend = "";
X	}
X}
X
X
X/* Insert a single character into the display */
Xvoid insch(ch)
X	int	ch;
X{
X	if (has_IM)
X		do_IM();
X	do_IC();
X	qaddch(ch);
X	if (has_EI)
X		do_EI();
X}
X
Xvoid wrefresh()
X{
X	if (stdscr != kbuf)
X	{
X		VOIDBIOS(;,ttywrite(kbuf, (unsigned)(stdscr - kbuf)));
X		stdscr = kbuf;
X	}
X}
X
Xvoid wqrefresh()
X{
X	if (stdscr - kbuf > 2000)
X	{
X		VOIDBIOS(stdscr = kbuf,
X		{
X			ttywrite(kbuf, (unsigned)(stdscr - kbuf)); 
X			stdscr = kbuf;
X		});
X	}
X}
X
X#ifndef NO_COLOR
X/* This function is called during termination.  It resets color modes */
Xint ansiquit()
X{
X	/* if ANSI color terminal, then reset the colors */
X	if (!strcmp(UP, "\033[A"))
X	{
X		tputs("\033[37;40m\033[m", 1, faddch);
X		clrtoeol();
X		return 1;
X	}
X	return 0;
X}
X
X/* This sets the color strings that work for ANSI terminals.  If the TERMCAP
X * doesn't look like an ANSI terminal, then it returns FALSE.  If the colors
X * aren't understood, it also returns FALSE.  If all goes well, it returns TRUE
X */
Xint ansicolor(cmode, attrbyte)
X	int	cmode;		/* mode to set, e.g. A_NORMAL */
X	int	attrbyte;	/* IBM PC attribute byte */
X{
X	char	temp[16];	/* hold the new mode string */
X
X	/* if not ANSI-ish, then fail */
X	if (strcmp(UP, "\033[A") && strcmp(UP, "\033OA"))
X	{
X		msg("Don't know how to set colors for this terminal");
X		return 0;
X	}
X
X	/* construct the color string */
X	sprintf(temp, "\033[m\033[3%c;4%c%s%sm",
X		"04261537"[attrbyte & 0x07],
X		"04261537"[(attrbyte >> 4) & 0x07],
X		(attrbyte & 0x08) ? ";1" : "",
X		(attrbyte & 0x80) ? ";5" : "");
X
X	/* stick it in the right place */
X	switch (cmode)
X	{
X	  case A_NORMAL:
X		if (!strcmp(MEcolor, normalcolor))
X			strcpy(MEcolor, temp);
X		if (!strcmp(UEcolor, normalcolor))
X			strcpy(UEcolor, temp);
X		if (!strcmp(AEcolor, normalcolor))
X			strcpy(AEcolor, temp);
X		if (!strcmp(SEcolor, normalcolor))
X			strcpy(SEcolor, temp);
X
X		strcpy(normalcolor, temp);
X		tputs(normalcolor, 1, faddch);
X		break;
X
X	  case A_BOLD:
X		strcpy(MDcolor, temp);
X		strcpy(MEcolor, normalcolor);
X		break;
X
X	  case A_UNDERLINE:
X		strcpy(UScolor, temp);
X		strcpy(UEcolor, normalcolor);
X		break;
X
X	  case A_ALTCHARSET:
X		strcpy(AScolor, temp);
X		strcpy(AEcolor, normalcolor);
X		break;
X
X	  case A_STANDOUT:
X		strcpy(SOcolor, temp);
X		strcpy(SEcolor, normalcolor);
X		break;
X
X#ifndef NO_POPUP
X	  case A_POPUP:
X		strcpy(POPUPcolor, temp);
X		break;
X#endif
X
X#ifndef NO_VISIBLE
X	  case A_VISIBLE:
X		strcpy(VISIBLEcolor, temp);
X		break;
X#endif
X	}
X
X	return 1;
X}
X
X
X/* This function outputs the ESC sequence needed to switch the screen back
X * to "normal" mode.  On color terminals which haven't had their color set
X * yet, this is one of the termcap strings; for color terminals that really
X * have had colors defined, we just the "normal color" escape sequence.
X */
Xendcolor()
X{
X	if (aend == ME)
X		tputs(MEcolor, 1, faddch);
X	else if (aend == UE)
X		tputs(UEcolor, 1, faddch);
X	else if (aend == AE)
X		tputs(AEcolor, 1, faddch);
X	else if (aend == SE)
X		tputs(SEcolor, 1, faddch);
X	aend = "";
X	return 0;
X}
X
X
X#endif /* !NO_COLOR */
/
echo x - curses.h
sed '/^X/s///' > curses.h << '/'
X/* curses.h */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This is the header file for a small, fast, fake curses package */
X
X/* termcap stuff */
Xextern char	*tgoto();
Xextern char	*tgetstr();
Xextern void	tputs();
X
X#if MSDOS
X/* BIOS interface used instead of termcap for MS-DOS */
Xextern int	vmode;
Xextern void	v_up();
Xextern void	v_cb();
Xextern void	v_cs();
Xextern void	v_ce();
Xextern void	v_cl();
Xextern void	v_cd();
Xextern void	v_al();
Xextern void	v_dl();
Xextern void	v_sr();
Xextern void	v_move();
X#endif
X
X/* faddch() is a function.  a pointer to it is passed to tputs() */
Xextern int	faddch();
X
X/* data types */
X#define WINDOW	char
X
X/* CONSTANTS & SYMBOLS */
X#define TRUE		1
X#define FALSE		0
X#define A_NORMAL	0
X#define A_STANDOUT	1
X#define A_BOLD		2
X#define A_UNDERLINE	3
X#define A_ALTCHARSET	4
X#define A_POPUP		5
X#define A_VISIBLE	6
X#define KBSIZ		4096
X
X/* figure out how many function keys we need to allow. */
X#ifndef NO_FKEY
X# ifdef NO_SHIFT_FKEY
X#  define	NFKEYS	10
X# else
X#  ifdef NO_CTRL_FKEY
X#   define	NFKEYS	20
X#  else
X#   ifdef NO_ALT_FKEY
X#    define	NFKEYS	30
X#   else
X#    define	NFKEYS	40
X#   endif
X#  endif
X# endif
Xextern char	*FKEY[NFKEYS];	/* :k0=:...:k9=: codes sent by function keys */
X#endif
X
X/* extern variables, defined in curses.c */
Xextern char	*termtype;	/* name of terminal entry */
Xextern short	ospeed;		/* tty speed, eg B2400 */
X#if OSK
Xextern char PC_;	/* Pad char */
Xextern char	*BC;	/* Backspace char string */
X#else
Xextern char	PC;		/* Pad char */
X#endif
Xextern WINDOW	*stdscr;	/* pointer into kbuf[] */
Xextern WINDOW	kbuf[KBSIZ];	/* a very large output buffer */
Xextern int	LINES;		/* :li#: number of rows */
Xextern int	COLS;		/* :co#: number of columns */
Xextern int	AM;		/* :am:  boolean: auto margins? */
Xextern int	PT;		/* :pt:  boolean: physical tabs? */
Xextern char	*VB;		/* :vb=: visible bell */
Xextern char	*UP;		/* :up=: move cursor up */
Xextern char	*SO;		/* :so=: standout start */
Xextern char	*SE;		/* :se=: standout end */
Xextern char	*US;		/* :us=: underline start */
Xextern char	*UE;		/* :ue=: underline end */
Xextern char	*MD;		/* :md=: bold start */
Xextern char	*ME;		/* :me=: bold end */
Xextern char	*AS;		/* :as=: alternate (italic) start */
Xextern char	*AE;		/* :ae=: alternate (italic) end */
X#ifndef NO_VISIBLE
Xextern char	*MV;		/* :mv=: "visible" selection start */
X#endif
Xextern char	*CM;		/* :cm=: cursor movement */
Xextern char	*CE;		/* :ce=: clear to end of line */
Xextern char	*CD;		/* :cd=: clear to end of screen */
Xextern char	*AL;		/* :al=: add a line */
Xextern char	*DL;		/* :dl=: delete a line */
X#if OSK
Xextern char	*SR_;		/* :sr=: scroll reverse */
X#else
Xextern char	*SR;		/* :sr=: scroll reverse */
X#endif
Xextern char	*KS;		/* :ks=: init string for cursor */
Xextern char	*KE;		/* :ke=: restore string for cursor */
Xextern char	*KU;		/* :ku=: sequence sent by up key */
Xextern char	*KD;		/* :kd=: sequence sent by down key */
Xextern char	*KL;		/* :kl=: sequence sent by left key */
Xextern char	*KR;		/* :kr=: sequence sent by right key */
Xextern char	*PU;		/* :PU=: key sequence sent by PgUp key */
Xextern char	*PD;		/* :PD=: key sequence sent by PgDn key */
Xextern char	*HM;		/* :HM=: key sequence sent by Home key */
Xextern char	*EN;		/* :EN=: key sequence sent by End key */
Xextern char	*KI;		/* :kI=: key sequence sent by Insert key */
Xextern char	*IM;		/* :im=: insert mode start */
Xextern char	*IC;		/* :ic=: insert following char */
Xextern char	*EI;		/* :ei=: insert mode end */
Xextern char	*DC;		/* :dc=: delete a character */
Xextern char	*TI;		/* :ti=: terminal init */	/* GB */
Xextern char	*TE;		/* :te=: terminal exit */	/* GB */
X#ifndef NO_CURSORSHAPE
Xextern char	*CQ;		/* :cQ=: normal cursor */
Xextern char	*CX;		/* :cX=: cursor used for EX command/entry */
Xextern char	*CV;		/* :cV=: cursor used for VI command mode */
Xextern char	*CI;		/* :cI=: cursor used for VI input mode */
Xextern char	*CR;		/* :cR=: cursor used for VI replace mode */
X#endif
Xextern char	*aend;		/* end an attribute -- either UE or ME */
Xextern char	ERASEKEY;	/* taken from the ioctl structure */
X#ifndef NO_COLOR
Xextern char	SOcolor[];
Xextern char	SEcolor[];
Xextern char	UScolor[];
Xextern char	UEcolor[];
Xextern char	MDcolor[];
Xextern char	MEcolor[];
Xextern char	AScolor[];
Xextern char	AEcolor[];
X# ifndef NO_POPUP
Xextern char	POPUPcolor[];
X# endif
X# ifndef NO_VISIBLE
Xextern char	VISIBLEcolor[];
X# endif
Xextern char	normalcolor[];
X#endif /* undef NO_COLOR */
X
X/* Msdos-versions may use bios; others always termcap.
X * Will emit some 'code has no effect' warnings in unix.
X */
X 
X#if MSDOS
Xextern char o_pcbios[1];		/* BAH! */
X#define	CHECKBIOS(x,y)	(*o_pcbios ? (x) : (y))
X#define VOIDBIOS(x,y)	{if (*o_pcbios) {x;} else {y;}}
X#else
X#define	CHECKBIOS(x,y)	(y)
X#define VOIDBIOS(x,y)	{y;}
X#endif
X
X#ifndef NO_COLOR
X# define setcolor(m,a)	CHECKBIOS(bioscolor(m,a), ansicolor(m,a))
X# define fixcolor()	VOIDBIOS(;, tputs(normalcolor, 1, faddch))
X# define quitcolor()	CHECKBIOS(biosquit(), ansiquit())
X# define do_SO()	VOIDBIOS((vmode=A_STANDOUT), tputs(SOcolor, 1, faddch))
X# define do_SE()	VOIDBIOS((vmode=A_NORMAL), tputs(SEcolor, 1, faddch))
X# define do_US()	VOIDBIOS((vmode=A_UNDERLINE), tputs(UScolor, 1, faddch))
X# define do_UE()	VOIDBIOS((vmode=A_NORMAL), tputs(UEcolor, 1, faddch))
X# define do_MD()	VOIDBIOS((vmode=A_BOLD), tputs(MDcolor, 1, faddch))
X# define do_ME()	VOIDBIOS((vmode=A_NORMAL), tputs(MEcolor, 1, faddch))
X# define do_AS()	VOIDBIOS((vmode=A_ALTCHARSET), tputs(AScolor, 1, faddch))
X# define do_AE()	VOIDBIOS((vmode=A_NORMAL), tputs(AEcolor, 1, faddch))
X# define do_POPUP()	VOIDBIOS((vmode=A_POPUP), tputs(POPUPcolor, 1, faddch))
X# define do_VISIBLE()	VOIDBIOS((vmode=A_VISIBLE), tputs(VISIBLEcolor, 1, faddch))
X#else
X# define do_SO()	VOIDBIOS((vmode=A_STANDOUT), tputs(SO, 1, faddch))
X# define do_SE()	VOIDBIOS((vmode=A_NORMAL), tputs(SE, 1, faddch))
X# define do_US()	VOIDBIOS((vmode=A_UNDERLINE), tputs(US, 1, faddch))
X# define do_UE()	VOIDBIOS((vmode=A_NORMAL), tputs(UE, 1, faddch))
X# define do_MD()	VOIDBIOS((vmode=A_BOLD), tputs(MD, 1, faddch))
X# define do_ME()	VOIDBIOS((vmode=A_NORMAL), tputs(ME, 1, faddch))
X# define do_AS()	VOIDBIOS((vmode=A_ALTCHARSET), tputs(AS, 1, faddch))
X# define do_AE()	VOIDBIOS((vmode=A_NORMAL), tputs(AE, 1, faddch))
X# define do_POPUP()	VOIDBIOS((vmode=A_POPUP), tputs(SO, 1, faddch))
X# define do_VISIBLE()	VOIDBIOS((vmode=A_VISIBLE), tputs(MV, 1, faddch))
X#endif
X
X#define	do_VB()		VOIDBIOS(;, tputs(VB, 1, faddch))
X#define	do_UP()		VOIDBIOS(v_up(), tputs(UP, 1, faddch))
X#undef	do_CM		/* move */
X#define	do_CE()		VOIDBIOS(v_ce(), tputs(CE, 1, faddch))
X#define	do_CD()		VOIDBIOS(v_cd(), tputs(CD, 1, faddch))
X#define	do_AL()		VOIDBIOS(v_al(), tputs(AL, LINES, faddch))
X#define	do_DL()		VOIDBIOS(v_dl(), tputs(DL, LINES, faddch))
X#if OSK
X#define	do_SR()		VOIDBIOS(v_sr(), tputs(SR_, 1, faddch))
X#else
X#define	do_SR()		VOIDBIOS(v_sr(), tputs(SR, 1, faddch))
X#endif
X#define do_KS()		VOIDBIOS(1, tputs(KS, 1, faddch))
X#define do_KE()		VOIDBIOS(1, tputs(KE, 1, faddch))
X#define	do_IM()		VOIDBIOS(;, tputs(IM, 1, faddch))
X#define	do_IC()		VOIDBIOS(;, tputs(IC, 1, faddch))
X#define	do_EI()		VOIDBIOS(;, tputs(EI, 1, faddch))
X#define	do_DC()		VOIDBIOS(;, tputs(DC, COLS, faddch))
X#define	do_TI()		VOIDBIOS(;, (void)ttywrite(TI, (unsigned)strlen(TI)))
X#define	do_TE()		VOIDBIOS(;, (void)ttywrite(TE, (unsigned)strlen(TE)))
X#ifndef NO_CURSORSHAPE
X# define do_CQ()	VOIDBIOS(v_cs(), tputs(CQ, 1, faddch))
X# define do_CX()	VOIDBIOS(v_cs(), tputs(CX, 1, faddch))
X# define do_CV()	VOIDBIOS(v_cs(), tputs(CV, 1, faddch))
X# define do_CI()	VOIDBIOS(v_cb(), tputs(CI, 1, faddch))
X# define do_CR()	VOIDBIOS(v_cb(), tputs(CR, 1, faddch))
X#endif
X#ifndef NO_COLOR
X# define do_aend()	VOIDBIOS((vmode=A_NORMAL), endcolor())
X#else
X# define do_aend()	VOIDBIOS((vmode=A_NORMAL), tputs(aend, 1, faddch))
X#endif
X
X#define	has_AM		CHECKBIOS(1, AM)
X#define	has_PT		CHECKBIOS(0, PT)
X#define	has_VB		CHECKBIOS((char *)0, VB)
X#define	has_UP		CHECKBIOS((char *)1, UP)
X#define	has_SO		CHECKBIOS((char)1, (*SO))
X#define	has_SE		CHECKBIOS((char)1, (*SE))
X#define	has_US		CHECKBIOS((char)1, (*US))
X#define	has_UE		CHECKBIOS((char)1, (*UE))
X#define	has_MD		CHECKBIOS((char)1, (*MD))
X#define	has_ME		CHECKBIOS((char)1, (*ME))
X#define	has_AS		CHECKBIOS((char)1, (*AS))
X#define	has_AE		CHECKBIOS((char)1, (*AE))
X#undef	has_CM		/* cursor move: don't need */
X#define	has_CB		CHECKBIOS(1, 0)
X#define	has_CS		CHECKBIOS(1, 0)
X#define	has_CE		CHECKBIOS((char *)1, CE)
X#define	has_CD		CHECKBIOS((char *)1, CD)
X#define	has_AL		CHECKBIOS((char *)1, AL)
X#define	has_DL		CHECKBIOS((char *)1, DL)
X#if OSK
X#define	has_SR		CHECKBIOS((char *)1, SR_)
X#else
X#define	has_SR		CHECKBIOS((char *)1, SR)
X#endif
X#define has_KS		CHECKBIOS((char)1, (*KS))
X#define has_KE		CHECKBIOS((char)1, (*KE))
X#define	has_KU		KU
X#define	has_KD		KD
X#define	has_KL		KL
X#define	has_KR		KR
X#define has_HM		HM
X#define has_EN		EN
X#define has_PU		PU
X#define has_PD		PD
X#define has_KI		KI
X#define	has_IM		CHECKBIOS((char)0, (*IM))
X#define	has_IC		CHECKBIOS((char)0, (*IC))
X#define	has_EI		CHECKBIOS((char)0, (*EI))
X#define	has_DC		CHECKBIOS((char *)0, DC)
X#define	has_TI		CHECKBIOS((char)0, (*TI))
X#define	has_TE		CHECKBIOS((char)0, (*TE))
X#ifndef NO_CURSORSHAPE
X#define has_CQ		CHECKBIOS((char *)1, CQ)
X#endif
X
X/* (pseudo)-Curses-functions */
X
X#ifdef lint
X# define _addCR		VOIDBIOS(;, (stdscr[-1] == '\n' ? qaddch('\r') : (stdscr[-1] = '\n')))
X#else
X# if OSK
X#  define _addCR	VOIDBIOS(;, (stdscr[-1] == '\n' ? qaddch('\l') : (stdscr[-1] = stdscr[-1])))
X# else
X#  define _addCR	VOIDBIOS(;, (stdscr[-1] == '\n' ? qaddch('\r') : 0))
X# endif
X#endif
X
X#ifdef AZTEC_C
X# define qaddch(ch)	CHECKBIOS(v_put(ch), (*stdscr = (ch), *stdscr++))
X#else
X#define qaddch(ch)	CHECKBIOS(v_put(ch), (*stdscr++ = (ch)))
X#endif
X
X#if OSK
X#define addch(ch)	if (qaddch(ch) == '\n') qaddch('\l'); else
X#else
X#define addch(ch)	if (qaddch(ch) == '\n') qaddch('\r'); else
X#endif
X
Xextern void initscr();
Xextern void endwin();
Xextern void suspend_curses();
Xextern void resume_curses();
Xextern void attrset();
Xextern void insch();
Xextern void qaddstr();
Xextern void wrefresh();
Xextern void wqrefresh();
X#define addstr(str)	{qaddstr(str); _addCR;}
X#define move(y,x)	VOIDBIOS(v_move(x,y), tputs(tgoto(CM, x, y), 1, faddch))
X#define mvaddch(y,x,ch)	{move(y,x); addch(ch);}
X#define refresh()	VOIDBIOS(;, wrefresh())
X#define standout()	do_SO()
X#define standend()	do_SE()
X#define clrtoeol()	do_CE()
X#define clrtobot()	do_CD()
X#define insertln()	do_AL()
X#define deleteln()	do_DL()
X#define delch()		do_DC()
X#define scrollok(w,b)
X#define raw()
X#define echo()
X#define cbreak()
X#define noraw()
X#define noecho()
X#define nocbreak()
/
echo x - cut.c
sed '/^X/s///' > cut.c << '/'
X/* cut.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains function which manipulate the cut buffers. */
X
X#include "config.h"
X#include "vi.h"
X#if TURBOC
X#include <process.h>		/* needed for getpid */
X#endif
X#if TOS
X#include <osbind.h>
X#define	rename(a,b)	Frename(0,a,b)
X#endif
X
X# define NANONS	9	/* number of anonymous buffers */
X
Xstatic struct cutbuf
X{
X	short	*phys;	/* pointer to an array of #s of BLKs containing text */
X	int	nblks;	/* number of blocks in phys[] array */
X	int	start;	/* offset into first block of start of cut */
X	int	end;	/* offset into last block of end of cut */
X	int	tmpnum;	/* ID number of the temp file */
X	char	lnmode;	/* boolean: line-mode cut? (as opposed to char-mode) */
X}
X	named[27],	/* cut buffers "a through "z and ". */
X	anon[NANONS];	/* anonymous cut buffers */
X
Xstatic char	cbname;	/* name chosen for next cut/paste operation */
Xstatic char	dotcb;	/* cut buffer to use if "doingdot" is set */
X
X
X#ifndef NO_RECYCLE
X/* This function builds a list of all blocks needed in the current tmp file
X * for the contents of cut buffers.
X * !!! WARNING: if you have more than ~450000 bytes of text in all of the
X * cut buffers, then this will fail disastrously, because buffer overflow
X * is *not* allowed for.
X */
Xint cutneeds(need)
X	BLK		*need;	/* this is where we deposit the list */
X{
X	struct cutbuf	*cb;	/* used to count through cut buffers */
X	int		i;	/* used to count through blocks of a cut buffer */
X	int		n;	/* total number of blocks in list */
X
X	n = 0;
X
X	/* first the named buffers... */
X	for (cb = named; cb < &named[27]; cb++)
X	{
X		if (cb->tmpnum != tmpnum)
X			continue;
X
X		for (i = cb->nblks; i-- > 0; )
X		{
X			need->n[n++] = cb->phys[i];
X		}
X	}
X
X	/* then the anonymous buffers */
X	for (cb = anon; cb < &anon[NANONS]; cb++)
X	{
X		if (cb->tmpnum != tmpnum)
X			continue;
X
X		for (i = cb->nblks; i-- > 0; )
X		{
X			need->n[n++] = cb->phys[i];
X		}
X	}
X
X	/* return the length of the list */
X	return n;
X}
X#endif
X
Xstatic void maybezap(num)
X	int	num;	/* the tmpnum of the temporary file to [maybe] delete */
X{
X	char	cutfname[80];
X	int	i;
X
X	/* if this is the current tmp file, then we'd better keep it! */
X	if (tmpfd >= 0 && num == tmpnum)
X	{
X		return;
X	}
X
X	/* see if anybody else needs this tmp file */
X	for (i = 27; --i >= 0; )
X	{
X		if (named[i].nblks > 0 && named[i].tmpnum == num)
X		{
X			break;
X		}
X	}
X	if (i < 0)
X	{
X		for (i = NANONS; --i >= 0 ; )
X		{
X			if (anon[i].nblks > 0 && anon[i].tmpnum == num)
X			{
X				break;
X			}
X		}
X	}
X
X	/* if nobody else needs it, then discard the tmp file */
X	if (i < 0)
X	{
X#if MSDOS || TOS
X		strcpy(cutfname, o_directory);
X		if ((i = strlen(cutfname)) && !strchr(":/\\", cutfname[i - 1]))
X			cutfname[i++] = SLASH;
X		sprintf(cutfname + i, TMPNAME + 3, getpid(), num);
X#else
X		sprintf(cutfname, TMPNAME, o_directory, getpid(), num);
X#endif
X		unlink(cutfname);
X	}
X}
X
X/* This function frees a cut buffer.  If it was the last cut buffer that
X * refered to an old temp file, then it will delete the temp file. */
Xstatic void cutfree(buf)
X	struct cutbuf	*buf;
X{
X	int	num;
X
X	/* return immediately if the buffer is already empty */
X	if (buf->nblks <= 0)
X	{
X		return;
X	}
X
X	/* else free up stuff */
X	num = buf->tmpnum;
X	buf->nblks = 0;
X#ifdef DEBUG
X	if (!buf->phys)
X		msg("cutfree() tried to free a NULL buf->phys pointer.");
X	else
X#endif
X	free((char *)buf->phys);
X
X	/* maybe delete the temp file */
X	maybezap(num);
X}
X
X/* This function is called when we are about to abort a tmp file.
X *
X * To minimize the number of extra files lying around, only named cut buffers
X * are preserved in a file switch; the anonymous buffers just go away.
X */
Xvoid cutswitch()
X{
X	int	i;
X
X	/* mark the current temp file as being "obsolete", and close it.  */
X	storename((char *)0);
X	close(tmpfd);
X	tmpfd = -1;
X
X	/* discard all anonymous cut buffers */
X	for (i = 0; i < NANONS; i++)
X	{
X		cutfree(&anon[i]);
X	}
X
X	/* delete the temp file, if we don't really need it */
X	maybezap(tmpnum);
X}
X
X/* This function should be called just before termination of vi */
Xvoid cutend()
X{
X	int	i;
X
X	/* free the anonymous buffers, if they aren't already free */
X	cutswitch();
X
X	/* free all named cut buffers, since they might be forcing an older
X	 * tmp file to be retained.
X	 */
X	for (i = 0; i < 27; i++)
X	{
X		cutfree(&named[i]);
X	}
X
X	/* delete the temp file */
X	maybezap(tmpnum);
X}
X
X
X/* This function is used to select the cut buffer to be used next */
Xvoid cutname(name)
X	int	name;	/* a single character */
X{
X	cbname = name;
X}
X
X
X
X
X/* This function copies a selected segment of text to a cut buffer */
Xvoid cut(from, to)
X	MARK	from;		/* start of text to cut */
X	MARK	to;		/* end of text to cut */
X{
X	int		first;	/* logical number of first block in cut */
X	int		last;	/* logical number of last block used in cut */
X	long		line;	/* a line number */
X	int		lnmode;	/* boolean: will this be a line-mode cut? */
X	MARK		delthru;/* end of text temporarily inserted for apnd */
X	REG struct cutbuf *cb;
X	REG long	l;
X	REG int		i;
X	REG char	*scan;
X	char		*blkc;
X
X	/* detect whether this must be a line-mode cut or char-mode cut */
X	if (markidx(from) == 0 && markidx(to) == 0)
X		lnmode = TRUE;
X	else
X		lnmode = FALSE;
X
X	/* by default, we don't "delthru" anything */
X	delthru = MARK_UNSET;
X
X	/* handle the "doingdot" quirks */
X	if (doingdot)
X	{
X		if (!cbname)
X		{
X			cbname = dotcb;
X		}
X	}
X	else if (cbname != '.')
X	{
X		dotcb = cbname;
X	}
X
X	/* decide which cut buffer to use */
X	if (!cbname)
X	{
X		/* free up the last anonymous cut buffer */
X		cutfree(&anon[NANONS - 1]);
X
X		/* shift the anonymous cut buffers */
X		for (i = NANONS - 1; i > 0; i--)
X		{
X			anon[i] = anon[i - 1];
X		}
X
X		/* use the first anonymous cut buffer */
X		cb = anon;
X		cb->nblks = 0;
X	}
X	else if (cbname >= 'a' && cbname <= 'z')
X	{
X		cb = &named[cbname - 'a'];
X		cutfree(cb);
X	}
X#ifndef CRUNCH
X	else if (cbname >= 'A' && cbname <= 'Z')
X	{
X		cb = &named[cbname - 'A'];
X		if (cb->nblks > 0)
X		{
X			/* resolve linemode/charmode differences */
X			if (!lnmode && cb->lnmode)
X			{
X				from &= ~(BLKSIZE - 1);
X				if (markidx(to) != 0 || to == from)
X				{
X					to = to + BLKSIZE - markidx(to);
X				}
X				lnmode = TRUE;
X			}
X
X			/* insert the old cut-buffer before the new text */
X			mark[28] = to;
X			delthru = paste(from, FALSE, TRUE);
X			if (delthru == MARK_UNSET)
X			{
X				return;
X			}
X			delthru++;
X			to = mark[28];
X		}
X		cutfree(cb);
X	}
X#endif /* not CRUNCH */
X	else if (cbname == '.')
X	{
X		cb = &named[26];
X		cutfree(cb);
X	}
X	else
X	{
X		msg("Invalid cut buffer name: \"%c", cbname);
X		dotcb = cbname = '\0';
X		return;
X	}
X	cbname = '\0';
X	cb->tmpnum = tmpnum;
X
X	/* detect whether we're doing a line mode cut */
X	cb->lnmode = lnmode;
X
X	/* ---------- */
X
X	/* Reporting... */	
X	if (markidx(from) == 0 && markidx(to) == 0)
X	{
X		rptlines = markline(to) - markline(from);
X		rptlabel = "yanked";
X	}
X
X	/* ---------- */
X
X	/* make sure each block has a physical disk address */
X	blksync();
X
X	/* find the first block in the cut */
X	line = markline(from);
X	for (first = 1; line > lnum[first]; first++)
X	{
X	}
X
X	/* fetch text of the block containing that line */
X	blkc = scan = blkget(first)->c;
X
X	/* find the mark in the block */
X	for (l = lnum[first - 1]; ++l < line; )
X	{
X		while (*scan++ != '\n')
X		{
X		}
X	}
X	scan += markidx(from);
X
X	/* remember the offset of the start */
X	cb->start = scan - blkc;
X
X	/* ---------- */
X
X	/* find the last block in the cut */
X	line = markline(to);
X	for (last = first; line > lnum[last]; last++)
X	{
X	}
X
X	/* fetch text of the block containing that line */
X	if (last != first)
X	{
X		blkc = scan = blkget(last)->c;
X	}
X	else
X	{
X		scan = blkc;
X	}
X
X	/* find the mark in the block */
X	for (l = lnum[last - 1]; ++l < line; )
X	{
X		while (*scan++ != '\n')
X		{
X		}
X	}
X	if (markline(to) <= nlines)
X	{
X		scan += markidx(to);
X	}
X
X	/* remember the offset of the end */
X	cb->end = scan - blkc;
X
X	/* ------- */
X
X	/* remember the physical block numbers of all included blocks */
X	cb->nblks = last - first;
X	if (cb->end > 0)
X	{
X		cb->nblks++;
X	}
X#ifdef lint
X	cb->phys = (short *)0;
X#else
X	cb->phys = (short *)malloc((unsigned)(cb->nblks * sizeof(short)));
X#endif
X	for (i = 0; i < cb->nblks; i++)
X	{
X		cb->phys[i] = hdr.n[first++];
X	}
X
X#ifndef CRUNCH
X	/* if we temporarily inserted text for appending, then delete that
X	 * text now -- before the user sees it.
X	 */
X	if (delthru)
X	{
X		line = rptlines;
X		delete(from, delthru);
X		rptlines = line;
X		rptlabel = "yanked";
X	}
X#endif /* not CRUNCH */
X}
X
X
Xstatic void readcutblk(cb, blkno)
X	struct cutbuf	*cb;
X	int		blkno;
X{
X	char		cutfname[50];/* name of an old temp file */
X	int		fd;	/* either tmpfd or the result of open() */
X#if MSDOS || TOS
X	int		i;
X#endif
X
X	/* decide which fd to use */
X	if (cb->tmpnum == tmpnum)
X	{
X		fd = tmpfd;
X	}
X	else
X	{
X#if MSDOS || TOS
X		strcpy(cutfname, o_directory);
X		if ((i = strlen(cutfname)) && !strchr(":/\\", cutfname[i-1]))
X			cutfname[i++]=SLASH;
X		sprintf(cutfname+i, TMPNAME+3, getpid(), cb->tmpnum);
X#else
X		sprintf(cutfname, TMPNAME, o_directory, getpid(), cb->tmpnum);
X#endif
X		fd = open(cutfname, O_RDONLY);
X	}
X
X	/* get the block */
X	lseek(fd, (long)cb->phys[blkno] * (long)BLKSIZE, 0);
X	if (read(fd, tmpblk.c, (unsigned)BLKSIZE) != BLKSIZE)
X	{
X		msg("Error reading back from tmp file for pasting!");
X	}
X
X	/* close the fd, if it isn't tmpfd */
X	if (fd != tmpfd)
X	{
X		close(fd);
X	}
X}
X
X
X/* This function inserts text from a cut buffer, and returns the MARK where
X * insertion ended.  Return MARK_UNSET on errors.
X */
XMARK paste(at, after, retend)
X	MARK	at;	/* where to insert the text */
X	int	after;	/* boolean: insert after mark? (rather than before) */
X	int	retend;	/* boolean: return end of text? (rather than start) */
X{
X	REG struct cutbuf	*cb;
X	REG int			i;
X
X	/* handle the "doingdot" quirks */
X	if (doingdot)
X	{
X		if (!cbname)
X		{
X			if (dotcb >= '1' && dotcb < '1' + NANONS - 1)
X			{
X				dotcb++;
X			}
X			cbname = dotcb;
X		}
X	}
X	else if (cbname != '.')
X	{
X		dotcb = cbname;
X	}
X
X	/* decide which cut buffer to use */
X	if (cbname >= 'A' && cbname <= 'Z')
X	{
X		cb = &named[cbname - 'A'];
X	}
X	else if (cbname >= 'a' && cbname <= 'z')
X	{
X		cb = &named[cbname - 'a'];
X	}
X	else if (cbname >= '1' && cbname <= '9')
X	{
X		cb = &anon[cbname - '1'];
X	}
X	else if (cbname == '.')
X	{
X		cb = &named[26];
X	}
X	else if (!cbname)
X	{
X		cb = anon;
X	}
X	else
X	{
X		msg("Invalid cut buffer name: \"%c", cbname);
X		cbname = '\0';
X		return MARK_UNSET;
X	}
X
X	/* make sure it isn't empty */
X	if (cb->nblks == 0)
X	{
X		if (cbname)
X			msg("Cut buffer \"%c is empty", cbname);
X		else
X			msg("Cut buffer is empty");
X		cbname = '\0';
X		return MARK_UNSET;
X	}
X	cbname = '\0';
X
X	/* adjust the insertion MARK for "after" and line-mode cuts */
X	if (cb->lnmode)
X	{
X		at &= ~(BLKSIZE - 1);
X		if (after)
X		{
X			at += BLKSIZE;
X		}
X	}
X	else if (after)
X	{
X		/* careful! if markidx(at) == 0 we might be pasting into an
X		 * empty line -- so we can't blindly increment "at".
X		 */
X		if (markidx(at) == 0)
X		{
X			pfetch(markline(at));
X			if (plen != 0)
X			{
X				at++;
X			}
X		}
X		else
X		{
X			at++;
X		}
X	}
X
X	/* put a copy of the "at" mark in the mark[] array, so it stays in
X	 * sync with changes made via add().
X	 */
X	mark[27] = at;
X
X	/* simple one-block paste? */
X	if (cb->nblks == 1)
X	{
X		/* get the block */
X		readcutblk(cb, 0);
X
X		/* isolate the text we need within it */
X		if (cb->end)
X		{
X			tmpblk.c[cb->end] = '\0';
X		}
X
X		/* insert it */
X		ChangeText
X		{
X			add(at, &tmpblk.c[cb->start]);
X		}
X	}
X	else
X	{
X		/* multi-block paste */
X
X		ChangeText
X		{
X			i = cb->nblks - 1;
X
X			/* add text from the last block first */
X			if (cb->end > 0)
X			{
X				readcutblk(cb, i);
X				tmpblk.c[cb->end] = '\0';
X				add(at, tmpblk.c);
X				i--;
X			}
X
X			/* add intervening blocks */
X			while (i > 0)
X			{
X				readcutblk(cb, i);
X				add(at, tmpblk.c);
X				i--;
X			}
X
X			/* add text from the first cut block */
X			readcutblk(cb, 0);
X			add(at, &tmpblk.c[cb->start]);
X		}
X	}
X
X	/* Reporting... */
X	rptlines = markline(mark[27]) - markline(at);
X	rptlabel = "pasted";
X
X	/* return the mark at the beginning/end of inserted text */
X	if (retend)
X	{
X		return mark[27] - 1L;
X	}
X	return at;
X}
X
X
X
X
X#ifndef NO_AT
X
X/* This function copies characters from a cut buffer into a string.
X * It returns the number of characters in the cut buffer.  If the cut
X * buffer is too large to fit in the string (i.e. if cb2str() returns
X * a number >= size) then the characters will not have been copied.
X * It returns 0 if the cut buffer is empty, and -1 for invalid cut buffers.
X */
Xint cb2str(name, buf, size)
X	int	name;	/* the name of a cut-buffer to get: a-z only! */
X	char	*buf;	/* where to put the string */
X	unsigned size;	/* size of buf */
X{
X	REG struct cutbuf	*cb;
X	REG char		*src;
X	REG char		*dest;
X
X	/* decide which cut buffer to use */
X	if (name >= 'a' && name <= 'z')
X	{
X		cb = &named[name - 'a'];
X	}
X	else
X	{
X		return -1;
X	}
X
X	/* if the buffer is empty, return 0 */
X	if (cb->nblks == 0)
X	{
X		return 0;
X	}
X
X	/* !!! if not a single-block cut, then fail */
X	if (cb->nblks != 1)
X	{
X		return size;
X	}
X
X	/* if too big, return the size now, without doing anything */
X	if (cb->end - cb->start >= size)
X	{
X		return cb->end - cb->start;
X	}
X
X	/* get the block */
X	readcutblk(cb, 0);
X
X	/* isolate the string within that blk */
X	if (cb->start == 0)
X	{
X		tmpblk.c[cb->end] = '\0';
X	}
X	else
X	{
X		for (dest = tmpblk.c, src = dest + cb->start; src < tmpblk.c + cb->end; )
X		{
X			*dest++ = *src++;
X		}
X		*dest = '\0';
X	}
X
X	/* copy the string into the buffer */
X	if (buf != tmpblk.c)
X	{
X		strcpy(buf, tmpblk.c);
X	}
X
X	/* return the length */
X	return cb->end - cb->start;
X}
X#endif
/
echo x - elvprsv.c
sed '/^X/s///' > elvprsv.c << '/'
X/* elvprsv.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains the portable sources for the "elvprsv" program.
X * "Elvprsv" is run by Elvis when Elvis is about to die.  It is also
X * run when the computer boots up.  It is not intended to be run directly
X * by the user, ever.
X *
X * Basically, this program does the following four things:
X *    - It extracts the text from the temporary file, and places the text in
X *	a file in the /usr/preserve directory.
X *    - It adds a line to the /usr/preserve/Index file, describing the file
X *	that it just preserved.
X *    - It removes the temporary file.
X *    -	It sends mail to the owner of the file, saying that the file was
X *	preserved, and how it can be recovered.
X *
X * The /usr/preserve/Index file is a log file that contains one line for each
X * file that has ever been preserved.  Each line of this file describes one
X * preserved file.  The first word on the line is the name of the file that
X * contains the preserved text.  The second word is the full pathname of the
X * file that was being edited; for anonymous buffers, this is the directory
X * name plus "/foo".
X *
X * If elvprsv's first argument (after the command name) starts with a hyphen,
X * then the characters after the hyphen are used as a description of when
X * the editor went away.  This is optional.
X *
X * The remaining arguments are all the names of temporary files that are
X * to be preserved.  For example, on a UNIX system, the /etc/rc file might
X * invoke it this way:
X *
X *	elvprsv "-the system went down" /tmp/elv_*.*
X *
X * This file contains only the portable parts of the preserve program.
X * It must #include a system-specific file.  The system-specific file is
X * expected to define the following functions:
X *
X *	char *ownername(char *filename)	- returns name of person who owns file
X *
X *	void mail(char *user, char *name, char *when)
X *					- tell user that file was preserved
X */
X
X#include <stdio.h>
X#include "config.h"
X#include "vi.h"
X
X#if AMIGA
XBLK tmpblk;
X#error AMIGA here DEBUG
X# include "amiwild.c"
X# include "amiprsv.c"
X#endif
X
X#if OSK
X# undef sprintf
X#endif
X
X#if ANY_UNIX || OSK
X# include "prsvunix.c"
X#endif
X
X#if MSDOS || TOS
X# include "prsvdos.c"
X# define WILDCARD_NO_MAIN
X# include "wildcard.c"
X#endif
X
X
XBLK	buf;
XBLK	hdr;
XBLK	name;
Xint	rewrite_now;	/* boolean: should we send text directly to orig file? */
X
X
X
X/* This function preserves a single file, and announces its success/failure
X * via an e-mail message.
X */
Xvoid preserve(tname, when)
X	char	*tname;		/* name of a temp file to be preserved */
X	char	*when;		/* description of when the editor died */
X{
X	int	infd;		/* fd used for reading from the temp file */
X	FILE	*outfp;		/* fp used for writing to the recovery file */
X	FILE	*index;		/* fp used for appending to index file */
X	char	outname[100];	/* the name of the recovery file */
X	char	*user;		/* name of the owner of the temp file */
X#if AMIGA
X	char	*prsvdir;
X#endif
X	int	i;
X
X	/* open the temp file */
X	infd = open(tname, O_RDONLY|O_BINARY);
X	if (infd < 0)
X	{
X		/* if we can't open the file, then we should assume that
X		 * the filename contains wildcard characters that weren't
X		 * expanded... and also assume that they weren't expanded
X		 * because there are no files that need to be preserved.
X		 * THEREFORE... we should silently ignore it.
X		 * (Or loudly ignore it if the user was using -R)
X		 */
X		if (rewrite_now)
X		{
X			perror(tname);
X		}
X		return;
X	}
X
X	/* read the header and name from the file */
X	if (read(infd, hdr.c, BLKSIZE) != BLKSIZE
X	 || read(infd, name.c, BLKSIZE) != BLKSIZE)
X	{
X		/* something wrong with the file - sorry */
X		fprintf(stderr, "%s: trucated header blocks\n", tname);
X		close(infd);
X		return;
X	}
X
X	/* If the filename block contains an empty string, then Elvis was
X	 * only keeping the temp file around because it contained some text
X	 * that was needed for a named cut buffer.  The user doesn't care
X	 * about that kind of temp file, so we should silently delete it.
X	 */
X	if (name.c[0] == '\0' && name.c[1] == '\177')
X	{
X		close(infd);
X		unlink(tname);
X		return;
X	}
X
X	if (rewrite_now)
X	{
X		/* we don't need to open the index file */
X		index = (FILE *)0;
X
X		/* make sure we can read every block! */
X		for (i = 1; i < MAXBLKS && hdr.n[i]; i++)
X		{
X			lseek(infd, (long)hdr.n[i] * (long)BLKSIZE, 0);
X			if (read(infd, buf.c, BLKSIZE) != BLKSIZE)
X			{
X				/* messed up header */
X				fprintf(stderr, "%s: unrecoverable -- header trashed\n", name.c);
X				close(infd);
X				return;
X			}
X		}
X
X		/* open the user's file for writing */
X		outfp = fopen(name.c, "w");
X		if (!outfp)
X		{
X			perror(name.c);
X			close(infd);
X			return;
X		}
X	}
X	else
X	{
X		/* open/create the index file */
X		index = fopen(PRSVINDEX, "a");
X		if (!index)
X		{
X			perror(PRSVINDEX);
X			exit(1);
X		}
X
X		/* create the recovery file in the PRESVDIR directory */
X#if AMIGA
X		prsvdir = &PRSVDIR[strlen(PRSVDIR) - 1];
X		if (*prsvdir == '/' || *prsvdir == ':')
X		{
X			sprintf(outname, "%sp%ld", PRSVDIR, ftell(index));
X		}
X		else
X#endif
X		sprintf(outname, "%s%cp%ld", PRSVDIR, SLASH, ftell(index));
X		outfp = fopen(outname, "w");
X		if (!outfp)
X		{
X			perror(outname);
X			close(infd);
X			fclose(index);
X			return;
X		}
X	}
X
X	/* write the text of the file out to the recovery file */
X	for (i = 1; i < MAXBLKS && hdr.n[i]; i++)
X	{
X		lseek(infd, (long)hdr.n[i] * (long)BLKSIZE, 0);
X		if (read(infd, buf.c, BLKSIZE) != BLKSIZE)
X		{
X			/* messed up header */
X			fprintf(stderr, "%s: unrecoverable -- header trashed\n", name.c);
X			fclose(outfp);
X			close(infd);
X			if (index)
X			{
X				fclose(index);
X			}
X			unlink(outname);
X			return;
X		}
X		fputs(buf.c, outfp);
X	}
X
X	/* add a line to the index file */
X	if (index)
X	{
X		fprintf(index, "%s %s\n", outname, name.c);
X	}
X
X	/* close everything */
X	close(infd);
X	fclose(outfp);
X	if (index)
X	{
X		fclose(index);
X	}
X
X	/* Are we doing this due to something more frightening than just
X	 * a ":preserve" command?
X	 */
X	if (*when)
X	{
X		/* send a mail message */
X		mail(ownername(tname), name.c, when);
X
X		/* remove the temp file -- the editor has died already */
X		unlink(tname);
X	}
X}
X
Xmain(argc, argv)
X	int	argc;
X	char	**argv;
X{
X	int	i;
X	char	*when = "the editor went away";
X
X#if MSDOS || TOS
X	/* expand any wildcards in the command line */
X	argv = wildexpand(&argc, argv);
X#endif
X
X	/* do we have a "when" argument? */
X	i = 1;
X	if (argc >= i + 1 && !strcmp(argv[i], "-R"))
X	{
X		rewrite_now = 1;
X		when = "";
X		i++;
X#if ANY_UNIX
X		setuid(geteuid());
X#endif
X	}
X#if OSK
X	else
X	{
X		setuid(0);
X	}
X#endif
X	if (argc >= i + 1 && argv[i][0] == '-')
X	{
X		when = argv[i] + 1;
X		i++;
X	}
X
X	/* preserve everything we're supposed to */
X	while (i < argc)
X	{
X		preserve(argv[i], when);
X		i++;
X	}
X}
/
echo x - elvrec.c
sed '/^X/s///' > elvrec.c << '/'
X/* elvrec.c */
X
X/* This file contains the file recovery program */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X#include <stdio.h>
X#include "config.h"
X#include "vi.h"
X
Xvoid recover(basename, outname)
X	char	*basename;	/* the name of the file to recover */
X	char	*outname;	/* the name of the file to write to */
X{
X	char	pathname[500];	/* full pathname of the file to recover */
X	char	line[600];	/* a line from the /usr/preserve/Index file */
X	int	ch;		/* a character from the text being recovered */
X	FILE	*from;		/* the /usr/preserve file, or /usr/preserve/Index */
X	FILE	*to;		/* the user's text file */
X	char	*ptr;
X#if OSK
X	int		uid;
X#endif
X
X	/* convert basename to a full pathname */
X	if (basename)
X	{
X#ifndef CRUNCH
X# if MSDOS || TOS
X		if (!basename[0] || basename[1] != ':')
X# else
X		if (basename[0] != SLASH)
X# endif
X		{
X			ptr = getcwd(pathname, sizeof pathname);
X			if (ptr != pathname)
X			{
X				strcpy(pathname, ptr);
X			}
X			ptr = pathname + strlen(pathname);
X			*ptr++ = SLASH;
X			strcpy(ptr, basename);
X		}
X		else
X#endif
X		{
X			strcpy(pathname, basename);
X		}
X	}
X
X#if OSK
X	uid = getuid();
X	if(setuid(0))
X		exit(_errmsg(errno, "Can't set uid\n"));
X#endif
X	/* scan the /usr/preserve/Index file, for the *oldest* unrecovered
X	 * version of this file.
X	 */
X	from = fopen(PRSVINDEX, "r");
X	while (from && fgets(line, sizeof line, from))
X	{
X		/* strip off the newline from the end of the string */
X		line[strlen(line) - 1] = '\0';
X
X		/* parse the line into a "preserve" name and a "text" name */
X		for (ptr = line; *ptr != ' '; ptr++)
X		{
X		}
X		*ptr++ = '\0';
X
X		/* If the "preserve" file is missing, then ignore this line
X		 * because it describes a file that has already been recovered.
X		 */
X		if (access(line, 0) < 0)
X		{
X			continue;
X		}
X
X		/* are we looking for a specific file? */
X		if (basename)
X		{
X			/* quit if we found it */
X			if (!strcmp(ptr, pathname))
X			{
X				break;
X			}
X		}
X		else
X		{
X			/* list this file as "available for recovery" */
X			puts(ptr);
X		}
X	}
X
X	/* file not found? */
X	if (!basename || !from || feof(from))
X	{
X		if (from != NULL) fclose(from);
X		if (basename)
X		{
X			fprintf(stderr, "%s: no recovered file has that exact name\n", pathname);
X		}
X		return;
X	}
X	if (from != NULL) fclose(from);
X
X	/* copy the recovered text back into the user's file... */
X
X	/* open the /usr/preserve file for reading */
X	from = fopen(line, "r");
X	if (!from)
X	{
X		perror(line);
X		exit(2);
X	}
X
X#if ANY_UNIX
X	/* Be careful about user-id.  We want to be running under the user's
X	 * real id when we open/create the user's text file... but we want
X	 * to be superuser when we delete the /usr/preserve file.  For UNIX,
X	 * we accomplish this by deleting the /usr/preserve file *now*,
X	 * when it is open but before we've read it.  Then we revert to the
X	 * user's real id.
X	 */
X	unlink(line);
X	setuid(getuid());
X#endif
X#if OSK
X	setuid(uid);
X#endif
X
X	if (outname == NULL) return;
X
X	/* open the user's file for writing */
X	to = fopen(outname, "w");
X	if (!to)
X	{
X		perror(ptr);
X		exit(2);
X	}
X
X	/* copy the text */
X	while ((ch = getc(from)) != EOF)
X	{
X		putc(ch, to);
X	}
X
X#if !ANY_UNIX
X#if OSK
X	fclose(from);
X	setuid(0);
X#endif
X	/* delete the /usr/preserve file */
X	unlink(line);
X#if OSK
X	setuid(uid);
X#endif
X#endif
X}
X
Xmain(argc, argv)
X	int	argc;
X	char	**argv;
X{
X	/* check arguments */
X	if (argc > 3)
X	{
X		fprintf(stderr, "usage: %s [preserved_file [recovery_file]]\n", argv[0]);
X		exit(1);
X	}
X
X	/* recover the requested file, or list recoverable files */
X	if (argc == 3)
X	{
X		/* recover the file, but write it to a different filename */
X		recover (argv[1], argv[2]);
X	}
X	else if (argc == 2)
X	{
X		/* recover the file */
X		recover(argv[1], argv[1]);
X	}
X	else
X	{
X		/* list the recoverable files */
X		recover((char *)0, (char *)0);
X	}
X
X	/* success! */
X	exit(0);
X}
/
echo x - ex.c
sed '/^X/s///' > ex.c << '/'
X/* ex.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains the code for reading ex commands. */
X
X#include "config.h"
X#include "ctype.h"
X#include "vi.h"
X
X/* This data type is used to describe the possible argument combinations */
Xtypedef short ARGT;
X#define FROM	1		/* allow a linespec */
X#define	TO	2		/* allow a second linespec */
X#define BANG	4		/* allow a ! after the command name */
X#define EXTRA	8		/* allow extra args after command name */
X#define XFILE	16		/* expand wildcards in extra part */
X#define NOSPC	32		/* no spaces allowed in the extra part */
X#define	DFLALL	64		/* default file range is 1,$ */
X#define DFLNONE	128		/* no default file range */
X#define NODFL	256		/* do not default to the current file name */
X#define EXRCOK	512		/* can be in a .exrc file */
X#define NL	1024		/* if mode!=MODE_EX, then write a newline first */
X#define PLUS	2048		/* allow a line number, as in ":e +32 foo" */
X#define ZERO	4096		/* allow 0 to be given as a line number */
X#define NOBAR	8192		/* treat following '|' chars as normal */
X#define FILES	(XFILE + EXTRA)	/* multiple extra files allowed */
X#define WORD1	(EXTRA + NOSPC)	/* one extra word allowed */
X#define FILE1	(FILES + NOSPC)	/* 1 file allowed, defaults to current file */
X#define NAMEDF	(FILE1 + NODFL)	/* 1 file allowed, defaults to "" */
X#define NAMEDFS	(FILES + NODFL)	/* multiple files allowed, default is "" */
X#define RANGE	(FROM + TO)	/* range of linespecs allowed */
X#define NONE	0		/* no args allowed at all */
X
X/* This array maps ex command names to command codes. The order in which
X * command names are listed below is significant -- ambiguous abbreviations
X * are always resolved to be the first possible match.  (e.g. "r" is taken
X * to mean "read", not "rewind", because "read" comes before "rewind")
X */
Xstatic struct
X{
X	char	*name;	/* name of the command */
X	CMD	code;	/* enum code of the command */
X	void	(*fn)();/* function which executes the command */
X	ARGT	argt;	/* command line arguments permitted/needed/used */
X}
X	cmdnames[] =
X{   /*	cmd name	cmd code	function	arguments */
X	{"append",	CMD_APPEND,	cmd_append,	FROM+ZERO+BANG	},
X#ifdef DEBUG
X	{"bug",		CMD_DEBUG,	cmd_debug,	RANGE+BANG+EXTRA+NL},
X#endif
X	{"change",	CMD_CHANGE,	cmd_append,	RANGE+BANG	},
X	{"delete",	CMD_DELETE,	cmd_delete,	RANGE+WORD1	},
X	{"edit",	CMD_EDIT,	cmd_edit,	BANG+FILE1+PLUS	},
X	{"file",	CMD_FILE,	cmd_file,	NAMEDF		},
X	{"global",	CMD_GLOBAL,	cmd_global,	RANGE+BANG+EXTRA+DFLALL+NOBAR},
X	{"insert",	CMD_INSERT,	cmd_append,	FROM+BANG	},
X	{"join",	CMD_INSERT,	cmd_join,	RANGE+BANG	},
X	{"k",		CMD_MARK,	cmd_mark,	FROM+WORD1	},
X	{"list",	CMD_LIST,	cmd_print,	RANGE+NL	},
X	{"move",	CMD_MOVE,	cmd_move,	RANGE+EXTRA	},
X	{"next",	CMD_NEXT,	cmd_next,	BANG+NAMEDFS	},
X	{"Next",	CMD_PREVIOUS,	cmd_next,	BANG		},
X	{"print",	CMD_PRINT,	cmd_print,	RANGE+NL	},
X	{"quit",	CMD_QUIT,	cmd_xit,	BANG		},
X	{"read",	CMD_READ,	cmd_read,	FROM+ZERO+NAMEDF},
X	{"substitute",	CMD_SUBSTITUTE,	cmd_substitute,	RANGE+EXTRA	},
X	{"to",		CMD_COPY,	cmd_move,	RANGE+EXTRA	},
X	{"undo",	CMD_UNDO,	cmd_undo,	NONE		},
X	{"vglobal",	CMD_VGLOBAL,	cmd_global,	RANGE+EXTRA+DFLALL+NOBAR},
X	{"write",	CMD_WRITE,	cmd_write,	RANGE+BANG+FILE1+DFLALL},
X	{"xit",		CMD_XIT,	cmd_xit,	BANG+NL		},
X	{"yank",	CMD_YANK,	cmd_delete,	RANGE+WORD1	},
X
X	{"!",		CMD_BANG,	cmd_shell,	EXRCOK+RANGE+NAMEDFS+DFLNONE+NL+NOBAR},
X	{"#",		CMD_NUMBER,	cmd_print,	RANGE+NL	},
X	{"<",		CMD_SHIFTL,	cmd_shift,	RANGE		},
X	{">",		CMD_SHIFTR,	cmd_shift,	RANGE		},
X	{"=",		CMD_EQUAL,	cmd_file,	RANGE		},
X	{"&",		CMD_SUBAGAIN,	cmd_substitute,	RANGE		},
X#ifndef NO_AT
X	{"@",		CMD_AT,		cmd_at,		EXTRA		},
X#endif
X
X#ifndef NO_ABBR
X	{"abbreviate",	CMD_ABBR,	cmd_map,	EXRCOK+BANG+EXTRA},
X#endif
X	{"args",	CMD_ARGS,	cmd_args,	EXRCOK+NAMEDFS	},
X#ifndef NO_ERRLIST
X	{"cc",		CMD_CC,		cmd_make,	BANG+FILES	},
X#endif
X	{"cd",		CMD_CD,		cmd_cd,		EXRCOK+BANG+NAMEDF},
X	{"copy",	CMD_COPY,	cmd_move,	RANGE+EXTRA	},
X#ifndef NO_DIGRAPH
X	{"digraph",	CMD_DIGRAPH,	cmd_digraph,	EXRCOK+BANG+EXTRA},
X#endif
X#ifndef NO_ERRLIST
X	{"errlist",	CMD_ERRLIST,	cmd_errlist,	BANG+NAMEDF	},
X#endif
X	{"ex",		CMD_EDIT,	cmd_edit,	BANG+FILE1	},
X	{"mark",	CMD_MARK,	cmd_mark,	FROM+WORD1	},
X#ifndef NO_MKEXRC
X	{"mkexrc",	CMD_MKEXRC,	cmd_mkexrc,	NAMEDF		},
X#endif
X	{"number",	CMD_NUMBER,	cmd_print,	RANGE+NL	},
X	{"put",		CMD_PUT,	cmd_put,	FROM+ZERO+WORD1	},
X	{"set",		CMD_SET,	cmd_set,	EXRCOK+EXTRA	},
X	{"shell",	CMD_SHELL,	cmd_shell,	NL		},
X	{"source",	CMD_SOURCE,	cmd_source,	EXRCOK+NAMEDF	},
X#ifdef SIGTSTP
X	{"stop",	CMD_STOP,	cmd_suspend,	NONE		},
X#endif
X	{"tag",		CMD_TAG,	cmd_tag,	BANG+WORD1	},
X	{"version",	CMD_VERSION,	cmd_version,	EXRCOK+NONE	},
X	{"visual",	CMD_VISUAL,	cmd_edit,	BANG+NAMEDF	},
X	{"wq",		CMD_WQUIT,	cmd_xit,	NL		},
X
X#ifdef DEBUG
X	{"debug",	CMD_DEBUG,	cmd_debug,	RANGE+BANG+EXTRA+NL},
X	{"validate",	CMD_VALIDATE,	cmd_validate,	BANG+NL		},
X#endif
X	{"chdir",	CMD_CD,		cmd_cd,		EXRCOK+BANG+NAMEDF},
X#ifndef NO_COLOR
X	{"color",	CMD_COLOR,	cmd_color,	EXRCOK+EXTRA	},
X#endif
X#ifndef NO_ERRLIST
X	{"make",	CMD_MAKE,	cmd_make,	BANG+NAMEDFS	},
X#endif
X	{"map",		CMD_MAP,	cmd_map,	EXRCOK+BANG+EXTRA},
X	{"previous",	CMD_PREVIOUS,	cmd_next,	BANG		},
X	{"rewind",	CMD_REWIND,	cmd_next,	BANG		},
X#ifdef SIGTSTP
X	{"suspend",	CMD_SUSPEND,	cmd_suspend,	NONE		},
X#endif
X	{"unmap",	CMD_UNMAP,	cmd_map,	EXRCOK+BANG+EXTRA},
X#ifndef NO_ABBR
X	{"unabbreviate",CMD_UNABBR,	cmd_map,	EXRCOK+WORD1	},
X#endif
X
X	{(char *)0}
X};
X
X
X/* This function parses a search pattern - given a pointer to a / or ?,
X * it replaces the ending / or ? with a \0, and returns a pointer to the
X * stuff that came after the pattern.
X */
Xchar	*parseptrn(ptrn)
X	REG char	*ptrn;
X{
X	REG char 	*scan;
X
X	for (scan = ptrn + 1;
X	     *scan && *scan != *ptrn;
X	     scan++)
X	{
X		/* allow backslashed versions of / and ? in the pattern */
X		if (*scan == '\\' && scan[1] != '\0')
X		{
X			scan++;
X		}
X	}
X	if (*scan)
X	{
X		*scan++ = '\0';
X	}
X
X	return scan;
X}
X
X
X/* This function parses a line specifier for ex commands */
Xchar *linespec(s, markptr)
X	REG char	*s;		/* start of the line specifier */
X	MARK		*markptr;	/* where to store the mark's value */
X{
X	long		num;
X	REG char	*t;
X
X	/* parse each ;-delimited clause of this linespec */
X	do
X	{
X		/* skip an initial ';', if any */
X		if (*s == ';')
X		{
X			s++;
X		}
X
X		/* skip leading spaces */
X		while (isspace(*s))
X		{
X			s++;
X		}
X
X		/* dot means current position */
X		if (*s == '.')
X		{
X			s++;
X			*markptr = cursor;
X		}
X		/* '$' means the last line */
X		else if (*s == '$')
X		{
X			s++;
X			*markptr = MARK_LAST;
X		}
X		/* digit means an absolute line number */
X		else if (isdigit(*s))
X		{
X			for (num = 0; isdigit(*s); s++)
X			{
X				num = num * 10 + *s - '0';
X			}
X			*markptr = MARK_AT_LINE(num);
X		}
X		/* appostrophe means go to a set mark */
X		else if (*s == '\'')
X		{
X			s++;
X			*markptr = m_tomark(cursor, 1L, (int)*s);
X			s++;
X		}
X		/* slash means do a search */
X		else if (*s == '/' || *s == '?')
X		{
X			/* put a '\0' at the end of the search pattern */
X			t = parseptrn(s);
X
X			/* search for the pattern */
X			*markptr &= ~(BLKSIZE - 1);
X			if (*s == '/')
X			{
X				pfetch(markline(*markptr));
X				if (plen > 0)
X					*markptr += plen - 1;
X				*markptr = m_fsrch(*markptr, s);
X			}
X			else
X			{
X				*markptr = m_bsrch(*markptr, s);
X			}
X
X			/* adjust command string pointer */
X			s = t;
X		}
X
X		/* if linespec was faulty, quit now */
X		if (!*markptr)
X		{
X			return s;
X		}
X
X		/* maybe add an offset */
X		t = s;
X		if (*t == '-' || *t == '+')
X		{
X			s++;
X			for (num = 0; isdigit(*s); s++)
X			{
X				num = num * 10 + *s - '0';
X			}
X			if (num == 0)
X			{
X				num = 1;
X			}
X			*markptr = m_updnto(*markptr, num, *t);
X		}
X	} while (*s == ';' || *s == '+' || *s == '-');
X
X	/* protect against invalid line numbers */
X	num = markline(*markptr);
X	if (num < 1L || num > nlines)
X	{
X		msg("Invalid line number -- must be from 1 to %ld", nlines);
X		*markptr = MARK_UNSET;
X	}
X
X	return s;
X}
X
X
X
X/* This function reads an ex command and executes it. */
Xvoid ex()
X{
X	char		cmdbuf[150];
X	REG int		cmdlen;
X	static long	oldline;
X
X	significant = FALSE;
X	oldline = markline(cursor);
X
X	while (mode == MODE_EX)
X	{
X		/* read a line */
X#ifdef CRUNCH
X		cmdlen = vgets(':', cmdbuf, sizeof(cmdbuf));
X#else
X		cmdlen = vgets(*o_prompt ? ':' : '\0', cmdbuf, sizeof(cmdbuf));
X#endif
X		if (cmdlen < 0)
X		{
X			return;
X		}
X
X		/* if empty line, assume ".+1" */
X		if (cmdlen == 0)
X		{
X			strcpy(cmdbuf, ".+1");
X			qaddch('\r');
X			clrtoeol();
X		}
X		else
X		{
X			addch('\n');
X		}
X		refresh();
X
X		/* parse & execute the command */
X		doexcmd(cmdbuf);
X
X		/* handle autoprint */
X		if (significant || markline(cursor) != oldline)
X		{
X			significant = FALSE;
X			oldline = markline(cursor);
X			if (*o_autoprint && mode == MODE_EX)
X			{
X				cmd_print(cursor, cursor, CMD_PRINT, FALSE, "");
X			}
X		}
X	}
X}
X
Xvoid doexcmd(cmdbuf)
X	char		*cmdbuf;	/* string containing an ex command */
X{
X	REG char	*scan;		/* used to scan thru cmdbuf */
X	MARK		frommark;	/* first linespec */
X	MARK		tomark;		/* second linespec */
X	REG int		cmdlen;		/* length of the command name given */
X	CMD		cmd;		/* what command is this? */
X	ARGT		argt;		/* argument types for this command */
X	short		forceit;	/* bang version of a command? */
X	REG int		cmdidx;		/* index of command */
X	REG char	*build;		/* used while copying filenames */
X	int		iswild;		/* boolean: filenames use wildcards? */
X	int		isdfl;		/* using default line ranges? */
X	int		didsub;		/* did we substitute file names for % or # */
X
X	/* ex commands can't be undone via the shift-U command */
X	U_line = 0L;
X
X	/* permit extra colons at the start of the line */
X	for (; *cmdbuf == ':'; cmdbuf++)
X	{
X	}
X
X	/* ignore command lines that start with a double-quote */
X	if (*cmdbuf == '"')
X	{
X		return;
X	}
X	scan = cmdbuf;
X
X	/* parse the line specifier */
X	if (nlines < 1)
X	{
X		/* no file, so don't allow addresses */
X	}
X	else if (*scan == '%')
X	{
X		/* '%' means all lines */
X		frommark = MARK_FIRST;
X		tomark = MARK_LAST;
X		scan++;
X	}
X	else if (*scan == '0')
X	{
X		frommark = tomark = MARK_UNSET;
X		scan++;
X	}
X	else
X	{
X		frommark = cursor;
X		scan = linespec(scan, &frommark);
X		tomark = frommark;
X		if (frommark && *scan == ',')
X		{
X			scan++;
X			scan = linespec(scan, &tomark);
X		}
X		if (!tomark)
X		{
X			/* faulty line spec -- fault already described */
X			return;
X		}
X		if (frommark > tomark)
X		{
X			msg("first address exceeds the second");
X			return;
X		}
X	}
X	isdfl = (scan == cmdbuf);
X
X	/* skip whitespace */
X	while (isspace(*scan))
X	{
X		scan++;
X	}
X
X	/* if no command, then just move the cursor to the mark */
X	if (!*scan)
X	{
X		if (tomark != MARK_UNSET)
X			cursor = tomark;
X		return;
X	}
X
X	/* figure out how long the command name is */
X	if (!isalpha(*scan))
X	{
X		cmdlen = 1;
X	}
X	else
X	{
X		for (cmdlen = 1;
X		     isalpha(scan[cmdlen]);
X		     cmdlen++)
X		{
X		}
X	}
X
X	/* lookup the command code */
X	for (cmdidx = 0;
X	     cmdnames[cmdidx].name && strncmp(scan, cmdnames[cmdidx].name, cmdlen);
X	     cmdidx++)
X	{
X	}
X	argt = cmdnames[cmdidx].argt;
X	cmd = cmdnames[cmdidx].code;
X	if (cmd == CMD_NULL)
X	{
X		msg("Unknown command \"%.*s\"", cmdlen, scan);
X		return;
X	}
X
X	/* !!! if the command doesn't have NOBAR set, then replace | with \0 */
X
X	/* if the command ended with a bang, set the forceit flag */
X	scan += cmdlen;
X	if ((argt & BANG) && *scan == '!')
X	{
X		scan++;
X		forceit = 1;
X	}
X	else
X	{
X		forceit = 0;
X	}
X
X	/* skip any more whitespace, to leave scan pointing to arguments */
X	while (isspace(*scan))
X	{
X		scan++;
X	}
X
X	/* a couple of special cases for filenames */
X	if (argt & XFILE)
X	{
X		/* if names were given, process them */
X		if (*scan)
X		{
X			for (build = tmpblk.c, iswild = didsub = FALSE; *scan; scan++)
X			{
X				switch (*scan)
X				{
X				  case '\\':
X					if (scan[1] == '\\' || scan[1] == '%' || scan[1] == '#')
X					{
X						*build++ = *++scan;
X					}
X					else
X					{
X						*build++ = '\\';
X					}
X					break;
X
X				  case '%':
X					if (!*origname)
X					{
X						msg("No filename to substitute for %%");
X						return;
X					}
X					strcpy(build, origname);
X					while (*build)
X					{
X						build++;
X					}
X					didsub = TRUE;
X					break;
X
X				  case '#':
X					if (!*prevorig)
X					{
X						msg("No filename to substitute for #");
X						return;
X					}
X					strcpy(build, prevorig);
X					while (*build)
X					{
X						build++;
X					}
X					didsub = TRUE;
X					break;
X
X				  case '*':
X				  case '?':
X#if !(MSDOS || TOS)
X				  case '[':
X				  case '`':
X				  case '{': /* } */
X				  case '$':
X				  case '~':
X#endif
X					*build++ = *scan;
X					iswild = TRUE;
X					break;
X
X				  default:
X					*build++ = *scan;
X				}
X			}
X			*build = '\0';
X
X			if (cmd == CMD_BANG
X			 || cmd == CMD_READ && tmpblk.c[0] == '!'
X			 || cmd == CMD_WRITE && tmpblk.c[0] == '!')
X			{
X				if (didsub)
X				{
X					if (mode != MODE_EX)
X					{
X						addch('\n');
X					}
X					addstr(tmpblk.c);
X					addch('\n');
X					exrefresh();
X				}
X			}
X			else
X			{
X				if (iswild && tmpblk.c[0] != '>')
X				{
X					scan = wildcard(tmpblk.c);
X				}
X			}
X		}
X		else /* no names given, maybe assume origname */
X		{
X			if (!(argt & NODFL))
X			{
X				strcpy(tmpblk.c, origname);
X			}
X			else
X			{
X				*tmpblk.c = '\0';
X			}
X		}
X
X		scan = tmpblk.c;
X	}
X
X	/* bad arguments? */
X	if (!(argt & EXRCOK) && nlines < 1L)
X	{
X		msg("Can't use the \"%s\" command in a %s file", cmdnames[cmdidx].name, EXRC);
X		return;
X	}
X	if (!(argt & (ZERO | EXRCOK)) && frommark == MARK_UNSET)
X	{
X		msg("Can't use address 0 with \"%s\" command.", cmdnames[cmdidx].name);
X		return;
X	}
X	if (!(argt & FROM) && frommark != cursor && nlines >= 1L)
X	{
X		msg("Can't use address with \"%s\" command.", cmdnames[cmdidx].name);
X		return;
X	}
X	if (!(argt & TO) && tomark != frommark && nlines >= 1L)
X	{
X		msg("Can't use a range with \"%s\" command.", cmdnames[cmdidx].name);
X		return;
X	}
X	if (!(argt & EXTRA) && *scan)
X	{
X		msg("Extra characters after \"%s\" command.", cmdnames[cmdidx].name);
X		return;
X	}
X	if ((argt & NOSPC) && !(cmd == CMD_READ && (forceit || *scan == '!')))
X	{
X		build = scan;
X#ifndef CRUNCH
X		if ((argt & PLUS) && *build == '+')
X		{
X			while (*build && !isspace(*build))
X			{
X				build++;
X			}
X			while (*build && isspace(*build))
X			{
X				build++;
X			}
X		}
X#endif /* not CRUNCH */
X		for (; *build; build++)
X		{
X			if (isspace(*build))
X			{
X				msg("Too many %s to \"%s\" command.",
X					(argt & XFILE) ? "filenames" : "arguments",
X					cmdnames[cmdidx].name);
X				return;
X			}
X		}
X	}
X
X	/* some commands have special default ranges */
X	if (isdfl && (argt & DFLALL))
X	{
X		frommark = MARK_FIRST;
X		tomark = MARK_LAST;
X	}
X	else if (isdfl && (argt & DFLNONE))
X	{
X		frommark = tomark = 0L;
X	}
X
X	/* write a newline if called from visual mode */
X	if ((argt & NL) && mode != MODE_EX && !exwrote)
X	{
X		addch('\n');
X		exrefresh();
X	}
X
X	/* act on the command */
X	(*cmdnames[cmdidx].fn)(frommark, tomark, cmd, forceit, scan);
X}
X
X
X/* This function executes EX commands from a file.  It returns 1 normally, or
X * 0 if the file could not be opened for reading.
X */
Xint doexrc(filename)
X	char	*filename;	/* name of a ".exrc" file */
X{
X	int	fd;		/* file descriptor */
X	int	len;		/* length of the ".exrc" file */
X
X	/* !!! kludge: we use U_text as the buffer.  This has the side-effect
X	 * of interfering with the shift-U visual command.  Disable shift-U.
X	 */
X	U_line = 0L;
X
X	/* open the file, read it, and close */
X	fd = open(filename, O_RDONLY);
X	if (fd < 0)
X	{
X		return 0;
X	}
X	len = tread(fd, U_text, BLKSIZE);
X	close(fd);
X
X	/* execute the string */
X	exstring(U_text, len, ctrl('V'));
X
X	return 1;
X}
X
X/* This function executes EX commands from a string.  The commands may be
X * separated by newlines or by | characters.  It also handles quoting.
X * Each individual command is limited to 132 bytes, but the total string
X * may be longer.
X */
Xvoid exstring(buf, len, qchar)
X	char	*buf;	/* the commands to execute */
X	int	len;	/* the length of the string */
X	int	qchar;	/* the quote character -- ^V for file, or \ for kbd */
X{
X	char	single[133];	/* a single command */
X	char	*src, *dest;
X	int	i;
X
X	/* find & do each command */
X	for (src = buf; src < &buf[len]; src++)
X	{
X		/* Copy a single command into single[].  Convert any quoted |
X		 * into a normal |, and stop at a newline or unquoted |.
X		 */
X		for (dest = single, i = 0;
X		     i < 132 && src < &buf[len] && *src != '\n' && *src != '|';
X		     src++, i++)
X		{
X			if (src[0] == qchar && src[1] == '|')
X			{
X				src++;
X			}
X			*dest++ = *src;
X		}
X		*dest = '\0';
X
X		/* do it */
X		doexcmd(single);
X	}
X}
/
echo x - fmt.c
sed '/^X/s///' > fmt.c << '/'
X/* fmt.c */
X
X/* usage: fmt [-width] [files]...
X *
X * Fmt rearrages text in order to make each line have roughly the
X * same width.  Indentation and word spacing is preserved.
X *
X * The default width is 72 characters, but you can override that via -width.
X * If no files are given on the command line, then it reads stdin.
X */
X
X#include <stdio.h>
X
X#ifndef TRUE
X# define TRUE	1
X# define FALSE	0
X#endif
X
X
X
Xint	width = 72;	/* the desired line width */
Xint	isblank;	/* is the current output line blank? */
Xint	indent;		/* width of the indentation */
Xchar	ind[512];	/* indentation text */
Xchar	word[1024];	/* word buffer */
X
X/* This function displays a usage message and quits */
Xvoid usage()
X{
X	fprintf(stderr, "usage: fmt [-width] [files]...\n");
X	exit(2);
X}
X
X
X
X/* This function outputs a single word.  It takes care of spacing and the
X * newlines within a paragraph.
X */
Xvoid putword()
X{
X	int		i;		/* index into word[], or whatever */
X	int		ww;		/* width of the word */
X	int		sw;		/* width of spacing after word */
X	static int	psw;		/* space width of previous word */
X	static int	tab;		/* the width of text already written */
X
X
X	/* separate the word and its spacing */
X	for (ww = 0; word[ww] && word[ww] != ' '; ww++)
X	{
X	}
X	sw = strlen(word) - ww;
X	word[ww] = '\0';
X
X	/* if no spacing (that is, the word was at the end of the line) then
X	 * assume 1 space unless the last char of the word was punctuation
X	 */
X	if (sw == 0)
X	{
X		sw = 1;
X		if (word[ww - 1] == '.' || word[ww - 1] == '?' || word[ww - 1] == '!')
X			sw = 2;
X	}
X
X	/* if this is the first word on the line... */
X	if (isblank)
X	{
X		/* output the indentation first */
X		fputs(ind, stdout);
X		tab = indent;
X	}
X	else /* text has already been written to this output line */
X	{
X		/* will the word fit on this line? */
X		if (psw + ww + tab <= width)
X		{
X			/* yes - so write the previous word's spacing */
X			for (i = 0; i < psw; i++)
X			{
X				putchar(' ');
X			}
X			tab += psw;
X		}
X		else
X		{
X			/* no, so write a newline and the indentation */
X			putchar('\n');
X			fputs(ind, stdout);
X			tab = indent;
X		}
X	}
X
X	/* write the word itself */
X	fputs(word, stdout);
X	tab += ww;
X
X	/* remember this word's spacing */
X	psw = sw;
X
X	/* this output line isn't blank anymore. */
X	isblank = FALSE;
X}
X
X
X
X/* This function reformats text. */
Xvoid fmt(in)
X	FILE	*in;		/* the name of the input stream */
X{
X	int	ch;		/* character from input stream */
X	int	prevch;		/* the previous character in the loop */
X	int	i;		/* index into ind[] or word[] */
X	int	inword;		/* boolean: are we between indent & newline? */
X
X
X	/* for each character in the stream... */
X	for (indent = -1, isblank = TRUE, inword = FALSE, i = 0, prevch = '\n';
X	     (ch = getc(in)) != EOF;
X	     prevch = ch)
X	{
X		/* is this the end of a line? */
X		if (ch == '\n')
X		{
X			/* if end of last word in the input line */
X			if (inword)
X			{
X				/* if it really is a word */
X				if (i > 0)
X				{
X					/* output it */
X					word[i] = '\0';
X					putword();
X				}
X			}
X			else /* blank line in input */
X			{
X				/* finish the previous paragraph */
X				if (!isblank)
X				{
X					putchar('\n');
X					isblank = TRUE;
X				}
X
X				/* output a blank line */
X				putchar('\n');
X			}
X
X			/* continue with next input line... */
X			indent = -1;
X			i = 0;
X			inword = FALSE;
X			continue;
X		}
X
X		/* if we're expecting indentation now... */
X		if (indent < 0)
X		{
X			/* if this is part of the indentation... */
X			if (ch == ' ' || ch == '\t')
X			{
X				/* remember it */
X				ind[i++] = ch;
X			}
X			else /* end of indentation */
X			{
X				/* mark the end of the indentation string */
X				ind[i] = '\0';
X
X				/* calculate the width of the indentation */
X				for (i = indent = 0; ind[i]; i++)
X				{
X					if (ind[i] == '\t')
X						indent = (indent | 7) + 1;
X					else
X						indent++;
X				}
X
X				/* reset the word index */
X				i = 0;
X
X				/* reprocess that last character */
X				ungetc(ch, in);
X			}
X
X			/* continue in the for-loop */
X			continue;
X		}
X
X		/* if we get here, we're either in a word or in the space
X		 * after a word.
X		 */
X		inword = TRUE;
X
X		/* is this the start of a new word? */
X		if (ch != ' ' && prevch == ' ')
X		{
X			/* yes!  output the previous word */
X			word[i] = '\0';
X			putword();
X
X			/* reset `i' to the start of the word[] buffer */
X			i = 0;
X		}
X		word[i++] = ch;
X	}
X
X	/* if necessary, write a final newline */
X	if (!isblank)
X	{
X		putchar('\n');
X		isblank = TRUE;
X	}
X}
X
X
X
X
X
Xint main(argc, argv)
X	int	argc;
X	char	**argv;
X{
X	FILE	*in;	/* an input stream */
X	int	error;	/* if non-zero, then an error occurred */
X	int	i;
X
X
X	/* handle the -width flag, if given */
X	if (argc > 1 && argv[1][0] == '-')
X	{
X		width = atoi(argv[1] + 1);
X		if (width <= 0)
X		{
X			usage();
X		}
X		argc--;
X		argv++;
X	}
X
X	/* if no filenames given, then process stdin */
X	if (argc == 1)
X	{
X		fmt(stdin);
X	}
X	else /* one or more filenames given */
X	{
X		for (error = 0, i = 1; i < argc; i++)
X		{
X			in = fopen(argv[i], "r");
X			if (!in)
X			{
X				perror(argv[i]);
X				error = 3;
X			}
X			else
X			{
X				fmt(in);
X				fclose(in);
X			}
X		}
X	}
X
X	/* exit, possibly indicating an error */
X	exit(error);
X	/*NOTREACHED*/
X}
/
echo x - input.c
sed '/^X/s///' > input.c << '/'
X/* input.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains the input() function, which implements vi's INPUT mode.
X * It also contains the code that supports digraphs.
X */
X
X#include "config.h"
X#include "ctype.h"
X#include "vi.h"
X
X
X#ifndef NO_DIGRAPH
Xstatic struct _DIG
X{
X	struct _DIG	*next;
X	char		key1;
X	char		key2;
X	char		dig;
X	char		save;
X} *digs;
X
Xchar digraph(key1, key2)
X	char	key1;	/* the underlying character */
X	char	key2;	/* the second character */
X{
X	int		newkey;
X	REG struct _DIG	*dp;
X
X	/* if digraphs are disabled, then just return the new char */
X	if (!*o_digraph)
X	{
X		return key2;
X	}
X
X	/* remember the new key, so we can return it if this isn't a digraph */
X	newkey = key2;
X
X	/* sort key1 and key2, so that their original order won't matter */
X	if (key1 > key2)
X	{
X		key2 = key1;
X		key1 = newkey;
X	}
X
X	/* scan through the digraph chart */
X	for (dp = digs;
X	     dp && (dp->key1 != key1 || dp->key2 != key2);
X	     dp = dp->next)
X	{
X	}
X
X	/* if this combination isn't in there, just use the new key */
X	if (!dp)
X	{
X		return newkey;
X	}
X
X	/* else use the digraph key */
X	return dp->dig;
X}
X
X/* this function lists or defines digraphs */
Xvoid do_digraph(bang, extra)
X	int	bang;
X	char	extra[];
X{
X	int		dig;
X	REG struct _DIG	*dp;
X	struct _DIG	*prev;
X	static int	user_defined = FALSE; /* boolean: are all later digraphs user-defined? */
X	char		listbuf[8];
X
X	/* if "extra" is NULL, then we've reached the end of the built-ins */
X	if (!extra)
X	{
X		user_defined = TRUE;
X		return;
X	}
X
X	/* if no args, then display the existing digraphs */
X	if (*extra < ' ')
X	{
X		listbuf[0] = listbuf[1] = listbuf[2] = listbuf[5] = ' ';
X		listbuf[7] = '\0';
X		for (dig = 0, dp = digs; dp; dp = dp->next)
X		{
X			if (dp->save || bang)
X			{
X				dig += 7;
X				if (dig >= COLS)
X				{
X					addch('\n');
X					exrefresh();
X					dig = 7;
X				}
X				listbuf[3] = dp->key1;
X				listbuf[4] = dp->key2;
X				listbuf[6] = dp->dig;
X				qaddstr(listbuf);
X			}
X		}
X		addch('\n');
X		exrefresh();
X		return;
X	}
X
X	/* make sure we have at least two characters */
X	if (!extra[1])
X	{
X		msg("Digraphs must be composed of two characters");
X		return;
X	}
X
X	/* sort key1 and key2, so that their original order won't matter */
X	if (extra[0] > extra[1])
X	{
X		dig = extra[0];
X		extra[0] = extra[1];
X		extra[1] = dig;
X	}
X
X	/* locate the new digraph character */
X	for (dig = 2; extra[dig] == ' ' || extra[dig] == '\t'; dig++)
X	{
X	}
X	dig = extra[dig];
X	if (!bang && dig)
X	{
X		dig |= 0x80;
X	}
X
X	/* search for the digraph */
X	for (prev = (struct _DIG *)0, dp = digs;
X	     dp && (dp->key1 != extra[0] || dp->key2 != extra[1]);
X	     prev = dp, dp = dp->next)
X	{
X	}
X
X	/* deleting the digraph? */
X	if (!dig)
X	{
X		if (!dp)
X		{
X#ifndef CRUNCH
X			msg("%c%c not a digraph", extra[0], extra[1]);
X#endif
X			return;
X		}
X		if (prev)
X			prev->next = dp->next;
X		else
X			digs = dp->next;
X		free(dp);
X		return;
X	}
X
X	/* if necessary, create a new digraph struct for the new digraph */
X	if (dig && !dp)
X	{
X		dp = (struct _DIG *)malloc(sizeof *dp);
X		if (!dp)
X		{
X			msg("Out of space in the digraph table");
X			return;
X		}
X		if (prev)
X			prev->next = dp;
X		else
X			digs = dp;
X		dp->next = (struct _DIG *)0;
X	}
X
X	/* assign it the new digraph value */
X	dp->key1 = extra[0];
X	dp->key2 = extra[1];
X	dp->dig = dig;
X	dp->save = user_defined;
X}
X
X# ifndef NO_MKEXRC
Xvoid savedigs(fd)
X	int		fd;
X{
X	static char	buf[] = "digraph! XX Y\n";
X	REG struct _DIG	*dp;
X
X	for (dp = digs; dp; dp = dp->next)
X	{
X		if (dp->save)
X		{
X			buf[9] = dp->key1;
X			buf[10] = dp->key2;
X			buf[12] = dp->dig;
X			write(fd, buf, (unsigned)14);
X		}
X	}
X}
X# endif
X#endif
X
X
X/* This function allows the user to replace an existing (possibly zero-length)
X * chunk of text with typed-in text.  It returns the MARK of the last character
X * that the user typed in.
X */
XMARK input(from, to, when, above)
X	MARK	from;	/* where to start inserting text */
X	MARK	to;	/* extent of text to delete */
X	int	when;	/* either WHEN_VIINP or WHEN_VIREP */
X	int	above;	/* boolean: take indentation from lower line? */
X{
X	char	key[2];	/* key char followed by '\0' char */
X	char	*build;	/* used in building a newline+indent string */
X	char	*scan;	/* used while looking at the indent chars of a line */
X	MARK	m;	/* some place in the text */
X#ifndef NO_EXTENSIONS
X	int	quit = FALSE;	/* boolean: are we exiting after this? */
X	int	inchg;	/* boolean: have we done a "beforedo()" yet? */
X#endif
X
X#ifdef DEBUG
X	/* if "from" and "to" are reversed, complain */
X	if (from > to)
X	{
X		msg("ERROR: input(%ld:%d, %ld:%d)",
X			markline(from), markidx(from),
X			markline(to), markidx(to));
X		return MARK_UNSET;
X	}
X#endif
X
X	key[1] = 0;
X
X	/* if we're replacing text with new text, save the old stuff */
X	/* (Alas, there is no easy way to save text for replace mode) */
X	if (from != to)
X	{
X		cut(from, to);
X	}
X
X	/* if doing a dot command, then reuse the previous text */
X	if (doingdot)
X	{
X		ChangeText
X		{
X			/* delete the text that's there now */
X			if (from != to)
X			{
X				delete(from, to);
X			}
X
X			/* insert the previous text */
X			cutname('.');
X			cursor = paste(from, FALSE, TRUE) + 1L;
X		}
X	}
X	else /* interactive version */
X	{
X		/* assume that whoever called this already did a beforedo() */
X#ifndef NO_EXTENSIONS
X		inchg = TRUE;
X#endif
X
X		/* if doing a change within the line... */
X		if (from != to && markline(from) == markline(to))
X		{
X			/* mark the end of the text with a "$" */
X			change(to - 1, to, "$");
X		}
X		else
X		{
X			/* delete the old text right off */
X			if (from != to)
X			{
X				delete(from, to);
X			}
X			to = from;
X		}
X
X		/* handle autoindent of the first line, maybe */
X		cursor = from;
X		m = (above ? (cursor + BLKSIZE) : (cursor - BLKSIZE));
X		if (*o_autoindent && markidx(m) == 0
X		 && markline(m) >= 1L && markline(m) <= nlines)
X		{
X			/* Only autoindent blank lines. */
X			pfetch(markline(cursor));
X			if (plen == 0)
X			{
X				/* Okay, we really want to autoindent */
X				pfetch(markline(m));
X				for (scan = ptext, build = tmpblk.c;
X				     *scan == ' ' || *scan == '\t';
X				     )
X				{
X					*build++ = *scan++;
X				}
X				if (build > tmpblk.c)
X				{
X					*build = '\0';
X					add(cursor, tmpblk.c);
X					cursor += (build - tmpblk.c);
X					if (cursor > to)
X						to = cursor;
X				}
X			}
X		}
X
X		/* repeatedly add characters from the user */
X		for (;;)
X		{
X			/* Get a character */
X			redraw(cursor, TRUE);
X#ifdef DEBUG2
X			msg("cursor=%ld.%d, to=%ld.%d",
X				markline(cursor), markidx(cursor),
X				markline(to), markidx(to));
X#endif
X#ifndef NO_ABBR
X			pfetch(markline(cursor));
X			build = ptext;
X			if (pline == markline(from))
X				build += markidx(from);
X			for (scan = ptext + markidx(cursor); --scan >= build && isalnum(*scan); )
X			{
X			}
X			scan++;
X			key[0] = getabkey(when, scan, (int)(ptext + markidx(cursor) - scan));
X#else
X			key[0] = getkey(when);
X#endif
X#ifndef NO_VISIBLE
X			if (key[0] != '\0' && V_from != MARK_UNSET)
X			{
X				msg("Can't modify text during a selection");
X				beep();
X				continue;
X			}
X#endif
X
X#ifndef NO_EXTENSIONS
X			if (key[0] == ctrl('O'))
X			{
X				if (inchg)
X				{
X					if (cursor < to)
X					{
X						delete(cursor, to);
X						redraw(cursor, TRUE);
X					}
X					afterdo();
X					inchg = FALSE;
X				}
X			}
X			else if (key[0] != ctrl('['))
X			{
X				if (!inchg)
X				{
X					beforedo(FALSE);
X					inchg = TRUE;
X				}
X			}
X#endif
X
X#ifndef CRUNCH
X			/* if wrapmargin is set & we're past the
X			 * warpmargin, then change the last whitespace
X			 * characters on line into a newline
X			 */
X			if (*o_wrapmargin != 0)
X			{
X				pfetch(markline(cursor));
X				if (idx2col(cursor, ptext, TRUE) > COLS - (*o_wrapmargin & 0xff))
X				{
X					build = tmpblk.c;
X					*build++ = '\n';
X					if (*o_autoindent)
X					{
X						/* figure out indent for next line */
X						for (scan = ptext; *scan == ' ' || *scan == '\t'; )
X						{
X							*build++ = *scan++;
X						}
X					}
X					*build = '\0';
X
X					scan = ptext + plen;
X					m = cursor & ~(BLKSIZE - 1);
X					while (ptext < scan)
X					{
X						scan--;
X						if (*scan != ' ' && *scan != '\t')
X							continue;
X
X						/*break up line, and we do autoindent if needed*/
X						change(m + (scan - ptext), m + (scan - ptext) + 1, tmpblk.c);
X						cursor = (cursor & ~(BLKSIZE - 1))
X							+ BLKSIZE
X							+ strlen(tmpblk.c) - 1
X							+ plen - (scan - ptext) - 1;
X
X						/*remove trailing spaces on previous line*/
X						pfetch(markline(m));
X						scan = ptext + plen;
X						while (ptext < scan)
X						{
X							scan--;
X							if (*scan != ' ' && *scan != '\t')
X								break;
X						}
X						delete(m + (scan-ptext) + 1, m + plen);
X
X						break;
X					}
X				}
X			}
X#endif /* !CRUNCH */
X
X			/* process it */
X			switch (*key)
X			{
X#ifndef NO_EXTENSIONS
X			  case ctrl('O'): /* special movement mapped keys */
X				*key = getkey(0);
X				switch (*key)
X				{
X				  case 'h':	m = m_left(cursor, 0L);		break;
X				  case 'j':
X				  case 'k':	m = m_updnto(cursor, 0L, *key);	break;
X				  case 'l':	m = cursor + 1;			break;
X				  case 'B':
X				  case 'b':	m = m_bword(cursor, 0L, *key);	break;
X				  case 'W':
X				  case 'w':	m = m_fword(cursor, 0L, *key, '\0');	break;
X				  case '^':	m = m_front(cursor, 0L);	break;
X				  case '$':	m = m_rear(cursor, 0L);		break;
X				  case ctrl('B'):
X				  case ctrl('F'):
X						m = m_scroll(cursor, 0L, *key); break;
X				  case 'x':
X#ifndef NO_VISIBLE
X						if (V_from)
X							beep();
X						else
X#endif
X						ChangeText
X						{
X							m = v_xchar(cursor, 0L, 'x');
X						}
X						break;
X				  case 'i':	m = to = from = cursor;
X						when = WHEN_VIINP + WHEN_VIREP - when;
X										break;
X				  case 'K':
X					pfetch(markline(cursor));
X					changes++; /* <- after this, we can alter ptext */
X					ptext[markidx(cursor)] = 0;
X					for (scan = ptext + markidx(cursor) - 1;
X					     scan >= ptext && isalnum(*scan);
X					     scan--)
X					{
X					}
X					scan++;
X					m = (*scan ? v_keyword(scan, cursor, 0L) : cursor);
X					break;
X
X# ifndef NO_VISIBLE
X				  case 'v':
X				  case 'V':
X					if (V_from)
X						V_from = MARK_UNSET;
X					else
X						V_from = cursor;
X					m = from = to = cursor;
X					V_linemd = (*key == 'V');
X					break;
X
X				  case 'd':
X				  case 'y':
X				  case '\\':
X					/* do nothing if unmarked */
X					if (!V_from)
X					{
X						msg("You must mark the text first");
X						beep();
X						break;
X					}
X
X					/* "from" must come before "to" */
X					if (V_from < cursor)
X					{
X						from = V_from;
X						to = cursor;
X					}
X					else
X					{
X						from = cursor;
X						to = V_from;
X					}
X
X					/* we don't need V_from anymore */
X					V_from = MARK_UNSET;
X
X					if (V_linemd)
X					{
X						/* adjust for line mode */
X						from &= ~(BLKSIZE - 1);
X						to |= (BLKSIZE - 1);
X					}
X					else
X					{
X						/* in character mode, we must
X						 * worry about deleting the newline
X						 * at the end of the last line
X						 */
X						pfetch(markline(to));
X						if (markidx(to) == plen)
X							to |= (BLKSIZE - 1);
X					}
X					to++;
X
X					switch (*key)
X					{
X					  case 'y':
X						cut(from, to);
X						break;
X
X					  case 'd':
X						ChangeText
X						{
X							cut(from, to);
X							delete(from, to);
X						}
X						cursor = from;
X						break;
X
X#ifndef NO_POPUP
X					  case '\\':
X						ChangeText
X						{
X							cursor = v_popup(from, to);
X						}
X						break;
X#endif
X					}
X					m = from = to = cursor;
X					break;
X
X				  case 'p':
X				  case 'P':
X					V_from = MARK_UNSET;
X					ChangeText
X					{
X						m = from = to = cursor = paste(cursor, (*key == 'p'), FALSE);
X					}
X					break;
X# endif /* !NO_VISIBLE */
X				  default:	m = MARK_UNSET;
X				}
X
X				/* adjust the moved cursor */
X				if (m != cursor)
X				{
X					m = adjmove(cursor, m, (*key == 'j' || *key == 'k' ? 0x20 : 0));
X					if (*key == '$' || (*key == 'l' && m <= cursor))
X					{
X						m++;
X					}
X				}
X
X				/* if the cursor is reasonable, use it */
X				if (m == MARK_UNSET)
X				{
X					beep();
X				}
X				else
X				{
X					from = to = cursor = m;
X				}
X				break;
X
X			  case ctrl('Z'):
X				if (getkey(0) == ctrl('Z'))
X				{
X					quit = TRUE;
X					goto BreakBreak;
X				}
X				break;
X#endif
X
X			  case ctrl('['):
X				/* if last line contains only whitespace, then remove whitespace */
X				if (*o_autoindent)
X				{
X					pfetch(markline(cursor));
X					for (scan = ptext; isspace(*scan); scan++)
X					{
X					}
X					if (scan > ptext && !*scan)
X					{
X						cursor &= ~(BLKSIZE - 1L);
X						if (to < cursor + plen)
X						{
X							to = cursor + plen;
X						}
X					}
X				}
X				goto BreakBreak;
X
X			  case ctrl('U'):
X				if (markline(cursor) == markline(from))
X				{
X					cursor = from;
X				}
X				else
X				{
X					cursor &= ~(BLKSIZE - 1);
X				}
X				break;
X
X			  case ctrl('D'):
X			  case ctrl('T'):
X				if (to > cursor)
X				{
X					delete(cursor, to);
X				}
X				mark[27] = cursor;
X				cmd_shift(cursor, cursor, *key == ctrl('D') ? CMD_SHIFTL : CMD_SHIFTR, TRUE, "");
X				if (mark[27])
X				{
X					cursor = mark[27];
X				}
X				else
X				{
X					cursor = m_front(cursor, 0L);
X				}
X				to = cursor;
X				break;
X
X			  case '\b':
X				if (cursor <= from)
X				{
X					beep();
X				}
X				else if (markidx(cursor) == 0)
X				{
X					cursor -= BLKSIZE;
X					pfetch(markline(cursor));
X					cursor += plen;
X				}
X				else
X				{
X					cursor--;
X				}
X				break;
X
X			  case ctrl('W'):
X				m = m_bword(cursor, 1L, 'b');
X				if (markline(m) == markline(cursor) && m >= from)
X				{
X					cursor = m;
X					if (from > cursor)
X					{
X						from = cursor;
X					}
X				}
X				else
X				{
X					beep();
X				}
X				break;
X
X			  case '\n':
X#if OSK
X			  case '\l':
X#else				  
X			  case '\r':
X#endif
X				build = tmpblk.c;
X				*build++ = '\n';
X				if (*o_autoindent)
X				{
X					/* figure out indent for next line */
X					pfetch(markline(cursor));
X					for (scan = ptext; *scan == ' ' || *scan == '\t'; )
X					{
X						*build++ = *scan++;
X					}
X
X					/* remove indent from this line, if blank */
X					if ((scan - ptext) >= markidx(cursor) && plen > 0)
X					{
X						to = cursor &= ~(BLKSIZE - 1);
X						delete(cursor, cursor + plen);
X					}
X				}
X				*build = 0;
X				if (cursor >= to && when != WHEN_VIREP)
X				{
X					add(cursor, tmpblk.c);
X				}
X				else
X				{
X					change(cursor, to, tmpblk.c);
X				}
X				redraw(cursor, TRUE);
X				to = cursor = (cursor & ~(BLKSIZE - 1))
X						+ BLKSIZE
X						+ (int)(build - tmpblk.c) - 1;
X				break;
X
X			  case ctrl('A'):
X			  case ctrl('P'):
X				if (cursor < to)
X				{
X					delete(cursor, to);
X				}
X				if (*key == ctrl('A'))
X				{
X					cutname('.');
X				}
X				to = cursor = paste(cursor, FALSE, TRUE) + 1L;
X				break;
X
X			  case ctrl('V'):
X				if (cursor >= to && when != WHEN_VIREP)
X				{
X					add(cursor, "^");
X				}
X				else
X				{
X					change(cursor, to, "^");
X					to = cursor + 1;
X				}
X				redraw(cursor, TRUE);
X				*key = getkey(0);
X				if (*key == '\n')
X				{
X					/* '\n' too hard to handle */
X#if OSK
X					*key = '\l';
X#else
X					*key = '\r';
X#endif
X				}
X				change(cursor, cursor + 1, key);
X				cursor++;
X				if (cursor > to)
X				{
X					to = cursor;
X				}
X				break;
X
X			  case ctrl('L'):
X			  case ctrl('R'):
X				redraw(MARK_UNSET, FALSE);
X				break;
X
X			  default:
X				if (cursor >= to && when != WHEN_VIREP)
X				{
X					add(cursor, key);
X					cursor++;
X					to = cursor;
X				}
X				else
X				{
X					pfetch(markline(cursor));
X					if (markidx(cursor) == plen)
X					{
X						add(cursor, key);
X					}
X					else
X					{
X#ifndef NO_DIGRAPH
X						*key = digraph(ptext[markidx(cursor)], *key);
X#endif
X						change(cursor, cursor + 1, key);
X					}
X					cursor++;
X				}
X#ifndef NO_SHOWMATCH
X				/* show matching "({[" if necessary */
X				if (*o_showmatch && strchr(")}]", *key))
X				{
X					redraw(cursor, TRUE);
X					m = m_match(cursor - 1, 0L);
X					if (markline(m) >= topline
X					 && markline(m) <= botline)
X					{
X						redraw(m, TRUE);
X						refresh();
X						sleep(1);
X					}
X				}
X#endif
X			} /* end switch(*key) */
X		} /* end for(;;) */
XBreakBreak:;
X		/* delete any excess characters */
X		if (cursor < to)
X		{
X#ifndef NO_EXTENSIONS
X			/* if we aren't in the middle of a change, start one! */
X			if (!inchg)
X			{
X				beforedo(FALSE);
X				inchg = TRUE;
X			}
X#endif
X			delete(cursor, to);
X		}
X
X	} /* end if doingdot else */
X
X	/* put the new text into a cut buffer for possible reuse */
X	if (!doingdot)
X	{
X		blksync();
X		cutname('.');
X		cut(from, cursor);
X	}
X
X	/* move to last char that we inputted, unless it was newline */
X	if (markidx(cursor) != 0)
X	{
X		cursor--;
X	}
X	redraw(cursor, FALSE);
X
X#ifndef NO_EXTENSIONS
X	if (quit)
X	{
X		/* if this is a nested "do", then cut it short */
X		abortdo();
X
X		/* exit, unless we can't write out the file */
X		cursor = v_xit(cursor, 0L, 'Z');
X	}
X#endif
X
X	rptlines = 0L;
X	return cursor;
X}
/
echo x - main.c
sed '/^X/s///' > main.c << '/'
X/* main.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains the main() function of vi */
X
X/* HACK! bcc needs to disable use of precompiled headers for this file,
X   or else command line args will not be passed to elvis */
X#if __BORLANDC__
X#include "borland.h"
X#endif
X
X#include "config.h"
X#include <setjmp.h>
X#include "vi.h"
X
Xextern		trapint(); /* defined below */
Xextern char	*getenv();
Xjmp_buf		jmpenv;
X
X#ifndef NO_DIGRAPH
Xstatic init_digraphs();
X#endif
X
X/*---------------------------------------------------------------------*/
X
X#if AMIGA
X# include "amiwild.c"
Xmain (argc, argv)
X#else
X# if VMS
X#  include "vmswild.c"
Xmain (argc, argv)
X# else
Xvoid main(argc, argv)
X# endif
X#endif
X	int	argc;
X	char	*argv[];
X{
X	int	i;
X	char	*cmd = (char *)0;
X	char	*err = (char *)0;
X	char	*str;
X	char	*tag = (char *)0;
X
X	/* set mode to MODE_VI or MODE_EX depending on program name */
X	switch (argv[0][strlen(argv[0]) - 1])
X	{
X	  case 'x':			/* "ex" */
X		mode = MODE_EX;
X		break;
X
X	  case 'w':			/* "view" */
X		mode = MODE_VI;
X		*o_readonly = TRUE;
X		break;
X#ifndef NO_EXTENSIONS
X	  case 't':			/* "edit" or "input" */
X		mode = MODE_VI;
X		*o_inputmode = TRUE;
X		break;
X#endif
X	  default:			/* "vi" or "elvis" */
X		mode = MODE_VI;
X	}
X
X#ifndef DEBUG
X# ifdef	SIGQUIT
X	/* normally, we ignore SIGQUIT.  SIGINT is trapped later */
X	signal(SIGQUIT, SIG_IGN);
X# endif
X#endif
X
X	/* temporarily ignore SIGINT */
X	signal(SIGINT, SIG_IGN);
X
X	/* start curses */
X	initscr();
X	cbreak();
X	noecho();
X	scrollok(stdscr, TRUE);
X
X	/* arrange for deadly signals to be caught */
X# ifdef SIGHUP
X	signal(SIGHUP, (void(*)()) deathtrap);
X# endif
X# ifndef DEBUG
X#  ifdef SIGILL
X	signal(SIGILL, (void(*)()) deathtrap);
X#  endif
X#  ifdef SIGBUS
X	signal(SIGBUS, (void(*)()) deathtrap);
X#  endif
X#  ifdef SIGSEGV
X	signal(SIGSEGV, (void(*)()) deathtrap);
X#  endif
X#  ifdef SIGSYS
X	signal(SIGSYS, (void(*)()) deathtrap);
X#  endif
X# endif /* !DEBUG */
X# ifdef SIGPIPE
X	signal(SIGPIPE, (void(*)()) deathtrap);
X# endif
X# ifdef SIGTERM
X	signal(SIGTERM, (void(*)()) deathtrap);
X# endif
X# ifdef SIGUSR1
X	signal(SIGUSR1, (void(*)()) deathtrap);
X# endif
X# ifdef SIGUSR2
X	signal(SIGUSR2, (void(*)()) deathtrap);
X# endif
X
X	/* initialize the options - must be done after initscr(), so that
X	 * we can alter LINES and COLS if necessary.
X	 */
X	initopts();
X
X	/* map the arrow keys.  The KU,KD,KL,and KR variables correspond to
X	 * the :ku=: (etc.) termcap capabilities.  The variables are defined
X	 * as part of the curses package.
X	 */
X	if (has_KU) mapkey(has_KU, "k",    WHEN_VICMD|WHEN_INMV, "<Up>");
X	if (has_KD) mapkey(has_KD, "j",    WHEN_VICMD|WHEN_INMV, "<Down>");
X	if (has_KL) mapkey(has_KL, "h",    WHEN_VICMD|WHEN_INMV, "<Left>");
X	if (has_KR) mapkey(has_KR, "l",    WHEN_VICMD|WHEN_INMV, "<Right>");
X	if (has_HM) mapkey(has_HM, "^",    WHEN_VICMD|WHEN_INMV, "<Home>");
X	if (has_EN) mapkey(has_EN, "$",    WHEN_VICMD|WHEN_INMV, "<End>");
X	if (has_PU) mapkey(has_PU, "\002", WHEN_VICMD|WHEN_INMV, "<PageUp>");
X	if (has_PD) mapkey(has_PD, "\006", WHEN_VICMD|WHEN_INMV, "<PageDn>");
X	if (has_KI) mapkey(has_KI, "i",    WHEN_VICMD|WHEN_INMV, "<Insert>");
X#if MSDOS
X# if RAINBOW
X	if (!strcmp("rainbow", o_term))
X	{
X		mapkey("\033[1~",  "/",		WHEN_VICMD,		"<Find>");
X		mapkey("\033[3~",  "x",		WHEN_VICMD|WHEN_INMV,	"<Remove>");
X		mapkey("\033[4~",  "v",		WHEN_VICMD|WHEN_INMV,	"<Select>");
X		mapkey("\033[17~", ":sh\n",	WHEN_VICMD,		"<Intrpt>");
X		mapkey("\033[19~", ":q\n",	WHEN_VICMD,		"<Cancel>");
X		mapkey("\033[21~", "ZZ",	WHEN_VICMD,		"<Exit>");
X		mapkey("\033[26~", "V",		WHEN_VICMD|WHEN_INMV,	"<AddlOp>");
X		mapkey("\033[28~", "\\",	WHEN_VICMD|WHEN_INMV,	"<Help>");
X		mapkey("\033[29~", "K",		WHEN_VICMD|WHEN_INMV,	"<Do>");
X	}
X	else
X# endif /* RAINBOW */
X	{
X		mapkey("#S", "x", WHEN_VICMD|WHEN_INMV,	"<Delete>");
X		mapkey("#s", "B", WHEN_VICMD|WHEN_INMV,	"^<Left>");
X		mapkey("#t", "W", WHEN_VICMD|WHEN_INMV,	"^<Right>");
X	}
X#else /* not MSDOS */
X# if COHERENT
X	mapkey("\033[P", "x", WHEN_VICMD|WHEN_INMV, "<Del>");
X# else
X#if AMIGA
X	mapkey("\233?~", "\\",	WHEN_VICMD|WHEN_INMV,	"<Help>");
X#endif
X
X	if (ERASEKEY != '\177')
X	{
X		mapkey("\177", "x", WHEN_VICMD|WHEN_INMV, "<Del>");
X	}
X# endif
X#endif
X
X#ifndef NO_DIGRAPH
X	init_digraphs();
X#endif /* NO_DIGRAPH */
X
X	/* process any flags */
X	for (i = 1; i < argc && *argv[i] == '-'; i++)
X	{
X		switch (argv[i][1])
X		{
X		  case 'R':	/* readonly */
X			*o_readonly = TRUE;
X			break;
X
X		  case 'L':
X		  case 'r':	/* recover */
X			msg("Use the `elvrec` program to recover lost files");
X			endmsgs();
X			refresh();
X			endwin();
X			exit(0);
X			break;
X
X		  case 't':	/* tag */
X			if (argv[i][2])
X			{
X				tag = argv[i] + 2;
X			}
X			else
X			{
X				tag = argv[++i];
X			}
X			break;
X
X		  case 'v':	/* vi mode */
X			mode = MODE_VI;
X			break;
X
X		  case 'e':	/* ex mode */
X			mode = MODE_EX;
X			break;
X#ifndef NO_EXTENSIONS
X		  case 'i':	/* input mode */
X			*o_inputmode = TRUE;
X			break;
X#endif
X#ifndef NO_ERRLIST
X		  case 'm':	/* use "errlist" as the errlist */
X			if (argv[i][2])
X			{
X				err = argv[i] + 2;
X			}
X			else if (i + 1 < argc)
X			{
X				err = argv[++i];
X			}
X			else
X			{
X				err = "";
X			}
X			break;
X#endif
X#ifndef CRUNCH
X		 case 'c':	/* run the following command, later */
X			if (argv[i][2])
X			{
X				cmd = argv[i] + 2;
X			}
X			else
X			{
X				cmd = argv[++i];
X			}
X			break;
X
X		  case 'w':	/* set the window size */
X			if (argv[i][2])
X			{
X				*o_window = atoi(argv[i] + 2);
X				wset = TRUE;
X			}
X			else
X			{
X				*o_window = atoi(argv[++i]);
X				wset = TRUE;
X			}
X			break;
X#endif
X		  default:
X			msg("Ignoring unknown flag \"%s\"", argv[i]);
X		}
X	}
X
X	/* if we were given an initial ex command, save it... */
X	if (i < argc && *argv[i] == '+')
X	{
X		if (argv[i][1])
X		{
X			cmd = argv[i++] + 1;
X		}
X		else
X		{
X			cmd = "$"; /* "vi + file" means start at EOF */
X			i++;
X		}
X	}
X
X	/* the remaining args are file names. */
X	if (i < argc)
X	{
X		strcpy(args, argv[i]);
X		while (++i < argc && strlen(args) + 1 + strlen(argv[i]) < sizeof args)
X		{
X			strcat(args, " ");
X			strcat(args, argv[i]);
X		}
X#if MSDOS || TOS
X		/* expand wildcard characters, if necessary */
X		if (strchr(args, '*') || strchr(args, '?'))
X		{
X			strcpy(args, wildcard(args));
X		}
X#endif
X		strcpy(tmpblk.c, args);
X		cmd_args(MARK_UNSET, MARK_UNSET, CMD_ARGS, TRUE, tmpblk.c);
X	}
X	else
X	{
X		/* empty args list */
X		args[0] = '\0';
X		nargs = 1;
X		argno = -1;
X	}
X
X	/* perform the .exrc files and EXINIT environment variable */
X#ifdef SYSEXRC
X	doexrc(SYSEXRC);
X#endif
X#ifdef HMEXRC
X	str = getenv("HOME");
X	if (str && *str)
X	{
X		strcpy(tmpblk.c, str);
X		str = tmpblk.c + strlen(tmpblk.c);
X#if !VMS
X# if AMIGA	/* Don't SLASH a device. "Elvis:.exrc" */
X		if (str[-1] != COLON && str[-1] != SLASH)
X# else
X		if (str[-1] != SLASH)
X# endif
X		{
X			*str++ = SLASH;
X		}
X#endif
X		strcpy(str, HMEXRC);
X		doexrc(tmpblk.c);
X	}
X#endif
X#ifndef CRUNCH
X	if (*o_exrc)
X#endif
X	{
X		doexrc(EXRC);
X	}
X#ifdef EXINIT
X	str = getenv(EXINIT);
X	if (str)
X	{
X		exstring(str, strlen(str), ctrl('V'));
X	}
X#endif
X
X	/* search for a tag (or an error) now, if desired */
X	blkinit();
X	if (tag)
X	{
X		cmd_tag(MARK_FIRST, MARK_FIRST, CMD_TAG, 0, tag);
X	}
X#ifndef NO_ERRLIST
X	else if (err)
X	{
X		cmd_errlist(MARK_FIRST, MARK_FIRST, CMD_ERRLIST, 0, err);
X	}
X#endif
X
X	/* if no tag/err, or tag failed, then start with first arg */
X	if (tmpfd < 0)
X	{
X		/* start with first arg */
X		cmd_next(MARK_UNSET, MARK_UNSET, CMD_NEXT, FALSE, "");
X
X		/* pretend to do something, just to force a recoverable
X		 * version of the file out to disk
X		 */
X		ChangeText
X		{
X		}
X		clrflag(file, MODIFIED);
X	}
X
X	/* now we do the immediate ex command that we noticed before */
X	if (cmd)
X	{
X		doexcmd(cmd);
X	}
X
X	/* repeatedly call ex() or vi() (depending on the mode) until the
X	 * mode is set to MODE_QUIT
X	 */
X	while (mode != MODE_QUIT)
X	{
X		if (setjmp(jmpenv))
X		{
X			/* Maybe we just aborted a change? */
X			abortdo();
X		}
X		signal(SIGINT, (void(*)()) trapint);
X
X		switch (mode)
X		{
X		  case MODE_VI:
X			vi();
X			break;
X
X		  case MODE_EX:
X			ex();
X			break;
X#ifdef DEBUG
X		  default:
X			msg("mode = %d?", mode);
X			mode = MODE_QUIT;
X#endif
X		}
X	}
X
X	/* free up the cut buffers */
X	cutend();
X
X	/* end curses */
X#ifndef	NO_CURSORSHAPE
X	if (has_CQ)
X		do_CQ();
X#endif
X	endmsgs();
X	move(LINES - 1, 0);
X	clrtoeol();
X	refresh();
X	endwin();
X
X	exit(0);
X	/*NOTREACHED*/
X}
X
X
X/*ARGSUSED*/
Xint trapint(signo)
X	int	signo;
X{
X	beep();
X	resume_curses(FALSE);
X	abortdo();
X#if OSK
X	sigmask(-1);
X#endif
X#if TURBOC || __GNUC__ || _ANSI
X	signal(signo, (void (*)())trapint);
X#else
X	signal(signo, trapint);
X#endif
X	doingglobal = FALSE;
X
X	longjmp(jmpenv, 1);
X
X	return 0;
X}
X
X
X
X#ifndef NO_DIGRAPH
X
X/* This stuff us used to build the default digraphs table. */
Xstatic char	digtable[][4] =
X{
X# ifdef CS_IBMPC
X	"C,\200",	"u\"\1",	"e'\2",		"a^\3",
X	"a\"\4",	"a`\5",		"a@\6",		"c,\7",
X	"e^\10",	"e\"\211",	"e`\12",	"i\"\13",
X	"i^\14",	"i`\15",	"A\"\16",	"A@\17",
X	"E'\20",	"ae\21",	"AE\22",	"o^\23",
X	"o\"\24",	"o`\25",	"u^\26",	"u`\27",
X	"y\"\30",	"O\"\31",	"U\"\32",	"a'\240",
X	"i'!",		"o'\"",		"u'#",		"n~$",
X	"N~%",		"a-&",		"o-'",		"~?(",
X	"~!-",		"\"<.",		"\">/",
X#  ifdef CS_SPECIAL
X	"2/+",		"4/,",		"^+;",		"^q<",
X	"^c=",		"^r>",		"^t?",		"pp]",
X	"^^^",		"oo_",		"*a`",		"*ba",
X	"*pc",		"*Sd",		"*se",		"*uf",
X	"*tg",		"*Ph",		"*Ti",		"*Oj",
X	"*dk",		"*Hl",		"*hm",		"*En",
X	"*No",		"eqp",		"pmq",		"ger",
X	"les",		"*It",		"*iu",		"*/v",
X	"*=w",		"sq{",		"^n|",		"^2}",
X	"^3~",		"^_\377",
X#  endif /* CS_SPECIAL */
X# endif /* CS_IBMPC */
X# ifdef CS_LATIN1
X	"~!!",		"a-*",		"\">+",		"o-:",
X	"\"<>",		"~??",
X
X	"A`@",		"A'A",		"A^B",		"A~C",
X	"A\"D",		"A@E",		"AEF",		"C,G",
X	"E`H",		"E'I",		"E^J",		"E\"K",
X	"I`L",		"I'M",		"I^N",		"I\"O",
X	"-DP",		"N~Q",		"O`R",		"O'S",
X	"O^T",		"O~U",		"O\"V",		"O/X",
X	"U`Y",		"U'Z",		"U^[",		"U\"\\",
X	"Y'_",
X
X	"a``",		"a'a",		"a^b",		"a~c",
X	"a\"d",		"a@e",		"aef",		"c,g",
X	"e`h",		"e'i",		"e^j",		"e\"k",
X	"i`l",		"i'm",		"i^n",		"i\"o",
X	"-dp",		"n~q",		"o`r",		"o's",
X	"o^t",		"o~u",		"o\"v",		"o/x",
X	"u`y",		"u'z",		"u^{",		"u\"|",
X	"y'~",
X# endif /* CS_LATIN1 */
X	""
X};
X
Xstatic init_digraphs()
X{
X	int	i;
X
X	for (i = 0; *digtable[i]; i++)
X	{
X		do_digraph(FALSE, digtable[i]);
X	}
X	do_digraph(FALSE, (char *)0);
X	return 0;
X}
X#endif /* NO_DIGRAPH */
/
echo x - misc.c
sed '/^X/s///' > misc.c << '/'
X/* misc.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains functions which didn't seem happy anywhere else */
X
X#include "config.h"
X#include "vi.h"
X
X
X/* find a particular line & return a pointer to a copy of its text */
Xchar *fetchline(line)
X	long	line;	/* line number of the line to fetch */
X{
X	int		i;
X	REG char	*scan;	/* used to search for the line in a BLK */
X	long		l;	/* line number counter */
X	static BLK	buf;	/* holds ONLY the selected line (as string) */
X	REG char	*cpy;	/* used while copying the line */
X	static long	nextline;	/* }  These four variables are used */
X	static long	chglevel;	/*  } to implement a shortcut when  */
X	static char	*nextscan;	/*  } consecutive lines are fetched */
X	static long	nextlnum;	/* }                                */
X
X	/* can we do a shortcut? */
X	if (changes == chglevel && line == nextline)
X	{
X		scan = nextscan;
X	}
X	else
X	{
X		/* scan lnum[] to determine which block its in */
X		for (i = 1; line > lnum[i]; i++)
X		{
X		}
X		nextlnum = lnum[i];
X
X		/* fetch text of the block containing that line */
X		scan = blkget(i)->c;
X
X		/* find the line in the block */
X		for (l = lnum[i - 1]; ++l < line; )
X		{
X			while (*scan++ != '\n')
X			{
X			}
X		}
X	}
X
X	/* copy it into a block by itself, with no newline */
X	for (cpy = buf.c; *scan != '\n'; )
X	{
X		*cpy++ = *scan++;
X	}
X	*cpy = '\0';
X
X	/* maybe speed up the next call to fetchline() ? */
X	if (line < nextlnum)
X	{
X		nextline = line + 1;
X		chglevel = changes;
X		nextscan = scan + 1;
X	}
X	else
X	{
X		nextline = 0;
X	}
X
X	/* Calls to fetchline() interfere with calls to pfetch().  Make sure
X	 * that pfetch() resets itself on its next invocation.
X	 */
X	pchgs = 0L;
X
X	/* Return a pointer to the line's text */
X	return buf.c;
X}
X
X
X/* error message from the regexp code */
Xvoid regerror(txt)
X	char	*txt;	/* an error message */
X{
X	msg("RE error: %s", txt);
X}
X
X/* This function is equivelent to the pfetch() macro */
Xvoid	pfetch(l)
X	long	l;	/* line number of line to fetch */
X{
X	if(l != pline || changes != pchgs)
X	{
X		pline = (l);
X		ptext = fetchline(pline);
X		plen = strlen(ptext);
X		pchgs = changes;
X	}
X}
/
echo x - modify.c
sed '/^X/s///' > modify.c << '/'
X/* modify.c */
X
X/* This file contains the low-level file modification functions:
X *	delete(frommark, tomark)	- removes line or portions of lines
X *	add(frommark, text)		- inserts new text
X *	change(frommark, tomark, text)	- delete, then add
X */
X
X#include "config.h"
X#include "vi.h"
X
X#ifdef DEBUG2
X# include <stdio.h>
Xstatic FILE *dbg;
X
X/*VARARGS1*/
Xdebout(msg, arg1, arg2, arg3, arg4, arg5)
X	char	*msg, *arg1, *arg2, *arg3, *arg4, *arg5;
X{
X	if (!dbg)
X	{
X		dbg = fopen("debug.out", "w");
X		if (!dbg)
X			return;
X		setbuf(dbg, (FILE *)0);
X	}
X	fprintf(dbg, msg, arg1, arg2, arg3, arg4, arg5);
X}
X#endif /* DEBUG2 */
X
X/* delete a range of text from the file */
Xvoid delete(frommark, tomark)
X	MARK		frommark;	/* first char to be deleted */
X	MARK		tomark;		/* AFTER last char to be deleted */
X{
X	int		i;		/* used to move thru logical blocks */
X	REG char	*scan;		/* used to scan thru text of the blk */
X	REG char	*cpy;		/* used when copying chars */
X	BLK		*blk;		/* a text block */
X	long		l;		/* a line number */
X	MARK		m;		/* a traveling version of frommark */
X
X#ifdef DEBUG2
X	debout("delete(%ld.%d, %ld.%d)\n", markline(frommark), markidx(frommark), markline(tomark), markidx(tomark));
X#endif
X
X	/* if not deleting anything, quit now */
X	if (frommark == tomark)
X	{
X		return;
X	}
X
X	/* This is a change */
X	changes++;
X	significant = TRUE;
X
X	/* supply clues to the redraw module */
X	redrawrange(markline(frommark), markline(tomark), markline(frommark));
X
X	/* adjust marks 'a through 'z and '' as needed */
X	l = markline(tomark);
X	for (i = 0; i < NMARKS; i++)
X	{
X		if (mark[i] < frommark)
X		{
X			continue;
X		}
X		else if (mark[i] < tomark)
X		{
X			mark[i] = MARK_UNSET;
X		}
X		else if (markline(mark[i]) == l)
X		{
X			if (markline(frommark) == l)
X			{
X				mark[i] -= markidx(tomark) - markidx(frommark);
X			}
X			else
X			{
X				mark[i] -= markidx(tomark);
X			}
X		}
X		else
X		{
X			mark[i] -= MARK_AT_LINE(l - markline(frommark));
X		}
X	}
X
X	/* Reporting... */
X	if (markidx(frommark) == 0 && markidx(tomark) == 0)
X	{
X		rptlines = markline(tomark) - markline(frommark);
X		rptlabel = "deleted";
X	}
X
X	/* find the block containing frommark */
X	l = markline(frommark);
X	for (i = 1; lnum[i] < l; i++)
X	{
X	}
X
X	/* process each affected block... */
X	for (m = frommark;
X	     m < tomark && lnum[i] < INFINITY;
X	     m = MARK_AT_LINE(lnum[i - 1] + 1))
X	{
X		/* fetch the block */
X		blk = blkget(i);
X
X		/* find the mark in the block */
X		scan = blk->c;
X		for (l = markline(m) - lnum[i - 1] - 1; l > 0; l--)
X		{
X			while (*scan++ != '\n')
X			{
X			}
X		}
X		scan += markidx(m);
X
X		/* figure out where the changes to this block end */
X		if (markline(tomark) > lnum[i])
X		{
X			cpy = blk->c + BLKSIZE;
X		}
X		else if (markline(tomark) == markline(m))
X		{
X			cpy = scan - markidx(m) + markidx(tomark);
X		}
X		else
X		{
X			cpy = scan;
X			for (l = markline(tomark) - markline(m);
X			     l > 0;
X			     l--)
X			{
X				while (*cpy++ != '\n')
X				{
X				}
X			}
X			cpy += markidx(tomark);
X		}
X
X		/* delete the stuff by moving chars within this block */
X		while (cpy < blk->c + BLKSIZE)
X		{
X			*scan++ = *cpy++;
X		}
X		while (scan < blk->c + BLKSIZE)
X		{
X			*scan++ = '\0';
X		}
X
X		/* adjust tomark to allow for lines deleted from this block */
X		tomark -= MARK_AT_LINE(lnum[i] + 1 - markline(m));
X
X		/* if this block isn't empty now, then advance i */
X		if (*blk->c)
X		{
X			i++;
X		}
X
X		/* the buffer has changed.  Update hdr and lnum. */
X		blkdirty(blk);
X	}
X
X	/* must have at least 1 line */
X	if (nlines == 0)
X	{
X		blk = blkadd(1);
X		blk->c[0] = '\n';
X		blkdirty(blk);
X		cursor = MARK_FIRST;
X	}
X}
X
X
X/* add some text at a specific place in the file */
Xvoid add(atmark, newtext)
X	MARK		atmark;		/* where to insert the new text */
X	char		*newtext;	/* NUL-terminated string to insert */
X{
X	REG char	*scan;		/* used to move through string */
X	REG char	*build;		/* used while copying chars */
X	int		addlines;	/* number of lines we're adding */
X	int		lastpart;	/* size of last partial line */
X	BLK		*blk;		/* the block to be modified */
X	int		blkno;		/* the logical block# of (*blk) */
X	REG char	*newptr;	/* where new text starts in blk */
X	BLK		buf;		/* holds chars from orig blk */
X	BLK		linebuf;	/* holds part of line that didn't fit */
X	BLK		*following;	/* the BLK following the last BLK */
X	int		i;
X	long		l;
X
X#ifdef DEBUG2
X	debout("add(%ld.%d, \"%s\")\n", markline(atmark), markidx(atmark), newtext);
X#endif
X#ifdef lint
X	buf.c[0] = 0;
X#endif
X	/* if not adding anything, return now */
X	if (!*newtext)
X	{
X		return;
X	}
X
X	/* This is a change */
X	changes++;
X	significant = TRUE;
X
X	/* count the number of lines in the new text */
X	for (scan = newtext, lastpart = addlines = 0; *scan; )
X	{
X		if (*scan++ == '\n')
X		{
X			addlines++;
X			lastpart = 0;
X		}
X		else
X		{
X			lastpart++;
X		}
X	}
X
X	/* Reporting... */
X	if (lastpart == 0 && markidx(atmark) == 0)
X	{
X		rptlines = addlines;
X		rptlabel = "added";
X	}
X
X	/* extract the line# from atmark */
X	l = markline(atmark);
X
X	/* supply clues to the redraw module */
X	if ((markidx(atmark) == 0 && lastpart == 0) || addlines == 0)
X	{
X		redrawrange(l, l, l + addlines);
X	}
X	else
X	{
X		/* make sure the last line gets redrawn -- it was
X		 * split, so its appearance has changed
X		 */
X		redrawrange(l, l + 1L, l + addlines + 1L);
X	}
X
X	/* adjust marks 'a through 'z and '' as needed */
X	for (i = 0; i < NMARKS; i++)
X	{
X		if (mark[i] < atmark)
X		{
X			/* earlier line, or earlier in same line: no change */
X			continue;
X		}
X		else if (markline(mark[i]) > l)
X		{
X			/* later line: move down a whole number of lines */
X			mark[i] += MARK_AT_LINE(addlines);
X		}
X		else
X		{
X			/* later in same line */
X			if (addlines > 0)
X			{
X				/* multi-line add, which split this line:
X				 * move down, and possibly left or right,
X				 * depending on where the split was and how
X				 * much text was inserted after the last \n
X				 */
X				mark[i] += MARK_AT_LINE(addlines) + lastpart - markidx(atmark);
X			}
X			else
X			{
X				/* totally within this line: move right */
X				mark[i] += lastpart;
X			}
X		}
X	}
X
X	/* get the block to be modified */
X	for (blkno = 1; lnum[blkno] < l && lnum[blkno + 1] < INFINITY; blkno++)
X	{
X	}
X	blk = blkget(blkno);
X	buf = *blk;
X
X	/* figure out where the new text starts */
X	for (newptr = buf.c, l = markline(atmark) - lnum[blkno - 1] - 1;
X	     l > 0;
X	     l--)
X	{
X		while (*newptr++ != '\n')
X		{
X		}
X	}
X	newptr += markidx(atmark);
X
X	/* keep start of old block */
X	build = blk->c + (int)(newptr - buf.c);
X
X	/* fill this block (or blocks) from the newtext string */
X	while (*newtext)
X	{
X		while (*newtext && build < blk->c + BLKSIZE - 1)
X		{
X			*build++ = *newtext++;
X		}
X		if (*newtext)
X		{
X			/* save the excess */
X			for (scan = linebuf.c + BLKSIZE;
X			     build > blk->c && build[-1] != '\n';
X			     )
X			{
X				*--scan = *--build;
X			}
X
X			/* write the block */
X			while (build < blk->c + BLKSIZE)
X			{
X				*build++ = '\0';
X			}
X			blkdirty(blk);
X
X			/* add another block */
X			blkno++;
X			blk = blkadd(blkno);
X
X			/* copy in the excess from last time */
X			for (build = blk->c; scan < linebuf.c + BLKSIZE; )
X			{
X				*build++ = *scan++;
X			}
X		}
X	}
X
X	/* fill this block(s) from remainder of orig block */
X	while (newptr < buf.c + BLKSIZE && *newptr)
X	{
X		while (newptr < buf.c + BLKSIZE
X		    && *newptr
X		    && build < blk->c + BLKSIZE - 1)
X		{
X			*build++ = *newptr++;
X		}
X		if (newptr < buf.c + BLKSIZE && *newptr)
X		{
X			/* save the excess */
X			for (scan = linebuf.c + BLKSIZE;
X			     build > blk->c && build[-1] != '\n';
X			     )
X			{
X				*--scan = *--build;
X			}
X
X			/* write the block */
X			while (build < blk->c + BLKSIZE)
X			{
X				*build++ = '\0';
X			}
X			blkdirty(blk);
X
X			/* add another block */
X			blkno++;
X			blk = blkadd(blkno);
X
X			/* copy in the excess from last time */
X			for (build = blk->c; scan < linebuf.c + BLKSIZE; )
X			{
X				*build++ = *scan++;
X			}
X		}
X	}
X
X	/* see if we can combine our last block with the following block */
X	if (lnum[blkno] < nlines && lnum[blkno + 1] - lnum[blkno] < (BLKSIZE >> 6))
X	{
X		/* hey, we probably can!  Get the following block & see... */
X		following = blkget(blkno + 1);
X		if (strlen(following->c) + (build - blk->c) < BLKSIZE - 1)
X		{
X			/* we can!  Copy text from following to blk */
X			for (scan = following->c; *scan; )
X			{
X				*build++ = *scan++;
X			}
X			while (build < blk->c + BLKSIZE)
X			{
X				*build++ = '\0';
X			}
X			blkdirty(blk);
X
X			/* pretend the following was the last blk */
X			blk = following;
X			build = blk->c;
X		}
X	}
X
X	/* that last block is dirty by now */
X	while (build < blk->c + BLKSIZE)
X	{
X		*build++ = '\0';
X	}
X	blkdirty(blk);
X}
X
X
X/* change the text of a file */
Xvoid change(frommark, tomark, newtext)
X	MARK	frommark, tomark;
X	char	*newtext;
X{
X	int	i;
X	long	l;
X	char	*text;
X	BLK	*blk;
X
X#ifdef DEBUG2
X	debout("change(%ld.%d, %ld.%d, \"%s\")\n", markline(frommark), markidx(frommark), markline(tomark), markidx(tomark), newtext);
X#endif
X
X	/* optimize for single-character replacement */
X	if (frommark + 1 == tomark && newtext[0] && !newtext[1] && newtext[0] != '\n')
X	{
X		/* find the block containing frommark */
X		l = markline(frommark);
X		for (i = 1; lnum[i] < l; i++)
X		{
X		}
X
X		/* get the block */
X		blk = blkget(i);
X
X		/* find the line within the block */
X		for (text = blk->c, i = l - lnum[i - 1] - 1; i > 0; text++)
X		{
X			if (*text == '\n')
X			{
X				i--;
X			}
X		}
X
X		/* replace the char */
X		text += markidx(frommark);
X		if (*text == newtext[0])
X		{
X			/* no change was needed - same char */
X			return;
X		}
X		else if (*text != '\n')
X		{
X			/* This is a change */
X			changes++;
X			significant = TRUE;
X			ChangeText
X			{
X				*text = newtext[0];
X				blkdirty(blk);
X			}
X			redrawrange(markline(frommark), markline(tomark), markline(frommark));
X			return;
X		}
X		/* else it is a complex change involving newline... */
X	}
X
X	/* couldn't optimize, so do delete & add */
X	ChangeText
X	{
X		delete(frommark, tomark);
X		add(frommark, newtext);
X		rptlabel = "changed";
X	}
X}
/
echo x - move1.c
sed '/^X/s///' > move1.c << '/'
X/* move1.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains most movement functions */
X
X#include "config.h"
X#include "vi.h"
X#include "ctype.h"
X
XMARK	m_updnto(m, cnt, cmd)
X	MARK	m;	/* movement is relative to this mark */
X	long	cnt;	/* a numeric argument */
X	char	cmd;	/* the command character */
X{
X	DEFAULT(cmd == 'G' ? nlines : 1L);
X
X	/* move up or down 'cnt' lines */
X	switch (cmd)
X	{
X	  case ctrl('P'):
X	  case '-':
X	  case 'k':
X		m -= MARK_AT_LINE(cnt);
X		break;
X
X	  case 'G':
X		if (cnt < 1L || cnt > nlines)
X		{
X			msg("Only %ld lines", nlines);
X			return MARK_UNSET;
X		}
X		m = MARK_AT_LINE(cnt);
X		break;
X
X	  case '_':
X		cnt--;
X		/* fall through... */
X
X	  default:
X		m += MARK_AT_LINE(cnt);
X	}
X
X	/* if that left us screwed up, then fail */
X	if (m < MARK_FIRST || markline(m) > nlines)
X	{
X		return MARK_UNSET;
X	}
X
X	return m;
X}
X
X/*ARGSUSED*/
XMARK	m_right(m, cnt, key, prevkey)
X	MARK	m;	/* movement is relative to this mark */
X	long	cnt;	/* a numeric argument */
X	int	key;	/* movement keystroke */
X	int	prevkey;/* operator keystroke, or 0 if none */
X{
X	int		idx;	/* index of the new cursor position */
X
X	DEFAULT(1);
X
X	/* If used with an operator, then move 1 less character, since the 'l'
X	 * command includes the character that it moves onto.
X	 */
X	if (prevkey != '\0')
X	{
X		cnt--;
X	}
X
X	/* move to right, if that's OK */
X	pfetch(markline(m));
X	idx = markidx(m) + cnt;
X	if (idx < plen)
X	{
X		m += cnt;
X	}
X	else
X	{
X		return MARK_UNSET;
X	}
X
X	return m;
X}
X
X/*ARGSUSED*/
XMARK	m_left(m, cnt)
X	MARK	m;	/* movement is relative to this mark */
X	long	cnt;	/* a numeric argument */
X{
X	DEFAULT(1);
X
X	/* move to the left, if that's OK */
X	if (markidx(m) >= cnt)
X	{
X		m -= cnt;
X	}
X	else
X	{
X		return MARK_UNSET;
X	}
X
X	return m;
X}
X
X/*ARGSUSED*/
XMARK	m_tocol(m, cnt, cmd)
X	MARK	m;	/* movement is relative to this mark */
X	long	cnt;	/* a numeric argument */
X	int	cmd;	/* either ctrl('X') or '|' */
X{
X	char	*text;	/* text of the line */
X	int	col;	/* column number */
X	int	idx;	/* index into the line */
X
X
X	/* if doing ^X, then adjust for sideways scrolling */
X	if (cmd == ctrl('X'))
X	{
X		DEFAULT(*o_columns & 0xff);
X		cnt += leftcol;
X	}
X	else
X	{
X		DEFAULT(1);
X	}
X
X	/* internally, columns are numbered 0..COLS-1, not 1..COLS */
X	cnt--;
X
X	/* if 0, that's easy */
X	if (cnt == 0)
X	{
X		m &= ~(BLKSIZE - 1);
X		return m;
X	}
X
X	/* find that column within the line */
X	pfetch(markline(m));
X	text = ptext;
X	for (col = idx = 0; col < cnt && *text; text++, idx++)
X	{
X		if (*text == '\t' && !*o_list)
X		{
X			col += *o_tabstop;
X			col -= col % *o_tabstop;
X		}
X		else if (UCHAR(*text) < ' ' || *text == '\177')
X		{
X			col += 2;
X		}
X#ifndef NO_CHARATTR
X		else if (text[0] == '\\' && text[1] == 'f' && text[2] && *o_charattr)
X		{
X			text += 2; /* plus one more as part of for loop */
X		}
X#endif
X		else
X		{
X			col++;
X		}
X	}
X	if (!*text)
X	{
X		/* the desired column was past the end of the line, so
X		 * act like the user pressed "$" instead.
X		 */
X		return m | (BLKSIZE - 1);
X	}
X	else
X	{
X		m = (m & ~(BLKSIZE - 1)) + idx;
X	}
X	return m;
X}
X
X/*ARGSUSED*/
XMARK	m_front(m, cnt)
X	MARK	m;	/* movement is relative to this mark */
X	long	cnt;	/* a numeric argument (ignored) */
X{
X	char	*scan;
X
X	/* move to the first non-whitespace character */
X	pfetch(markline(m));
X	scan = ptext;
X	m &= ~(BLKSIZE - 1);
X	while (*scan == ' ' || *scan == '\t')
X	{
X		scan++;
X		m++;
X	}
X
X	return m;
X}
X
X/*ARGSUSED*/
XMARK	m_rear(m, cnt)
X	MARK	m;	/* movement is relative to this mark */
X	long	cnt;	/* a numeric argument (ignored) */
X{
X	/* Try to move *EXTREMELY* far to the right.  It is fervently hoped
X	 * that other code will convert this to a more reasonable MARK before
X	 * anything tries to actually use it.  (See adjmove() in vi.c)
X	 */
X	return m | (BLKSIZE - 1);
X}
X
X#ifndef NO_SENTENCE
Xstatic int isperiod(ptr)
X	char	*ptr;	/* pointer to possible sentence-ender */
X{
X	/* if not '.', '?', or '!', then it isn't a sentence ender */
X	if (*ptr != '.' && *ptr != '?' && *ptr != '!')
X	{
X		return FALSE;
X	}
X
X	/* skip any intervening ')', ']', or '"' characters */
X	do
X	{
X		ptr++;
X	} while (*ptr == ')' || *ptr == ']' || *ptr == '"');
X
X	/* do we have two spaces or EOL? */
X	if (!*ptr || ptr[0] == ' ' && ptr[1] == ' ')
X	{
X		return TRUE;
X	}
X	return FALSE;
X}
X
X/*ARGSUSED*/
XMARK	m_sentence(m, cnt, cmd)
X	MARK	m;	/* movement is relative to this mark */
X	long	cnt;	/* a numeric argument */
X	int	cmd;	/* either '(' or ')' */
X{
X	REG char	*text;
X	REG long	l;
X
X	DEFAULT(1);
X
X	/* If '(' command, then move back one word, so that if we hit '(' at
X	 * the start of a sentence we don't simply stop at the end of the
X	 * previous sentence and bounce back to the start of this one again.
X	 */
X	if (cmd == '(')
X	{
X		m = m_bword(m, 1L, 'b');
X		if (!m)
X		{
X			return m;
X		}
X	}
X
X	/* get the current line */
X	l = markline(m);
X	pfetch(l);
X	text = ptext + markidx(m);
X
X	/* for each requested sentence... */
X	while (cnt-- > 0)
X	{
X		/* search forward for one of [.?!] followed by spaces or EOL */
X		do
X		{
X			if (cmd == ')')
X			{
X				/* move forward, wrap at end of line */
X				if (!text[0])
X				{
X					if (l >= nlines)
X					{
X						return MARK_UNSET;
X					}
X					l++;
X					pfetch(l);
X					text = ptext;
X				}
X				else
X				{
X					text++;
X				}
X			}
X			else
X			{
X				/* move backward, wrap at beginning of line */
X				if (text == ptext)
X				{
X					do
X					{
X						if (l <= 1)
X						{
X							return MARK_FIRST;
X						}
X						l--;
X						pfetch(l);
X					} while (!*ptext);
X					text = ptext + plen - 1;
X				}
X				else
X				{
X					text--;
X				}
X			}
X		} while (!isperiod(text));
X	}
X
X	/* construct a mark for this location */
X	m = buildmark(text);
X
X	/* move forward to the first word of the next sentence */
X	m = m_fword(m, 1L, 'w', '\0');
X
X	return m;
X}
X#endif
X
XMARK	m_paragraph(m, cnt, cmd)
X	MARK	m;	/* movement is relative to this mark */
X	long	cnt;	/* a numeric argument */
X	int	cmd;	/* either '{' or '}' */
X{
X	char	*text;	/* text of the current line */
X	char	*pscn;	/* used to scan thru value of "paragraphs" option */
X	long	l, ol;	/* current line number, original line number */
X	int	dir;	/* -1 if we're moving up, or 1 if down */
X	char	col0;	/* character to expect in column 0 */
X#ifndef NO_SENTENCE
X# define SENTENCE(x)	(x)
X	char	*list;	/* either o_sections or o_paragraph */
X#else
X# define SENTENCE(x)
X#endif
X
X	DEFAULT(1);
X
X	/* set the direction, based on the command */
X	switch (cmd)
X	{
X	  case '{':
X		dir = -1;
X		col0 = '\0';
X		SENTENCE(list = o_paragraphs); 
X		break;
X
X	  case '}':
X		dir = 1;
X		col0 = '\0';
X		SENTENCE(list = o_paragraphs); 
X		break;
X
X	  case '[':
X		if (getkey(0) != '[')
X		{
X			return MARK_UNSET;
X		}
X		dir = -1;
X		col0 = '{';
X		SENTENCE(list = o_sections); 
X		break;
X
X	  case ']':
X		if (getkey(0) != ']')
X		{
X			return MARK_UNSET;
X		}
X		dir = 1;
X		col0 = '{';
X		SENTENCE(list = o_sections); 
X		break;
X	}
X	ol = l = markline(m);
X
X	/* for each paragraph that we want to travel through... */
X	while (l > 0 && l <= nlines && cnt-- > 0)
X	{
X		/* skip blank lines between paragraphs */
X		while (l > 0 && l <= nlines && col0 == *(text = fetchline(l)))
X		{
X			l += dir;
X		}
X
X		/* skip non-blank lines that aren't paragraph separators
X		 */
X		do
X		{
X#ifndef NO_SENTENCE
X			if (*text == '.' && l != ol)
X			{
X				for (pscn = list; pscn[0] && pscn[1]; pscn += 2)
X				{
X					if (pscn[0] == text[1] && pscn[1] == text[2])
X					{
X						pscn = (char *)0;
X						goto BreakBreak;
X					}
X				}
X			}
X#endif
X			l += dir;
X		} while (l > 0 && l <= nlines && col0 != *(text = fetchline(l)));
XBreakBreak:	;
X	}
X
X	if (l > nlines)
X	{
X		m = MARK_LAST;
X	}
X	else if (l <= 0)
X	{
X		m = MARK_FIRST;
X	}
X	else
X	{
X		m = MARK_AT_LINE(l);
X	}
X	return m;
X}
X
X
X/*ARGSUSED*/
XMARK	m_match(m, cnt)
X	MARK	m;	/* movement is relative to this mark */
X	long	cnt;	/* a numeric argument (normally 0) */
X{
X	long	l;
X	REG char	*text;
X	REG char	match;
X	REG char	nest;
X	REG int		count;
X
X#ifndef NO_EXTENSIONS
X	/* if we're given a number, then treat it as a percentage of the file */
X	if (cnt > 0)
X	{
X		/* make sure it is a reasonable number */
X		if (cnt > 100)
X		{
X			msg("can only be from 1%% to 100%%");
X			return MARK_UNSET;
X		}
X
X		/* return the appropriate line number */
X		l = (nlines - 1L) * cnt / 100L + 1L;
X		return MARK_AT_LINE(l);
X	}
X#endif /* undef NO_EXTENSIONS */
X
X	/* get the current line */
X	l = markline(m);
X	pfetch(l);
X	text = ptext + markidx(m);
X
X	/* search forward within line for one of "[](){}" */
X	for (match = '\0'; !match && *text; text++)
X	{
X		/* tricky way to recognize 'em in ASCII */
X		nest = *text;
X		if ((nest & 0xdf) == ']' || (nest & 0xdf) == '[')
X		{
X			match = nest ^ ('[' ^ ']');
X		}
X		else if ((nest & 0xfe) == '(')
X		{
X			match = nest ^ ('(' ^ ')');
X		}
X		else
X		{
X			match = 0;
X		}
X	}
X	if (!match)
X	{
X		return MARK_UNSET;
X	}
X	text--;
X
X	/* search forward or backward for match */
X	if (match == '(' || match == '[' || match == '{')
X	{
X		/* search backward */
X		for (count = 1; count > 0; )
X		{
X			/* wrap at beginning of line */
X			if (text == ptext)
X			{
X				do
X				{
X					if (l <= 1L)
X					{
X						return MARK_UNSET;
X					}
X					l--;
X					pfetch(l);
X				} while (!*ptext);
X				text = ptext + plen - 1;
X			}
X			else
X			{
X				text--;
X			}
X
X			/* check the char */
X			if (*text == match)
X				count--;
X			else if (*text == nest)
X				count++;
X		}
X	}
X	else
X	{
X		/* search forward */
X		for (count = 1; count > 0; )
X		{
X			/* wrap at end of line */
X			if (!*text)
X			{
X				if (l >= nlines)
X				{
X					return MARK_UNSET;
X				}
X				l++;
X				pfetch(l);
X				text = ptext;
X			}
X			else
X			{
X				text++;
X			}
X
X			/* check the char */
X			if (*text == match)
X				count--;
X			else if (*text == nest)
X				count++;
X		}
X	}
X
X	/* construct a mark for this place */
X	m = buildmark(text);
X	return m;
X}
X
X/*ARGSUSED*/
XMARK	m_tomark(m, cnt, key)
X	MARK	m;	/* movement is relative to this mark */
X	long	cnt;	/* (ignored) */
X	int	key;	/* keystroke - the mark to move to */
X{
X	/* mark '' is a special case */
X	if (key == '\'' || key == '`')
X	{
X		if (mark[26] == MARK_UNSET)
X		{
X			return MARK_FIRST;
X		}
X		else
X		{
X			return mark[26];
X		}
X	}
X
X	/* if not a valid mark number, don't move */
X	if (key < 'a' || key > 'z')
X	{
X		return MARK_UNSET;
X	}
X
X	/* return the selected mark -- may be MARK_UNSET */
X	if (!mark[key - 'a'])
X	{
X		msg("mark '%c is unset", key);
X	}
X	return mark[key - 'a'];
X}
X
/
echo x - move2.c
sed '/^X/s///' > move2.c << '/'
X/* move2.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This function contains the movement functions that perform RE searching */
X
X#include "config.h"
X#include "vi.h"
X#include "regexp.h"
X
Xextern long	atol();
X
Xstatic regexp	*re;	/* compiled version of the pattern to search for */
Xstatic		prevsf;	/* boolean: previous search direction was forward? */
X
X#ifndef NO_EXTENSIONS
X/*ARGSUSED*/
XMARK m_wsrch(word, m, cnt)
X	char	*word;	/* the word to search for */
X	MARK	m;	/* the starting point */
X	int	cnt;	/* ignored */
X{
X	char	buffer[30];
X
X	/* wrap \< and \> around the word */
X	strcpy(buffer, "/\\<");
X	strcat(buffer, word);
X	strcat(buffer, "\\>");
X
X	/* show the searched-for word on the bottom line */
X	move(LINES - 1, 0);
X	qaddstr(buffer);
X	clrtoeol();
X	refresh();
X
X	/* search for the word */
X	return m_fsrch(m, buffer);
X}
X#endif
X
XMARK	m_nsrch(m)
X	MARK	m;	/* where to start searching */
X{
X	if (prevsf)
X	{
X		m = m_fsrch(m, (char *)0);
X		prevsf = TRUE;
X	}
X	else
X	{
X		m = m_bsrch(m, (char *)0);
X		prevsf = FALSE;
X	}
X	return m;
X}
X
XMARK	m_Nsrch(m)
X	MARK	m;	/* where to start searching */
X{
X	if (prevsf)
X	{
X		m = m_bsrch(m, (char *)0);
X		prevsf = TRUE;
X	}
X	else
X	{
X		m = m_fsrch(m, (char *)0);
X		prevsf = FALSE;
X	}
X	return m;
X}
X
XMARK	m_fsrch(m, ptrn)
X	MARK	m;	/* where to start searching */
X	char	*ptrn;	/* pattern to search for */
X{
X	long	l;	/* line# of line to be searched */
X	char	*line;	/* text of line to be searched */
X	int	wrapped;/* boolean: has our search wrapped yet? */
X	int	pos;	/* where we are in the line */
X#ifndef CRUNCH
X	long	delta = INFINITY;/* line offset, for things like "/foo/+1" */
X#endif
X
X	/* remember: "previous search was forward" */
X	prevsf = TRUE;
X
X	if (ptrn && *ptrn)
X	{
X		/* locate the closing '/', if any */
X		line = parseptrn(ptrn);
X#ifndef CRUNCH
X		if (*line)
X		{
X			delta = atol(line);
X		}
X#endif
X		ptrn++;
X
X		/* free the previous pattern */
X		if (re) free(re);
X
X		/* compile the pattern */
X		re = regcomp(ptrn);
X		if (!re)
X		{
X			return MARK_UNSET;
X		}
X	}
X	else if (!re)
X	{
X		msg("No previous expression");
X		return MARK_UNSET;
X	}
X
X	/* search forward for the pattern */
X	pos = markidx(m) + 1;
X	pfetch(markline(m));
X	if (pos >= plen)
X	{
X		pos = 0;
X		m = (m | (BLKSIZE - 1)) + 1;
X	}
X	wrapped = FALSE;
X	for (l = markline(m); l != markline(m) + 1 || !wrapped; l++)
X	{
X		/* wrap search */
X		if (l > nlines)
X		{
X			/* if we wrapped once already, then the search failed */
X			if (wrapped)
X			{
X				break;
X			}
X
X			/* else maybe we should wrap now? */
X			if (*o_wrapscan)
X			{
X				l = 0;
X				wrapped = TRUE;
X				continue;
X			}
X			else
X			{
X				break;
X			}
X		}
X
X		/* get this line */
X		line = fetchline(l);
X
X		/* check this line */
X		if (regexec(re, &line[pos], (pos == 0)))
X		{
X			/* match! */
X			if (wrapped && *o_warn)
X				msg("(wrapped)");
X#ifndef CRUNCH
X			if (delta != INFINITY)
X			{
X				l += delta;
X				if (l < 1 || l > nlines)
X				{
X					msg("search offset too big");
X					return MARK_UNSET;
X				}
X				force_flags = LNMD|INCL;
X				return MARK_AT_LINE(l);
X			}
X#endif
X			return MARK_AT_LINE(l) + (int)(re->startp[0] - line);
X		}
X		pos = 0;
X	}
X
X	/* not found */
X	msg(*o_wrapscan ? "Not found" : "Hit bottom without finding RE");
X	return MARK_UNSET;
X}
X
XMARK	m_bsrch(m, ptrn)
X	MARK	m;	/* where to start searching */
X	char	*ptrn;	/* pattern to search for */
X{
X	long	l;	/* line# of line to be searched */
X	char	*line;	/* text of line to be searched */
X	int	wrapped;/* boolean: has our search wrapped yet? */
X	int	pos;	/* last acceptable idx for a match on this line */
X	int	last;	/* remembered idx of the last acceptable match on this line */
X	int	try;	/* an idx at which we strat searching for another match */
X#ifndef CRUNCH
X	long	delta = INFINITY;/* line offset, for things like "/foo/+1" */
X#endif
X
X	/* remember: "previous search was not forward" */
X	prevsf = FALSE;
X
X	if (ptrn && *ptrn)
X	{
X		/* locate the closing '?', if any */
X		line = parseptrn(ptrn);
X#ifndef CRUNCH
X		if (*line)
X		{
X			delta = atol(line);
X		}
X#endif
X		ptrn++;
X
X		/* free the previous pattern, if any */
X		if (re) free(re);
X
X		/* compile the pattern */
X		re = regcomp(ptrn);
X		if (!re)
X		{
X			return MARK_UNSET;
X		}
X	}
X	else if (!re)
X	{
X		msg("No previous expression");
X		return MARK_UNSET;
X	}
X
X	/* search backward for the pattern */
X	pos = markidx(m);
X	wrapped = FALSE;
X	for (l = markline(m); l != markline(m) - 1 || !wrapped; l--)
X	{
X		/* wrap search */
X		if (l < 1)
X		{
X			if (*o_wrapscan)
X			{
X				l = nlines + 1;
X				wrapped = TRUE;
X				continue;
X			}
X			else
X			{
X				break;
X			}
X		}
X
X		/* get this line */
X		line = fetchline(l);
X
X		/* check this line */
X		if (regexec(re, line, 1) && (int)(re->startp[0] - line) < pos)
X		{
X			/* match!  now find the last acceptable one in this line */
X			do
X			{
X				last = (int)(re->startp[0] - line);
X				try = (int)(re->endp[0] - line);
X			} while (try > 0
X				 && regexec(re, &line[try], FALSE)
X				 && (int)(re->startp[0] - line) < pos);
X
X			if (wrapped && *o_warn)
X				msg("(wrapped)");
X#ifndef CRUNCH
X			if (delta != INFINITY)
X			{
X				l += delta;
X				if (l < 1 || l > nlines)
X				{
X					msg("search offset too big");
X					return MARK_UNSET;
X				}
X				force_flags = LNMD|INCL;
X				return MARK_AT_LINE(l);
X			}
X#endif
X			return MARK_AT_LINE(l) + last;
X		}
X		pos = BLKSIZE;
X	}
X
X	/* not found */
X	msg(*o_wrapscan ? "Not found" : "Hit top without finding RE");
X	return MARK_UNSET;
X}
X
/
echo x - move3.c
sed '/^X/s///' > move3.c << '/'
X/* move3.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains movement functions that perform character searches */
X
X#include "config.h"
X#include "vi.h"
X
X#ifndef NO_CHARSEARCH
Xstatic MARK	(*prevfwdfn)();	/* function to search in same direction */
Xstatic MARK	(*prevrevfn)();	/* function to search in opposite direction */
Xstatic char	prev_key;	/* sought cvhar from previous [fFtT] */
X
XMARK	m__ch(m, cnt, cmd)
X	MARK	m;	/* current position */
X	long	cnt;
X	char	cmd;	/* command: either ',' or ';' */
X{
X	MARK	(*tmp)();
X
X	if (!prevfwdfn)
X	{
X		msg("No previous f, F, t, or T command");
X		return MARK_UNSET;
X	}
X
X	if (cmd == ',')
X	{
X		m =  (*prevrevfn)(m, cnt, prev_key);
X
X		/* Oops! we didn't want to change the prev*fn vars! */
X		tmp = prevfwdfn;
X		prevfwdfn = prevrevfn;
X		prevrevfn = tmp;
X
X		return m;
X	}
X	else
X	{
X		return (*prevfwdfn)(m, cnt, prev_key);
X	}
X}
X
X/* move forward within this line to next occurrence of key */
XMARK	m_fch(m, cnt, key)
X	MARK	m;	/* where to search from */
X	long	cnt;
X	char	key;	/* what to search for */
X{
X	REG char	*text;
X
X	DEFAULT(1);
X
X	prevfwdfn = m_fch;
X	prevrevfn = m_Fch;
X	prev_key = key;
X
X	pfetch(markline(m));
X	text = ptext + markidx(m);
X	while (cnt-- > 0)
X	{
X		do
X		{
X			m++;
X			text++;
X		} while (*text && *text != key);
X	}
X	if (!*text)
X	{
X		return MARK_UNSET;
X	}
X	return m;
X}
X
X/* move backward within this line to previous occurrence of key */
XMARK	m_Fch(m, cnt, key)
X	MARK	m;	/* where to search from */
X	long	cnt;
X	char	key;	/* what to search for */
X{
X	REG char	*text;
X
X	DEFAULT(1);
X
X	prevfwdfn = m_Fch;
X	prevrevfn = m_fch;
X	prev_key = key;
X
X	pfetch(markline(m));
X	text = ptext + markidx(m);
X	while (cnt-- > 0)
X	{
X		do
X		{
X			m--;
X			text--;
X		} while (text >= ptext && *text != key);
X	}
X	if (text < ptext)
X	{
X		return MARK_UNSET;
X	}
X	return m;
X}
X
X/* move forward within this line almost to next occurrence of key */
XMARK	m_tch(m, cnt, key)
X	MARK	m;	/* where to search from */
X	long	cnt;
X	char	key;	/* what to search for */
X{
X	/* skip the adjacent char */
X	pfetch(markline(m));
X	if (plen <= markidx(m))
X	{
X		return MARK_UNSET;
X	}
X	m++;
X
X	m = m_fch(m, cnt, key);
X	if (m == MARK_UNSET)
X	{
X		return MARK_UNSET;
X	}
X
X	prevfwdfn = m_tch;
X	prevrevfn = m_Tch;
X
X	return m - 1;
X}
X
X/* move backward within this line almost to previous occurrence of key */
XMARK	m_Tch(m, cnt, key)
X	MARK	m;	/* where to search from */
X	long	cnt;
X	char	key;	/* what to search for */
X{
X	/* skip the adjacent char */
X	if (markidx(m) == 0)
X	{
X		return MARK_UNSET;
X	}
X	m--;
X
X	m = m_Fch(m, cnt, key);
X	if (m == MARK_UNSET)
X	{
X		return MARK_UNSET;
X	}
X
X	prevfwdfn = m_Tch;
X	prevrevfn = m_tch;
X
X	return m + 1;
X}
X#endif
/
echo x - move4.c
sed '/^X/s///' > move4.c << '/'
X/* move4.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains movement functions which are screen-relative */
X
X#include "config.h"
X#include "vi.h"
X
X/* This moves the cursor to a particular row on the screen */
X/*ARGSUSED*/
XMARK m_row(m, cnt, key)
X	MARK	m;	/* the cursor position */
X	long	cnt;	/* the row we'll move to */
X	int	key;	/* the keystroke of this move - H/L/M */
X{
X	DEFAULT(1);
X
X	/* calculate destination line based on key */
X	cnt--;
X	switch (key)
X	{
X	  case 'H':
X		cnt = topline + cnt;
X		break;
X
X	  case 'M':
X		cnt = topline + (LINES - 1) / 2;
X		break;
X
X	  case 'L':
X		cnt = botline - cnt;
X		break;
X	}
X
X	/* return the mark of the destination line */
X	return MARK_AT_LINE(cnt);
X}
X
X
X/* This function repositions the current line to show on a given row */
XMARK m_z(m, cnt, key)
X	MARK	m;	/* the cursor */
X	long	cnt;	/* the line number we're repositioning */
X	int	key;	/* key struck after the z */
X{
X	long	newtop;
X	int	i;
X
X	/* Which line are we talking about? */
X	if (cnt < 0 || cnt > nlines)
X	{
X		return MARK_UNSET;
X	}
X	if (cnt)
X	{
X		m = MARK_AT_LINE(cnt);
X		newtop = cnt;
X	}
X	else
X	{
X		newtop = markline(m);
X	}
X
X	/* allow a "window size" number to be entered */
X	for (i = 0; key >= '0' && key <= '9'; key = getkey(0))
X	{
X		i = i * 10 + key - '0';
X	}
X#ifndef CRUNCH
X	if (i > 0 && i <= LINES - 1)
X	{
X		*o_window = i;
X		wset = TRUE;
X	}
X#else
X	/* the number is ignored if -DCRUNCH */
X#endif
X
X	/* figure out which line will have to be at the top of the screen */
X	switch (key)
X	{
X	  case '\n':
X#if OSK
X	  case '\l':
X#else
X	  case '\r':
X#endif
X	  case '+':
X		break;
X
X	  case '.':
X	  case 'z':
X		newtop -= LINES / 2;
X		break;
X
X	  case '-':
X		newtop -= LINES - 1;
X		break;
X
X	  default:
X		return MARK_UNSET;
X	}
X
X	/* make the new topline take effect */
X	redraw(MARK_UNSET, FALSE);
X	if (newtop >= 1)
X	{
X		topline = newtop;
X	}
X	else
X	{
X		topline = 1L;
X	}
X	redrawrange(0L, INFINITY, INFINITY);
X
X	/* The cursor doesn't move */
X	return m;
X}
X
X
X/* This function scrolls the screen.  It does this by calling redraw() with
X * an off-screen line as the argument.  It will move the cursor if necessary
X * so that the cursor is on the new screen.
X */
X/*ARGSUSED*/
XMARK m_scroll(m, cnt, key)
X	MARK	m;	/* the cursor position */
X	long	cnt;	/* for some keys: the number of lines to scroll */
X	int	key;	/* keystroke that causes this movement */
X{
X	MARK	tmp;	/* a temporary mark, used as arg to redraw() */
X
X	/* adjust cnt, and maybe *o_scroll, depending of key */
X	switch (key)
X	{
X	  case ctrl('F'):
X	  case ctrl('B'):
X		DEFAULT(1);
X		redrawrange(0L, INFINITY, INFINITY); /* force complete redraw */
X		cnt = cnt * (LINES - 1) - 2; /* keeps two old lines on screen */
X		break;
X
X	  case ctrl('E'):
X	  case ctrl('Y'):
X		DEFAULT(1);
X		break;
X
X	  case ctrl('U'):
X	  case ctrl('D'):
X		if (cnt == 0) /* default */
X		{
X			cnt = *o_scroll;
X		}
X		else
X		{
X			if (cnt > LINES - 1)
X			{
X				cnt = LINES - 1;
X			}
X			*o_scroll = cnt;
X		}
X		break;
X	}
X
X	/* scroll up or down, depending on key */
X	switch (key)
X	{
X	  case ctrl('B'):
X	  case ctrl('Y'):
X	  case ctrl('U'):
X		cnt = topline - cnt;
X		if (cnt < 1L)
X		{
X			cnt = 1L;
X			m = MARK_FIRST;
X		}
X		tmp = MARK_AT_LINE(cnt) + markidx(m);
X		redraw(tmp, FALSE);
X		if (markline(m) > botline)
X		{
X			m = MARK_AT_LINE(botline);
X		}
X		break;
X
X	  case ctrl('F'):
X	  case ctrl('E'):
X	  case ctrl('D'):
X		cnt = botline + cnt;
X		if (cnt > nlines)
X		{
X			cnt = nlines;
X			m = MARK_LAST;
X		}
X		tmp = MARK_AT_LINE(cnt) + markidx(m);
X		redraw(tmp, FALSE);
X		if (markline(m) < topline)
X		{
X			m = MARK_AT_LINE(topline);
X		}
X		break;
X	}
X
X	return m;
X}
/
echo x - move5.c
sed '/^X/s///' > move5.c << '/'
X/* move5.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains the word-oriented movement functions */
X
X#include "config.h"
X#include "ctype.h"
X#include "vi.h"
X
XMARK	m_fword(m, cnt, cmd, prevkey)
X	MARK	m;	/* movement is relative to this mark */
X	long	cnt;	/* a numeric argument */
X	int	cmd;	/* either 'w' or 'W' */
X	int	prevkey;/* previous command... if 'c' then exclude whitespace */
X{
X	REG long	l;
X	REG char	*text;
X	REG int		i;
X
X	DEFAULT(1);
X
X	l = markline(m);
X	pfetch(l);
X	text = ptext + markidx(m);
X
X#ifndef CRUNCH
X	/* As a special case, "cw" or "cW" on whitespace without a count
X	 * treats the single whitespace character under the cursor as a word.
X	 */
X	if (cnt == 1L && prevkey == 'c' && isspace(*text))
X	{
X		return m + 1L;
X	}
X#endif
X
X	while (cnt-- > 0) /* yes, ASSIGNMENT! */
X	{
X		i = *text++;
X
X		if (cmd == 'W')
X		{
X			/* include any non-whitespace */
X			while (i && !isspace(i))
X			{
X				i = *text++;
X			}
X		}
X		else if (isalnum(i) || i == '_')
X		{
X			/* include an alphanumeric word */
X			while (i && isalnum(i))
X			{
X				i = *text++;
X			}
X		}
X		else
X		{
X			/* include contiguous punctuation */
X			while (i && !isalnum(i) && !isspace(i))
X			{
X				i = *text++;
X			}
X		}
X
X		/* if not part of "cw" or "cW" command... */
X		if (prevkey != 'c' || cnt > 0)
X		{
X			/* include trailing whitespace */
X			while (!i || isspace(i))
X			{
X				/* did we hit the end of this line? */
X				if (!i)
X				{
X					/* "dw" shouldn't delete newline after word */
X					if (prevkey && cnt == 0)
X					{
X						break;
X					}
X
X					/* move to next line, if there is one */
X					l++;
X					if (l > nlines)
X					{
X						return MARK_UNSET;
X					}
X					pfetch(l);
X					text = ptext;
X				}
X
X				i = *text++;
X			}
X		}
X		text--;
X	}
X
X	/* if argument to operator, then back off 1 char since "w" and "W"
X	 * include the last char in the affected text.
X	 */
X	if (prevkey)
X	{
X		text--;
X	}
X
X	/* construct a MARK for this place */
X	m = buildmark(text);
X	return m;
X}
X
X
XMARK	m_bword(m, cnt, cmd)
X	MARK	m;	/* movement is relative to this mark */
X	long	cnt;	/* a numeric argument */
X	int	cmd;	/* either 'b' or 'B' */
X{
X	REG long	l;
X	REG char	*text;
X
X	DEFAULT(1);
X
X	l = markline(m);
X	pfetch(l);
X	text = ptext + markidx(m);
X	while (cnt-- > 0) /* yes, ASSIGNMENT! */
X	{
X		text--;
X
X		/* include preceding whitespace */
X		while (text < ptext || isspace(*text))
X		{
X			/* did we hit the end of this line? */
X			if (text < ptext)
X			{
X				/* move to preceding line, if there is one */
X				l--;
X				if (l <= 0)
X				{
X					return MARK_UNSET;
X				}
X				pfetch(l);
X				text = ptext + plen - 1;
X			}
X			else
X			{
X				text--;
X			}
X		}
X
X		if (cmd == 'B')
X		{
X			/* include any non-whitespace */
X			while (text >= ptext && !isspace(*text))
X			{
X				text--;
X			}
X		}
X		else if (isalnum(*text) || *text == '_')
X		{
X			/* include an alphanumeric word */
X			while (text >= ptext && isalnum(*text))
X			{
X				text--;
X			}
X		}
X		else
X		{
X			/* include contiguous punctuation */
X			while (text >= ptext && !isalnum(*text) && !isspace(*text))
X			{
X				text--;
X			}
X		}
X		text++;
X	}
X
X	/* construct a MARK for this place */
X	m = buildmark(text);
X	return m;
X}
X
XMARK	m_eword(m, cnt, cmd)
X	MARK	m;	/* movement is relative to this mark */
X	long	cnt;	/* a numeric argument */
X	int	cmd;	/* either 'e' or 'E' */
X{
X	REG long	l;
X	REG char	*text;
X	REG int		i;
X
X	DEFAULT(1);
X
X	l = markline(m);
X	pfetch(l);
X	text = ptext + markidx(m);
X	while (cnt-- > 0) /* yes, ASSIGNMENT! */
X	{
X		if (*text)
X			text++;
X		i = *text++;
X
X		/* include preceding whitespace */
X		while (!i || isspace(i))
X		{
X			/* did we hit the end of this line? */
X			if (!i)
X			{
X				/* move to next line, if there is one */
X				l++;
X				if (l > nlines)
X				{
X					return MARK_UNSET;
X				}
X				pfetch(l);
X				text = ptext;
X			}
X
X			i = *text++;
X		}
X
X		if (cmd == 'E')
X		{
X			/* include any non-whitespace */
X			while (i && !isspace(i))
X			{
X				i = *text++;
X			}
X		}
X		else if (isalnum(i) || i == '_')
X		{
X			/* include an alphanumeric word */
X			while (i && isalnum(i))
X			{
X				i = *text++;
X			}
X		}
X		else
X		{
X			/* include contiguous punctuation */
X			while (i && !isalnum(i) && !isspace(i))
X			{
X				i = *text++;
X			}
X		}
X		text -= 2;
X	}
X
X	/* construct a MARK for this place */
X	m = buildmark(text);
X	return m;
X}
/
echo x - novice.rc
sed '/^X/s///' > novice.rc << '/'
Xmap #1 visual v
Xmap #2 visual V
Xmap #3 visual y
Xmap #4 visual d
Xmap #5 visual p
Xmap #6 visual \
Xmap #7 j{!}fmt
Xmap! #7 j{!}fmt
Xmap #8 :set negnumber
Xmap! #8 :set negnumber
Xmap #9 :set negcharattr neghideformat
Xmap! #9 :set negcharattr neghideformat
Xmap #10 :%s/old/new/g
Xmap! #10 :%s/old/new/g
Xset report=1 nomagic ignorecase showmode nowrapscan autowrite
/
echo x - opts.c
sed '/^X/s///' > opts.c << '/'
X/* opts.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains the code that manages the run-time options -- The 
X * values that can be modified via the "set" command.
X */
X
X#include "config.h"
X#include "vi.h"
X#include "ctype.h"
X#ifndef NULL
X#define NULL (char *)0
X#endif
Xextern char	*getenv();
X
X/* maximum width to permit for strings, including ="" */
X#define MAXWIDTH 20
X
X/* These are the default values of all options */
Xchar	o_autoindent[1] =	{FALSE};
Xchar	o_autoprint[1] =	{TRUE};
Xchar	o_autotab[1] =		{TRUE};
Xchar	o_autowrite[1] = 	{FALSE};
Xchar	o_columns[3] =		{80, 32, 255};
Xchar	o_directory[30] =	TMPDIR;
Xchar	o_edcompatible[1] =	{FALSE};
Xchar	o_equalprg[80] =	{"fmt"};
Xchar	o_errorbells[1] =	{TRUE};
Xchar	o_exrefresh[1] =	{TRUE};
Xchar	o_ignorecase[1] =	{FALSE};
Xchar	o_keytime[3] =		{2, 0, 50};
Xchar	o_keywordprg[80] =	{KEYWORDPRG};
Xchar	o_lines[3] =		{25, 2, 96};
Xchar	o_list[1] =		{FALSE};
Xchar	o_number[1] =		{FALSE};
Xchar	o_readonly[1] =		{FALSE};
Xchar	o_remap[1] =		{TRUE};
Xchar	o_report[3] =		{5, 1, 127};
Xchar	o_scroll[3] =		{12, 1, 127};
Xchar	o_shell[60] =		SHELL;
Xchar	o_shiftwidth[3] =	{8, 1, 255};
Xchar	o_sidescroll[3] =	{8, 1, 40};
Xchar	o_sync[1] =		{NEEDSYNC};
Xchar	o_tabstop[3] =		{8, 1, 40};
Xchar	o_term[30] =		"?";
Xchar	o_flash[1] =		{TRUE};
Xchar	o_warn[1] =		{TRUE};
Xchar	o_wrapscan[1] =		{TRUE};
X
X#ifndef CRUNCH
Xchar	o_beautify[1] =		{FALSE};
Xchar	o_exrc[1] =		{FALSE};
Xchar	o_mesg[1] =		{TRUE};
Xchar	o_more[1] =		{TRUE};
Xchar	o_novice[1] =		{FALSE};
Xchar	o_prompt[1] =		{TRUE};
Xchar	o_taglength[3] =	{0, 0, 30};
Xchar	o_terse[1] =		{FALSE};
Xchar	o_window[3] =		{0, 1, 24};
Xchar	o_wrapmargin[3] =	{0, 0, 255};
Xchar	o_writeany[1] =		{FALSE};
X#endif
X
X#ifndef NO_ERRLIST
Xchar	o_cc[30] =		{CC_COMMAND};
Xchar	o_make[30] =		{MAKE_COMMAND};
X#endif
X
X#ifndef NO_CHARATTR
Xchar	o_charattr[1] =		{FALSE};
X#endif
X
X#ifndef NO_DIGRAPH
Xchar	o_digraph[1] =		{FALSE};
Xchar	o_flipcase[80]
X# ifdef CS_IBMPC
X	= {"\207\200\201\232\202\220\204\216\206\217\221\222\224\231\244\245"}
X# endif
X# ifdef CS_LATIN1
X	/* initialized by initopts() */
X# endif
X	;
X#endif
X
X#ifndef NO_SENTENCE
Xchar	o_hideformat[1] =	{FALSE};
X#endif
X
X#ifndef NO_EXTENSIONS
Xchar	o_inputmode[1] =	{FALSE};
Xchar	o_ruler[1] =		{FALSE};
X#endif
X
X#ifndef NO_MAGIC
Xchar	o_magic[1] =		{TRUE};
X#endif
X
X#ifndef NO_MODELINES
Xchar	o_modelines[1] =	{FALSE};
X#endif
X
X#ifndef NO_SENTENCE
Xchar	o_paragraphs[30] =	"PPppIPLPQP";
Xchar	o_sections[30] =	"NHSHSSSEse";
X#endif
X
X#if MSDOS
Xchar	o_pcbios[1] =		{TRUE};
X#endif
X
X#ifndef NO_SHOWMATCH
Xchar	o_showmatch[1] =	{FALSE};
X#endif
X
X#ifndef	NO_SHOWMODE
Xchar	o_smd[1] =		{FALSE};
X#endif
X
X
X/* The following describes the names & types of all options */
X#define BOOL	0
X#define	NUM	1
X#define	STR	2
X#define SET	0x01	/* this option has had its value altered */
X#define CANSET	0x02	/* this option can be set at any time */
X#define RCSET	0x06	/* this option can be set in a .exrc file only */
X#define NOSAVE	0x0a	/* this option should never be saved by mkexrc */
X#define WSET	0x20	/* is this the "window" size option? */
X#define MR	0x40	/* does this option affect the way text is displayed? */
Xstruct
X{
X	char	*name;	/* name of an option */
X	char	*nm;	/* short name of an option */
X	char	type;	/* type of an option */
X	char	flags;	/* boolean: has this option been set? */
X	char	*value;	/* value */
X}
X	opts[] =
X{
X	/* name			type	flags		value */
X	{ "autoindent",	"ai",	BOOL,	CANSET,		o_autoindent	},
X	{ "autoprint",	"ap",	BOOL,	CANSET,		o_autoprint	},
X	{ "autotab",	"at",	BOOL,	CANSET,		o_autotab	},
X	{ "autowrite",	"aw",	BOOL,	CANSET,		o_autowrite	},
X#ifndef CRUNCH
X	{ "beautify",	"bf",	BOOL,	CANSET,		o_beautify	},
X#endif
X#ifndef NO_ERRLIST
X	{ "cc",		"cc",	STR,	CANSET,		o_cc		},
X#endif
X#ifndef NO_CHARATTR
X	{ "charattr",	"ca",	BOOL,	CANSET|MR,	o_charattr	},
X#endif
X	{ "columns",	"co",	NUM,	SET|NOSAVE|MR,	o_columns	},
X#ifndef NO_DIGRAPH
X	{ "digraph",	"dig",	BOOL,	CANSET,		o_digraph	},
X#endif
X	{ "directory",	"dir",	STR,	RCSET,		o_directory	},
X	{ "edcompatible","ed",	BOOL,	CANSET,		o_edcompatible	},
X	{ "equalprg",	"ep",	STR,	CANSET,		o_equalprg	},
X	{ "errorbells",	"eb",	BOOL,	CANSET,		o_errorbells	},
X#ifndef CRUNCH
X	{ "exrc",	"exrc",	BOOL,	CANSET,		o_exrc		},
X#endif
X	{ "exrefresh",	"er",	BOOL,	CANSET,		o_exrefresh	},
X	{ "flash",	"vbell",BOOL,	CANSET,		o_flash		},
X#ifndef NO_DIGRAPH
X	{ "flipcase",	"fc",	STR,	CANSET,		o_flipcase	},
X#endif
X#ifndef NO_SENTENCE
X	{ "hideformat",	"hf",	BOOL,	CANSET|MR,	o_hideformat	},
X#endif
X	{ "ignorecase",	"ic",	BOOL,	CANSET,		o_ignorecase	},
X#ifndef NO_EXTENSIONS
X	{ "inputmode",	"im",	BOOL,	CANSET,		o_inputmode	},
X#endif
X	{ "keytime",	"kt",	NUM,	CANSET,		o_keytime	},
X	{ "keywordprg",	"kp",	STR,	CANSET,		o_keywordprg	},
X	{ "lines",	"ls",	NUM,	SET|NOSAVE|MR,	o_lines		},
X	{ "list",	"li",	BOOL,	CANSET|MR,	o_list		},
X#ifndef NO_MAGIC
X	{ "magic",	"ma",	BOOL,	CANSET,		o_magic		},
X#endif
X#ifndef NO_ERRLIST
X	{ "make",	"mk",	STR,	CANSET,		o_make		},
X#endif
X#ifndef CRUNCH
X	{ "mesg",	"me",	BOOL,	CANSET,		o_mesg		},
X#endif
X#ifndef NO_MODELINES
X	{ "modelines",	"ml",	BOOL,	CANSET,		o_modelines	},
X#endif
X#ifndef CRUNCH
X	{ "more",	"mo",	BOOL,	CANSET,		o_more		},
X	{ "novice",	"nov",	BOOL,	CANSET,		o_novice	},
X#endif
X	{ "number",	"nu",	BOOL,	CANSET|MR,	o_number	},
X#ifndef NO_SENTENCE
X	{ "paragraphs",	"para",	STR,	CANSET,		o_paragraphs	},
X#endif
X#if MSDOS
X	{ "pcbios",	"pc",	BOOL,	SET|NOSAVE,	o_pcbios	},
X#endif
X#ifndef CRUNCH
X	{ "prompt",	"pr",	BOOL,	CANSET,		o_prompt	},
X#endif
X	{ "readonly",	"ro",	BOOL,	CANSET,		o_readonly	},
X	{ "remap",	"remap",BOOL,	CANSET,		o_remap		},
X	{ "report",	"re",	NUM,	CANSET,		o_report	},
X#ifndef NO_EXTENSIONS
X	{ "ruler",	"ru",	BOOL,	CANSET,		o_ruler		},
X#endif
X	{ "scroll",	"sc",	NUM,	CANSET,		o_scroll	},
X#ifndef NO_SENTENCE
X	{ "sections",	"sect",	STR,	CANSET,		o_sections	},
X#endif
X	{ "shell",	"sh",	STR,	CANSET,		o_shell		},
X#ifndef NO_SHOWMATCH
X	{ "showmatch",	"sm",	BOOL,	CANSET,		o_showmatch	},
X#endif
X#ifndef	NO_SHOWMODE
X	{ "showmode",	"smd",	BOOL,	CANSET,		o_smd		},
X#endif
X	{ "shiftwidth",	"sw",	NUM,	CANSET,		o_shiftwidth	},
X	{ "sidescroll",	"ss",	NUM,	CANSET,		o_sidescroll	},
X	{ "sync",	"sy",	BOOL,	CANSET,		o_sync		},
X	{ "tabstop",	"ts",	NUM,	CANSET|MR,	o_tabstop	},
X#ifndef CRUNCH
X	{ "taglength",	"tl",	NUM,	CANSET,		o_taglength	},
X#endif
X	{ "term",	"te",	STR,	SET,		o_term		},
X#ifndef CRUNCH
X	{ "terse",	"tr",	BOOL,	CANSET,		o_terse		},
X	{ "timeout",	"to",	BOOL,	CANSET,		o_keytime	},
X#endif
X#ifndef CRUNCH
X	{ "window",	"wi",	NUM,	CANSET|MR|WSET,	o_window	},
X	{ "wrapmargin",	"wm",	NUM,	CANSET,		o_wrapmargin	},
X#endif
X	{ "wrapscan",	"ws",	BOOL,	CANSET,		o_wrapscan	},
X#ifndef CRUNCH
X	{ "writeany",	"wr",	BOOL,	CANSET,		o_writeany	},
X#endif
X	{ NULL, NULL, 0, CANSET, NULL }
X};
X
X
X/* This function initializes certain options from environment variables, etc. */
Xvoid initopts()
X{
X	char	*val;
X	int	i;
X
X	/* set some stuff from environment variables */
X#if MSDOS
X	if (val = getenv("COMSPEC")) /* yes, ASSIGNMENT! */
X#else
X	if (val = getenv("SHELL")) /* yes, ASSIGNMENT! */
X#endif
X	{
X		strcpy(o_shell, val);
X	}
X
X	strcpy(o_term, termtype);
X#if MSDOS
X	if (strcmp(termtype, "pcbios"))
X	{
X		o_pcbios[0] = FALSE;
X	}
X	else
X	{
X		o_pcbios[0] = TRUE;
X	}
X#endif
X
X#if AMIGA || MSDOS || TOS
X	if ((val = getenv("TMP")) /* yes, ASSIGNMENT! */
X	||  (val = getenv("TEMP")))
X		strcpy(o_directory, val);
X#endif
X
X#ifndef CRUNCH
X	if ((val = getenv("LINES")) && atoi(val) > 4) /* yes, ASSIGNMENT! */
X	{
X		LINES = atoi(val);
X	}
X	if ((val = getenv("COLUMNS")) && atoi(val) > 30) /* yes, ASSIGNMENT! */
X	{
X		COLS = atoi(val);
X	}
X#endif
X	*o_lines = LINES;
X	*o_columns = COLS;
X	*o_scroll = LINES / 2 - 1;
X#ifndef CRUNCH
X	if (o_window[0] == 0)
X	{
X		o_window[0] = o_window[2] = *o_lines;
X	}
X#endif
X
X	/* disable the flash option if we don't know how to do a flash */
X	if (!has_VB)
X	{
X		for (i = 0; opts[i].value != o_flash; i++)
X		{
X		}
X		opts[i].flags &= ~CANSET;
X		*o_flash = FALSE;
X	}
X
X#ifndef NO_DIGRAPH
X# ifdef CS_LATIN1
X	for (i = 0, val = o_flipcase; i < 32; i++)
X	{
X		/* leave out the multiply/divide symbols */
X		if (i == 23)
X			continue;
X
X		/* add lower/uppercase pair */
X		*val++ = i + 0xe0;
X		*val++ = i + 0xc0;
X	}
X	*val = '\0';
X# endif /* CS_LATIN1 */
X
X	/* initialize the ctype package */
X	_ct_init(o_flipcase);
X#else
X	_ct_init("");
X#endif /* not NO_DIGRAPH */
X}
X
X/* This function lists the current values of all options */
Xvoid dumpopts(all)
X	int	all;	/* boolean: dump all options, or just set ones? */
X{
X#ifndef NO_OPTCOLS
X	int	i, j, k;
X	char	nbuf[4];	/* used for converting numbers to ASCII */
X	int	widths[5];	/* width of each column, including gap */
X	int	ncols;		/* number of columns */
X	int	nrows;		/* number of options per column */
X	int	nset;		/* number of options to be output */
X	int	width;		/* width of a particular option */
X	int	todump[60];	/* indicies of options to be dumped */
X
X	/* step 1: count the number of set options */
X	for (nset = i = 0; opts[i].name; i++)
X	{
X		if (all || (opts[i].flags & SET))
X		{
X			todump[nset++] = i;
X		}
X	}
X
X	/* step two: try to use as many columns as possible */
X	for (ncols = (nset > 5 ? 5 : nset); ncols > 1; ncols--)
X	{
X		/* how many would go in this column? */
X		nrows = (nset + ncols - 1) / ncols;
X
X		/* figure out the width of each column */
X		for (i = 0; i < ncols; i++)
X		{
X			widths[i] = 0;
X			for (j = 0, k = nrows * i; j < nrows && k < nset; j++, k++)
X			{
X				/* figure out the width of a particular option */
X				switch (opts[todump[k]].type)
X				{
X				  case BOOL:
X					if (!*opts[todump[k]].value)
X						width = 2;
X					else
X						width = 0;
X					break;
X
X				  case STR:
X					width = 3 + strlen(opts[todump[k]].value);
X					if (width > MAXWIDTH)
X						width = MAXWIDTH;
X					break;
X
X				  case NUM:
X					width = 4;
X					break;
X				}
X				width += strlen(opts[todump[k]].name);
X
X				/* if this is the widest so far, widen col */
X				if (width > widths[i])
X				{
X					widths[i] = width;
X				}
X			}
X
X		}
X
X		/* if the total width is narrow enough, then use it */
X		for (width = -2, i = 0; i < ncols; i++)
X		{
X			width += widths[i] + 2;
X		}
X		if (width < COLS - 1)
X		{
X			break;
X		}
X	}
X
X	/* step 3: output the columns */
X	nrows = (nset + ncols - 1) / ncols;
X	for (i = 0; i < nrows; i++)
X	{
X		for (j = 0; j < ncols; j++)
X		{
X			/* if we hit the end of the options, quit */
X			k = i + j * nrows;
X			if (k >= nset)
X			{
X				break;
X			}
X
X			/* output this option's value */
X			width = 0;
X			switch (opts[todump[k]].type)
X			{
X			  case BOOL:
X				if (!*opts[todump[k]].value)
X				{
X					qaddch('n');
X					qaddch('o');
X					width = 2;
X				}
X				qaddstr(opts[todump[k]].name);
X				width += strlen(opts[todump[k]].name);
X				break;
X
X			  case NUM:
X				sprintf(nbuf, "%-3d", UCHAR(*opts[todump[k]].value));
X				qaddstr(opts[todump[k]].name);
X				qaddch('=');
X				qaddstr(nbuf);
X				width = 4 + strlen(opts[todump[k]].name);
X				break;
X
X			  case STR:
X				qaddstr(opts[todump[k]].name);
X				qaddch('=');
X				qaddch('"');
X				strcpy(tmpblk.c, opts[todump[k]].value);
X				width = 3 + strlen(tmpblk.c);
X				if (width > MAXWIDTH)
X				{
X					width = MAXWIDTH;
X					strcpy(tmpblk.c + MAXWIDTH - 6, "...");
X				}
X				qaddstr(tmpblk.c);
X				qaddch('"');
X				width += strlen(opts[todump[k]].name);
X				break;
X			}
X
X			/* pad the field to the correct size */
X			if (k + nrows <= nset)
X			{
X				while (width < widths[j] + 2)
X				{
X					qaddch(' ');
X					width++;
X				}
X			}
X		}
X		addch('\n');
X		exrefresh();
X	}
X#else
X	int	i;
X	int	col;
X	char	nbuf[4];
X
X	for (i = col = 0; opts[i].name; i++)
X	{
X		/* if not set and not all, ignore this option */
X		if (!all && !(opts[i].flags & SET))
X		{
X			continue;
X		}
X
X		/* align this option in one of the columns */
X		if (col > 52)
X		{
X			addch('\n');
X			col = 0;
X		}
X		else if (col > 26)
X		{
X			while (col < 52)
X			{
X				qaddch(' ');
X				col++;
X			}
X		}
X		else if (col > 0)
X		{
X			while (col < 26)
X			{
X				qaddch(' ');
X				col++;
X			}
X		}
X
X		switch (opts[i].type)
X		{
X		  case BOOL:
X			if (!*opts[i].value)
X			{
X				qaddch('n');
X				qaddch('o');
X				col += 2;
X			}
X			qaddstr(opts[i].name);
X			col += strlen(opts[i].name);
X			break;
X
X		  case NUM:
X			sprintf(nbuf, "%-3d", UCHAR(*opts[i].value));
X			qaddstr(opts[i].name);
X			qaddch('=');
X			qaddstr(nbuf);
X			col += 4 + strlen(opts[i].name);
X			break;
X
X		  case STR:
X			qaddstr(opts[i].name);
X			qaddch('=');
X			qaddch('"');
X			qaddstr(opts[i].value);
X			qaddch('"');
X			col += 3 + strlen(opts[i].name) + strlen(opts[i].value);
X			break;
X		}
X		exrefresh();
X	}
X	if (col > 0)
X	{
X		addch('\n');
X		exrefresh();
X	}
X#endif
X}
X
X#ifndef NO_MKEXRC
X/* This function saves the current configuration of options to a file */
Xvoid saveopts(fd)
X	int	fd;	/* file descriptor to write to */
X{
X	int	i;
X	char	buf[256], *pos;
X
X	/* write each set options */
X	for (i = 0; opts[i].name; i++)
X	{
X		/* if unset or unsettable, ignore this option */
X		if ((opts[i].flags & (SET|CANSET|NOSAVE)) != (SET|CANSET))
X		{
X			continue;
X		}
X
X		strcpy(buf, "set ");
X		pos = &buf[4];
X		switch (opts[i].type)
X		{
X		  case BOOL:
X			if (!*opts[i].value)
X			{
X				*pos++='n';
X				*pos++='o';
X			}
X			strcpy(pos, opts[i].name);
X			strcat(pos, "\n");
X			break;
X
X		  case NUM:
X			sprintf(pos, "%s=%-3d\n", opts[i].name, *opts[i].value & 0xff);
X			break;
X
X		  case STR:
X			sprintf(pos, "%s=\"%s\"\n", opts[i].name, opts[i].value);
X			break;
X		}
X		twrite(fd, buf, (unsigned)strlen(buf));
X	}
X}
X#endif
X
X
X/* This function changes the values of one or more options. */
Xvoid setopts(assignments)
X	char	*assignments;	/* a string containing option assignments */
X{
X	char	*name;		/* name of variable in assignments */
X	char	*value;		/* value of the variable */
X	char	*scan;		/* used for moving through strings */
X	char	*build;		/* used for copying chars from "scan" */
X	char	*prefix;	/* pointer to "neg" or "no" at front of a boolean */
X	int	quote;		/* boolean: inside '"' quotes? */
X	int	i, j;
X
X#ifndef CRUNCH
X	/* reset the upper limit of "window" option to lines-1 */
X	*o_window = *o_lines - 1;
X#endif
X
X	/* for each assignment... */
X	for (name = assignments; *name; )
X	{
X		/* skip whitespace */
X		if (*name == ' ' || *name == '\t')
X		{
X			name++;
X			continue;
X		}
X
X		/* after the name, find the value (if any) */
X		for (scan = name; isalnum(*scan); scan++)
X		{
X		}
X		if (*scan == '=')
X		{
X			*scan++ = '\0';
X			value = build = scan;
X			for (quote = FALSE; *scan && (quote || !isspace(*scan)); scan++)
X			{
X				if (*scan == '"')
X				{
X					quote = !quote;
X				}
X				else if (*scan == '\\' && scan[1])
X				{
X					*build++ = *++scan;
X				}
X				else
X				{
X					*build++ = *scan;
X				}
X			}
X			if (*scan)
X				scan++;
X			*build = '\0';
X		}
X		else /* no "=" so it is probably boolean... */
X		{
X			if (*scan)
X			{
X				*scan++ = '\0';
X			}
X			value = NULL;
X			prefix = name;
X#ifndef CRUNCH
X			if (!strcmp(name, "novice"))
X				/* don't check for a "no" prefix */;
X			else
X#endif
X			if (prefix[0] == 'n' && prefix[1] == 'o')
X				name += 2;
X			else if (prefix[0] == 'n' && prefix[1] == 'e' && prefix[2] == 'g')
X				name += 3;
X		}
X
X		/* find the variable */
X		for (i = 0;
X		     opts[i].name && strcmp(opts[i].name, name) && strcmp(opts[i].nm, name);
X		     i++)
X		{
X		}
X
X		/* change the variable */
X		if (!opts[i].name)
X		{
X			msg("invalid option name \"%s\"", name);
X		}
X		else if ((opts[i].flags & CANSET) != CANSET)
X		{
X			msg("option \"%s\" can't be altered", name);
X		}
X		else if ((opts[i].flags & RCSET) != CANSET && nlines >= 1L)
X		{
X			msg("option \"%s\" can only be set in a %s file", name, EXRC);
X		}
X		else if (value)
X		{
X			switch (opts[i].type)
X			{
X			  case BOOL:
X				msg("option \"[no]%s\" is boolean", name);
X				break;
X
X			  case NUM:
X				j = atoi(value);
X				if (j == 0 && *value != '0')
X				{
X					msg("option \"%s\" must have a numeric value", name);
X				}
X				else if (j < opts[i].value[1] || j > (opts[i].value[2] & 0xff))
X				{
X					msg("option \"%s\" must have a value between %d and %d",
X						name, opts[i].value[1], opts[i].value[2] & 0xff);
X				}
X				else
X				{
X					*opts[i].value = atoi(value);
X					opts[i].flags |= SET;
X				}
X				break;
X
X			  case STR:
X				strcpy(opts[i].value, value);
X				opts[i].flags |= SET;
X				break;
X			}
X			if (opts[i].flags & MR)
X			{
X				redraw(MARK_UNSET, FALSE);
X			}
X#ifndef CRUNCH
X			if (opts[i].flags & WSET)
X			{
X				wset = TRUE;
X			}
X#endif
X		}
X		else /* valid option, no value */
X		{
X			if (opts[i].type == BOOL)
X			{
X				if (prefix == name)
X					*opts[i].value = TRUE;
X				else if (prefix[1] == 'o')
X					*opts[i].value = FALSE;
X				else
X					*opts[i].value = !*opts[i].value;
X
X				opts[i].flags |= SET;
X				if (opts[i].flags & MR)
X				{
X					redraw(MARK_UNSET, FALSE);
X				}
X			}
X			else
X			{
X				msg("option \"%s\" must be given a value", name);
X			}
X		}
X
X		/* move on to the next option */
X		name = scan;
X	}
X
X	/* special processing ... */
X
X#ifndef CRUNCH
X	/* if "novice" is set, then ":set report=1 showmode nomagic" */
X	if (*o_novice)
X	{
X		*o_report = 1;
X# ifndef NO_SHOWMODE
X		*o_smd = TRUE;
X# endif
X# ifndef NO_MAGIC
X		*o_magic = FALSE;
X# endif
X	}
X#endif
X
X	/* if "readonly" then set the READONLY flag for this file */
X	if (*o_readonly)
X	{
X		setflag(file, READONLY);
X	}
X
X#ifndef NO_DIGRAPH
X	/* re-initialize the ctype package */
X	_ct_init(o_flipcase);
X#endif /* not NO_DIGRAPH */
X
X	/* copy o_lines and o_columns into LINES and COLS */
X	LINES = (*o_lines & 255);
X	COLS = (*o_columns & 255);
X}
/
echo x - prsvunix.c
sed '/^X/s///' > prsvunix.c << '/'
X/* prsvunix.c */
X
X/* This file contains the UNIX-specific parts of the "elvprsv" program. */
X
X#if OSK
X#define ELVPRSV
X#include "osk.c"
X#else
X#include <sys/stat.h>
X#include <pwd.h>
X#endif
Xextern struct passwd *getpwuid();
X
X/* This variable is used to add extra error messages for mail sent to root */
Xchar *ps;
X
X/* This function returns the login name of the owner of a file */
Xchar *ownername(filename)
X	char	*filename;	/* name of a file */
X{
X	struct stat	st;
X	struct passwd	*pw;
X
X	/* stat the file, to get its uid */
X	if (stat(filename, &st) < 0)
X	{
X		ps = "stat() failed";
X		return "root";
X	}
X
X	/* get the /etc/passwd entry for that user */
X	pw = getpwuid(st.st_uid);
X	if (!pw)
X	{
X		ps = "uid not found in password file";
X		return "root";
X	}
X
X	/* return the user's name */
X	return pw->pw_name;
X}
X
X
X/* This function sends a mail message to a given user, saying that a file
X * has been preserved.
X */
Xvoid mail(user, file, when)
X	char	*user;	/* name of user who should receive the mail */
X	char	*file;	/* name of original text file that was preserved */
X	char	*when;	/* description of why the file was preserved */
X{
X	char	cmd[80];/* buffer used for constructing a "mail" command */
X	FILE	*m, *popen();	/* stream used for giving text to the "mail" program */
X	char	*base;	/* basename of the file */
X
X	/* separate the directory name from the basename. */
X	for (base = file + strlen(file); --base > file && *base != SLASH; )
X	{
X	}
X	if (*base == SLASH)
X	{
X		*base++ = '\0';
X	}
X
X	/* for anonymous buffers, pretend the name was "foo" */
X	if (!strcmp(base, "*"))
X	{
X		base = "foo";
X	}
X
X	/* open a pipe to the "mail" program */
X#if OSK
X	sprintf(cmd, "mail \"-s=%s preserved!\" %s", base, user);
X#else /* ANY_UNIX */
X	sprintf(cmd, "mail %s >/dev/null 2>/dev/null", user);
X#endif
X	m = popen(cmd, "w");
X	if (!m)
X	{
X		/* Can't send mail!  Hope the user figures it out. */
X		return;
X	}
X
X	/* Tell the user that the file was preserved */
X	fprintf(m, "A version of your file \"%s%c%s\"\n", file, SLASH, base);
X	fprintf(m, "was preserved when %s.\n", when);
X	fprintf(m, "To recover this file, do the following:\n");
X	fprintf(m, "\n");
X#if OSK
X	fprintf(m, "     chd %s\n", file);
X#else /* ANY_UNIX */
X	fprintf(m, "     cd %s\n", file);
X#endif
X	fprintf(m, "     elvrec %s\n", base);
X	fprintf(m, "\n");
X	fprintf(m, "With fond wishes for a speedy recovery,\n");
X	fprintf(m, "                                    Elvis\n");
X	if (ps)
X	{
X		fprintf(m, "\nP.S. %s\n", ps);
X		ps = (char *)0;
X	}
X
X	/* close the stream */
X	pclose(m);
X}
/
echo x - recycle.c
sed '/^X/s///' > recycle.c << '/'
X/* recycle.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains the functions perform garbage collection and allocate
X * reusable blocks.
X */
X
X#include "config.h"
X#include "vi.h"
X
X#ifndef NO_RECYCLE
X/* this whole file would have be skipped if NO_RECYCLE is defined */
X
X
X#define BTST(bitno, byte)	((byte) & (1 << (bitno)))
X#define BSET(bitno, byte)	((byte) |= (1 << (bitno)))
X#define BCLR(bitno, byte)	((byte) &= ~(1 << (bitno)))
X
X#define TST(blkno)		((blkno) < MAXBIT ? BTST((blkno) & 7, bitmap[(blkno) >> 3]) : 1)
X#define SET(blkno)		if ((blkno) < MAXBIT) BSET((blkno) & 7, bitmap[(blkno) >> 3])
X#define CLR(blkno)		if ((blkno) < MAXBIT) BCLR((blkno) & 7, bitmap[(blkno) >> 3])
X
X/* bitmap of free blocks in first 4096k of tmp file */
Xstatic unsigned char bitmap[512];
X#define MAXBIT	(sizeof bitmap << 3)
X
X/* this function locates all free blocks in the current tmp file */
Xvoid garbage()
X{
X	int	i;
X	BLK	oldhdr;
X
X	/* start by assuming every block is free */
X	for (i = 0; i < sizeof bitmap; i++)
X	{
X		bitmap[i] = 255;
X	}
X
X	/* header blocks aren't free */
X#ifndef lint
X	CLR(0);
X	CLR(1);
X#endif
X
X	/* blocks needed for current hdr aren't free */
X	for (i = 1; i < MAXBLKS; i++)
X	{
X		CLR(hdr.n[i]);
X	}
X
X	/* blocks needed for undo version aren't free */
X	lseek(tmpfd, 0L, 0);
X	if (read(tmpfd, &oldhdr, (unsigned)sizeof oldhdr) != sizeof oldhdr)
X	{
X		msg("garbage() failed to read oldhdr??");
X		for (i = 0; i < sizeof bitmap; i++)
X		{
X			bitmap[i] = 0;
X		}
X		return;
X	}
X	for (i = 1; i < MAXBLKS; i++)
X	{
X		CLR(oldhdr.n[i]);
X	}
X
X	/* blocks needed for cut buffers aren't free */
X	for (i = cutneeds(&oldhdr) - 1; i >= 0; i--)
X	{
X		CLR(oldhdr.n[i]);
X	}
X}
X
X/* This function allocates the first available block in the tmp file */
Xlong allocate()
X{
X	int	i;
X	long	offset;
X
X	/* search for the first byte with a free bit set */
X	for (i = 0; i < sizeof bitmap && bitmap[i] == 0; i++)
X	{
X	}
X
X	/* if we hit the end of the bitmap, return the end of the file */
X	if (i == sizeof bitmap)
X	{
X		offset = lseek(tmpfd, 0L, 2);
X	}
X	else /* compute the offset for the free block */
X	{
X		for (i <<= 3; TST(i) == 0; i++)
X		{
X		}
X		offset = (long)i * (long)BLKSIZE;
X
X		/* mark the block as "allocated" */
X		CLR(i);
X	}
X
X	return offset;
X}
X
X#endif
X
X#ifdef DEBUG
X# include <stdio.h>
X# undef malloc
X# undef free
X# define MEMMAGIC 0x19f72cc0L
X# define MAXALLOC 800
Xstatic char *allocated[MAXALLOC];
Xstatic char *fromfile[MAXALLOC];
Xstatic int  fromline[MAXALLOC]; 
Xstatic int  sizes[MAXALLOC];
X
Xchar *dbmalloc(size, file, line)
X	int	size;
X	char	*file;
X	int	line;
X{
X	char	*ret;
X	int	i;
X
X	size = size + sizeof(long) - (size % sizeof(long));
X	ret = (char *)malloc(size + 2 * sizeof(long)) + sizeof(long);
X	for (i = 0; i < MAXALLOC && allocated[i]; i++)
X	{
X	}
X	if (i == MAXALLOC)
X	{
X		endwin();
X		fprintf(stderr, "\r\n%s(%d): Too many malloc calls!\n", file, line);
X		abort();
X	}
X	sizes[i] = size/sizeof(long);
X	allocated[i] = ret;
X	fromfile[i] = file;
X	fromline[i] = line;
X	((long *)ret)[-1] = MEMMAGIC;
X	((long *)ret)[sizes[i]] = MEMMAGIC;
X	return ret;
X}
X
Xdbfree(ptr, file, line)
X	char	*ptr;
X	char	*file;
X	int	line;
X{
X	int	i;
X
X	for (i = 0; i < MAXALLOC && allocated[i] != ptr; i++)
X	{
X	}
X	if (i == MAXALLOC)
X	{
X		endwin();
X		fprintf(stderr, "\r\n%s(%d): attempt to free mem that wasn't allocated\n", file, line);
X		abort();
X	}
X	allocated[i] = (char *)0;
X	if (((long *)ptr)[-1] != MEMMAGIC)
X	{
X		endwin();
X		fprintf(stderr, "\r\n%s(%d): underflowed malloc space, allocated at %s(%d)\n", file, line, fromfile[i], fromline[i]);
X		abort();
X	}
X	if (((long *)ptr)[sizes[i]] != MEMMAGIC)
X	{
X		endwin();
X		fprintf(stderr, "\r\n%s(%d): overflowed malloc space, allocated at %s(%d)\n", file, line, fromfile[i], fromline[i]);
X		abort();
X	}
X	free(ptr - sizeof(long));
X}
X#endif
/
echo x - redraw.c
sed '/^X/s///' > redraw.c << '/'
X/* redraw.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains functions that draw text on the screen.  The major entry
X * points are:
X *	redrawrange()	- called from modify.c to give hints about what parts
X *			  of the screen need to be redrawn.
X *	redraw()	- redraws the screen (or part of it) and positions
X *			  the cursor where it belongs.
X *	idx2col()	- converts a markidx() value to a logical column number.
X */
X
X#include "config.h"
X#include "vi.h"
X
X/* This variable contains the line number that smartdrawtext() knows best */
Xstatic long smartlno;
X
X/* This function remembers where changes were made, so that the screen can be
X * redraw in a more efficient manner.
X */
Xstatic long	redrawafter;	/* line# of first line that must be redrawn */
Xstatic long	preredraw;	/* line# of last line changed, before change */
Xstatic long	postredraw;	/* line# of last line changed, after change */
Xstatic int	mustredraw;	/* boolean: anything forcing a screen update? */
Xvoid redrawrange(after, pre, post)
X	long	after;	/* lower bound of redrawafter */
X	long	pre;	/* upper bound of preredraw */
X	long	post;	/* upper bound of postredraw */
X{
X	if (after == redrawafter)
X	{
X		/* multiple insertions/deletions at the same place -- combine
X		 * them
X		 */
X		preredraw -= (post - pre);
X		if (postredraw < post)
X		{
X			preredraw += (post - postredraw);
X			postredraw = post;
X		}
X		if (redrawafter > preredraw)
X		{
X			redrawafter = preredraw;
X		}
X		if (redrawafter < 1L)
X		{
X			redrawafter = 0L;
X			preredraw = postredraw = INFINITY;
X		}
X	}
X	else if (postredraw > 0L)
X	{
X		/* multiple changes in different places -- redraw everything
X		 * after "after".
X		 */
X		postredraw = preredraw = INFINITY;
X		if (after < redrawafter)
X			redrawafter = after;
X	}
X	else
X	{
X		/* first change */
X		redrawafter = after;
X		preredraw = pre;
X		postredraw = post;
X	}
X	mustredraw = TRUE;
X}
X
X
X#ifndef NO_CHARATTR
X/* see if a given line uses character attribute strings */
Xstatic int hasattr(lno, text)
X	long		lno;	/* the line# of the cursor */
X	REG char	*text;	/* the text of the line, from fetchline */
X{
X	static long	plno;	/* previous line number */
X	static long	chgs;	/* previous value of changes counter */
X	static int	panswer;/* previous answer */
X	char		*scan;
X
X	/* if charattr is off, then the answer is "no, it doesn't" */
X	if (!*o_charattr)
X	{
X		chgs = 0; /* <- forces us to check if charattr is later set */
X		return FALSE;
X	}
X
X	/* if we already know the answer, return it... */
X	if (lno == plno && chgs == changes)
X	{
X		return panswer;
X	}
X
X	/* get the line & look for "\fX" */
X	if (!text[0] || !text[1] || !text[2])
X	{
X		panswer = FALSE;
X	}
X	else
X	{
X		for (scan = text; scan[2] && !(scan[0] == '\\' && scan[1] == 'f'); scan++)
X		{
X		}
X		panswer = (scan[2] != '\0');
X	}
X
X	/* save the results */
X	plno = lno;
X	chgs = changes;
X
X	/* return the results */
X	return panswer;
X}
X#endif
X
X
X#ifndef NO_VISIBLE
X/* This function checks to make sure that the correct lines are shown in
X * reverse-video.  This is used to handle the "v" and "V" commands.
X */
Xstatic long	vizlow, vizhigh;	/* the starting and ending lines */
Xstatic int	vizleft, vizright;	/* starting & ending indicies */
Xstatic int	vizchange;		/* boolean: must use stupid drawtext? */
Xstatic void setviz(curs)
X	MARK		curs;
X{
X	long		newlow, newhigh;
X	long		extra = 0L;
X
X	/* for now, assume the worst... */
X	vizchange = TRUE;
X
X	/* set newlow & newhigh according to V_from and cursor */
X	if (!V_from)
X	{
X		/* no lines should have reverse-video */
X		if (vizlow)
X		{
X			redrawrange(vizlow, vizhigh + 1L, vizhigh + 1L);
X			vizlow = vizhigh = 0L;
X		}
X		else
X		{
X			vizchange = FALSE;
X		}
X		return;
X	}
X
X	/* figure out which lines *SHOULD* have hilites */
X	if (V_from < curs)
X	{
X		newlow = markline(V_from);
X		newhigh = markline(curs);
X		vizleft = markidx(V_from);
X		vizright = markidx(curs) + 1;
X	}
X	else
X	{
X		newlow = markline(curs);
X		newhigh = markline(V_from);
X		vizleft = markidx(curs);
X		vizright = markidx(V_from) + 1;
X	}
X
X	/* adjust for line-mode hiliting */
X	if (V_linemd)
X	{
X		vizleft = 0;
X		vizright = BLKSIZE - 1;
X	}
X	else
X	{
X		extra = 1L;
X	}
X
X	/* arrange for the necessary lines to be redrawn */
X	if (vizlow == 0L)
X	{
X		/* just starting to redraw */
X		redrawrange(newlow, newhigh, newhigh);
X	}
X	else
X	{
X		/* Were new lines added/removed at the front? */
X		if (newlow != vizlow)
X		{
X			if (newlow < vizlow)
X				redrawrange(newlow, vizlow + extra, vizlow + extra);
X			else
X				redrawrange(vizlow, newlow + extra, newlow + extra);
X		}
X
X		/* Were new lines added/removed at the back? */
X		if (newhigh != vizhigh)
X		{
X			if (newhigh < vizhigh)
X				redrawrange(newhigh + 1L - extra, vizhigh + 1L, vizhigh + 1L);
X			else
X				redrawrange(vizhigh + 1L - extra, newhigh, newhigh);
X		}
X	}
X
X	/* remember which lines will contain hilighted text now */
X	vizlow = newlow;
X	vizhigh = newhigh;
X}
X#endif /* !NO_VISIBLE */
X
X
X/* This function converts a MARK to a column number.  It doesn't automatically
X * adjust for leftcol; that must be done by the calling function
X */
Xint idx2col(curs, text, inputting)
X	MARK		curs;	/* the line# & index# of the cursor */
X	REG char	*text;	/* the text of the line, from fetchline */
X	int		inputting;	/* boolean: called from input() ? */
X{
X	static MARK	pcursor;/* previous cursor, for possible shortcut */
X	static MARK	pcol;	/* column number for pcol */
X	static long	chgs;	/* previous value of changes counter */
X	REG int		col;	/* used to count column numbers */
X	REG int		idx;	/* used to count down the index */
X	REG int		i;
X
X	/* for now, assume we have to start counting at the left edge */
X	col = 0;
X	idx = markidx(curs);
X
X	/* if the file hasn't changed & line number is the same & it has no
X	 * embedded character attribute strings, can we do shortcuts?
X	 */
X	if (chgs == changes
X	 && !((curs ^ pcursor) & ~(BLKSIZE - 1))
X#ifndef NO_CHARATTR
X	 && !hasattr(markline(curs), text)
X#endif
X	)
X	{
X		/* no movement? */
X		if (curs == pcursor)
X		{
X			/* return the column of the char; for tabs, return its last column */
X			if (text[idx] == '\t' && !inputting && !*o_list)
X			{
X				return pcol + *o_tabstop - (pcol % *o_tabstop) - 1;
X			}
X			else
X			{
X				return pcol;
X			}
X		}
X
X		/* movement to right? */
X		if (curs > pcursor)
X		{
X			/* start counting from previous place */
X			col = pcol;
X			idx = markidx(curs) - markidx(pcursor);
X			text += markidx(pcursor);
X		}
X	}
X
X	/* count over to the char after the idx position */
X	while (idx > 0 && (i = *text)) /* yes, ASSIGNMENT! */
X	{
X		if (i == '\t' && !*o_list)
X		{
X			col += *o_tabstop;
X			col -= col % *o_tabstop;
X		}
X		else if (i >= '\0' && i < ' ' || i == '\177')
X		{
X			col += 2;
X		}
X#ifndef NO_CHARATTR
X		else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr)
X		{
X			text += 2; /* plus one more at bottom of loop */
X			idx -= 2;
X		}			
X#endif
X		else
X		{
X			col++;
X		}
X		text++;
X		idx--;
X	}
X
X	/* save stuff to speed next call */
X	pcursor = curs;
X	pcol = col;
X	chgs = changes;
X
X	/* return the column of the char; for tabs, return its last column */
X	if (*text == '\t' && !inputting && !*o_list)
X	{
X		return col + *o_tabstop - (col % *o_tabstop) - 1;
X	}
X	else
X	{
X		return col;
X	}
X}
X
X
X/* This function is similar to idx2col except that it takes care of sideways
X * scrolling - for the given line, at least.
X */
Xint mark2phys(m, text, inputting)
X	MARK	m;		/* a mark to convert */
X	char	*text;		/* the line that m refers to */
X	int	inputting;	/* boolean: caled from input() ? */
X{
X	int	i;
X
X	i = idx2col(m, text, inputting);
X	while (i < leftcol)
X	{
X		leftcol -= *o_sidescroll;
X		mustredraw = TRUE;
X		redrawrange(1L, INFINITY, INFINITY);
X	}
X	while (i > rightcol)
X	{
X		leftcol += *o_sidescroll;
X		mustredraw = TRUE;
X		redrawrange(1L, INFINITY, INFINITY);
X	}
X	physrow = markline(m) - topline;
X	physcol = i - leftcol;
X	if (*o_number)
X		physcol += 8;
X
X	return physcol;
X}
X
X/* This function draws a single line of text on the screen.  The screen's
X * cursor is assumed to be located at the leftmost column of the appropriate
X * row.
X */
Xstatic void drawtext(text, lno, clr)
X	REG char	*text;	/* the text to draw */
X	long		lno;	/* the number of the line to draw */
X	int		clr;	/* boolean: do a clrtoeol? */
X{
X	REG int		col;	/* column number */
X	REG int		i;
X	REG int		tabstop;	/* *o_tabstop */
X	REG int		limitcol;	/* leftcol or leftcol + COLS */
X	int		abnormal;	/* boolean: charattr != A_NORMAL? */
X#ifndef NO_VISIBLE
X	int		rev;		/* boolean: standout mode, too? */
X	int		idx = 0;
X#endif
X	char		numstr[9];
X
X	/* show the line number, if necessary */
X	if (*o_number)
X	{
X		sprintf(numstr, "%6ld |", lno);
X		qaddstr(numstr);
X	}
X
X#ifndef NO_SENTENCE
X	/* if we're hiding format lines, and this is one of them, then hide it */
X	if (*o_hideformat && *text == '.')
X	{
X		clrtoeol();
X#if OSK
X		qaddch('\l');
X#else
X		qaddch('\n');
X#endif
X		return;
X	}
X#endif
X
X	/* move some things into registers... */
X	limitcol = leftcol;
X	tabstop = *o_tabstop;
X	abnormal = FALSE;
X
X#ifndef CRUNCH
X	if (clr)
X		clrtoeol();
X#endif
X
X	/* skip stuff that was scrolled off left edge */
X	for (col = 0;
X	     (i = *text) && col < limitcol; /* yes, ASSIGNMENT! */
X	     text++)
X	{
X#ifndef NO_VISIBLE
X		idx++;
X#endif
X		if (i == '\t' && !*o_list)
X		{
X			col = col + tabstop - (col % tabstop);
X		}
X		else if (i >= 0 && i < ' ' || i == '\177')
X		{
X			col += 2;
X		}
X#ifndef NO_CHARATTR
X		else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr)
X		{
X			text += 2; /* plus one more as part of "for" loop */
X
X			/* since this attribute might carry over, we need it */
X			switch (*text)
X			{
X			  case 'R':
X			  case 'P':
X				attrset(A_NORMAL);
X				abnormal = FALSE;
X				break;
X
X			  case 'B':
X				attrset(A_BOLD);
X				abnormal = TRUE;
X				break;
X
X			  case 'U':
X				attrset(A_UNDERLINE);
X				abnormal = TRUE;
X				break;
X
X			  case 'I':
X				attrset(A_ALTCHARSET);
X				abnormal = TRUE;
X				break;
X			}
X		}
X#endif
X		else
X		{
X			col++;
X		}
X	}
X
X#ifndef NO_VISIBLE
X	/* Should we start hiliting at the first char of this line? */
X	if ((lno > vizlow && lno <= vizhigh
X	    || lno == vizlow && vizleft < idx)
X	   && !(lno == vizhigh && vizright < idx))
X	{
X		do_VISIBLE();
X		rev = TRUE;
X	}
X#endif
X
X	/* adjust for control char that was partially visible */
X	while (col > limitcol)
X	{
X		qaddch(' ');
X		limitcol++;
X	}
X
X	/* now for the visible characters */
X	limitcol = leftcol + COLS;
X	if (*o_number)
X		limitcol -= 8;
X	for (; (i = *text) && col < limitcol; text++)
X	{
X#ifndef NO_VISIBLE
X		/* maybe turn hilite on/off in the middle of the line */
X		if (lno == vizlow && vizleft == idx)
X		{
X			do_VISIBLE();
X			rev = TRUE;
X		}
X		if (lno == vizhigh && vizright == idx)
X		{
X			do_SE();
X			rev = FALSE;
X		}
X		idx++;
X
X		/* if hiliting, never emit physical tabs */
X		if (rev && i == '\t' && !*o_list)
X		{
X			i = col + tabstop - (col % tabstop);
X			do
X			{
X				qaddch(' ');
X				col++;
X			} while (col < i);
X		}
X		else
X#endif /* !NO_VISIBLE */
X		if (i == '\t' && !*o_list)
X		{
X			i = col + tabstop - (col % tabstop);
X			if (i < limitcol)
X			{
X#ifdef CRUNCH
X				if (!clr && has_PT && !((i - leftcol) & 7))
X#else
X				if (has_PT && !((i - leftcol) & 7))
X#endif
X				{
X					do
X					{
X						qaddch('\t');
X						col += 8; /* not exact! */
X					} while (col < i);
X					col = i; /* NOW it is exact */
X				}
X				else
X				{
X					do
X					{
X						qaddch(' ');
X						col++;
X					} while (col < i);
X				}
X			}
X			else /* tab ending after screen? next line! */
X			{
X				col = limitcol;
X				if (has_AM)
X				{
X					addch('\n');	/* GB */
X				}
X			}
X		}
X		else if (i >= 0 && i < ' ' || i == '\177')
X		{
X			col += 2;
X			qaddch('^');
X			if (col <= limitcol)
X			{
X				qaddch(i ^ '@');
X			}
X		}
X#ifndef NO_CHARATTR
X		else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr)
X		{
X			text += 2; /* plus one more as part of "for" loop */
X			switch (*text)
X			{
X			  case 'R':
X			  case 'P':
X				attrset(A_NORMAL);
X				abnormal = FALSE;
X				break;
X
X			  case 'B':
X				attrset(A_BOLD);
X				abnormal = TRUE;
X				break;
X
X			  case 'U':
X				attrset(A_UNDERLINE);
X				abnormal = TRUE;
X				break;
X
X			  case 'I':
X				attrset(A_ALTCHARSET);
X				abnormal = TRUE;
X				break;
X			}
X		}
X#endif
X		else
X		{
X			col++;
X			qaddch(i);
X		}
X	}
X
X	/* get ready for the next line */
X#ifndef NO_CHARATTR
X	if (abnormal)
X	{
X		attrset(A_NORMAL);
X	}
X#endif
X	if (*o_list && col < limitcol)
X	{
X		qaddch('$');
X		col++;
X	}
X
X#ifndef NO_VISIBLE
X	/* did we hilite this whole line?  If so, STOP! */
X	if (rev)
X	{
X		do_SE();
X	}
X#endif
X
X#ifdef CRUNCH
X	if (clr && col < limitcol)
X	{
X		clrtoeol();
X	}
X#endif
X	if (!has_AM || col < limitcol)
X	{
X		addch('\n');
X	}
X
X	wqrefresh();
X}
X
X
X#ifndef CRUNCH
Xstatic void nudgecursor(same, scan, new, lno)
X	int	same;	/* number of chars to be skipped over */
X	char	*scan;	/* where the same chars end */
X	char	*new;	/* where the visible part of the line starts */
X	long	lno;	/* line number of this line */
X{
X	int	col;
X
X	if (same > 0)
X	{
X		if (same < 5)
X		{
X			/* move the cursor by overwriting */
X			while (same > 0)
X			{
X				qaddch(scan[-same]);
X				same--;
X			}
X		}
X		else
X		{
X			/* move the cursor by calling move() */
X			col = (int)(scan - new);
X			if (*o_number)
X				col += 8;
X			move((int)(lno - topline), col);
X		}
X	}
X}
X#endif /* not CRUNCH */
X
X/* This function draws a single line of text on the screen, possibly with
X * some cursor optimization.  The cursor is repositioned before drawing
X * begins, so its position before doesn't really matter.
X */
Xstatic void smartdrawtext(text, lno, showit)
X	REG char	*text;	/* the text to draw */
X	long		lno;	/* line number of the text */
X	int		showit;	/* boolean: output line? (else just remember it) */
X{
X#ifdef CRUNCH
X	move((int)(lno - topline), 0);
X	if (showit)
X	{
X		drawtext(text, lno, TRUE);
X	}
X#else /* not CRUNCH */
X	static char	old[256];	/* how the line looked last time */
X	char		new[256];	/* how it looks now */
X	char		*build;		/* used to put chars into new[] */
X	char		*scan;		/* used for moving thru new[] or old[] */
X	char		*end;		/* last non-blank changed char */
X	char		*shift;		/* used to insert/delete chars */
X	int		same;		/* length of a run of unchanged chars */
X	int		limitcol;
X	int		col;
X	int		i;
X	char		numstr[9];
X
X# ifndef NO_CHARATTR
X	/* if this line has attributes, do it the dumb way instead */
X	if (hasattr(lno, text))
X	{
X		move((int)(lno - topline), 0);
X		drawtext(text, lno, TRUE);
X		return;
X	}
X# endif
X# ifndef NO_SENTENCE
X	/* if this line is a format line, & we're hiding format lines, then
X	 * let the dumb drawtext() function handle it
X	 */
X	if (*o_hideformat && *text == '.')
X	{
X		move((int)(lno - topline), 0);
X		drawtext(text, lno, TRUE);
X		return;
X	}
X# endif
X# ifndef NO_VISIBLE
X	if (vizchange)
X	{
X		move((int)(lno - topline), 0);
X		drawtext(text, lno, TRUE);
X		smartlno = 0L;
X		return;
X	}
X# endif
X
X	/* skip stuff that was scrolled off left edge */
X	limitcol = leftcol;
X	for (col = 0;
X	     (i = *text) && col < limitcol; /* yes, ASSIGNMENT! */
X	     text++)
X	{
X		if (i == '\t' && !*o_list)
X		{
X			col = col + *o_tabstop - (col % *o_tabstop);
X		}
X		else if (i >= 0 && i < ' ' || i == '\177')
X		{
X			col += 2;
X		}
X		else
X		{
X			col++;
X		}
X	}
X
X	/* adjust for control char that was partially visible */
X	build = new;
X	while (col > limitcol)
X	{
X		*build++ = ' ';
X		limitcol++;
X	}
X
X	/* now for the visible characters */
X	limitcol = leftcol + COLS;
X	if (*o_number)
X		limitcol -= 8;
X	for (; (i = *text) && col < limitcol; text++)
X	{
X		if (i == '\t' && !*o_list)
X		{
X			i = col + *o_tabstop - (col % *o_tabstop);
X			while (col < i && col < limitcol)
X			{
X				*build++ = ' ';
X				col++;
X			}
X		}
X		else if (i >= 0 && i < ' ' || i == '\177')
X		{
X			col += 2;
X			*build++ = '^';
X			if (col <= limitcol)
X			{
X				*build++ = (i ^ '@');
X			}
X		}
X		else
X		{
X			col++;
X			*build++ = i;
X		}
X	}
X	if (col < limitcol && *o_list)
X	{
X		*build++ = '$';
X		col++;
X	}
X	end = build;
X	while (col < limitcol)
X	{
X		*build++ = ' ';
X		col++;
X	}
X
X	/* if we're just supposed to remember this line, then remember it */
X	if (!showit)
X	{
X		smartlno = lno;
X		strncpy(old, new, COLS);
X		return;
X	}
X
X	/* locate the last non-blank character */
X	while (end > new && end[-1] == ' ')
X	{
X		end--;
X	}
X
X	/* can we optimize the displaying of this line? */
X	if (lno != smartlno)
X	{
X		/* nope, can't optimize - different line */
X		move((int)(lno - topline), 0);
X
X		/* show the line number, if necessary */
X		if (*o_number)
X		{
X			sprintf(numstr, "%6ld |", lno);
X			qaddstr(numstr);
X		}
X
X		/* show the new line */
X		for (scan = new, build = old; scan < end; )
X		{
X			qaddch(*scan);
X			*build++ = *scan++;
X		}
X		if (end < new + COLS - (*o_number ? 8 : 0))
X		{
X			clrtoeol();
X			while (build < old + COLS)
X			{
X				*build++ = ' ';
X			}
X		}
X		smartlno = lno;
X		return;
X	}
X
X	/* skip any initial unchanged characters */
X	for (scan = new, build = old; scan < end && *scan == *build; scan++, build++)
X	{
X	}
X	i = (scan - new);
X	if (*o_number)
X		i += 8;
X	move((int)(lno - topline), i);
X
X	/* The in-between characters must be changed */
X	same = 0;
X	while (scan < end)
X	{
X		/* is this character a match? */
X		if (scan[0] == build[0])
X		{
X			same++;
X		}
X		else /* do we want to insert? */
X		if (scan < end - 1 && scan[1] == build[0] && (has_IC || has_IM))
X		{
X			nudgecursor(same, scan, new, lno);
X			same = 0;
X
X			insch(*scan);
X			for (shift = old + COLS; --shift > build; )
X			{
X				shift[0] = shift[-1];
X			}
X			*build = *scan;
X		}
X		else /* do we want to delete? */
X		if (build < old + COLS - 1 && scan[0] == build[1] && has_DC)
X		{
X			nudgecursor(same, scan, new, lno);
X			same = 0;
X
X			delch();
X			same++;
X			for (shift = build; shift < old + COLS - 1; shift++)
X			{
X				shift[0] = shift[1];
X			}
X			if (*o_number)
X				shift -= 8;
X			*shift = ' ';
X		}
X		else /* we must overwrite */
X		{
X			nudgecursor(same, scan, new, lno);
X			same = 0;
X
X			addch(*scan);
X			*build = *scan;
X		}
X
X		build++;
X		scan++;
X	}
X
X	/* maybe clear to EOL */
X	end = old + COLS - (*o_number ? 8 : 0);
X	while (build < end && *build == ' ')
X	{
X		build++;
X	}
X	if (build < end)
X	{
X		nudgecursor(same, scan, new, lno);
X		same = 0;
X
X		clrtoeol();
X		while (build < old + COLS)
X		{
X			*build++ = ' ';
X		}
X	}
X#endif /* not CRUNCH */
X}
X
X
X/* This function is used in visual mode for drawing the screen (or just parts
X * of the screen, if that's all thats needed).  It also takes care of
X * scrolling.
X */
Xvoid redraw(curs, inputting)
X	MARK	curs;		/* where to leave the screen's cursor */
X	int	inputting;	/* boolean: being called from input() ? */
X{
X	char		*text;		/* a line of text to display */
X	static long	chgs;		/* previous changes level */
X	long		l;
X	int		i;
X#ifndef CRUNCH
X	static long	showtop;	/* top line in window */
X	static long	showbottom;	/* bottom line in window */
X#endif
X
X	/* if curs == MARK_UNSET, then we should reset internal vars */
X	if (curs == MARK_UNSET)
X	{
X		if (topline < 1 || topline > nlines)
X		{
X			topline = 1L;
X		}
X		else
X		{
X			move(LINES - 1, 0);
X			clrtoeol();
X		}
X		leftcol = 0;
X		mustredraw = TRUE;
X		redrawafter = INFINITY;
X		preredraw = 0L;
X		postredraw = 0L;
X		chgs = 0;
X		smartlno = 0L;
X#ifndef NO_VISIBLE
X		vizlow = vizhigh = 0L;
X		vizchange = FALSE;
X#endif
X#ifndef CRUNCH
X		showtop = 0;
X		showbottom = INFINITY;
X#endif
X		return;
X	}
X
X#ifndef NO_VISIBLE
X	/* adjustments to hilited area may force extra lines to be redrawn. */
X	setviz(curs);
X#endif
X
X	/* figure out which column the cursor will be in */
X	l = markline(curs);
X	text = fetchline(l);
X	mark2phys(curs, text, inputting);
X
X#ifndef NO_COLOR
X	fixcolor();
X#endif
X
X	/* adjust topline, if necessary, to get the cursor on the screen */
X	if (l >= topline && l <= botline)
X	{
X		/* it is on the screen already */
X
X		/* if the file was changed but !mustredraw, then redraw line */
X		if (!mustredraw && (chgs != changes
X#ifndef NO_VISIBLE
X			|| V_from
X#endif
X#ifndef CRUNCH
X			|| l < showtop || l > showbottom
X#endif
X							))
X		{
X			smartdrawtext(text, l, (chgs != changes));
X		}
X	}
X	else if (l < topline && l > topline - LINES && (has_SR || has_AL))
X	{
X		/* near top - scroll down */
X		if (!mustredraw)
X		{
X			move(0,0);
X			while (l < topline)
X			{
X				topline--;
X				if (has_SR)
X				{
X					do_SR();
X				}
X				else
X				{
X					insertln();
X				}
X				text = fetchline(topline);
X				drawtext(text, topline, FALSE);
X				do_UP();
X			}
X
X			/* blank out the last line */
X			move(LINES - 1, 0);
X			clrtoeol();
X		}
X		else
X		{
X			topline = l;
X			redrawrange(0L, INFINITY, INFINITY);
X		}
X	}
X	else if (l > topline && l < botline + LINES)
X	{
X		/* near bottom -- scroll up */
X		if (!mustredraw)
X		{
X			move(LINES - 1,0);
X			clrtoeol();
X			while (l > botline)
X			{
X				topline++; /* <-- also adjusts botline */
X				text = fetchline(botline);
X				drawtext(text, botline, FALSE);
X			}
X#ifndef CRUNCH
X			showbottom = l;
X#endif
X		}
X		else
X		{
X			topline = l - (LINES - 2);
X			redrawrange(0L, INFINITY, INFINITY);
X		}
X	}
X	else
X	{
X		/* distant line - center it & force a redraw */
X		topline = l - (LINES / 2) - 1;
X		if (topline < 1)
X		{
X			topline = 1;
X		}
X		redrawrange(0L, INFINITY, INFINITY);
X		changes++;
X	}
X
X#ifndef CRUNCH
X	/* make sure the current line is included in the "window" */
X	if (l < showtop)
X	{
X		redrawrange(l, showtop, showtop);
X		showtop = l;
X	}
X	if (l > showbottom)
X	{
X		redrawrange(showbottom, l, l);
X		showbottom = l;
X	}
X#endif
X
X
X	/* Now... do we really have to redraw? */
X	if (mustredraw)
X	{
X		/* If redrawfter (and friends) aren't set, assume we should
X		 * redraw everything.
X		 */
X		if (redrawafter == INFINITY)
X		{
X			redrawafter = 0L;
X			preredraw = postredraw = INFINITY;
X		}
X
X#ifndef CRUNCH
X		/* shrink the window, if possible */
X		if (showtop < topline)
X		{
X			showtop = topline;
X		}
X		if (showbottom > botline)
X		{
X			showbottom = botline;
X		}
X		if (postredraw == INFINITY)
X		{
X			/* these will be set to more reasonable values later */
X			showtop = INFINITY;
X			showbottom = 0L;
X		}
X#endif
X
X		/* adjust smartlno to correspond with inserted/deleted lines */
X		if (smartlno >= redrawafter)
X		{
X			if (smartlno < preredraw && postredraw != preredraw) /*!!!*/
X			{
X				smartlno = 0L;
X			}
X			else
X			{
X				smartlno += (postredraw - preredraw);
X			}
X		}
X
X		/* should we insert some lines into the screen? */
X		if (preredraw < postredraw && preredraw <= botline)
X		{
X			/* lines were inserted into the file */
X
X			/* decide where insertion should start */
X			if (preredraw < topline)
X			{
X				l = topline;
X			}
X			else
X			{
X				l = preredraw;
X			}
X
X			/* insert the lines... maybe */
X			if (l + postredraw - preredraw > botline || !has_AL || *o_number)
X			{
X				/* Whoa!  a whole screen full - just redraw */
X				preredraw = postredraw = INFINITY;
X			}
X			else
X			{
X				/* really insert 'em */
X				move((int)(l - topline), 0);
X				for (i = postredraw - preredraw; i > 0; i--)
X				{
X					insertln();
X				}
X
X				/* NOTE: the contents of those lines will be
X				 * drawn as part of the regular redraw loop.
X				 */
X
X				/* clear the last line */
X				move(LINES - 1, 0);
X				clrtoeol();
X			}
X		}
X
X		/* do we want to delete some lines from the screen? */
X		if (preredraw > postredraw && postredraw <= botline)
X		{
X			if (preredraw > botline || !has_DL || *o_number)
X			{
X				postredraw = preredraw = INFINITY;
X			}
X			else /* we'd best delete some lines from the screen */
X			{
X				/* clear the last line, so it doesn't look
X				 * ugly as it gets pulled up into the screen
X				 */
X				move(LINES - 1, 0);
X				clrtoeol();
X
X				/* delete the lines */
X				move((int)(postredraw - topline), 0);
X			 	for (l = postredraw;
X				     l < preredraw && l <= botline;
X				     l++)
X				{
X					deleteln();
X				}
X
X				/* draw the lines that are now newly visible
X				 * at the bottom of the screen
X				 */
X				i = LINES - 1 + (postredraw - preredraw);
X				move(i, 0);
X				for (l = topline + i; l <= botline; l++)
X				{
X					/* clear this line */
X					clrtoeol();
X
X					/* draw the line, or ~ for non-lines */
X					if (l <= nlines)
X					{
X						text = fetchline(l);
X						drawtext(text, l, FALSE);
X					}
X					else
X					{
X						addstr("~\n");
X					}
X				}
X			}
X		}
X
X		/* redraw the current line */
X		l = markline(curs);
X		pfetch(l);
X		smartdrawtext(ptext, l, TRUE);
X
X#ifndef CRUNCH
X		/* decide which lines must be in the "window" around the cursor */
X		l = markline(curs);
X		if ((*o_window & 0xff) + 1 == LINES)
X		{
X			showtop = 1;
X			showbottom = INFINITY;
X		}
X		else if (l < showtop || l > showbottom)
X		{
X			l -= (*o_window & 0xff) / 2;
X			if (l < topline)
X			{
X				l = topline;
X			}
X			if (l < showtop)
X			{
X				showtop = l;
X			}
X			l += (*o_window & 0xff) - 1;
X			if (l > botline)
X			{
X				showtop = showtop - l + botline;
X				l = botline;
X			}
X			if (l > showbottom)
X			{
X				showbottom = l;
X			}
X		}
X#endif
X
X		/* decide where we should start redrawing from */
X		if (redrawafter < topline)
X		{
X			l = topline;
X		}
X		else
X		{
X			l = redrawafter;
X		}
X		if (l <= botline && l < postredraw && (l != smartlno || botline != smartlno))
X		{
X			/* draw the other lines */
X			move((int)(l - topline), 0);
X			for (; l <= botline && l < postredraw; l++)
X			{
X				/* we already drew the current line, so skip it now */
X				if (l == smartlno)
X				{
X#if OSK
X					qaddch('\l');
X#else
X					qaddch('\n');
X#endif
X					continue;
X				}
X
X				/* draw the line, or ~ for non-lines */
X				if (l > nlines)
X				{
X					qaddch('~');
X					clrtoeol();
X					addch('\n');
X				}
X#ifndef CRUNCH
X				else if (l < showtop || l > showbottom)
X				{
X					qaddch('@');
X					clrtoeol();
X					addch('\n');
X				}
X#endif
X				else
X				{
X					text = fetchline(l);
X					drawtext(text, l, TRUE);
X				}
X			}
X		}
X
X		mustredraw = FALSE;
X	}
X
X	/* force total (non-partial) redraw next time if not set */
X	redrawafter = INFINITY;
X	preredraw = 0L;
X	postredraw = 0L;
X
X	/* move the cursor to where it belongs */
X	move((int)(markline(curs) - topline), physcol);
X	wqrefresh();
X
X	chgs = changes;
X}
/
echo x - ref.c
sed '/^X/s///' > ref.c << '/'
X/* ref2.c */
X
X/* This is a totally rewritten version of ref.  This version looks for the
X * desired function name in the "tags" file, and then reads the header out
X * from the source file.  There is no longer any need for a "refs" file.
X *
X * Usage:	ref [-a] [-t] [-f file] [-c class] tag
X * Options:	-t	   output tag info, not the description
X *		-f file	   default filename for static functions
X *		-c class   default class names for class functions
X */
X
X#include <stdio.h>
X#include "config.h"
Xextern char	*getenv();
Xextern char	*fgets();
X
X
X/* This is the default path that is searched for tags */
X#if OSK
X# define DEFTAGPATH ".:/dd/defs:/dd/defs/sys:/dd/usr/src/lib:../lib:/dd/usr/lib"
X#else
X# if ANY_UNIX
X#  define DEFTAGPATH ".:/usr/include:/usr/include/sys:/usr/src/lib:../lib:/usr/local/lib"
X# else
X#  if MSDOS || TOS
X#   define DEFTAGPATH ".;C:\\include;C:\\include\\sys;C:\\lib;..\\lib"
X#   define SEP ';'
X#  else
X#   if AMIGA
X#    define DEFTAGPATH ".;Include:;Include:sys"
X#    define SEP ';'
X#   else /* any other OS */
X#    define DEFTAGPATH "."
X#   endif
X#  endif
X# endif
X#endif
X
X#ifndef SEP
X# define SEP ':'
X#endif
X
X
X/* These variables reflect the command-line options given by the user. */
Xint	taginfo;	/* boolean: give only the tag info? (not header?) */
Xchar	*def_file;	/* default filename for static functions */
Xchar	*def_class;	/* default classname for class members */
Xint	colons;		/* #colons in tag: 0=normal, 1=static, 2=member */
X
X/* This function checks for a tag in the "tags" file of given directory.
X * If the tag is found, then it returns a pointer to a static buffer which
X * contains the filename, a tab character, and a linespec for finding the
X * the tag.  If the tag is not found in the "tags" file, or if the "tags"
X * file cannot be opened or doesn't exist, then this function returns NULL.
X */
Xchar *cktagdir(tag, dir)
X	char	*tag;	/* name of the tag to look for */
X	char	*dir;	/* name of the directory to check */
X{
X	char	buf[BLKSIZE];
X	static char found[BLKSIZE];
X	FILE	*tfile;
X	int	len;
X
X#if AMIGA
X	if (dir[strlen(dir) - 1] == COLON)
X	    sprintf(buf, "%s%s", dir, TAGS);   /* no slash after colon. */
X	else
X#endif
X	/* construct the name of the "tags" file in this directory */
X	sprintf(buf, "%s%c%s", dir, SLASH, TAGS);
X
X	/* Try to open the tags file.  Return NULL if can't open */
X#if AMIGA
X	if (buf[0] == '.' && buf[1] == SLASH)
X	    tfile = fopen(&buf[2], "r");
X	else
X#endif
X	tfile = fopen(buf, "r");
X	if (!tfile)
X	{
X		return (char *)0;
X	}
X
X	/* compute the length of the tagname once */
X	len = strlen(tag);
X
X	/* read lines until we get the one for this tag */
X	found[0] = '\0';
X	while (fgets(buf, sizeof buf, tfile))
X	{
X		/* is this the one we want? */
X		if (!strncmp(buf, tag, len) && buf[len] == '\t')
X		{
X			/* we've found a match -- remember it */
X			strcpy(found, buf);
X
X			/* if there is no default file, or this match is in
X			 * the default file, then we've definitely found the
X			 * one we want.  Break out of the loop now.
X			 */
X			if (!def_file || !strncmp(&buf[len + 1], def_file, strlen(def_file)))
X			{
X				break;
X			}
X		}
X	}
X
X	/* we're through reading */
X	fclose(tfile);
X
X	/* if there's anything in found[], use it */
X	if (found[0])
X	{
X		return &found[len + 1];
X	}
X
X	/* else we didn't find it */
X	return (char *)0;
X}
X
X/* This function reads a single textline from a binary file.  It returns
X * the number of bytes read, or 0 at EOF.
X */
Xint getline(buf, limit, fp)
X	char	*buf;	/* buffer to read into */
X	int	limit;	/* maximum characters to read */
X	FILE	*fp;	/* binary stream to read from */
X{
X	int	bytes;	/* number of bytes read so far */
X	int	ch;	/* single character from file */
X
X	for (bytes = 0, ch = 0; ch != '\n' && --limit > 0 && (ch = getc(fp)) != EOF; bytes++)
X	{
X#if MSDOS || TOS
X		/* since this is a binary file, we'll need to manually strip CR's */
X		if (ch == '\r')
X		{
X			continue;
X		}
X#endif
X		*buf++ = ch;
X	}
X	*buf = '\0';
X
X	return bytes;
X}
X
X
X/* This function reads a source file, looking for a given tag.  If it finds
X * the tag, then it displays it and returns TRUE.  Otherwise it returns FALSE.
X * To display the tag, it attempts to output any introductory comment, the
X * tag line itself, and any arguments.  Arguments are assumed to immediately
X * follow the tag line, and start with whitespace.  Comments are assumed to
X * start with lines that begin with "/*", "//", "(*", or "--", and end at the
X * tag line or at a blank line.
X */
Xint lookup(dir, entry)
X	char	*dir;	/* name of the directory that contains the source */
X	char	*entry;	/* source filename, <Tab>, linespec */
X{
X	char	buf[BLKSIZE];	/* pathname of sourcefile */
X	long	lnum;		/* line number */
X	long	here;		/* seek position where current line began */
X	long	comment;	/* seek position of introductory comment, or -1L */
X	FILE	*sfile;		/* used for reading the source file */
X	int	len;		/* length of string */
X	char	*ptr;
X
X
X	/* construct the pathname of the source file */
X	strcpy(buf, dir);
X	ptr = buf + strlen(buf);
X#if AMIGA
X	if (ptr[-1] != COLON)
X#endif
X	*ptr++ = SLASH;
X	while (*entry != '\t')
X	{
X		*ptr++ = *entry++;
X	}
X	*ptr = '\0';
X	entry++;
X
X	/* searching for string or number? */
X	if (*entry >= '0' && *entry <= '9')
X	{
X		/* given a specific line number */
X		lnum = atol(entry);
X		entry = (char *)0;
X	}
X	else
X	{
X		/* given a string -- strip off "/^" and "$/\n" */
X		entry += 2;
X		len = strlen(entry) - 2;
X		if (entry[len - 1] == '$')
X		{
X			entry[len - 1] = '\n';
X		}
X		lnum = 0L;
X	}
X
X	/* Open the file.  Note that we open the file in binary mode even
X	 * though we know it is a text file, because ftell() and fseek()
X	 * don't work on text files.
X	 */
X#if MSDOS || TOS
X	sfile = fopen(buf, "rb");
X#else
X# if AMIGA
X	if (buf[0] == '.' && buf[1] == SLASH)
X	    sfile = fopen(&buf[2], "r");
X	else
X# endif
X	sfile = fopen(buf, "r");
X#endif
X	if (!sfile)
X	{
X		/* can't open the real source file.  Try "refs" instead */
X#if AMIGA
X		if (dir[strlen(dir) - 1] == COLON)
X			sprintf(buf, "%srefs", dir);
X		else
X#endif
X		sprintf(buf, "%s%crefs", dir, SLASH);
X#if MSDOS || TOS
X		sfile = fopen(buf, "rb");
X#else
X# if AMIGA
X		if (buf[0] == '.' && buf[1] == SLASH)
X		    sfile = fopen(&buf[2], "r");
X		else
X# endif
X		sfile = fopen(buf, "r");
X#endif
X		if (!sfile)
X		{
X			/* failed! */
X			return 0;
X		}
X	}
X
X	/* search the file */
X	for (comment = -1L; here = ftell(sfile), getline(buf, BLKSIZE, sfile) > 0; )
X	{
X		/* Is this the start/end of a comment? */
X		if (comment == -1L)
X		{
X			/* starting a comment? */
X			if (buf[0] == '/' && buf[1] == '*'
X			 || buf[0] == '/' && buf[1] == '/'
X			 || buf[0] == '(' && buf[1] == '*'
X			 || buf[0] == '-' && buf[1] == '-')
X			{
X				comment = here;
X			}
X		}
X		else
X		{
X			/* ending a comment? */
X			if (buf[0] == '\n' || buf[0] == '#')
X			{
X				comment = -1L;
X			}
X		}
X
X		/* is this the tag line? */
X		if (--lnum == 0L || (entry && !strncmp(buf, entry, len)))
X		{
X			/* if there were introductory comments, show them */
X			if (comment != -1L)
X			{
X				fseek(sfile, comment, 0);
X				while (comment != here)
X				{
X					getline(buf, BLKSIZE, sfile);
X					fputs(buf, stdout);
X					comment = ftell(sfile);
X				}
X
X				/* re-fetch the tag line */
X				fgets(buf, BLKSIZE, sfile);
X			}
X
X			/* show the tag line */
X			fputs(buf, stdout);
X
X			/* show any argument lines */
X			while (getline(buf, BLKSIZE, sfile) > 0
X			    && buf[0] != '#'
X			    && strchr(buf, '{') == (char *)0)
X			{
X				fputs(buf, stdout);
X			}
X
X			/* Done!  Close the file, and return TRUE */
X			fclose(sfile);
X			return 1;
X		}
X	}
X
X	/* not found -- return FALSE */
X	return 0;
X}
X
X/* This function searches through the entire search path for a given tag.
X * If it finds the tag, then it displays the info and returns TRUE;
X * otherwise it returns FALSE.
X */
Xint find(tag)
X	char	*tag;	/* the tag to look up */
X{
X	char	*tagpath;
X	char	dir[80];
X	char	*ptr;
X	int	len;
X
X	if (colons == 1)
X	{
X		/* looking for static function -- only look in current dir */
X		tagpath = ".";
X	}
X	else
X	{
X		/* get the tagpath from the environment.  Default to DEFTAGPATH */
X		tagpath = getenv("TAGPATH");
X		if (!tagpath)
X		{
X			tagpath = DEFTAGPATH;
X		}
X	}
X
X	/* for each entry in the path... */
X	while (*tagpath)
X	{
X		/* Copy the entry into the dir[] buffer */
X		for (ptr = dir; *tagpath && *tagpath != SEP; tagpath++)
X		{
X			*ptr++ = *tagpath;
X		}
X		if (*tagpath == SEP)
X		{
X			tagpath++;
X		}
X
X		/* if the entry ended with "/tags", then strip that off */
X		len = strlen(TAGS);
X		if (&dir[len] < ptr && ptr[-len - 1] == SLASH && !strncmp(&ptr[-len], TAGS, len))
X		{
X			ptr -= len + 1;
X		}
X
X		/* if the entry is now an empty string, then assume "." */
X		if (ptr == dir)
X		{
X			*ptr++ = '.';
X		}
X		*ptr = '\0';
X
X		/* look for the tag in this path.  If found, then display it
X		 * and exit.
X		 */
X		ptr = cktagdir(tag, dir);
X		if (ptr)
X		{
X			/* just supposed to display tag info? */
X			if (taginfo)
X			{
X				/* then do only that! */
X				if (strcmp(dir, "."))
X				{
X					printf("%s%c%s", dir, SLASH, ptr);
X				}
X				else
X				{
X					/* avoid leading "./" if possible */
X					fputs(ptr, stdout);
X				}
X				return 1;
X			}
X			else
X			{
X				/* else look up the declaration of the thing */
X				return lookup(dir, ptr);
X			}
X		}
X	}
X
X	/* if we get here, then the tag wasn't found anywhere */
X	return 0;
X}
X
Xvoid usage()
X{
X	fputs("usage: ref [-a] [-t] [-c class] [-f file] tag\n", stderr);
X	fputs("   -a        function's args may be flush against left margin\n", stderr);
X	fputs("   -t        output tag info, instead of the function header\n", stderr);
X	fputs("   -f File   tag might be a static function in File\n", stderr);
X	fputs("   -c Class  tag might be a member of class Class\n", stderr);
X	exit(2);
X}
X
X
Xint countcolons(str)
X	char	*str;
X{
X	while (*str != ':' && *str)
X	{
X		str++;
X	}
X	if (str[0] != ':')
X	{
X		return 0;
X	}
X	else if (str[1] != ':')
X	{
X		return 1;
X	}
X	return 2;
X}
X
Xint main(argc, argv)
X	int	argc;
X	char	**argv;
X{
X	char	def_tag[100];	/* used to build tag name with default file/class */
X	int	i;
X
X	/* parse flags */
X	for (i = 1; i < argc && argv[i][0] == '-'; i++)
X	{
X		switch (argv[i][1])
X		{
X		  case 't':
X			taginfo = 1;
X			break;
X
X		  case 'f':
X			if (argv[i][2])
X			{
X				def_file = &argv[i][2];
X			}
X			else if (++i < argc)
X			{
X				def_file = argv[i];
X			}
X			else
X			{
X				usage();
X			}
X			break;
X
X		  case 'c':
X			if (argv[i][2])
X			{
X				def_class = &argv[i][2];
X			}
X			else if (++i < argc)
X			{
X				def_class = argv[i];
X			}
X			else
X			{
X				usage();
X			}
X			break;
X
X		  default:
X			usage();
X		}
X	}
X
X	/* if no tag was given, complain */
X	if (i + 1 != argc)
X	{
X		usage();
X	}
X
X	/* does the tag have an explicit class or file? */
X	colons = countcolons(argv[i]);
X
X	/* if not, then maybe try some defaults */
X	if (colons == 0)
X	{
X		/* try a static function in the file first */
X		if (def_file)
X		{
X			sprintf(def_tag, "%s:%s", def_file, argv[i]);
X			colons = 1;
X			if (find(def_tag))
X			{
X				exit(0);
X			}
X		}
X
X		/* try a member function for a class */
X		if (def_class)
X		{
X			sprintf(def_tag, "%s::%s", def_class, argv[i]);
X			colons = 2;
X			if (find(def_tag))
X			{
X				exit(0);
X			}
X		}
X
X		/* oh, well */
X		colons = 0;
X	}
X
X	/* find the tag */
X	if (find(argv[i]))
X	{
X		exit(0);
X	}
X
X	exit(1);
X	/*NOTREACHED*/
X}
/
echo x - regexp.c
sed '/^X/s///' > regexp.c << '/'
X/* regexp.c */
X
X/* This file contains the code that compiles regular expressions and executes
X * them.  It supports the same syntax and features as vi's regular expression
X * code.  Specifically, the meta characters are:
X *	^	matches the beginning of a line
X *	$	matches the end of a line
X *	\<	matches the beginning of a word
X *	\>	matches the end of a word
X *	.	matches any single character
X *	[]	matches any character in a character class
X *	\(	delimits the start of a subexpression
X *	\)	delimits the end of a subexpression
X *	*	repeats the preceding 0 or more times
X * NOTE: You cannot follow a \) with a *.
X *
X * The physical structure of a compiled RE is as follows:
X *	- First, there is a one-byte value that says how many character classes
X *	  are used in this regular expression
X *	- Next, each character class is stored as a bitmap that is 256 bits
X *	  (32 bytes) long.
X *	- A mixture of literal characters and compiled meta characters follows.
X *	  This begins with M_BEGIN(0) and ends with M_END(0).  All meta chars
X *	  are stored as a \n followed by a one-byte code, so they take up two
X *	  bytes apiece.  Literal characters take up one byte apiece.  \n can't
X *	  be used as a literal character.
X *
X * If NO_MAGIC is defined, then a different set of functions is used instead.
X * That right, this file contains TWO versions of the code.
X */
X
X#include <setjmp.h>
X#include "config.h"
X#include "ctype.h"
X#include "vi.h"
X#include "regexp.h"
X
X
X
Xstatic char	*previous;	/* the previous regexp, used when null regexp is given */
X
X
X#ifndef NO_MAGIC
X/* THE REAL REGEXP PACKAGE IS USED UNLESS "NO_MAGIC" IS DEFINED */
X
X/* These are used to classify or recognize meta-characters */
X#define META		'\0'
X#define BASE_META(m)	((m) - 256)
X#define INT_META(c)	((c) + 256)
X#define IS_META(m)	((m) >= 256)
X#define IS_CLASS(m)	((m) >= M_CLASS(0) && (m) <= M_CLASS(9))
X#define IS_START(m)	((m) >= M_START(0) && (m) <= M_START(9))
X#define IS_END(m)	((m) >= M_END(0) && (m) <= M_END(9))
X#define IS_CLOSURE(m)	((m) >= M_SPLAT && (m) <= M_RANGE)
X#define ADD_META(s,m)	(*(s)++ = META, *(s)++ = BASE_META(m))
X#define GET_META(s)	(*(s) == META ? INT_META(*++(s)) : *s)
X
X/* These are the internal codes used for each type of meta-character */
X#define M_BEGLINE	256		/* internal code for ^ */
X#define M_ENDLINE	257		/* internal code for $ */
X#define M_BEGWORD	258		/* internal code for \< */
X#define M_ENDWORD	259		/* internal code for \> */
X#define M_ANY		260		/* internal code for . */
X#define M_SPLAT		261		/* internal code for * */
X#define M_PLUS		262		/* internal code for \+ */
X#define M_QMARK		263		/* internal code for \? */
X#define M_RANGE		264		/* internal code for \{ */
X#define M_CLASS(n)	(265+(n))	/* internal code for [] */
X#define M_START(n)	(275+(n))	/* internal code for \( */
X#define M_END(n)	(285+(n))	/* internal code for \) */
X
X/* These are used during compilation */
Xstatic int	class_cnt;	/* used to assign class IDs */
Xstatic int	start_cnt;	/* used to assign start IDs */
Xstatic int	end_stk[NSUBEXP];/* used to assign end IDs */
Xstatic int	end_sp;
Xstatic char	*retext;	/* points to the text being compiled */
X
X/* error-handling stuff */
Xjmp_buf	errorhandler;
X#define FAIL(why)	regerror(why); longjmp(errorhandler, 1)
X
X
X
X
X
X/* This function builds a bitmap for a particular class */
Xstatic char *makeclass(text, bmap)
X	REG char	*text;	/* start of the class */
X	REG char	*bmap;	/* the bitmap */
X{
X	REG int		i;
X	int		complement = 0;
X
X
X	/* zero the bitmap */
X	for (i = 0; bmap && i < 32; i++)
X	{
X		bmap[i] = 0;
X	}
X
X	/* see if we're going to complement this class */
X	if (*text == '^')
X	{
X		text++;
X		complement = 1;
X	}
X
X	/* add in the characters */
X	while (*text && *text != ']')
X	{
X		/* is this a span of characters? */
X		if (text[1] == '-' && text[2])
X		{
X			/* spans can't be backwards */
X			if (text[0] > text[2])
X			{
X				FAIL("Backwards span in []");
X			}
X
X			/* add each character in the span to the bitmap */
X			for (i = text[0]; bmap && i <= text[2]; i++)
X			{
X				bmap[i >> 3] |= (1 << (i & 7));
X			}
X
X			/* move past this span */
X			text += 3;
X		}
X		else
X		{
X			/* add this single character to the span */
X			i = *text++;
X			if (bmap)
X			{
X				bmap[i >> 3] |= (1 << (i & 7));
X			}
X		}
X	}
X
X	/* make sure the closing ] is missing */
X	if (*text++ != ']')
X	{
X		FAIL("] missing");
X	}
X
X	/* if we're supposed to complement this class, then do so */
X	if (complement && bmap)
X	{
X		for (i = 0; i < 32; i++)
X		{
X			bmap[i] = ~bmap[i];
X		}
X	}
X
X	return text;
X}
X
X
X
X
X/* This function gets the next character or meta character from a string.
X * The pointer is incremented by 1, or by 2 for \-quoted characters.  For [],
X * a bitmap is generated via makeclass() (if re is given), and the
X * character-class text is skipped.
X */
Xstatic int gettoken(sptr, re)
X	char	**sptr;
X	regexp	*re;
X{
X	int	c;
X
X	c = **sptr;
X	++*sptr;
X	if (c == '\\')
X	{
X		c = **sptr;
X		++*sptr;
X		switch (c)
X		{
X		  case '<':
X			return M_BEGWORD;
X
X		  case '>':
X			return M_ENDWORD;
X
X		  case '(':
X			if (start_cnt >= NSUBEXP)
X			{
X				FAIL("Too many \\(s");
X			}
X			end_stk[end_sp++] = start_cnt;
X			return M_START(start_cnt++);
X
X		  case ')':
X			if (end_sp <= 0)
X			{
X				FAIL("Mismatched \\)");
X			}
X			return M_END(end_stk[--end_sp]);
X
X		  case '*':
X			return (*o_magic ? c : M_SPLAT);
X
X		  case '.':
X			return (*o_magic ? c : M_ANY);
X
X		  case '+':
X			return M_PLUS;
X
X		  case '?':
X			return M_QMARK;
X#ifndef CRUNCH
X		  case '{':
X			return M_RANGE;
X#endif
X		  default:
X			return c;
X		}
X	}
X	else if (*o_magic)
X	{
X		switch (c)
X		{
X		  case '^':
X			if (*sptr == retext + 1)
X			{
X				return M_BEGLINE;
X			}
X			return c;
X
X		  case '$':
X			if (!**sptr)
X			{
X				return M_ENDLINE;
X			}
X			return c;
X
X		  case '.':
X			return M_ANY;
X
X		  case '*':
X			return M_SPLAT;
X
X		  case '[':
X			/* make sure we don't have too many classes */
X			if (class_cnt >= 10)
X			{
X				FAIL("Too many []s");
X			}
X
X			/* process the character list for this class */
X			if (re)
X			{
X				/* generate the bitmap for this class */
X				*sptr = makeclass(*sptr, re->program + 1 + 32 * class_cnt);
X			}
X			else
X			{
X				/* skip to end of the class */
X				*sptr = makeclass(*sptr, (char *)0);
X			}
X			return M_CLASS(class_cnt++);
X
X		  default:
X			return c;
X		}
X	}
X	else	/* unquoted nomagic */
X	{
X		switch (c)
X		{
X		  case '^':
X			if (*sptr == retext + 1)
X			{
X				return M_BEGLINE;
X			}
X			return c;
X
X		  case '$':
X			if (!**sptr)
X			{
X				return M_ENDLINE;
X			}
X			return c;
X
X		  default:
X			return c;
X		}
X	}
X	/*NOTREACHED*/
X}
X
X
X
X
X/* This function calculates the number of bytes that will be needed for a
X * compiled RE.  Its argument is the uncompiled version.  It is not clever
X * about catching syntax errors; that is done in a later pass.
X */
Xstatic unsigned calcsize(text)
X	char		*text;
X{
X	unsigned	size;
X	int		token;
X
X	retext = text;
X	class_cnt = 0;
X	start_cnt = 1;
X	end_sp = 0;
X	size = 5;
X	while ((token = gettoken(&text, (regexp *)0)) != 0)
X	{
X		if (IS_CLASS(token))
X		{
X			size += 34;
X		}
X#ifndef CRUNCH
X		else if (token == M_RANGE)
X		{
X			size += 4;
X			while ((token = gettoken(&text, (regexp *)0)) != 0
X			    && token != '}')
X			{
X			}
X			if (!token)
X			{
X				return size;
X			}
X		}
X#endif
X		else if (IS_META(token))
X		{
X			size += 2;
X		}
X		else
X		{
X			size++;
X		}
X	}
X
X	return size;
X}
X
X
X
X/* This function compiles a regexp. */
Xregexp *regcomp(exp)
X	char		*exp;
X{
X	int		needfirst;
X	unsigned	size;
X	int		token;
X	int		peek;
X	char		*build;
X	regexp		*re;
X#ifndef CRUNCH
X	int		from;
X	int		to;
X	int		digit;
X#endif
X
X
X	/* prepare for error handling */
X	re = (regexp *)0;
X	if (setjmp(errorhandler))
X	{
X		if (re)
X		{
X			free(re);
X		}
X		return (regexp *)0;
X	}
X
X	/* if an empty regexp string was given, use the previous one */
X	if (*exp == 0)
X	{
X		if (!previous)
X		{
X			FAIL("No previous RE");
X		}
X		exp = previous;
X	}
X	else /* non-empty regexp given, so remember it */
X	{
X		if (previous)
X			free(previous);
X		previous = (char *)malloc((unsigned)(strlen(exp) + 1));
X		if (previous)
X			strcpy(previous, exp);
X	}
X
X	/* allocate memory */
X	class_cnt = 0;
X	start_cnt = 1;
X	end_sp = 0;
X	retext = exp;
X	size = calcsize(exp) + sizeof(regexp) + 10; /* !!! 10 bytes for slop */
X#ifdef lint
X	re = ((regexp *)0) + size;
X#else
X	re = (regexp *)malloc((unsigned)size);
X#endif
X	if (!re)
X	{
X		FAIL("Not enough memory for this RE");
X	}
X
X	/* compile it */
X	build = &re->program[1 + 32 * class_cnt];
X	re->program[0] = class_cnt;
X	for (token = 0; token < NSUBEXP; token++)
X	{
X		re->startp[token] = re->endp[token] = (char *)0;
X	}
X	re->first = 0;
X	re->bol = 0;
X	re->minlen = 0;
X	needfirst = 1;
X	class_cnt = 0;
X	start_cnt = 1;
X	end_sp = 0;
X	retext = exp;
X	for (token = M_START(0), peek = gettoken(&exp, re);
X	     token;
X	     token = peek, peek = gettoken(&exp, re))
X	{
X		/* special processing for the closure operator */
X		if (IS_CLOSURE(peek))
X		{
X			/* detect misuse of closure operator */
X			if (IS_START(token))
X			{
X				FAIL("Closure operator follows nothing");
X			}
X			else if (IS_META(token) && token != M_ANY && !IS_CLASS(token))
X			{
X				FAIL("Closure operators can only follow a normal character or . or []");
X			}
X
X#ifndef CRUNCH
X			/* if \{ \} then read the range */
X			if (peek == M_RANGE)
X			{
X				from = 0;
X				for (digit = gettoken(&exp, re);
X				     !IS_META(digit) && isdigit(digit);
X				     digit = gettoken(&exp, re))
X				{
X					from = from * 10 + digit - '0';
X				}
X				if (digit == '}')
X				{
X					to = from;
X				}
X				else if (digit == ',')
X				{
X					to = 0;
X					for (digit = gettoken(&exp, re);
X					     !IS_META(digit) && isdigit(digit);
X					     digit = gettoken(&exp, re))
X					{
X						to = to * 10 + digit - '0';
X					}
X					if (to == 0)
X					{
X						to = 255;
X					}
X				}
X				if (digit != '}')
X				{
X					FAIL("Bad characters after \\{");
X				}
X				else if (to < from || to == 0 || from >= 255)
X				{
X					FAIL("Invalid range for \\{ \\}");
X				}
X				re->minlen += from;
X			}
X			else
X#endif
X			if (peek != M_SPLAT)
X			{
X				re->minlen++;
X			}
X
X			/* it is okay -- make it prefix instead of postfix */
X			ADD_META(build, peek);
X#ifndef CRUNCH
X			if (peek == M_RANGE)
X			{
X				*build++ = from;
X				*build++ = (to < 255 ? to : 255);
X			}
X#endif
X			
X
X			/* take care of "needfirst" - is this the first char? */
X			if (needfirst && peek == M_PLUS && !IS_META(token))
X			{
X				re->first = token;
X			}
X			needfirst = 0;
X
X			/* we used "peek" -- need to refill it */
X			peek = gettoken(&exp, re);
X			if (IS_CLOSURE(peek))
X			{
X				FAIL("* or \\+ or \\? doubled up");
X			}
X		}
X		else if (!IS_META(token))
X		{
X			/* normal char is NOT argument of closure */
X			if (needfirst)
X			{
X				re->first = token;
X				needfirst = 0;
X			}
X			re->minlen++;
X		}
X		else if (token == M_ANY || IS_CLASS(token))
X		{
X			/* . or [] is NOT argument of closure */
X			needfirst = 0;
X			re->minlen++;
X		}
X
X		/* the "token" character is not closure -- process it normally */
X		if (token == M_BEGLINE)
X		{
X			/* set the BOL flag instead of storing M_BEGLINE */
X			re->bol = 1;
X		}
X		else if (IS_META(token))
X		{
X			ADD_META(build, token);
X		}
X		else
X		{
X			*build++ = token;
X		}
X	}
X
X	/* end it with a \) which MUST MATCH the opening \( */
X	ADD_META(build, M_END(0));
X	if (end_sp > 0)
X	{
X		FAIL("Not enough \\)s");
X	}
X
X	return re;
X}
X
X
X
X/*---------------------------------------------------------------------------*/
X
X
X/* This function checks for a match between a character and a token which is
X * known to represent a single character.  It returns 0 if they match, or
X * 1 if they don't.
X */
Xint match1(re, ch, token)
X	regexp		*re;
X	REG char	ch;
X	REG int		token;
X{
X	if (!ch)
X	{
X		/* the end of a line can't match any RE of width 1 */
X		return 1;
X	}
X	if (token == M_ANY)
X	{
X		return 0;
X	}
X	else if (IS_CLASS(token))
X	{
X		if (re->program[1 + 32 * (token - M_CLASS(0)) + (ch >> 3)] & (1 << (ch & 7)))
X			return 0;
X	}
X	else if (ch == token || *o_ignorecase && tolower(ch) == tolower(token))
X	{
X		return 0;
X	}
X	return 1;
X}
X
X
X
X/* This function checks characters up to and including the next closure, at
X * which point it does a recursive call to check the rest of it.  This function
X * returns 0 if everything matches, or 1 if something doesn't match.
X */
Xint match(re, str, prog, here)
X	regexp		*re;	/* the regular expression */
X	char		*str;	/* the string */
X	REG char	*prog;	/* a portion of re->program, an compiled RE */
X	REG char	*here;	/* a portion of str, the string to compare it to */
X{
X	REG int		token;	/* the roken pointed to by prog */
X	REG int		nmatched;/* counter, used during closure matching */ 
X	REG int		closure;/* the token denoting the type of closure */
X	int		from;	/* minimum number of matches in closure */
X	int		to;	/* maximum number of matches in closure */
X
X	for (token = GET_META(prog); !IS_CLOSURE(token); prog++, token = GET_META(prog))
X	{
X		switch (token)
X		{
X		/*case M_BEGLINE: can't happen; re->bol is used instead */
X		  case M_ENDLINE:
X			if (*here)
X				return 1;
X			break;
X
X		  case M_BEGWORD:
X			if (here != str &&
X			   (here[-1] == '_' || isalnum(here[-1])))
X				return 1;
X			break;
X
X		  case M_ENDWORD:
X			if (here[0] == '_' || isalnum(here[0]))
X				return 1;
X			break;
X
X		  case M_START(0):
X		  case M_START(1):
X		  case M_START(2):
X		  case M_START(3):
X		  case M_START(4):
X		  case M_START(5):
X		  case M_START(6):
X		  case M_START(7):
X		  case M_START(8):
X		  case M_START(9):
X			re->startp[token - M_START(0)] = (char *)here;
X			break;
X
X		  case M_END(0):
X		  case M_END(1):
X		  case M_END(2):
X		  case M_END(3):
X		  case M_END(4):
X		  case M_END(5):
X		  case M_END(6):
X		  case M_END(7):
X		  case M_END(8):
X		  case M_END(9):
X			re->endp[token - M_END(0)] = (char *)here;
X			if (token == M_END(0))
X			{
X				return 0;
X			}
X			break;
X
X		  default: /* literal, M_CLASS(n), or M_ANY */
X			if (match1(re, *here, token) != 0)
X				return 1;
X			here++;
X		}
X	}
X
X	/* C L O S U R E */
X
X	/* step 1: see what we have to match against, and move "prog" to point
X	 * to the remainder of the compiled RE.
X	 */
X	closure = token;
X	prog++;
X	switch (closure)
X	{
X	  case M_SPLAT:
X		from = 0;
X		to = strlen(str);	/* infinity */
X		break;
X
X	  case M_PLUS:
X		from = 1;
X		to = strlen(str);	/* infinity */
X		break;
X
X	  case M_QMARK:
X		from = 0;
X		to = 1;
X		break;
X
X#ifndef CRUNCH
X	  case M_RANGE:
X		from = UCHAR(*prog++);
X		to = UCHAR(*prog++);
X		if (to == 255)
X		{
X			to = strlen(str); /* infinity */
X		}
X		break;
X#endif
X	}
X	token = GET_META(prog);
X	prog++;
X
X	/* step 2: see how many times we can match that token against the string */
X	for (nmatched = 0;
X	     nmatched < to && *here && match1(re, *here, token) == 0;
X	     nmatched++, here++)
X	{
X	}
X
X	/* step 3: try to match the remainder, and back off if it doesn't */
X	while (nmatched >= from && match(re, str, prog, here) != 0)
X	{
X		nmatched--;
X		here--;
X	}
X
X	/* so how did it work out? */
X	if (nmatched >= from)
X		return 0;
X	return 1;
X}
X
X
X
X/* This function searches through a string for text that matches an RE. */
Xint regexec(re, str, bol)
X	regexp	*re;	/* the compiled regexp to search for */
X	char	*str;	/* the string to search through */
X	int	bol;	/* boolean: does str start at the beginning of a line? */
X{
X	char	*prog;	/* the entry point of re->program */
X	int	len;	/* length of the string */
X	REG char	*here;
X
X	/* if must start at the beginning of a line, and this isn't, then fail */
X	if (re->bol && !bol)
X	{
X		return 0;
X	}
X
X	len = strlen(str);
X	prog = re->program + 1 + 32 * re->program[0];
X
X	/* search for the RE in the string */
X	if (re->bol)
X	{
X		/* must occur at BOL */
X		if ((re->first
X			&& match1(re, *(char *)str, re->first))/* wrong first letter? */
X		 || len < re->minlen			/* not long enough? */
X		 || match(re, (char *)str, prog, str))	/* doesn't match? */
X			return 0;			/* THEN FAIL! */
X	}
X#ifndef CRUNCH
X	else if (!*o_ignorecase)
X	{
X		/* can occur anywhere in the line, noignorecase */
X		for (here = (char *)str;
X		     (re->first && re->first != *here)
X			|| match(re, (char *)str, prog, here);
X		     here++, len--)
X		{
X			if (len < re->minlen)
X				return 0;
X		}
X	}
X#endif
X	else
X	{
X		/* can occur anywhere in the line, ignorecase */
X		for (here = (char *)str;
X		     (re->first && match1(re, *here, (int)re->first))
X			|| match(re, (char *)str, prog, here);
X		     here++, len--)
X		{
X			if (len < re->minlen)
X				return 0;
X		}
X	}
X
X	/* if we didn't fail, then we must have succeeded */
X	return 1;
X}
X
X/*============================================================================*/
X#else /* NO_MAGIC */
X
Xregexp *regcomp(exp)
X	char	*exp;
X{
X	char	*src;
X	char	*dest;
X	regexp	*re;
X	int	i;
X
X	/* allocate a big enough regexp structure */
X#ifdef lint
X	re = (regexp *)0;
X#else
X	re = (regexp *)malloc((unsigned)(strlen(exp) + 1 + sizeof(struct regexp)));
X#endif
X	if (!re)
X	{
X		regerror("Could not malloc a regexp structure");
X		return (regexp *)0;
X	}
X
X	/* initialize all fields of the structure */
X	for (i = 0; i < NSUBEXP; i++)
X	{
X		re->startp[i] = re->endp[i] = (char *)0;
X	}
X	re->minlen = 0;
X	re->first = 0;
X	re->bol = 0;
X
X	/* copy the string into it, translating ^ and $ as needed */
X	for (src = exp, dest = re->program + 1; *src; src++)
X	{
X		switch (*src)
X		{
X		  case '^':
X			if (src == exp)
X			{
X				re->bol += 1;
X			}
X			else
X			{
X				*dest++ = '^';
X				re->minlen++;
X			}
X			break;
X
X		  case '$':
X			if (!src[1])
X			{
X				re->bol += 2;
X			}
X			else
X			{
X				*dest++ = '$';
X				re->minlen++;
X			}
X			break;
X
X		  case '\\':
X			if (src[1])
X			{
X				*dest++ = *++src;
X				re->minlen++;
X			}
X			else
X			{
X				regerror("extra \\ at end of regular expression");
X			}
X			break;
X
X		  default:
X			*dest++ = *src;
X			re->minlen++;
X		}
X	}
X	*dest = '\0';
X
X	return re;
X}
X
X
X/* This "helper" function checks for a match at a given location.  It returns
X * 1 if it matches, 0 if it doesn't match here but might match later on in the
X * string, or -1 if it could not possibly match
X */
Xstatic int reghelp(prog, string, bolflag)
X	struct regexp	*prog;
X	char		*string;
X	int		bolflag;
X{
X	char		*scan;
X	char		*str;
X
X	/* if ^, then require bolflag */
X	if ((prog->bol & 1) && !bolflag)
X	{
X		return -1;
X	}
X
X	/* if it matches, then it will start here */
X	prog->startp[0] = string;
X
X	/* compare, possibly ignoring case */
X	if (*o_ignorecase)
X	{
X		for (scan = &prog->program[1]; *scan; scan++, string++)
X			if (tolower(*scan) != tolower(*string))
X				return *string ? 0 : -1;
X	}
X	else
X	{
X		for (scan = &prog->program[1]; *scan; scan++, string++)
X			if (*scan != *string)
X				return *string ? 0 : -1;
X	}
X
X	/* if $, then require string to end here, too */
X	if ((prog->bol & 2) && *string)
X	{
X		return 0;
X	}
X
X	/* if we get to here, it matches */
X	prog->endp[0] = string;
X	return 1;
X}
X
X
X
Xint regexec(prog, string, bolflag)
X	struct regexp	*prog;
X	char		*string;
X	int		bolflag;
X{
X	int		rc;
X
X	/* keep trying to match it */
X	for (rc = reghelp(prog, string, bolflag); rc == 0; rc = reghelp(prog, string, 0))
X	{
X		string++;
X	}
X
X	/* did we match? */
X	return rc == 1;
X}
X#endif
/
echo x - regexp.h
sed '/^X/s///' > regexp.h << '/'
X/*
X * Definitions etc. for regexp(3) routines.
X *
X * Caveat:  this is V8 regexp(3) [actually, a reimplementation thereof],
X * not the System V one.
X */
X#define NSUBEXP  10
X
Xtypedef struct regexp {
X	char	*startp[NSUBEXP];
X	char	*endp[NSUBEXP];
X	int	minlen;		/* length of shortest possible match */
X	char	first;		/* first character, if known; else \0 */
X	char	bol;		/* boolean: must start at beginning of line? */
X	char	program[1];	/* Unwarranted chumminess with compiler. */
X} regexp;
X
Xextern regexp *regcomp();
Xextern int regexec();
Xextern void regsub();
Xextern void regerror();
/
echo x - regsub.c
sed '/^X/s///' > regsub.c << '/'
X/* regsub.c */
X
X/* This file contains the regsub() function, which performs substitutions
X * after a regexp match has been found.
X */
X
X#include "config.h"
X#include "ctype.h"
X#include "vi.h"
X#include "regexp.h"
X
X
X/* perform substitutions after a regexp match */
Xvoid regsub(re, src, dst)
X	regexp		*re;	/* the regexp with pointers into matched text */
X	REG char	*src;	/* the replacement string */
X	REG char	*dst;	/* where to put the result of the subst */
X{
X	REG char	*cpy;	/* pointer to start of text to copy */
X	REG char	*end;	/* pointer to end of text to copy */
X	REG char	c;
X	char		*start;
X#ifndef CRUNCH
X	int		mod = 0;/* used to track \U, \L, \u, \l, and \E */
X	int		len;	/* used to calculate length of subst string */
X	static char	*prev;	/* a copy of the text from the previous subst */
X
X	/* replace \~ (or maybe ~) by previous substitution text */
X
X	/* step 1: calculate the length of the new substitution text */
X	for (len = strlen(src), c = '\0', cpy = src; *cpy; cpy++)
X	{
X# ifdef NO_MAGIC
X		if (c == '\\' && *cpy == '~')
X# else
X		if (c == (*o_magic ? '\0' : '\\') && *cpy == '~')
X# endif
X		{
X			if (!prev)
X			{
X				regerror("No prev text to substitute for ~");
X				return;
X			}
X			len += strlen(prev) - 1;
X# ifndef NO_MAGIC
X			if (!*o_magic)
X# endif
X				len -= 1; /* because we lose the \ too */
X		}
X
X		/* watch backslash quoting */
X		if (c != '\\' && *cpy == '\\')
X			c = '\\';
X		else
X			c = '\0';
X	}
X
X	/* allocate memory for the ~ed version of src */
X	start = cpy = (char *)malloc((unsigned)(len + 1));
X	if (!cpy)
X	{
X		regerror("Not enough memory for ~ expansion");
X		return;
X	}
X
X	/* copy src into start, replacing the ~s by the previous text */
X	while (*src)
X	{
X# ifndef NO_MAGIC
X		if (*o_magic && *src == '~')
X		{
X			strcpy(cpy, prev);
X			cpy += strlen(prev);
X			src++;
X		}
X		else if (!*o_magic && *src == '\\' && *(src + 1) == '~')
X# else /* NO_MAGIC */
X		if (*src == '\\' && *(src + 1) == '~')
X# endif /* NO_MAGIC */
X		{
X			strcpy(cpy, prev);
X			cpy += strlen(prev);
X			src += 2;
X		}
X		else
X		{
X			*cpy++ = *src++;
X		}
X	}
X	*cpy = '\0';
X#ifdef DEBUG
X	if ((int)(cpy - start) != len)
X	{
X		msg("Bug in regsub.c! Predicted length = %d, Actual length = %d", len, (int)(cpy - start));
X	}
X#endif
X
X	/* remember this as the "previous" for next time */
X	if (prev)
X		free(prev);
X	prev = src = start;
X
X#endif /* undef CRUNCH */
X
X	start = src;
X	while ((c = *src++) != '\0')
X	{
X#ifndef NO_MAGIC
X		/* recognize any meta characters */
X		if (c == '&' && *o_magic)
X		{
X			cpy = re->startp[0];
X			end = re->endp[0];
X		}
X		else
X#endif /* not NO_MAGIC */
X		if (c == '\\')
X		{
X			c = *src++;
X			switch (c)
X			{
X#ifndef NO_MAGIC
X			  case '0':
X			  case '1':
X			  case '2':
X			  case '3':
X			  case '4':
X			  case '5':
X			  case '6':
X			  case '7':
X			  case '8':
X			  case '9':
X				/* \0 thru \9 mean "copy subexpression" */
X				c -= '0';
X				cpy = re->startp[c];
X				end = re->endp[c];
X				break;
X# ifndef CRUNCH
X			  case 'U':
X			  case 'u':
X			  case 'L':
X			  case 'l':
X				/* \U and \L mean "convert to upper/lowercase" */
X				mod = c;
X				continue;
X
X			  case 'E':
X			  case 'e':
X				/* \E ends the \U or \L */
X				mod = 0;
X				continue;
X# endif /* not CRUNCH */
X			  case '&':
X				/* "\&" means "original text" */
X				if (*o_magic)
X				{
X					*dst++ = c;
X					continue;
X				}
X				cpy = re->startp[0];
X				end = re->endp[0];
X				break;
X
X#else /* NO_MAGIC */
X			  case '&':
X				/* "\&" means "original text" */
X				cpy = re->startp[0];
X				end = re->endp[0];
X				break;
X#endif /* NO_MAGIC */
X			  default:
X				/* ordinary char preceded by backslash */
X				*dst++ = c;
X				continue;
X			}
X		}
X#ifndef CRUNCH
X# if OSK
X		else if (c == '\l')
X# else
X		else if (c == '\r')
X# endif
X		{
X			/* transliterate ^M into newline */
X			*dst++ = '\n';
X			continue;
X		}
X#endif /* !CRUNCH */
X		else
X		{
X			/* ordinary character, so just copy it */
X			*dst++ = c;
X			continue;
X		}
X
X		/* Note: to reach this point in the code, we must have evaded
X		 * all "continue" statements.  To do that, we must have hit
X		 * a metacharacter that involves copying.
X		 */
X
X		/* if there is nothing to copy, loop */
X		if (!cpy)
X			continue;
X
X		/* copy over a portion of the original */
X		while (cpy < end)
X		{
X#ifndef NO_MAGIC
X# ifndef CRUNCH
X			switch (mod)
X			{
X			  case 'U':
X			  case 'u':
X				/* convert to uppercase */
X				*dst++ = toupper(*cpy++);
X				break;
X
X			  case 'L':
X			  case 'l':
X				/* convert to lowercase */
X				*dst++ = tolower(*cpy++);
X				break;
X
X			  default:
X				/* copy without any conversion */
X				*dst++ = *cpy++;
X			}
X
X			/* \u and \l end automatically after the first char */
X			if (mod && (mod == 'u' || mod == 'l'))
X			{
X				mod = 0;
X			}
X# else /* CRUNCH */
X			*dst++ = *cpy++;
X# endif /* CRUNCH */
X#else /* NO_MAGIC */
X			*dst++ = *cpy++;
X#endif /* NO_MAGIC */
X		}
X	}
X	*dst = '\0';
X}
/
echo x - system.c
sed '/^X/s///' > system.c << '/'
X/* system.c  -- UNIX version */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains a new version of the system() function and related stuff.
X *
X * Entry points are:
X *	system(cmd)		- run a single shell command
X *	wildcard(names)		- expand wildcard characters in filanames
X *	filter(m,n,cmd,back)	- run text lines through a filter program
X *
X * This is probably the single least portable file in the program.  The code
X * shown here should work correctly if it links at all; it will work on UNIX
X * and any O.S./Compiler combination which adheres to UNIX forking conventions.
X */
X
X#include "config.h"
X#include "vi.h"
Xextern char	**environ;
X
X#if ANY_UNIX
X
X/* This is a new version of the system() function.  The only difference
X * between this one and the library one is: this one uses the o_shell option.
X */
Xint system(cmd)
X	char	*cmd;	/* a command to run */
X{
X	int	pid;	/* process ID of child */
X	int	died;
X	int	status;	/* exit status of the command */
X
X
X	signal(SIGINT, SIG_IGN);
X	pid = fork();
X	switch (pid)
X	{
X	  case -1:						/* error */
X		msg("fork() failed");
X		status = -1;
X		break;
X
X	  case 0:						/* child */
X		/* for the child, close all files except stdin/out/err */
X		for (status = 3; status < 60 && (close(status), errno != EINVAL); status++)
X		{
X		}
X
X		signal(SIGINT, SIG_DFL);
X		if (cmd == o_shell)
X		{
X			execle(o_shell, o_shell, (char *)0, environ);
X		}
X		else
X		{
X			execle(o_shell, o_shell, "-c", cmd, (char *)0, environ);
X		}
X		msg("execle(\"%s\", ...) failed", o_shell);
X		exit(1); /* if we get here, the exec failed */
X
X	  default:						/* parent */
X		do
X		{
X			died = wait(&status);
X		} while (died >= 0 && died != pid);
X		if (died < 0)
X		{
X			status = -1;
X		}
X#if __GNUC__ || _ANSI
X		signal(SIGINT, (void (*)()) trapint);
X#else
X		signal(SIGINT, trapint);
X#endif
X	}
X
X	return status;
X}
X
X/* This private function opens a pipe from a filter.  It is similar to the
X * system() function above, and to popen(cmd, "r").
X */
Xint rpipe(cmd, in)
X	char	*cmd;	/* the filter command to use */
X	int	in;	/* the fd to use for stdin */
X{
X	int	r0w1[2];/* the pipe fd's */
X
X	/* make the pipe */
X	if (pipe(r0w1) < 0)
X	{
X		return -1;	/* pipe failed */
X	}
X
X	/* The parent process (elvis) ignores signals while the filter runs.
X	 * The child (the filter program) will reset this, so that it can
X	 * catch the signal.
X	 */
X	signal(SIGINT, SIG_IGN);
X
X	switch (fork())
X	{
X	  case -1:						/* error */
X		return -1;
X
X	  case 0:						/* child */
X		/* close the "read" end of the pipe */
X		close(r0w1[0]);
X
X		/* redirect stdout to go to the "write" end of the pipe */
X		close(1);
X		dup(r0w1[1]);
X		close(2);
X		dup(r0w1[1]);
X		close(r0w1[1]);
X
X		/* redirect stdin */
X		if (in != 0)
X		{
X			close(0);
X			dup(in);
X			close(in);
X		}
X
X		/* the filter should accept SIGINT signals */
X		signal(SIGINT, SIG_DFL);
X
X		/* exec the shell to run the command */
X		execle(o_shell, o_shell, "-c", cmd, (char *)0, environ);
X		exit(1); /* if we get here, exec failed */
X
X	  default:						/* parent */
X		/* close the "write" end of the pipe */	
X		close(r0w1[1]);
X
X		return r0w1[0];
X	}
X}
X
X#endif /* non-DOS */
X
X#if OSK
X
X/* This private function opens a pipe from a filter.  It is similar to the
X * system() function above, and to popen(cmd, "r").
X */
Xint rpipe(cmd, in)
X	char	*cmd;	/* the filter command to use */
X	int	in;	/* the fd to use for stdin */
X{
X	return osk_popen(cmd, "r", in, 0);
X}	
X#endif
X
X#if ANY_UNIX || OSK
X
X/* This function closes the pipe opened by rpipe(), and returns 0 for success */
Xint rpclose(fd)
X	int	fd;
X{
X	int	status;
X
X	close(fd);
X	wait(&status);
X#if __GNUC__ || _ANSI
X	signal(SIGINT, (void (*)()) trapint);
X#else
X	signal(SIGINT, trapint);
X#endif
X	return status;
X}
X
X#endif /* non-DOS */
X
X/* This function expands wildcards in a filename or filenames.  It does this
X * by running the "echo" command on the filenames via the shell; it is assumed
X * that the shell will expand the names for you.  If for any reason it can't
X * run echo, then it returns the names unmodified.
X */
X
X#if MSDOS || TOS
X#define	PROG	"wildcard "
X#define	PROGLEN	9
X#include <string.h>
X#else
X#define	PROG	"echo "
X#define	PROGLEN	5
X#endif
X
X#if !AMIGA
Xchar *wildcard(names)
X	char	*names;
X{
X
X# if VMS
X/* 
X   We could use expand() [vmswild.c], but what's the point on VMS? 
X   Anyway, echo is the wrong thing to do, it takes too long to build
X   a subprocess on VMS and any "echo" program would have to be supplied
X   by elvis.  More importantly, many VMS utilities expand names 
X   themselves (the shell doesn't do any expansion) so the concept is
X   non-native.  jdc
X*/
X	return names;
X# else
X
X	int	i, j, fd;
X	REG char *s, *d;
X
X
X	/* build the echo command */
X	if (names != tmpblk.c)
X	{
X		/* the names aren't in tmpblk.c, so we can do it the easy way */
X		strcpy(tmpblk.c, PROG);
X		strcat(tmpblk.c, names);
X	}
X	else
X	{
X		/* the names are already in tmpblk.c, so shift them to make
X		 * room for the word "echo "
X		 */
X		for (s = names + strlen(names) + 1, d = s + PROGLEN; s > names; )
X		{
X			*--d = *--s;
X		}
X		strncpy(names, PROG, PROGLEN);
X	}
X
X	/* run the command & read the resulting names */
X	fd = rpipe(tmpblk.c, 0);
X	if (fd < 0) return names;
X	i = 0;
X	do
X	{
X		j = tread(fd, tmpblk.c + i, BLKSIZE - i);
X		i += j;
X	} while (j > 0);
X
X	/* successful? */
X	if (rpclose(fd) == 0 && j == 0 && i < BLKSIZE && i > 0)
X	{
X		tmpblk.c[i-1] = '\0'; /* "i-1" so we clip off the newline */
X		return tmpblk.c;
X	}
X	else
X	{
X		return names;
X	}
X# endif
X}
X#endif
X
X/* This function runs a range of lines through a filter program, and replaces
X * the original text with the filtered version.  As a special case, if "to"
X * is MARK_UNSET, then it runs the filter program with stdin coming from
X * /dev/null, and inserts any output lines.
X */
Xint filter(from, to, cmd, back)
X	MARK	from, to;	/* the range of lines to filter */
X	char	*cmd;		/* the filter command */
X	int	back;		/* boolean: will we read lines back? */
X{
X	int	scratch;	/* fd of the scratch file */
X	int	fd;		/* fd of the pipe from the filter */
X	char	scrout[50];	/* name of the scratch out file */
X	MARK	new;		/* place where new text should go */
X	long	sent, rcvd;	/* number of lines sent/received */
X	int	i, j;
X
X	/* write the lines (if specified) to a temp file */
X	if (to)
X	{
X		/* we have lines */
X#if MSDOS || TOS
X		strcpy(scrout, o_directory);
X		if ((i=strlen(scrout)) && !strchr("\\/:", scrout[i-1]))
X			scrout[i++]=SLASH;
X		strcpy(scrout+i, SCRATCHOUT+3);
X#else
X		sprintf(scrout, SCRATCHOUT, o_directory);
X#endif
X		mktemp(scrout);
X		cmd_write(from, to, CMD_BANG, FALSE, scrout);
X		sent = markline(to) - markline(from) + 1L;
X
X		/* use those lines as stdin */
X		scratch = open(scrout, O_RDONLY);
X		if (scratch < 0)
X		{
X			unlink(scrout);
X			return -1;
X		}
X	}
X	else
X	{
X		scratch = 0;
X		sent = 0L;
X	}
X
X	/* start the filter program */
X#if VMS
X	/* 
X	   VMS doesn't know a thing about file descriptor 0.  The rpipe
X	   concept is non-portable.  Hence we need a file name argument.
X	*/
X	fd = rpipe(cmd, scratch, scrout);
X#else
X	fd = rpipe(cmd, scratch);
X#endif
X	if (fd < 0)
X	{
X		if (to)
X		{
X			close(scratch);
X			unlink(scrout);
X		}
X		return -1;
X	}
X
X	if (back)
X	{
X		ChangeText
X		{
X			/* adjust MARKs for whole lines, and set "new" */
X			from &= ~(BLKSIZE - 1);
X			if (to)
X			{
X				to &= ~(BLKSIZE - 1);
X				to += BLKSIZE;
X				new = to;
X			}
X			else
X			{
X				new = from + BLKSIZE;
X			}
X
X#if VMS
X/* Reading from a VMS mailbox (pipe) is record oriented... */
X# define tread vms_pread
X#endif
X
X			/* repeatedly read in new text and add it */
X			rcvd = 0L;
X			while ((i = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
X			{
X				tmpblk.c[i] = '\0';
X				add(new, tmpblk.c);
X#if VMS
X				/* What!  An advantage to record oriented reads? */
X				new += (i - 1);
X				new = (new & ~(BLKSIZE - 1)) + BLKSIZE;
X				rcvd++;
X#else
X				for (i = 0; tmpblk.c[i]; i++)
X				{
X					if (tmpblk.c[i] == '\n')
X					{
X						new = (new & ~(BLKSIZE - 1)) + BLKSIZE;
X						rcvd++;
X					}
X					else
X					{
X						new++;
X					}
X				}
X#endif
X			}
X		}
X
X		/* delete old text, if any */
X		if (to)
X		{
X			cut(from, to);
X			delete(from, to);
X		}
X	}
X	else
X	{
X		/* read the command's output, and copy it to the screen */
X		while ((i = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
X		{
X			for (j = 0; j < i; j++)
X			{
X				addch(tmpblk.c[j]);
X			}
X		}
X		rcvd = 0;
X	}
X
X	/* Reporting... */
X	if (sent >= *o_report || rcvd >= *o_report)
X	{
X		if (sent > 0L && rcvd > 0L)
X		{
X			msg("%ld lines out, %ld lines back", sent, rcvd);
X		}
X		else if (sent > 0)
X		{
X			msg("%ld lines written to filter", sent);
X		}
X		else
X		{
X			msg("%ld lines read from filter", rcvd);
X		}
X	}
X	rptlines = 0L;
X
X	/* cleanup */
X	rpclose(fd);
X	if (to)
X	{
X		close(scratch);
X		unlink(scrout);
X	}
X	return 0;
X}
/
echo x - tinyprnt.c
sed '/^X/s///' > tinyprnt.c << '/'
X/* tinyprnt.c */
X
X#if OSK
X#define sprintf Sprintf
X#endif
X
X/* This is a limited version of sprintf().  It is useful for Minix-PC and
X * Coherent-286 because those systems are both limited to 64k+64k and the
X * standard sprintf() is just too damn big.
X *
X * It should also be useful for OS-9 because OS-9's sprintf() doesn't
X * understand the true meaning of asterisks in a format string.  This one
X * does.
X */
X
X/* Place-holders in format strings look like "%<pad><clip><type>".
X *
X * The <pad> adds space to the front (or, if negative, to the back) of the
X * output value, to pad it to a given width.  If <pad> is absent, then 0 is
X * assumed.  If <pad> is an asterisk, then the next argument is assumed to
X * be an (int) which used as the pad width.
X *
X * The <clip> string can be absent, in which case no clipping is done.
X * However, if it is present, then it should be either a "." followed by
X * a number, or a "." followed by an asterisk.  The asterisk means that the
X * next argument is an (int) which should be used as the pad width.  Clipping
X * only affects strings; for other data types it is ignored.
X *
X * The <type> is one of "s" for strings, "c" for characters (really ints that
X * are assumed to be legal char values), "d" for ints, "ld" for long ints, or
X * "%" to output a percent sign.
X */
X
X/* NOTE: Variable argument lists are handled by direct stack-twiddling. Sorry! */
X
Xstatic void cvtnum(buf, num, base)
X	char		*buf;	/* where to store the number */
X	unsigned long	num;	/* the number to convert */
X	int		base;	/* either 8, 10, or 16 */
X{
X	static char	digits[] = "0123456789abcdef";
X	unsigned long	tmp;
X
X	/* if the number is 0, then just stuff a "0" into the buffer */
X	if (num == 0L)
X	{
X		buf[0] = '0';
X		buf[1] = '\0';
X		return;
X	}
X
X	/* use tmp to figure out how many digits we'll need */
X	for (tmp = num; tmp > 0; tmp /= base)
X	{
X		buf++;
X	}
X
X	/* mark the spot that will be the end of the string */
X	*buf = '\0';
X
X	/* generate all digits, as needed */
X	for (tmp = num; tmp > 0; tmp /= base)
X	{
X		*--buf = digits[tmp % base];
X	}
X}
X
Xint sprintf(buf, fmt, argref)
X	char	*buf;	/* where to deposit the formatted output */
X	char	*fmt;	/* the format string */
X	int	argref;	/* the first argument is located at &argref */
X{
X	char	*argptr;/* pointer to next argument on the stack */
X	int	pad;	/* value of the pad string */
X	int	clip;	/* value of the clip string */
X	long	num;	/* a binary number being converted to ASCII digits */
X	long	digit;	/* used during conversion */
X	char	*src, *dst;
X
X	/* make argptr point to the first argument after the format string */
X	argptr = (char *)&argref;
X
X	/* loop through the whole format string */
X	while (*fmt)
X	{
X		/* if not part of a place-holder, then copy it literally */
X		if (*fmt != '%')
X		{
X			*buf++ = *fmt++;
X			continue;
X		}
X
X		/* found a place-holder!  Get <pad> value */
X		fmt++;
X		if ('*' == *fmt)
X		{
X			pad = *((int *)argptr)++;
X			fmt++;
X		}
X		else if (*fmt == '-' || (*fmt >= '0' && *fmt <= '9'))
X		{
X			pad = atol(fmt);
X			do
X			{
X				fmt++;
X			} while (*fmt >= '0' && *fmt <= '9');
X		}
X		else
X		{
X			pad = 0;
X		}
X
X		/* get a <clip> value */
X		if (*fmt == '.')
X		{
X			fmt++;
X			if ('*' == *fmt)
X			{
X				clip = *((int *)argptr)++;
X				fmt++;
X			}
X			else if (*fmt >= '0' && *fmt <= '9')
X			{
X				clip = atol(fmt);
X				do
X				{
X					fmt++;
X				} while (*fmt >= '0' && *fmt <= '9');
X			}
X		}
X		else
X		{
X			clip = 0;
X		}
X
X		/* handle <type>, possibly noticing <clip> */
X		switch (*fmt++)
X		{
X		  case 'c':
X			buf[0] = *((int *)argptr)++;
X			buf[1] = '\0';
X			break;
X
X		  case 's':
X			src = *((char **)argptr)++;
X			if (!src)
X			{
X				src = "(null)";
X			}
X			if (clip)
X			{
X				strncpy(buf, src, clip);
X				buf[clip] = '\0';
X			}
X			else
X			{
X				strcpy(buf, src);
X			}
X			break;
X
X		  case 'l':
X			fmt++; /* to skip the "d" in "%ld" */
X			num = *((long *)argptr)++;
X			dst = buf;
X			if (num < 0)
X			{
X				*dst++ = '-';
X				num = -num;
X			}
X			cvtnum(dst, num, 10);
X			break;
X
X		  case 'x':
X			num = *((int *)argptr)++;
X			cvtnum(buf, num, 16);
X			break;
X
X		  case 'd':
X			num = *((int *)argptr)++;
X			dst = buf;
X			if (num < 0)
X			{
X				*dst++ = '-';
X				num = -num;
X			}
X			cvtnum(dst, num, 10);
X			break;
X
X		  default:
X			buf[0] = fmt[-1];
X			buf[1] = '\0';
X		}
X
X		/* now fix the padding, if the value is too short */
X		clip = strlen(buf);
X		if (pad < 0)
X		{
X			/* add spaces after the value */
X			pad = -pad - clip;
X			for (buf += clip; pad > 0; pad--)
X			{
X				*buf++ = ' ';
X			}
X			*buf = '\0';
X		}
X		else
X		{
X			/* add spaces before the value */
X			pad -= clip;
X			if (pad > 0)
X			{
X				src = buf + clip;
X				dst = src + pad;
X				*dst = '\0';
X				while (src > buf)
X				{
X					*--dst = *--src;
X				}
X				while (dst > buf)
X				{
X					*--dst = ' ';
X				}
X			}
X			buf += strlen(buf);
X		}
X	}
X
X	/* mark the end of the output string */
X	*buf = '\0';
X}
/
echo x - tinytcap.c
sed '/^X/s///' > tinytcap.c << '/'
X/* tinytcap.c */
X
X/* This file contains functions which simulate the termcap functions.
X *
X * It doesn't access a "termcap" file.  Instead, it uses an initialized array
X * of strings to store the entries.  Any string that doesn't start with a ':'
X * is taken to be the name of a type of terminal.  Any string that does start
X * with a ':' is interpretted as the list of fields describing all of the
X * terminal types that precede it.
X *
X * Note: since these are C strings, you can't use special sequences like
X * ^M or \E in the fields; your C compiler won't understand them.  Also,
X * at run time there is no way to tell the difference between ':' and '\072'
X * so I sure hope your terminal definition doesn't require a ':' character.
X *
X * getenv(TERM) on VMS checks the SET TERM device setting.  To implement
X * non-standard terminals set the logical ELVIS_TERM in VMS. (jdc)
X *
X * Other possible terminal types are...
X * 	TERM_WYSE925	- "wyse925", a Wyse 50 terminal emulating Televideo 925
X * ... or you could set $TERMCAP to the terminal's description string, which
X * $TERM set up to match it.
X *
X * Note that you can include several terminal types at the same time.  Elvis
X * chooses which entry to use at runtime, based primarily on the value of $TERM.
X */
X
X
X#include "config.h"
Xextern char *getenv();
X
X/* decide which terminal descriptions should *always* be included. */
X#if MSDOS
X# define	TERM_NANSI
X# define	TERM_DOSANSI
X# if RAINBOW
X#  define	TERM_RAINBOW
X# endif
X#endif
X
X#if VMS
X# define	TERM_VT100
X# define	TERM_VT100W
X# define	TERM_VT52
X#endif
X
X#if AMIGA
X# define	TERM_AMIGA	/* Internal Amiga termcap entry */
X/* # define	TERM_VT52	/* The rest of these are here for those */
X# define	TERM_VT100	/* people who want to use elvis over an */
X/* # define	TERM_NANSI	/* AUX: port (serial.device). */
X/* # define	TERM_DOSANSI	/* Take out all but AMIGA to save memory. */
X/* # define	TERM_MINIX	/* Vanilla ANSI? */
X/* # define	TERM_925	/* Hang a terminal off your Amiga */
X#endif
X
X#if MINIX || UNIXV
X# define	TERM_MINIX
X#endif
X
X#if COHERENT
X# define	TERM_COHERENT
X#endif
X
X#if TOS
X# define	TERM_ATARI
X#endif
X
Xstatic char *termcap[] =
X{
X#ifdef TERM_AMIGA
X"AA",
X"amiga",
X"Amiga ANSI",
X/* Amiga termcap modified from version 1.3 by Kent Polk */
X":co#80:li#24:am:bs:bw:xn:\
X:AL=\233%dL:DC=\233%dP:DL=\233%dM:DO=\233%dB:\
X:LE=\233%dD:RI=\233%dC:SF=\233%dS:SR=\233%dT:UP=\233%dA:IC=\233%d@:\
X:ae=\2330m:al=\233L:as=\2333m:bl=\007:bt=\233Z:cd=\233J:\
X:ce=\233K:cl=\013:cm=\233%i%d;%dH:dc=\233P:dl=\233M:do=\233B:\
X:kb=^H:ho=\233H:ic=\233@:is=\23320l:\
X:mb=\2337;2m:md=\2331m:me=\2330m:mh=\2332m:mk=\2338m:mr=\2337m:nd=\233C:\
X:rs=\033c:se=\2330m:sf=\233S:so=\2337m:sb=\233T:sr=\233T:ue=\23323m:\
X:up=\233A:us=\2334m:vb=\007:ve=\233\040p:vi=\2330\040p:\
X:k1=\2330~:k2=\2331~:k3=\2332~:k4=\2333~:k5=\2334~:\
X:k6=\2335~:k7=\2336~:k8=\2337~:k9=\2338~:k0=\2339~:\
X:s1=\23310~:s2=\23311~:s3=\23312~:s4=\23313~:s5=\23314~:\
X:s6=\23315~:s7=\23316~:s8=\23317~:s9=\23318~:s0=\23319~:\
X:kd=\233B:kl=\233D:kn#10:kr=\233C:ku=\233A:le=\233D:\
X:kP=\233T:kN=\233S:kh=\233\040A:kH=\233\040@:",
X#endif
X
X#ifdef TERM_NANSI
X"fansi",
X"nnansi",
X"nansi",
X"pcbios",
X":al=\033[L:dl=\033[M:am:bs:ce=\033[K:cl=\033[2J:\
X:cm=\033[%i%d;%dH:co#80:do=\033[B:\
X:k1=#;:k2=#<:k3=#=:k4=#>:k5=#?:k6=#@:k7=#A:k8=#B:k9=#C:k0=#D:\
X:s1=#T:s2=#U:s3=#V:s4=#W:s5=#X:s6=#Y:s7=#Z:s8=#[:s9=#\\:s0=#]:\
X:c1=#^:c2=#_:c3=#`:c4=#a:c5=#b:c6=#c:c7=#d:c8=#e:c9=#f:c0=#g:\
X:a1=#h:a2=#i:a3=#j:a4=#k:a5=#l:a6=#m:a7=#n:a8=#o:a9=#p:a0=#q:\
X:kd=#P:kh=#G:kH=#O:kI=#R:kl=#K:kN=#Q:kP=#I:kr=#M:ku=#H:\
X:li#25:md=\033[1m:me=\033[m:nd=\033[C:se=\033[m:so=\033[7m:\
X:ue=\033[m:up=\033[A:us=\033[4m:",
X#endif
X
X#ifdef TERM_DOSANSI
X#if !ANY_UNIX
X"ansi",
X#endif
X"dosansi",
X":am:bs:ce=\033[K:cl=\033[2J:\
X:cm=\033[%i%d;%dH:co#80:do=\033[B:\
X:k1=#;:k2=#<:k3=#=:k4=#>:k5=#?:k6=#@:k7=#A:k8=#B:k9=#C:k0=#D:\
X:s1=#T:s2=#U:s3=#V:s4=#W:s5=#X:s6=#Y:s7=#Z:s8=#[:s9=#\\:s0=#]:\
X:c1=#^:c2=#_:c3=#`:c4=#a:c5=#b:c6=#c:c7=#d:c8=#e:c9=#f:c0=#g:\
X:a1=#h:a2=#i:a3=#j:a4=#k:a5=#l:a6=#m:a7=#n:a8=#o:a9=#p:a0=#q:\
X:kd=#P:kh=#G:kH=#O:kI=#R:kl=#K:kN=#Q:kP=#I:kr=#M:ku=#H:\
X:li#25:md=\033[1m:me=\033[m:nd=\033[C:se=\033[m:so=\033[7m:\
X:ue=\033[m:up=\033[A:us=\033[4m:",
X#endif
X
X#ifdef TERM_RAINBOW
X"vt220",
X"rainbow",
X":al=\033[L:dl=\033[M:am:bs:ce=\033[K:cl=\033[2J:\
X:cm=\033[%i%d;%dH:co#80:do=\033[B:kd=\033[B:kl=\033[D:\
X:kr=\033[C:ku=\033[A:kP=\033[5~:kN=\033[6~:kI=\033[2~:\
X:li#24:md=\033[1m:me=\033[m:nd=\033[C:se=\033[m:so=\033[7m:\
X:ue=\033[m:up=\033[A:us=\033[4m:xn:",
X#endif
X
X#ifdef TERM_VT100
X"vt100-80",
X"vt200-80",
X"vt300-80",
X"vt101-80",
X"vt102-80",
X":al=\033[L:am:bs:ce=\033[K:cl=\033[2J:cm=\033[%i%d;%dH:\
X:co#80:dl=\033[M:do=\033[B:k0=\033[20~:k1=\033[1~:\
X:k2=\033[2~:k3=\033[3~:k4=\033[4~:k5=\033[5~:k6=\033[6~:\
X:k7=\033[17~:k8=\033[18~:k9=\033[19~:kd=\033[B:kh=\033[H:\
X:kH=\033[Y:kI=\033[I:kl=\033[D:kN=\033[U:kP=\033[V:\
X:kr=\033[C:ku=\033[A:li#24:md=\033[1m:me=\033[m:nd=\033[C:\
X:se=\033[m:so=\033[7m:ti=\033[1;24r\033[24;1H:\
X:ue=\033[m:up=\033[A:us=\033[4m:xn:",
X#endif
X
X#ifdef TERM_VT100W
X"vt100-w",
X"vt200-w",
X"vt300-w",
X"vt101-w",
X"vt102-w",
X"vt100-132",
X"vt200-132",
X"vt300-132",
X"vt101-132",
X"vt102-132",
X":al=\033[L:am:bs:ce=\033[K:cl=\033[2J:cm=\033[%i%d;%dH:\
X:co#132:dl=\033[M:do=\033[B:k0=\033[20~:k1=\033[1~:\
X:k2=\033[2~:k3=\033[3~:k4=\033[4~:k5=\033[5~:k6=\033[6~:\
X:k7=\033[17~:k8=\033[18~:k9=\033[19~:kd=\033[B:kh=\033[H:\
X:kH=\033[Y:kI=\033[I:kl=\033[D:kN=\033[U:kP=\033[V:\
X:kr=\033[C:ku=\033[A:li#24:md=\033[1m:me=\033[m:nd=\033[C:\
X:se=\033[m:so=\033[7m:ti=\033[1;24r\033[24;1H:\
X:ue=\033[m:up=\033[A:us=\033[4m:xn:",
X#endif
X
X#ifdef TERM_VT52
X"vt52",
X":do=\n:le=\b:up=\033A:nd=\033C:cm=\033Y%+ %+ :ti=\033e\033v:\
X:sr=\033I:cd=\033J:ce=\033K:cl=\033H\033J:co#80:li#24:\
X:ku=\033A:kd=\033B:kr=\033C:kl=\033D:kb=\b:pt:am:xn:bs:",
X#endif
X
X#ifdef TERM_MINIX
X"minix",
X"ansi",
X"AT386",
X":al=\033[L:am:bs:ce=\033[K:cl=\033[2J:cm=\033[%i%d;%dH:\
X:co#80:dl=\033[M:do=\033[B:k0=\033[20~:k1=\033[1~:\
X:k2=\033[2~:k3=\033[3~:k4=\033[4~:k5=\033[5~:k6=\033[6~:\
X:k7=\033[17~:k8=\033[18~:k9=\033[19~:kd=\033[B:kh=\033[H:\
X:kH=\033[Y:kI=\033[I:kl=\033[D:kN=\033[U:kP=\033[V:\
X:kr=\033[C:ku=\033[A:li#25:md=\033[1m:me=\033[m:nd=\033[C:\
X:se=\033[m:so=\033[7m:ue=\033[m:up=\033[A:us=\033[4m:",
X#endif /* MINIX */
X
X#ifdef TERM_COHERENT
X"coherent",
X"ansipc",
X":al=\033[L:am:bs:ce=\033[K:cl=\033[2J:cm=\033[%i%d;%dH:\
X:co#80:dl=\033[M:do=\033[B:k0=\033[0x:k1=\033[1x:k2=\033[2x:\
X:k3=\033[3x:k4=\033[4x:k5=\033[5x:k6=\033[6x:\
X:k7=\033[7x:k8=\033[8x:k9=\033[9x:kd=\033[B:kh=\033[H:\
X:kH=\033[24H:kI=\033[@:kl=\033[D:kN=\033[U:kP=\033[V:\
X:kr=\033[C:ku=\033[A:li#24:md=\033[1m:me=\033[m:\
X:nd=\033[C:se=\033[m:so=\033[7m:ue=\033[m:up=\033[A:\
X:us=\033[4m:",
X#endif /* COHERENT */
X
X#ifdef TERM_ATARI
X"atari-st",
X"vt52",
X":al=\033L:am:bs:ce=\033K:cl=\033E:cm=\033Y%i%+ %+ :\
X:co#80:dl=\033M:do=\033B:\
X:k1=#;:k2=#<:k3=#=:k4=#>:k5=#?:k6=#@:k7=#A:k8=#B:k9=#C:k0=#D:\
X:s1=#T:s2=#U:s3=#V:s4=#W:s5=#X:s6=#Y:s7=#Z:s8=#[:s9=#\\:s0=#]:\
X:c1=#^:c2=#_:c3=#`:c4=#a:c5=#b:c6=#c:c7=#d:c8=#e:c9=#f:c0=#g:\
X:a1=#h:a2=#i:a3=#j:a4=#k:a5=#l:a6=#m:a7=#n:a8=#o:a9=#p:a0=#q:\
Xkd=#P:kh=#G:kI=#R:kl=#K:kr=#M:ku=#H:li#25:nd=\033C:se=\033q:\
X:so=\033p:te=:ti=\033e\033v:up=\033A:",
X#endif
X
X#ifdef TERM_925
X"wyse925",
X":xn@:\
X:hs:am:bs:co#80:li#24:cm=\033=%+ %+ :cl=\033*:cd=\033y:\
X:ce=\033t:is=\033l\033\":\
X:al=\033E:dl=\033R:im=:ei=:ic=\033Q:dc=\033W:\
X:ho=\036:nd=\014:bt=\033I:pt:so=\033G4:se=\033G0:sg#1:us=\033G8:ue=\033G0:ug#1:\
X:up=\013:do=\026:kb=\010:ku=\013:kd=\026:kl=\010:kr=\014:\
X:kh=\036:ma=\026\012\014 :\
X:k1=\001@\r:k2=\001A\r:k3=\001B\r:k4=\001C\r:k5=\001D\r:k6=\001E\r:k7=\001F\r:\
X:k8=\001G\r:k9=\001H\r:k0=\001I\r:ko=ic,dc,al,dl,cl,ce,cd,bt:\
X:ts=\033f:fs=\033g:ds=\033h:sr=\033j:",  /* was :xn: for tvi925 alone*/
X#endif
X
X(char *)0
X};
X
X
Xstatic char *fields;
X
X
X/*ARGSUSED*/
Xint tgetent(bp, name)
X	char	*bp;	/* buffer for storing the entry -- ignored */
X	char	*name;	/* name of the entry */
X{
X	int	i;
X
X	/* if TERMCAP is defined, and seems to match, then use it */
X	fields = getenv("TERMCAP");
X	if (fields)
X	{
X		for (i = 0; fields[i] && fields[i] != ':'; i++)
X		{
X			if (!strncmp(fields + i, name, strlen(name)))
X			{
X				return 1;
X			}
X		}
X	}
X
X	/* locate the entry in termcap[] */
X	for (i = 0; termcap[i] && strcmp(termcap[i], name); i++)
X	{
X	}
X	if (!termcap[i])
X	{
X		return 0;
X	}
X
X	/* search forward for fields */
X	while (termcap[i][0] != ':')
X	{
X		i++;
X	}
X	fields = termcap[i];
X	return 1;
X}
X
X
Xstatic char *find(id, vtype)
X	char	*id;	/* name of a value to locate */
X	int	vtype;	/* '=' for strings, '#' for numbers, or 0 for bools */
X{
X	int	i;
X
X	/* search for a ':' followed by the two-letter id */
X	for (i = 0; fields[i]; i++)
X	{
X		if (fields[i] == ':'
X		 && fields[i + 1] == id[0]
X		 && fields[i + 2] == id[1])
X		{
X			/* if correct type, then return its value */
X			if (fields[i + 3] == vtype)
X				return &fields[i + 4];
X			else
X				return (char *)0;
X		}
X	}
X	return (char *)0;
X}
X
Xint tgetnum(id)
X	char	*id;
X{
X	id = find(id, '#');
X	if (id)
X	{
X		return atoi(id);
X	}
X	return -1;
X}
X
Xint tgetflag(id)
X	char	*id;
X{
X	if (find(id, ':'))
X	{
X		return 1;
X	}
X	return 0;
X}
X
X/*ARGSUSED*/
Xchar *tgetstr(id, bp)
X	char	*id;
X	char	**bp;	/* pointer to pointer to buffer - ignored */
X{
X	char	*cpy;
X
X	/* find the string */
X	id = find(id, '=');
X	if (!id)
X	{
X		return (char *)0;
X	}
X
X	/* copy it into the buffer, and terminate it with NUL */
X	for (cpy = *bp; *id != ':'; )
X	{
X		if (id[0] == '\\' && id[1] == 'E')
X			*cpy++ = '\033', id += 2;
X		else
X			*cpy++ = *id++;
X	}
X	*cpy++ = '\0';
X
X	/* update the bp pointer */
X	id = *bp;
X	*bp = cpy;
X
X	/* return a pointer to the copy of the string */
X	return id;
X}
X
X/*ARGSUSED*/
Xchar *tgoto(cm, destcol, destrow)
X	char	*cm;	/* cursor movement string -- ignored */
X	int	destcol;/* destination column, 0 - 79 */
X	int	destrow;/* destination row, 0 - 24 */
X{
X	static char buf[30];
X
X#ifdef CRUNCH
X# if TOS
X	sprintf(buf, "\033Y%c%c", ' ' + destrow, ' ' + destcol);
X# else
X	sprintf(buf, "\033[%d;%dH", destrow + 1, destcol + 1);
X# endif
X#else
X	if (cm[1] == 'Y' || cm[1] == '=')
X		sprintf(buf, "\033%c%c%c", cm[1], ' ' + destrow, ' ' + destcol);
X	else
X		sprintf(buf, "\033[%d;%dH", destrow + 1, destcol + 1);
X#endif
X	return buf;
X}
X
X/*ARGSUSED*/
Xvoid tputs(cp, affcnt, outfn)
X	char	*cp;		/* the string to output */
X	int	affcnt;		/* number of affected lines -- ignored */
X	int	(*outfn)();	/* the output function */
X{
X	while (*cp)
X	{
X		(*outfn)(*cp);
X		cp++;
X	}
X}
/
echo x - tio.c
sed '/^X/s///' > tio.c << '/'
X/* tio.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains terminal I/O functions */
X
X#include "config.h"
X#include "vi.h"
X#include "ctype.h"
X
X
X/* This function reads in a line from the terminal. */
Xint vgets(prompt, buf, bsize)
X	char	prompt;	/* the prompt character, or '\0' for none */
X	char	*buf;	/* buffer into which the string is read */
X	int	bsize;	/* size of the buffer */
X{
X	int	len;	/* how much we've read so far */
X	int	ch;	/* a character from the user */
X	int	quoted;	/* is the next char quoted? */
X	int	tab;	/* column position of cursor */
X	char	widths[132];	/* widths of characters */
X	int	word;	/* index of first letter of word */
X#ifndef NO_DIGRAPH
X	int	erased;	/* 0, or first char of a digraph */
X#endif
X
X	/* show the prompt */
X	move(LINES - 1, 0);
X	tab = 0;
X	if (prompt)
X	{
X		addch(prompt);
X		tab = 1;
X	}
X	clrtoeol();
X	refresh();
X
X	/* read in the line */
X#ifndef NO_DIGRAPH
X	erased =
X#endif
X	quoted = len = 0;
X	for (;;)
X	{
X#ifndef NO_ABBR
X		if (quoted || mode == MODE_EX)
X		{
X			ch = getkey(0);
X		}
X		else
X		{
X			/* maybe expand an abbreviation while getting key */
X			for (word = len; --word >= 0 && isalnum(buf[word]); )
X			{
X			}
X			word++;
X			ch = getabkey(WHEN_EX, &buf[word], len - word);
X		}
X#else
X		ch = getkey(0);
X#endif
X#ifndef NO_EXTENSIONS
X		if (ch == ctrl('O'))
X		{
X			ch = getkey(quoted ? 0 : WHEN_EX);
X		}
X#endif
X
X		/* some special conversions */
X		if (ch == ctrl('D') && len == 0)
X			ch = ctrl('[');
X#ifndef NO_DIGRAPH
X		if (*o_digraph && erased != 0 && ch != '\b')
X		{
X			ch = digraph(erased, ch);
X			erased = 0;
X		}
X#endif
X
X		/* inhibit detection of special chars (except ^J) after a ^V */
X		if (quoted && ch != '\n')
X		{
X			ch |= 256;
X		}
X
X		/* process the character */
X		switch(ch)
X		{
X		  case ctrl('V'):
X			qaddch('^');
X			qaddch('\b');
X			quoted = TRUE;
X			break;
X
X		  case ctrl('['):
X			return -1;
X
X		  case '\n':
X#if OSK
X		  case '\l':
X#else
X		  case '\r':
X#endif
X			clrtoeol();
X			goto BreakBreak;
X
X		  case '\b':
X			if (len > 0)
X			{
X				len--;
X#ifndef NO_DIGRAPH
X				erased = buf[len];
X#endif
X				for (ch = widths[len]; ch > 0; ch--)
X					addch('\b');
X				if (mode == MODE_EX)
X				{
X					clrtoeol();
X				}
X				tab -= widths[len];
X			}
X			else
X			{
X				return -1;
X			}
X			break;
X
X		  default:
X			/* strip off quotation bit */
X			if (ch & 256)
X			{
X				ch &= ~256;
X				qaddch(' ');
X				qaddch('\b');
X			}
X
X			/* add & echo the char */
X			if (len < bsize - 1)
X			{
X				if (ch == '\t' && !quoted)
X				{
X					widths[len] = *o_tabstop - (tab % *o_tabstop);
X					addstr("        " + 8 - widths[len]);
X					tab += widths[len];
X				}
X				else if (ch > 0 && ch < ' ') /* > 0 by GB */
X				{
X					addch('^');
X					addch(ch + '@');
X					widths[len] = 2;
X					tab += 2;
X				}
X				else if (ch == '\177')
X				{
X					addch('^');
X					addch('?');
X					widths[len] = 2;
X					tab += 2;
X				}
X				else
X				{
X					addch(ch);
X					widths[len] = 1;
X					tab++;
X				}
X				buf[len++] = ch;
X			}
X			else
X			{
X				beep();
X			}
X			quoted = FALSE;
X		}
X	}
XBreakBreak:
X	refresh();
X	buf[len] = '\0';
X	return len;
X}
X
X
Xstatic int	manymsgs; /* This variable keeps msgs from overwriting each other */
Xstatic char	pmsg[80]; /* previous message (waiting to be displayed) */
X
X
Xstatic int showmsg()
X{
X	/* if there is no message to show, then don't */
X	if (!manymsgs)
X		return FALSE;
X
X	/* display the message */
X	move(LINES - 1, 0);
X	if (*pmsg)
X	{
X		standout();
X		qaddch(' ');
X		qaddstr(pmsg);
X		qaddch(' ');
X		standend();
X	}
X	clrtoeol();
X
X	manymsgs = FALSE;
X	return TRUE;
X}
X
X
Xvoid endmsgs()
X{
X	if (manymsgs)
X	{
X		showmsg();
X		addch('\n');
X	}
X}
X
X/* Write a message in an appropriate way.  This should really be a varargs
X * function, but there is no such thing as vwprintw.  Hack!!!
X *
X * In MODE_EX or MODE_COLON, the message is written immediately, with a
X * newline at the end.
X *
X * In MODE_VI, the message is stored in a character buffer.  It is not
X * displayed until getkey() is called.  msg() will call getkey() itself,
X * if necessary, to prevent messages from being lost.
X *
X * msg("")		- clears the message line
X * msg("%s %d", ...)	- does a printf onto the message line
X */
X/*VARARGS1*/
Xvoid msg(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
X	char	*fmt;
X	long	arg1, arg2, arg3, arg4, arg5, arg6, arg7;
X{
X	if (mode != MODE_VI)
X	{
X		sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
X		qaddstr(pmsg);
X		addch('\n');
X		exrefresh();
X	}
X	else
X	{
X		/* wait for keypress between consecutive msgs */
X		if (manymsgs)
X		{
X			getkey(WHEN_MSG);
X		}
X
X		/* real message */
X		sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
X		if (*fmt)
X		{
X			manymsgs = TRUE;
X		}
X	}
X}
X
X
X/* This function calls refresh() if the option exrefresh is set */
Xvoid exrefresh()
X{
X	char	*scan;
X
X	/* If this ex command wrote ANYTHING set exwrote so vi's  :  command
X	 * can tell that it must wait for a user keystroke before redrawing.
X	 */
X	for (scan=kbuf; scan<stdscr; scan++)
X		if (*scan == '\n')
X			exwrote = TRUE;
X
X	/* now we do the refresh thing */
X	if (*o_exrefresh)
X	{
X		refresh();
X	}
X	else
X	{
X		wqrefresh();
X	}
X	if (mode != MODE_VI)
X	{
X		manymsgs = FALSE;
X	}
X}
X
X
X/* This structure is used to store maps and abbreviations.  The distinction
X * between them is that maps are stored in the list referenced by the "maps"
X * pointer, while abbreviations are referenced by the "abbrs" pointer.
X */
Xtypedef struct _map
X{
X	struct _map	*next;	/* another abbreviation */
X	short		len;	/* length of the "rawin" characters */
X	short		flags;	/* various flags */
X	char		*label;	/* label of the map/abbr, or NULL */
X	char		*rawin;	/* the "rawin" characters */
X	char		*cooked;/* the "cooked" characters */
X} MAP;
X
Xstatic char	keybuf[KEYBUFSIZE];
Xstatic int	cend;	/* end of input characters */
Xstatic int	user;	/* from user through end are chars typed by user */
Xstatic int	next;	/* index of the next character to be returned */
Xstatic MAP	*match;	/* the matching map, found by countmatch() */
Xstatic MAP	*maps;	/* the map table */
X#ifndef NO_ABBR
Xstatic MAP	*abbrs;	/* the abbreviation table */
X#endif
X
X
X
X/* ring the terminal's bell */
Xvoid beep()
X{
X	/* do a visible/audible bell */
X	if (*o_flash)
X	{
X		do_VB();
X		refresh();
X	}
X	else if (*o_errorbells)
X	{
X		ttywrite("\007", 1);
X	}
X
X	/* discard any buffered input, and abort macros */
X	next = user = cend;
X}
X
X
X
X/* This function replaces a "rawin" character sequence with the "cooked" version,
X * by modifying the internal type-ahead buffer.
X */
Xvoid execmap(rawlen, cookedstr, visual)
X	int	rawlen;		/* length of rawin text -- string to delete */
X	char	*cookedstr;	/* the cooked text -- string to insert */
X	int	visual;		/* boolean -- chars to be executed in visual mode? */
X{
X	int	cookedlen;
X	char	*src, *dst;
X	int	i;
X
X	/* find the length of the cooked string */
X	cookedlen = strlen(cookedstr);
X#ifndef NO_EXTENSIONS
X	if (visual)
X	{
X		cookedlen *= 2;
X	}
X#endif
X
X	/* if too big to fit in type-ahead buffer, then don't do it */
X	if (cookedlen + (cend - next) - rawlen > KEYBUFSIZE)
X	{
X		return;
X	}
X
X	/* shift to make room for cookedstr at the front of keybuf */
X	src = &keybuf[next + rawlen];
X	dst = &keybuf[cookedlen];
X	i = cend - (next + rawlen);
X	if (src >= dst)
X	{
X		while (i-- > 0)
X		{
X			*dst++ = *src++;
X		}
X	}
X	else
X	{
X		src += i;
X		dst += i;
X		while (i-- > 0)
X		{
X			*--dst = *--src;
X		}
X	}
X
X	/* insert cookedstr, and adjust offsets */
X	cend += cookedlen - rawlen - next;
X	user += cookedlen - rawlen - next;
X	next = 0;
X	for (dst = keybuf, src = cookedstr; *src; )
X	{
X#ifndef NO_EXTENSIONS
X		if (visual)
X		{
X			*dst++ = ctrl('O');
X			cookedlen--;
X		}
X#endif
X		*dst++ = *src++;
X	}
X
X#ifdef DEBUG2
X	{
X#include <stdio.h>
X		FILE	*debout;
X		int		i;
X
X		debout = fopen("debug.out", "a");
X		fprintf(debout, "After execmap(%d, \"%s\", %d)...\n", rawlen, cookedstr, visual);
X		for (i = 0; i < cend; i++)
X		{
X			if (i == next) fprintf(debout, "(next)");
X			if (i == user) fprintf(debout, "(user)");
X			if (UCHAR(keybuf[i]) < ' ')
X				fprintf(debout, "^%c", keybuf[i] ^ '@');
X			else
X				fprintf(debout, "%c", keybuf[i]);
X		}
X		fprintf(debout, "(end)\n");
X		fclose(debout);
X	}
X#endif
X}
X
X/* This function calls ttyread().  If necessary, it will also redraw the screen,
X * change the cursor shape, display the mode, and update the ruler.  If the
X * number of characters read is 0, and we didn't time-out, then it exits because
X * we've apparently reached the end of an EX script.
X */
Xstatic int fillkeybuf(when, timeout)
X	int	when;	/* mixture of WHEN_XXX flags */
X	int	timeout;/* timeout in 1/10 second increments, or 0 */
X{
X	int	nkeys;
X#ifndef NO_SHOWMODE
X	static int	oldwhen;	/* "when" from last time */
X	static int	oldleft;
X	static long	oldtop;
X	static long	oldnlines;
X	char		*str;
X#endif
X#ifndef NO_CURSORSHAPE
X	static int	oldcurs;
X#endif
X
X#ifdef DEBUG
X	watch();
X#endif
X
X
X#ifndef NO_CURSORSHAPE
X	/* make sure the cursor is the right shape */
X	if (has_CQ)
X	{
X		if (when != oldcurs)
X		{
X			switch (when)
X			{
X			  case WHEN_EX:		do_CX();	break;
X			  case WHEN_VICMD:	do_CV();	break;
X			  case WHEN_VIINP:	do_CI();	break;
X			  case WHEN_VIREP:	do_CR();	break;
X			}
X			oldcurs = when;
X		}
X	}
X#endif
X
X#ifndef NO_SHOWMODE
X	/* if "showmode" then say which mode we're in */
X	if (*o_smd && (when & WHENMASK))
X	{
X		/* redraw the screen before we check to see whether the
X		 * "showmode" message needs to be redrawn.
X		 */
X		redraw(cursor, !(when & WHEN_VICMD));
X
X		/* now the "topline" test should be valid */
X		if (when != oldwhen || topline != oldtop || leftcol != oldleft || nlines != oldnlines)
X		{
X			oldwhen = when;
X			oldtop = topline;
X			oldleft = leftcol;
X			oldnlines = nlines;
X
X			if (when & WHEN_VICMD)	    str = "Command";
X			else if (when & WHEN_VIINP) str = " Input ";
X			else if (when & WHEN_VIREP) str = "Replace";
X			else if (when & WHEN_REP1)  str = " Rep 1 ";
X			else if (when & WHEN_CUT)   str = "BufName";
X			else if (when & WHEN_MARK)  str = "Mark AZ";
X			else if (when & WHEN_CHAR)  str = "Dest Ch";
X			else			    str = (char *)0;
X
X			if (str)
X			{
X				move(LINES - 1, COLS - 10);
X				standout();
X				qaddstr(str);
X				standend();
X			}
X		}
X	}
X#endif
X
X#ifndef NO_EXTENSIONS
X	/* maybe display the ruler */
X	if (*o_ruler && (when & (WHEN_VICMD|WHEN_VIINP|WHEN_VIREP)))
X	{
X		char	buf[20];
X
X		redraw(cursor, !(when & WHEN_VICMD));
X		pfetch(markline(cursor));
X		sprintf(buf, "%7ld,%-4d", markline(cursor), 1 + idx2col(cursor, ptext, when & (WHEN_VIINP|WHEN_VIREP)));
X		move(LINES - 1, COLS - 22);
X		addstr(buf);
X	}
X#endif
X
X	/* redraw, so the cursor is in the right place */
X	if (when & WHENMASK)
X	{
X		redraw(cursor, !(when & (WHENMASK & ~(WHEN_VIREP|WHEN_VIINP))));
X	}
X
X	/* Okay, now we can finally read the rawin keystrokes */
X	refresh();
X	nkeys = ttyread(keybuf + cend, sizeof keybuf - cend, timeout);
X
X	/* if nkeys == 0 then we've reached EOF of an ex script. */
X	if (nkeys == 0 && timeout == 0)
X	{
X		tmpabort(TRUE);
X		move(LINES - 1, 0);
X		clrtoeol();
X		refresh();
X		endwin();
X		exit(1);
X	}
X
X	cend += nkeys;
X	user += nkeys;
X	return nkeys;
X}
X
X
X/* This function counts the number of maps that could match the characters
X * between &keybuf[next] and &keybuf[cend], including incomplete matches.
X * The longest comlete match is remembered via the "match" variable.
X */
Xstatic int countmatch(when)
X	int	when;	/* mixture of WHEN_XXX flags */
X{
X	MAP	*map;
X	int	count;
X
X	/* clear the "match" variable */
X	match = (MAP *)0;
X
X	/* check every map */
X	for (count = 0, map = maps; map; map = map->next)
X	{
X		/* can't match if wrong mode */
X		if ((map->flags & when) == 0)
X		{
X			continue;
X		}
X
X		/* would this be a complete match? */
X		if (map->len <= cend - next)
X		{
X			/* Yes, it would be.  Now does it really match? */
X			if (!strncmp(map->rawin, &keybuf[next], map->len))
X			{
X				count++;
X
X				/* if this is the longest complete match,
X				 * then remember it.
X				 */
X				if (!match || match->len < map->len)
X				{
X					match = map;
X				}
X			}
X		}
X		else
X		{
X			/* No, it wouldn't.  But check for partial match */
X			if (!strncmp(map->rawin, &keybuf[next], cend - next))
X			{
X				count++;
X			}
X		}
X	}
X	return count;
X}
X
X
X#ifndef NO_ABBR
X/* This function checks to see whether a word is an abbreviation.  If it is,
X * then an appropriate number of backspoace characters is inserted into the
X * type-ahead buffer, followed by the expanded form of the abbreviation.
X */
Xstatic void expandabbr(word, wlen)
X	char	*word;
X	int	wlen;
X{
X	MAP	*abbr;
X
X	/* if the next character wouldn't end the word, then don't expand */
X	if (isalnum(keybuf[next]) || keybuf[next] == ctrl('V'))
X	{
X		return;
X	}
X
X	/* find the abbreviation, if any */
X	for (abbr = abbrs;
X	     abbr && (abbr->len != wlen || strncmp(abbr->rawin, word, wlen));
X	     abbr = abbr->next)
X	{
X	}
X
X	/* If an abbreviation was found, then expand it by inserting the long
X	 * version into the type-ahead buffer, and then inserting (in front of
X	 * the long version) enough backspaces to erase to the short version.
X	 */
X	if (abbr)
X	{
X		execmap(0, abbr->cooked, FALSE);
X		while (wlen > 15)
X		{
X			execmap(0, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", FALSE);
X			wlen -= 15;
X		}
X		if (wlen > 0)
X		{
X			execmap(0, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" + 15 - wlen, FALSE);
X		}
X	}
X}
X#endif
X
X
X/* This function calls getabkey() without attempting to expand abbreviations */
Xint getkey(when)
X	int	when;	/* mixture of WHEN_XXX flags */
X{
X	return getabkey(when, "", 0);
X}
X
X
X/* This is it.  This function returns keystrokes one-at-a-time, after mapping
X * and abbreviations have been taken into account.
X */
Xint getabkey(when, word, wlen)
X	int	when;	/* mixture of WHEN_XXX flags */
X	char	*word;	/* a word that may need to be expanded as an abbr */
X	int	wlen;	/* length of "word" -- since "word" might not have \0 */
X{
X	int	matches;
X
X	/* if this key is needed for delay between multiple error messages,
X	 * then reset the manymsgs flag and abort any mapped key sequence.
X	 */
X	if (showmsg())
X	{
X		if (when == WHEN_MSG)
X		{
X#ifndef CRUNCH
X			if (!*o_more)
X			{
X				refresh();
X				return ' ';
X			}
X#endif
X			qaddstr("[More...]");
X			refresh();
X			execmap(user, "", FALSE);
X		}
X	}
X
X#ifdef DEBUG
X	/* periodically check for screwed up internal tables */
X	watch();
X#endif
X
X	/* if buffer empty, read some characters without timeout */
X	if (next >= cend)
X	{
X		next = user = cend = 0;
X		fillkeybuf(when, 0);
X	}
X
X	/* try to map the key, unless already mapped and not ":set noremap" */
X	if (next >= user || *o_remap)
X	{
X		do
X		{
X			do
X			{
X				matches = countmatch(when);
X			} while (matches > 1 && fillkeybuf(when, *o_keytime) > 0);
X			if (matches == 1)
X			{
X				execmap(match->len, match->cooked,
X					(match->flags & WHEN_INMV) != 0 
X					 && (when & (WHEN_VIINP|WHEN_VIREP)) != 0);
X			}
X		} while (*o_remap && matches == 1);
X	}
X
X#ifndef NO_ABBR
X	/* try to expand an abbreviation, except in visual command mode */
X	if (wlen > 0 && (mode & (WHEN_EX|WHEN_VIINP|WHEN_VIREP)) != 0)
X	{
X		expandabbr(word, wlen);
X	}
X#endif
X
X	/* ERASEKEY should always be mapped to '\b'. */
X	if (keybuf[next] == ERASEKEY)
X	{
X		keybuf[next] = '\b';
X	}
X
X	/* return the next key */
X	return keybuf[next++];
X}
X
X/* This function maps or unmaps a key */
Xvoid mapkey(rawin, cooked, when, name)
X	char	*rawin;	/* the input key sequence, before mapping */
X	char	*cooked;/* after mapping -- or NULL to remove map */
X	short	when;	/* bitmap of when mapping should happen */
X	char	*name;	/* name of the key, NULL for no name, "abbr" for abbr */
X{
X	MAP	**head;	/* head of list of maps or abbreviations */
X	MAP	*scan;	/* used for scanning through the list */
X	MAP	*prev;	/* used during deletions */
X
X	/* Is this a map or an abbreviation?  Choose the right list. */
X#ifndef NO_ABBR
X	head = ((!name || strcmp(name, "abbr")) ? &maps : &abbrs);
X#else
X	head = &maps;
X#endif
X
X	/* try to find the map in the list */
X	for (scan = *head, prev = (MAP *)0;
X	     scan && (strcmp(rawin, scan->rawin) ||
X		!(scan->flags & when & (WHEN_EX|WHEN_VICMD|WHEN_VIINP|WHEN_VIREP)));
X	     prev = scan, scan = scan->next)
X	{
X	}
X
X	/* trying to map? (not unmap) */
X	if (cooked && *cooked)
X	{
X		/* if map starts with "visual ", then mark it as a visual map */
X		if (head == &maps && !strncmp(cooked, "visual ", 7))
X		{
X			cooked += 7;
X			when |= WHEN_INMV;
X		}
X
X		/* "visual" maps always work in input mode */
X		if (when & WHEN_INMV)
X		{
X			when |= WHEN_VIINP|WHEN_VIREP|WHEN_POPUP;
X		}
X
X		/* if not already in the list, then allocate a new structure */
X		if (!scan)
X		{
X			scan = (MAP *)malloc(sizeof(MAP));
X			scan->len = strlen(rawin);
X			scan->rawin = malloc(scan->len + 1);
X			strcpy(scan->rawin, rawin);
X			scan->flags = when;
X			scan->label = name;
X			if (*head)
X			{
X				prev->next = scan;
X			}
X			else
X			{
X				*head = scan;
X			}
X			scan->next = (MAP *)0;
X		}
X		else /* recycle old structure */
X		{
X			free(scan->cooked);
X		}
X		scan->cooked = malloc(strlen(cooked) + 1);
X		strcpy(scan->cooked, cooked);
X	}
X	else /* unmapping */
X	{
X		/* if nothing to unmap, then exit silently */
X		if (!scan)
X		{
X			return;
X		}
X
X		/* unlink the structure from the list */
X		if (prev)
X		{
X			prev->next = scan->next;
X		}
X		else
X		{
X			*head = scan->next;
X		}
X
X		/* free it, and the strings that it refers to */
X		free(scan->rawin);
X		free(scan->cooked);
X		free(scan);
X	}
X}
X
X
X/* This function returns a printable version of a string.  It uses tmpblk.c */
Xchar *printable(str)
X	char	*str;	/* the string to convert */
X{
X	char	*build;	/* used for building the string */
X
X	for (build = tmpblk.c; *str; str++)
X	{
X#if AMIGA
X		if (*str == '\233')
X		{
X			*build++ = '<';
X			*build++ = 'C';
X			*build++ = 'S';
X			*build++ = 'I';
X			*build++ = '>';
X		} else 
X#endif
X		if (UCHAR(*str) < ' ' || *str == '\177')
X		{
X			*build++ = '^';
X			*build++ = *str ^ '@';
X		}
X		else
X		{
X			*build++ = *str;
X		}
X	}
X	*build = '\0';
X	return tmpblk.c;
X}
X
X/* This function displays the contents of either the map table or the
X * abbreviation table.  User commands call this function as follows:
X *	:map	dumpkey(WHEN_VICMD, FALSE);
X *	:map!	dumpkey(WHEN_VIREP|WHEN_VIINP, FALSE);
X *	:abbr	dumpkey(WHEN_VIINP|WHEN_VIREP, TRUE);
X *	:abbr!	dumpkey(WHEN_EX|WHEN_VIINP|WHEN_VIREP, TRUE);
X */
Xvoid dumpkey(when, abbr)
X	int	when;	/* WHEN_XXXX of mappings to be dumped */
X	int	abbr;	/* boolean: dump abbreviations instead of maps? */
X{
X	MAP	*scan;
X	char	*str;
X	int	len;
X
X#ifndef NO_ABBR
X	for (scan = (abbr ? abbrs : maps); scan; scan = scan->next)
X#else
X	for (scan = maps; scan; scan = scan->next)
X#endif
X	{
X		/* skip entries that don't match "when" */
X		if ((scan->flags & when) == 0)
X		{
X			continue;
X		}
X
X		/* dump the key label, if any */
X		if (!abbr)
X		{
X			len = 8;
X			if (scan->label)
X			{
X				qaddstr(scan->label);
X				len -= strlen(scan->label);
X			}
X			do
X			{
X				qaddch(' ');
X			} while (len-- > 0);
X		}
X
X		/* dump the rawin version */
X		str = printable(scan->rawin);
X		qaddstr(str);
X		len = strlen(str);
X		do
X		{
X			qaddch(' ');
X		} while (len++ < 8);
X			
X		/* dump the mapped version */
X#ifndef NO_EXTENSIONS
X		if ((scan->flags & WHEN_INMV) && (when & (WHEN_VIINP|WHEN_VIREP)))
X		{
X			qaddstr("visual ");
X		}
X#endif
X		str = printable(scan->cooked);
X		qaddstr(str);
X		addch('\n');
X		exrefresh();
X	}
X}
X
X#ifndef NO_MKEXRC
X
Xstatic safequote(str)
X	char	*str;
X{
X	char	*build;
X
X	build = tmpblk.c + strlen(tmpblk.c);
X	while (*str)
X	{
X		if (*str <= ' ' && *str >= 1 || *str == '|')
X		{
X			*build++ = ctrl('V');
X		}
X		*build++ = *str++;
X	}
X	*build = '\0';
X}
X
X/* This function saves the contents of either the map table or the
X * abbreviation table into a file.  Both the "bang" and "no bang" versions
X * are saved.
X *	:map	dumpkey(WHEN_VICMD, FALSE);
X *	:map!	dumpkey(WHEN_VIREP|WHEN_VIINP, FALSE);
X *	:abbr	dumpkey(WHEN_VIINP|WHEN_VIREP, TRUE);
X *	:abbr!	dumpkey(WHEN_EX|WHEN_VIINP|WHEN_VIREP, TRUE);
X */
Xsavemaps(fd, abbr)
X	int	fd;	/* file descriptor of an open file to write to */
X	int	abbr;	/* boolean: do abbr table? (else do map table) */
X{
X	MAP	*scan;
X	char	*str;
X	int	bang;
X	int	when;
X	int	len;
X
X# ifndef NO_ABBR
X	for (scan = (abbr ? abbrs : maps); scan; scan = scan->next)
X# else
X	for (scan = maps; scan; scan = scan->next)
X# endif
X	{
X		/* skip maps that have labels, except for function keys */
X		if (scan->label && *scan->label != '#')
X		{
X			continue;
X		}
X
X		for (bang = 0; bang < 2; bang++)
X		{
X			/* decide which "when" flags we want */
X# ifndef NO_ABBR
X			if (abbr)
X				when = (bang ? WHEN_EX|WHEN_VIINP|WHEN_VIREP : WHEN_VIINP|WHEN_VIREP);
X			else
X# endif
X				when = (bang ? WHEN_VIREP|WHEN_VIINP : WHEN_VICMD);
X
X			/* skip entries that don't match "when" */
X			if ((scan->flags & when) == 0)
X			{
X				continue;
X			}
X
X			/* write a "map" or "abbr" command name */
X# ifndef NO_ABBR
X			if (abbr)
X				strcpy(tmpblk.c, "abbr");
X			else
X# endif
X				strcpy(tmpblk.c, "map");
X
X			/* maybe write a bang.  Definitely write a space */
X			if (bang)
X				strcat(tmpblk.c, "! ");
X			else
X				strcat(tmpblk.c, " ");
X
X			/* write the rawin version */
X# ifndef NO_FKEY
X			if (scan->label)
X				strcat(tmpblk.c, scan->label);
X			else
X# endif
X				safequote(scan->rawin);
X			strcat(tmpblk.c, " ");
X				
X			/* dump the mapped version */
X# ifndef NO_EXTENSIONS
X			if ((scan->flags & WHEN_INMV) && (when & (WHEN_VIINP|WHEN_VIREP)))
X			{
X				strcat(tmpblk.c, "visual ");
X			}
X# endif
X			safequote(scan->cooked);
X			strcat(tmpblk.c, "\n");
X			twrite(fd, tmpblk.c, strlen(tmpblk.c));
X		}
X	}
X}
X#endif
/
echo x - tmp.c
sed '/^X/s///' > tmp.c << '/'
X/* tmp.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains functions which create & readback a TMPFILE */
X
X
X#include "config.h"
X#include "vi.h"
X#if TOS
X# include <stat.h>
X#else
X# if OSK
X#  include "osk.h"
X# else
X#  if AMIGA
X#   include "amistat.h"
X#  else
X#   include <sys/stat.h>
X#  endif
X# endif
X#endif
X#if TURBOC
X# include <process.h>
X#endif
X
X#ifndef NO_MODELINES
Xstatic void do_modelines(l, stop)
X	long	l;	/* line number to start at */
X	long	stop;	/* line number to stop at */
X{
X	char	*str;	/* used to scan through the line */
X	char	*start;	/* points to the start of the line */
X	char	buf[80];
X
X	/* if modelines are disabled, then do nothing */
X	if (!*o_modelines)
X	{
X		return;
X	}
X
X	/* for each line... */
X	for (; l <= stop; l++)
X	{
X		/* for each position in the line.. */
X		for (str = fetchline(l); *str; str++)
X		{
X			/* if it is the start of a modeline command... */
X			if ((str[0] == 'e' && str[1] == 'x'
X			  || str[0] == 'v' && str[1] == 'i')
X			  && str[2] == ':')
X			{
X				start = str += 3;
X
X				/* find the end */
X				for (str = start + strlen(start); *--str != ':'; )
X				{
X				}
X
X				/* if it is a well-formed modeline, execute it */
X				if (str > start && str - start < sizeof buf)
X				{
X					strncpy(buf, start, (int)(str - start));
X					exstring(buf, str - start, '\\');
X					break;
X				}
X			}
X		}
X	}
X}
X#endif
X
X
X/* The FAIL() macro prints an error message and then exits. */
X#define FAIL(why,arg)	mode = MODE_EX; msg(why, arg); endwin(); exit(9)
X
X/* This is the name of the temp file */
Xstatic char	tmpname[80];
X
X/* This function creates the temp file and copies the original file into it.
X * Returns if successful, or stops execution if it fails.
X */
Xint tmpstart(filename)
X	char		*filename; /* name of the original file */
X{
X	int		origfd;	/* fd used for reading the original file */
X	struct stat	statb;	/* stat buffer, used to examine inode */
X	REG BLK		*this;	/* pointer to the current block buffer */
X	REG BLK		*next;	/* pointer to the next block buffer */
X	int		inbuf;	/* number of characters in a buffer */
X	int		nread;	/* number of bytes read */
X	REG int		j, k;
X	int		i;
X	long		nbytes;
X
X	/* switching to a different file certainly counts as a change */
X	changes++;
X	redraw(MARK_UNSET, FALSE);
X
X	/* open the original file for reading */
X	*origname = '\0';
X	if (filename && *filename)
X	{
X		strcpy(origname, filename);
X		origfd = open(origname, O_RDONLY);
X		if (origfd < 0 && errno != ENOENT)
X		{
X			msg("Can't open \"%s\"", origname);
X			return tmpstart("");
X		}
X		if (origfd >= 0)
X		{
X			if (stat(origname, &statb) < 0)
X			{
X				FAIL("Can't stat \"%s\"", origname);
X			}
X#if TOS
X			if (origfd >= 0 && (statb.st_mode & S_IJDIR))
X#else
X# if OSK
X			if (origfd >= 0 && (statb.st_mode & S_IFDIR))
X# else
X			if (origfd >= 0 && (statb.st_mode & S_IFMT) != S_IFREG)
X# endif
X#endif
X			{
X				msg("\"%s\" is not a regular file", origname);
X				return tmpstart("");
X			}
X		}
X		else
X		{
X			stat(".", &statb);
X		}
X		if (origfd >= 0)
X		{
X			origtime = statb.st_mtime;
X#if OSK
X			if (*o_readonly || !(statb.st_mode &
X				  ((getuid() >> 16) == 0 ? S_IOWRITE | S_IWRITE :
X				  ((statb.st_gid != (getuid() >> 16) ? S_IOWRITE : S_IWRITE)))))
X#endif
X#if AMIGA || MSDOS || (TOS && defined(__GNUC__))
X			if (*o_readonly || !(statb.st_mode & S_IWRITE))
X#endif
X#if TOS && !defined(__GNUC__)
X			if (*o_readonly || (statb.st_mode & S_IJRON))
X#endif
X#if ANY_UNIX
X			if (*o_readonly || !(statb.st_mode &
X				  ((geteuid() == 0) ? 0222 :
X				  ((statb.st_uid != geteuid() ? 0022 : 0200)))))
X#endif
X#if VMS
X			if (*o_readonly)
X#endif
X			{
X				setflag(file, READONLY);
X			}
X		}
X		else
X		{
X			origtime = 0L;
X		}
X	}
X	else
X	{
X		setflag(file, NOFILE);
X		origfd = -1;
X		origtime = 0L;
X		stat(".", &statb);
X	}
X
X	/* make a name for the tmp file */
X	tmpnum++;
X#if MSDOS || TOS
X	/* MS-Dos doesn't allow multiple slashes, but supports drives
X	 * with current directories.
X	 * This relies on TMPNAME beginning with "%s\\"!!!!
X	 */
X	strcpy(tmpname, o_directory);
X	if ((i = strlen(tmpname)) && !strchr(":/\\", tmpname[i-1]))
X		tmpname[i++]=SLASH;
X	sprintf(tmpname+i, TMPNAME+3, getpid(), tmpnum);
X#else
X	sprintf(tmpname, TMPNAME, o_directory, getpid(), tmpnum);
X#endif
X
X	/* make sure nobody else is editing the same file */
X	if (access(tmpname, 0) == 0)
X	{
X		FAIL("Temp file \"%s\" already exists?", tmpname);
X	}
X
X	/* create the temp file */
X#if ANY_UNIX
X	close(creat(tmpname, 0600));		/* only we can read it */
X#else
X	close(creat(tmpname, FILEPERMS));	/* anybody body can read it, alas */
X#endif
X	tmpfd = open(tmpname, O_RDWR | O_BINARY);
X	if (tmpfd < 0)
X	{
X		FAIL("Can't create temp file... Does directory \"%s\" exist?", o_directory);
X		return 1;
X	}
X
X	/* allocate space for the header in the file */
X	write(tmpfd, hdr.c, (unsigned)BLKSIZE);
X	write(tmpfd, tmpblk.c, (unsigned)BLKSIZE);
X
X#ifndef NO_RECYCLE
X	/* initialize the block allocator */
X	/* This must already be done here, before the first attempt
X	 * to write to the new file! GB */
X	garbage();
X#endif
X
X	/* initialize lnum[] */
X	for (i = 1; i < MAXBLKS; i++)
X	{
X		lnum[i] = INFINITY;
X	}
X	lnum[0] = 0;
X
X	/* if there is no original file, then create a 1-line file */
X	if (origfd < 0)
X	{
X		hdr.n[0] = 0;	/* invalid inode# denotes new file */
X
X		this = blkget(1); 	/* get the new text block */
X		strcpy(this->c, "\n");	/* put a line in it */
X
X		lnum[1] = 1L;	/* block 1 ends with line 1 */
X		nlines = 1L;	/* there is 1 line in the file */
X		nbytes = 1L;
X
X		if (*origname)
X		{
X			msg("\"%s\" [NEW FILE]  1 line, 1 char", origname);
X		}
X		else
X		{
X			msg("\"[NO FILE]\"  1 line, 1 char");
X		}
X	}
X	else /* there is an original file -- read it in */
X	{
X		nbytes = nlines = 0;
X
X		/* preallocate 1 "next" buffer */
X		i = 1;
X		next = blkget(i);
X		inbuf = 0;
X
X		/* loop, moving blocks from orig to tmp */
X		for (;;)
X		{
X			/* "next" buffer becomes "this" buffer */
X			this = next;
X
X			/* read [more] text into this block */
X			nread = tread(origfd, &this->c[inbuf], BLKSIZE - 1 - inbuf);
X			if (nread < 0)
X			{
X				close(origfd);
X				close(tmpfd);
X				tmpfd = -1;
X				unlink(tmpname);
X				FAIL("Error reading \"%s\"", origname);
X			}
X
X			/* convert NUL characters to something else */
X			for (j = k = inbuf; k < inbuf + nread; k++)
X			{
X				if (!this->c[k])
X				{
X					setflag(file, HADNUL);
X					this->c[j++] = 0x80;
X				}
X#ifndef CRUNCH
X				else if (*o_beautify && this->c[k] < ' ' && this->c[k] > 0)
X				{
X					if (this->c[k] == '\t'
X					 || this->c[k] == '\n'
X					 || this->c[k] == '\f')
X					{
X						this->c[j++] = this->c[k];
X					}
X					else if (this->c[k] == '\b')
X					{
X						/* delete '\b', but complain */
X						setflag(file, HADBS);
X					}
X					/* else silently delete control char */
X				}
X#endif
X				else
X				{
X					this->c[j++] = this->c[k];
X				}
X			}
X			inbuf = j;
X
X			/* if the buffer is empty, quit */
X			if (inbuf == 0)
X			{
X				goto FoundEOF;
X			}
X
X#if MSDOS || TOS
X/* BAH! MS text mode read fills inbuf, then compresses eliminating \r
X   but leaving garbage at end of buf. The same is true for TURBOC. GB. */
X
X			memset(this->c + inbuf, '\0', BLKSIZE - inbuf);
X#endif
X
X			/* search backward for last newline */
X			for (k = inbuf; --k >= 0 && this->c[k] != '\n';)
X			{
X			}
X			if (k++ < 0)
X			{
X				if (inbuf >= BLKSIZE - 1)
X				{
X					k = 80;
X				}
X				else
X				{
X					k = inbuf;
X				}
X			}
X
X			/* allocate next buffer */
X			next = blkget(++i);
X
X			/* move fragmentary last line to next buffer */
X			inbuf -= k;
X			for (j = 0; k < BLKSIZE; j++, k++)
X			{
X				next->c[j] = this->c[k];
X				this->c[k] = 0;
X			}
X
X			/* if necessary, add a newline to this buf */
X			for (k = BLKSIZE - inbuf; --k >= 0 && !this->c[k]; )
X			{
X			}
X			if (this->c[k] != '\n')
X			{
X				setflag(file, ADDEDNL);
X				this->c[k + 1] = '\n';
X			}
X
X			/* count the lines in this block */
X			for (k = 0; k < BLKSIZE && this->c[k]; k++)
X			{
X				if (this->c[k] == '\n')
X				{
X					nlines++;
X				}
X				nbytes++;
X			}
X			lnum[i - 1] = nlines;
X		}
XFoundEOF:
X
X		/* if this is a zero-length file, add 1 line */
X		if (nlines == 0)
X		{
X			this = blkget(1); 	/* get the new text block */
X			strcpy(this->c, "\n");	/* put a line in it */
X
X			lnum[1] = 1;	/* block 1 ends with line 1 */
X			nlines = 1;	/* there is 1 line in the file */
X			nbytes = 1;
X		}
X
X#if MSDOS || TOS
X		/* each line has an extra CR that we didn't count yet */
X		nbytes += nlines;
X#endif
X
X		/* report the number of lines in the file */
X		msg("\"%s\" %s %ld line%s, %ld char%s",
X			origname,
X			(tstflag(file, READONLY) ? "[READONLY]" : ""),
X			nlines,
X			nlines == 1 ? "" : "s",
X			nbytes,
X			nbytes == 1 ? "" : "s");
X	}
X
X	/* initialize the cursor to start of line 1 */
X	cursor = MARK_FIRST;
X
X	/* close the original file */
X	close(origfd);
X
X	/* any other messages? */
X	if (tstflag(file, HADNUL))
X	{
X		msg("This file contained NULs.  They've been changed to \\x80 chars");
X	}
X	if (tstflag(file, ADDEDNL))
X	{
X		msg("Newline characters have been inserted to break up long lines");
X	}
X#ifndef CRUNCH
X	if (tstflag(file, HADBS))
X	{
X		msg("Backspace characters deleted due to ':set beautify'");
X	}
X#endif
X
X	storename(origname);
X
X#ifndef NO_MODELINES
X	if (nlines > 10)
X	{
X		do_modelines(1L, 5L);
X		do_modelines(nlines - 4L, nlines);
X	}
X	else
X	{
X		do_modelines(1L, nlines);
X	}
X#endif
X
X	/* force all blocks out onto the disk, to support file recovery */
X	blksync();
X
X	return 0;
X}
X
X
X
X/* This function copies the temp file back onto an original file.
X * Returns TRUE if successful, or FALSE if the file could NOT be saved.
X */
Xint tmpsave(filename, bang)
X	char	*filename;	/* the name to save it to */
X	int	bang;		/* forced write? */
X{
X	int		fd;	/* fd of the file we're writing to */
X	REG int		len;	/* length of a text block */
X	REG BLK		*this;	/* a text block */
X	long		bytes;	/* byte counter */
X	REG int		i;
X
X	/* if no filename is given, assume the original file name */
X	if (!filename || !*filename)
X	{
X		filename = origname;
X	}
X
X	/* if still no file name, then fail */
X	if (!*filename)
X	{
X		msg("Don't know a name for this file -- NOT WRITTEN");
X		return FALSE;
X	}
X
X	/* can't rewrite a READONLY file */
X#if AMIGA
X	if (!strcmp(filename, origname) && tstflag(file, READONLY) && !bang)
X#else
X	if (!strcmp(filename, origname) && *o_readonly && !bang)
X#endif
X	{
X		msg("\"%s\" [READONLY] -- NOT WRITTEN", filename);
X		return FALSE;
X	}
X
X	/* open the file */
X	if (*filename == '>' && filename[1] == '>')
X	{
X		filename += 2;
X		while (*filename == ' ' || *filename == '\t')
X		{
X			filename++;
X		}
X#ifdef O_APPEND
X		fd = open(filename, O_WRONLY|O_APPEND);
X#else
X		fd = open(filename, O_WRONLY);
X		lseek(fd, 0L, 2);
X#endif
X	}
X	else
X	{
X		/* either the file must not exist, or it must be the original
X		 * file, or we must have a bang, or "writeany" must be set.
X		 */
X		if (strcmp(filename, origname) && access(filename, 0) == 0 && !bang
X#ifndef CRUNCH
X		    && !*o_writeany
X#endif
X				   )
X		{
X			msg("File already exists - Use :w! to overwrite");
X			return FALSE;
X		}
X#if VMS
X		/* Create a new VMS version of this file. */
X		{ 
X		char *strrchr(), *ptr = strrchr(filename,';');
X		if (ptr) *ptr = '\0';  /* Snip off any ;number in the name */
X		}
X#endif
X		fd = creat(filename, FILEPERMS);
X	}
X	if (fd < 0)
X	{
X		msg("Can't write to \"%s\" -- NOT WRITTEN", filename);
X		return FALSE;
X	}
X
X	/* write each text block to the file */
X	bytes = 0L;
X	for (i = 1; i < MAXBLKS && (this = blkget(i)) && this->c[0]; i++)
X	{
X		for (len = 0; len < BLKSIZE && this->c[len]; len++)
X		{
X		}
X		if (twrite(fd, this->c, len) < len)
X		{
X			msg("Trouble writing to \"%s\"", filename);
X			if (!strcmp(filename, origname))
X			{
X				setflag(file, MODIFIED);
X			}
X			close(fd);
X			return FALSE;
X		}
X		bytes += len;
X	}
X
X	/* reset the "modified" flag, but not the "undoable" flag */
X	clrflag(file, MODIFIED);
X	significant = FALSE;
X
X	/* report lines & characters */
X#if MSDOS || TOS
X	bytes += nlines; /* for the inserted carriage returns */
X#endif
X	msg("Wrote \"%s\"  %ld lines, %ld characters", filename, nlines, bytes);
X
X	/* close the file */
X	close(fd);
X
X	return TRUE;
X}
X
X
X/* This function deletes the temporary file.  If the file has been modified
X * and "bang" is FALSE, then it returns FALSE without doing anything; else
X * it returns TRUE.
X *
X * If the "autowrite" option is set, then instead of returning FALSE when
X * the file has been modified and "bang" is false, it will call tmpend().
X */
Xint tmpabort(bang)
X	int	bang;
X{
X	/* if there is no file, return successfully */
X	if (tmpfd < 0)
X	{
X		return TRUE;
X	}
X
X	/* see if we must return FALSE -- can't quit */
X	if (!bang && tstflag(file, MODIFIED))
X	{
X		/* if "autowrite" is set, then act like tmpend() */
X		if (*o_autowrite)
X			return tmpend(bang);
X		else
X			return FALSE;
X	}
X
X	/* delete the tmp file */
X	cutswitch();
X	strcpy(prevorig, origname);
X	prevline = markline(cursor);
X	*origname = '\0';
X	origtime = 0L;
X	blkinit();
X	nlines = 0;
X	initflags();
X	return TRUE;
X}
X
X/* This function saves the file if it has been modified, and then deletes
X * the temporary file. Returns TRUE if successful, or FALSE if the file
X * needs to be saved but can't be.  When it returns FALSE, it will not have
X * deleted the tmp file, either.
X */
Xint tmpend(bang)
X	int	bang;
X{
X	/* save the file if it has been modified */
X	if (tstflag(file, MODIFIED) && !tmpsave((char *)0, FALSE) && !bang)
X	{
X		return FALSE;
X	}
X
X	/* delete the tmp file */
X	tmpabort(TRUE);
X
X	return TRUE;
X}
X
X
X/* If the tmp file has been changed, then this function will force those
X * changes to be written to the disk, so that the tmp file will survive a
X * system crash or power failure.
X */
X#if AMIGA || MSDOS || TOS
Xsync()
X{
X	/* MS-DOS and TOS don't flush their buffers until the file is closed,
X	 * so here we close the tmp file and then immediately reopen it.
X	 */
X	close(tmpfd);
X	tmpfd = open(tmpname, O_RDWR | O_BINARY);
X	return 0;
X}
X#endif
X
X
X/* This function stores the file's name in the second block of the temp file.
X * SLEAZE ALERT!  SLEAZE ALERT!  The "tmpblk" buffer is probably being used
X * to store the arguments to a command, so we can't use it here.  Instead,
X * we'll borrow the buffer that is used for "shift-U".
X */
Xstorename(name)
X	char	*name;	/* the name of the file - normally origname */
X{
X#ifndef CRUNCH
X	int	len;
X	char	*ptr;
X#endif
X
X	/* we're going to clobber the U_text buffer, so reset U_line */
X	U_line = 0L;
X
X	if (!name)
X	{
X		strncpy(U_text, "", BLKSIZE);
X		U_text[1] = 127;
X	}
X#ifndef CRUNCH
X	else if (*name != SLASH)
X	{
X		/* get the directory name */
X		ptr = getcwd(U_text, BLKSIZE);
X		if (ptr != U_text)
X		{
X			strcpy(U_text, ptr);
X		}
X
X		/* append a slash to the directory name */
X		len = strlen(U_text);
X		U_text[len++] = SLASH;
X
X		/* append the filename, padded with heaps o' NULs */
X		strncpy(U_text + len, *name ? name : "foo", BLKSIZE - len);
X	}
X#endif
X	else
X	{
X		/* copy the filename into U_text */
X		strncpy(U_text, *name ? name : "foo", BLKSIZE);
X	}
X
X	if (tmpfd >= 0)
X	{
X		/* write the name out to second block of the temp file */
X		lseek(tmpfd, (long)BLKSIZE, 0);
X		write(tmpfd, U_text, (unsigned)BLKSIZE);
X	}
X	return 0;
X}
X
X
X
X/* This function handles deadly signals.  It restores sanity to the terminal
X * preserves the current temp file, and deletes any old temp files.
X */
Xint deathtrap(sig)
X	int	sig;	/* the deadly signal that we caught */
X{
X	char	*why;
X
X	/* restore the terminal's sanity */
X	endwin();
X
X#ifdef CRUNCH
X	why = "-Elvis died";
X#else
X	/* give a more specific description of how Elvis died */
X	switch (sig)
X	{
X# ifdef SIGHUP
X	  case SIGHUP:	why = "-the modem lost its carrier";		break;
X# endif
X# ifndef DEBUG
X#  ifdef SIGILL
X	  case SIGILL:	why = "-Elvis hit an illegal instruction";	break;
X#  endif
X#  ifdef SIGBUS
X	  case SIGBUS:	why = "-Elvis had a bus error";			break;
X#  endif
X#  if defined(SIGSEGV) && !defined(TOS)
X	  case SIGSEGV:	why = "-Elvis had a segmentation violation";	break;
X#  endif
X#  ifdef SIGSYS
X	  case SIGSYS:	why = "-Elvis munged a system call";		break;
X#  endif
X# endif /* !DEBUG */
X# ifdef SIGPIPE
X	  case SIGPIPE:	why = "-the pipe reader died";			break;
X# endif
X# ifdef SIGTERM
X	  case SIGTERM:	why = "-Elvis was terminated";			break;
X# endif
X# if !MINIX
X#  ifdef SIGUSR1
X	  case SIGUSR1:	why = "-Elvis was killed via SIGUSR1";		break;
X#  endif
X#  ifdef SIGUSR2
X	  case SIGUSR2:	why = "-Elvis was killed via SIGUSR2";		break;
X#  endif
X# endif
X	  default:	why = "-Elvis died";				break;
X	}
X#endif
X
X	/* if we had a temp file going, then preserve it */
X	if (tmpnum > 0 && tmpfd >= 0)
X	{
X		close(tmpfd);
X		sprintf(tmpblk.c, "%s \"%s\" %s", PRESERVE, why, tmpname);
X		system(tmpblk.c);
X	}
X
X	/* delete any old temp files */
X	cutend();
X
X	/* exit with the proper exit status */
X	exit(sig);
X}
/
echo x - unix.c
sed '/^X/s///' > unix.c << '/'
X/* unix.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains the unix-specific versions the ttyread() functions.
X * There are actually three versions of ttyread() defined here, because
X * BSD, SysV, and V7 all need quite different implementations.
X */
X
X#include "config.h"
X#if ANY_UNIX
X# include "vi.h"
X
X# if BSD
X/* For BSD, we use select() to wait for characters to become available,
X * and then do a read() to actually get the characters.  We also try to
X * handle SIGWINCH -- if the signal arrives during the select() call, then
X * we adjust the o_columns and o_lines variables, and fake a control-L.
X */
X#  include <sys/types.h>
X#  include <sys/time.h>
Xint ttyread(buf, len, time)
X	char	*buf;	/* where to store the gotten characters */
X	int	len;	/* maximum number of characters to read */
X	int	time;	/* maximum time to allow for reading */
X{
X	fd_set	rd;	/* the file descriptors that we want to read from */
X	static	tty;	/* 'y' if reading from tty, or 'n' if not a tty */
X	int	i;
X	struct timeval t;
X	struct timeval *tp;
X
X
X	/* do we know whether this is a tty or not? */
X	if (!tty)
X	{
X		tty = (isatty(0) ? 'y' : 'n');
X	}
X
X	/* compute the timeout value */
X	if (time)
X	{
X		t.tv_sec = time / 10;
X		t.tv_usec = (time % 10) * 100000L;
X		tp = &t;
X	}
X	else
X	{
X		tp = (struct timeval *)0;
X	}
X
X	/* loop until we get characters or a definite EOF */
X	for (;;)
X	{
X		if (tty == 'y')
X		{
X			/* wait until timeout or characters are available */
X			FD_ZERO(&rd);
X			FD_SET(0, &rd);
X			i = select(1, &rd, (fd_set *)0, (fd_set *)0, tp);
X		}
X		else
X		{
X			/* if reading from a file or pipe, never timeout!
X			 * (This also affects the way that EOF is detected)
X			 */
X			i = 1;
X		}
X	
X		/* react accordingly... */
X		switch (i)
X		{
X		  case -1:	/* assume we got an EINTR because of SIGWINCH */
X			if (*o_lines != LINES || *o_columns != COLS)
X			{
X				*o_lines = LINES;
X				*o_columns = COLS;
X#ifndef CRUNCH
X				if (!wset)
X				{
X					*o_window = LINES - 1;
X				}
X#endif
X				if (mode != MODE_EX)
X				{
X					/* pretend the user hit ^L */
X					*buf = ctrl('L');
X					return 1;
X				}
X			}
X			break;
X	
X		  case 0:	/* timeout */
X			return 0;
X	
X		  default:	/* characters available */
X			return read(0, buf, len);
X		}
X	}
X}
X# else
X
X# if M_SYSV
X/* For System-V or Coherent, we use VMIN/VTIME to implement the timeout.
X * For no timeout, VMIN should be 1 and VTIME should be 0; for timeout,
X * VMIN should be 0 and VTIME should be the timeout value.
X */
X#  include <termio.h>
Xint ttyread(buf, len, time)
X	char	*buf;	/* where to store the gotten characters */
X	int	len;	/* maximum number of characters to read */
X	int	time;	/* maximum time to allow for reading */
X{
X	struct termio tio;
X	int	bytes;	/* number of bytes actually read */
X
X	/* arrange for timeout */
X	ioctl(0, TCGETA, &tio);
X	if (time)
X	{
X		tio.c_cc[VMIN] = 0;
X		tio.c_cc[VTIME] = time;
X	}
X	else
X	{
X		tio.c_cc[VMIN] = 1;
X		tio.c_cc[VTIME] = 0;
X	}
X	ioctl(0, TCSETA, &tio);
X
X	/* Perform the read.  Loop if EINTR error happens */
X	while ((bytes = read(0, buf, len)) < 0)
X	{
X		/* probably EINTR error because a SIGWINCH was received */
X		if (*o_lines != LINES || *o_columns != COLS)
X		{
X			*o_lines = LINES;
X			*o_columns = COLS;
X#ifndef CRUNCH
X			if (!wset)
X			{
X				*o_window = LINES - 1;
X			}
X#endif
X			if (mode != MODE_EX)
X			{
X				/* pretend the user hit ^L */
X				*buf = ctrl('L');
X				return 1;
X			}
X		}
X	}
X
X	/* return the number of bytes read */
X	return bytes;
X
X	/* NOTE: The terminal may be left in a timeout-mode after this function
X	 * returns.  This shouldn't be a problem since Elvis *NEVER* tries to
X	 * read from the keyboard except through this function.
X	 */
X}
X
X# else /* any other version of UNIX, assume it is V7 compatible */
X
X/* For V7 UNIX (including Minix) we set an alarm() before doing a blocking
X * read(), and assume that the SIGALRM signal will cause the read() function
X * to give up.
X */
X
X#include <setjmp.h>
X
Xstatic jmp_buf env;
X
X/*ARGSUSED*/
Xint dummy(signo)
X	int	signo;
X{
X	longjmp(env, 1);
X}
Xint ttyread(buf, len, time)
X	char	*buf;	/* where to store the gotten characters */
X	int	len;	/* maximum number of characters to read */
X	int	time;	/* maximum time to allow for reading */
X{
X	/* arrange for timeout */
X#if __GNUC__ || _ANSI
X	signal(SIGALRM, (void (*)()) dummy);
X#else
X	signal(SIGALRM, dummy);
X#endif
X	alarm(time);
X
X	/* perform the blocking read */
X	if (setjmp(env) == 0)
X	{
X		len = read(0, buf, len);
X	}
X	else /* I guess we timed out */
X	{
X		len = 0;
X	}
X
X	/* cancel the alarm */
X#if _ANSI
X	signal(SIGALRM, (void (*)())dummy); /* work around a bug in Minix */
X#else
X	signal(SIGALRM, dummy);		    /* work around a bug in Minix */
X#endif
X	alarm(0);
X
X	/* return the number of bytes read */
X	if (len < 0)
X		len = 0;
X	return len;
X}
X
X# endif /* !(M_SYSV || COHERENT) */
X# endif /* !BSD */
X
X#endif /* ANY_UNIX */
/
echo x - vars.c
sed '/^X/s///' > vars.c << '/'
X/* vars.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains variables which weren't happy anyplace else */
X
X#include "config.h"
X#include "vi.h"
X
X/*------------------------------------------------------------------------*/
X
X/* used to remember whether the file has been modified */
Xstruct _viflags	viflags;
X
X/* used to access the tmp file */
Xlong		lnum[MAXBLKS];
Xlong		nlines;
Xint		tmpfd = -1;
Xint		tmpnum;
X#ifndef CRUNCH
Xint		wset = FALSE;
X#endif
X
X/* used to keep track of the current file & alternate file */
Xlong		origtime;
Xchar		origname[256];
Xchar		prevorig[256];
Xlong		prevline = 1;
X
X/* used to track various places in the text */
XMARK		mark[NMARKS];	/* marks 'a through 'z, plus mark '' */
XMARK		cursor;		/* the cursor position within the file */
X
X/* which mode of the editor we're in */
Xint		mode;		/* vi mode? ex mode? quitting? */
X
X/* used to manage the args list */
Xchar		args[BLKSIZE];	/* list of filenames to edit */
Xint		argno;		/* index of current file in args list */
Xint		nargs;		/* number of filenames in args[] */
X
X/* dummy var, never explicitly referenced */
Xint		bavar;		/* used only in BeforeAfter macros */
X
X/* used to detect changes that invalidate cached text/blocks */
Xlong		changes;	/* incremented when file is changed */
Xint		significant;	/* boolean: was a *REAL* change made? */
X
X/* used to support the pfetch() macro */
Xint		plen;		/* length of the line */
Xlong		pline;		/* line number that len refers to */
Xlong		pchgs;		/* "changes" level that len refers to */
Xchar		*ptext;		/* text of previous line, if valid */
X
X/* misc temporary storage - mostly for strings */
XBLK		tmpblk;		/* a block used to accumulate changes */
X
X/* screen oriented stuff */
Xlong		topline;	/* file line number of top line */
Xint		leftcol;	/* column number of left col */
Xint		physcol;	/* physical column number that cursor is on */
Xint		physrow;	/* physical row number that cursor is on */
X
X/* used to help minimize that "[Hit a key to continue]" message */
Xint		exwrote;	/* Boolean: was the last ex command wordy? */
X
X/* This variable affects the behaviour of certain functions -- most importantly
X * the input function.
X */
Xint		doingdot;	/* boolean: are we doing the "." command? */
X
X/* This variable affects the behaviour of the ":s" command, and it is also
X * used to detect & prohibit nesting of ":g" commands
X */
Xint		doingglobal;	/* boolean: are doing a ":g" command? */
X
X/* This variable is zeroed before a command executes, and later ORed with the
X * command's flags after the command has been executed.  It is used to force
X * certain flags to be TRUE for *some* invocations of a particular command.
X * For example, "/regexp/+offset" forces the LNMD flag, and sometimes a "p"
X * or "P" command will force FRNT.
X */
Xint		force_flags;
X
X/* These are used for reporting multi-line changes to the user */
Xlong		rptlines;	/* number of lines affected by a command */
Xchar		*rptlabel;	/* description of how lines were affected */
X
X/* These store info that pertains to the shift-U command */
Xlong	U_line;			/* line# of the undoable line, or 0l for none */
Xchar	U_text[BLKSIZE];	/* contents of the undoable line */
X
X
X#ifndef NO_VISIBLE
X/* These are used to implement the 'v' and 'V' commands */
XMARK	V_from;			/* starting point for v or V */
Xint	V_linemd;		/* boolean: doing line-mode version? (V, not v) */
X#endif
X
X/* Bigger stack req'ed for TOS and TURBOC */
X
X#if TOS
Xlong	_stksize = 16384;
X#endif
X
X#if TURBOC
X#include <dos.h>
Xextern unsigned _stklen = 16384U;
X#endif
/
echo x - vcmd.c
sed '/^X/s///' > vcmd.c << '/'
X/* vcmd.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains the functions that handle VI commands */
X
X
X#include "config.h"
X#include "ctype.h"
X#include "vi.h"
X#if MSDOS
X# include <process.h>
X# include <string.h>
X#endif
X#if TOS
X# include <osbind.h>
X# include <string.h>
X#endif
X#if OSK
X# include <stdio.h>
X#endif
X
X
X/* This function puts the editor in EX mode */
XMARK v_quit()
X{
X	move(LINES - 1, 0);
X	mode = MODE_EX;
X	return cursor;
X}
X
X/* This function causes the screen to be redrawn */
XMARK v_redraw()
X{
X	redraw(MARK_UNSET, FALSE);
X	return cursor;
X}
X
X/* This function executes a string of EX commands, and waits for a user keystroke
X * before returning to the VI screen.  If that keystroke is another ':', then
X * another EX command is read and executed.
X */
X/*ARGSUSED*/
XMARK v_1ex(m, text)
X	MARK	m;	/* the current line */
X	char	*text;	/* the first command to execute */
X{
X	/* run the command.  be careful about modes & output */
X	exwrote = (mode == MODE_COLON);
X	doexcmd(text);
X	exrefresh();
X
X	/* if mode is no longer MODE_VI, then we should quit right away! */
X	if (mode != MODE_VI && mode != MODE_COLON)
X		return cursor;
X
X	/* The command did some output.  Wait for a keystoke. */
X	if (exwrote)
X	{
X		mode = MODE_VI;	
X		msg("[Hit <RETURN> to continue]");
X		if (getkey(0) == ':')
X		{	mode = MODE_COLON;
X			addch('\n');
X		}
X		else
X			redraw(MARK_UNSET, FALSE);
X	}
X
X	return cursor;
X}
X
X/* This function undoes the last change */
X/*ARGSUSED*/
XMARK v_undo(m)
X	MARK	m;	/* (ignored) */
X{
X	if (undo())
X	{
X		redraw(MARK_UNSET, FALSE);
X	}
X	return cursor;
X}
X
X/* This function deletes the character(s) that the cursor is on */
XMARK v_xchar(m, cnt, cmd)
X	MARK	m;	/* where to start deletions */
X	long	cnt;	/* number of chars to delete */
X	int	cmd;	/* either 'x' or 'X' */
X{
X	DEFAULT(1);
X
X	/* for 'X', adjust so chars are deleted *BEFORE* cursor */
X	if (cmd == 'X')
X	{
X		if (markidx(m) < cnt)
X			return MARK_UNSET;
X		m -= cnt;
X	}
X
X	/* make sure we don't try to delete more thars than there are */
X	pfetch(markline(m));
X	if (markidx(m + cnt) > plen)
X	{
X		cnt = plen - markidx(m);
X	}
X	if (cnt == 0L)
X	{
X		return MARK_UNSET;
X	}
X
X	/* do it */
X	ChangeText
X	{
X		cut(m, m + cnt);
X		delete(m, m + cnt);
X	}
X	return m;
X}
X
X/* This function defines a mark */
X/*ARGSUSED*/
XMARK v_mark(m, count, key)
X	MARK	m;	/* where the mark will be */
X	long	count;	/* (ignored) */
X	int	key;	/* the ASCII label of the mark */
X{
X	if (key < 'a' || key > 'z')
X	{
X		msg("Marks must be from a to z");
X	}
X	else
X	{
X		mark[key - 'a'] = m;
X	}
X	return m;
X}
X
X/* This function toggles upper & lower case letters */
XMARK v_ulcase(m, cnt)
X	MARK	m;	/* where to make the change */
X	long	cnt;	/* number of chars to flip */
X{
X	REG char 	*pos;
X	REG int		j;
X
X	DEFAULT(1);
X
X	/* fetch the current version of the line */
X	pfetch(markline(m));
X
X	/* for each position in the line */
X	for (j = 0, pos = &ptext[markidx(m)]; j < cnt && *pos; j++, pos++)
X	{
X		if (isupper(*pos))
X		{
X			tmpblk.c[j] = tolower(*pos);
X		}
X		else
X		{
X			tmpblk.c[j] = toupper(*pos);
X		}
X	}
X
X	/* if the new text is different from the old, then change it */
X	if (strncmp(tmpblk.c, &ptext[markidx(m)], j))
X	{
X		ChangeText
X		{
X			tmpblk.c[j] = '\0';
X			change(m, m + j, tmpblk.c);
X		}
X	}
X
X	return m + j;
X}
X
X
XMARK v_replace(m, cnt, key)
X	MARK	m;	/* first char to be replaced */
X	long	cnt;	/* number of chars to replace */
X	int	key;	/* what to replace them with */
X{
X	REG char	*text;
X	REG int		i;
X
X	DEFAULT(1);
X
X	/* map ^M to '\n' */
X	if (key == '\r')
X	{
X		key = '\n';
X	}
X
X	/* make sure the resulting line isn't too long */
X	if (cnt > BLKSIZE - 2 - markidx(m))
X	{
X		cnt = BLKSIZE - 2 - markidx(m);
X	}
X
X	/* build a string of the desired character with the desired length */
X	for (text = tmpblk.c, i = cnt; i > 0; i--)
X	{
X		*text++ = key;
X	}
X	*text = '\0';
X
X	/* make sure cnt doesn't extend past EOL */
X	pfetch(markline(m));
X	key = markidx(m);
X	if (key + cnt > plen)
X	{
X		cnt = plen - key;
X	}
X
X	/* do the replacement */
X	ChangeText
X	{
X		change(m, m + cnt, tmpblk.c);
X	}
X
X	if (*tmpblk.c == '\n')
X	{
X		return (m & ~(BLKSIZE - 1)) + cnt * BLKSIZE;
X	}
X	else
X	{
X		return m + cnt - 1;
X	}
X}
X
XMARK v_overtype(m)
X	MARK		m;	/* where to start overtyping */
X{
X	MARK		end;	/* end of a substitution */
X	static long	width;	/* width of a single-line replace */
X
X	/* the "doingdot" version of replace is really a substitution */
X	if (doingdot)
X	{
X		/* was the last one really repeatable? */
X		if (width < 0)
X		{
X			msg("Can't repeat a multi-line overtype command");
X			return MARK_UNSET;
X		}
X
X		/* replacing nothing by nothing?  Don't bother */
X		if (width == 0)
X		{
X			return m;
X		}
X
X		/* replace some chars by repeated text */
X		return v_subst(m, width);
X	}
X
X	/* Normally, we input starting here, in replace mode */
X	ChangeText
X	{
X		end = input(m, m, WHEN_VIREP, FALSE);
X	}
X
X	/* if we ended on the same line we started on, then this
X	 * overtype is repeatable via the dot key.
X	 */
X	if (markline(end) == markline(m) && end >= m - 1L)
X	{
X		width = end - m + 1L;
X	}
X	else /* it isn't repeatable */
X	{
X		width = -1L;
X	}
X
X	return end;
X}
X
X
X/* This function selects which cut buffer to use */
X/*ARGSUSED*/
XMARK v_selcut(m, cnt, key)
X	MARK	m;
X	long	cnt;
X	int	key;
X{
X	cutname(key);
X	return m;
X}
X
X/* This function pastes text from a cut buffer */
X/*ARGSUSED*/
XMARK v_paste(m, cnt, cmd)
X	MARK	m;	/* where to paste the text */
X	long	cnt;	/* (ignored) */
X	int	cmd;	/* either 'p' or 'P' */
X{
X	MARK	dest;
X
X	ChangeText
X	{
X		/* paste the text, and find out where it ends */
X		dest = paste(m, cmd == 'p', TRUE);
X
X		/* was that a line-mode paste? */
X		if (dest && markline(dest) != markline(m))
X		{
X			/* line-mode pastes leave the cursor at the front
X			 * of the first pasted line.
X			 */
X			dest = m;
X			if (cmd == 'p')
X			{
X				dest += BLKSIZE;
X			}
X			force_flags |= FRNT;
X		}
X	}
X	return dest;
X}
X
X/* This function yanks text into a cut buffer */
XMARK v_yank(m, n)
X	MARK	m, n;	/* range of text to yank */
X{
X	cut(m, n);
X	return m;
X}
X
X/* This function deletes a range of text */
XMARK v_delete(m, n)
X	MARK	m, n;	/* range of text to delete */
X{
X	/* illegal to try and delete nothing */
X	if (n <= m)
X	{
X		return MARK_UNSET;
X	}
X
X	/* Do it */
X	ChangeText
X	{
X		cut(m, n);
X		delete(m, n);
X	}
X	return m;
X}
X
X
X/* This starts input mode without deleting anything */
XMARK v_insert(m, cnt, key)
X	MARK	m;	/* where to start (sort of) */
X	long	cnt;	/* repeat how many times? */
X	int	key;	/* what command is this for? {a,A,i,I,o,O} */
X{
X	int	wasdot;
X	long	reps;
X	int	above;	/* boolean: new line going above old line? */
X
X	DEFAULT(1);
X
X	ChangeText
X	{
X		/* tweak the insertion point, based on command key */
X		above = FALSE;
X		switch (key)
X		{
X		  case 'i':
X			break;
X
X		  case 'a':
X			pfetch(markline(m));
X			if (plen > 0)
X			{
X				m++;
X			}
X			break;
X
X		  case 'I':
X			m = m_front(m, 1L);
X			break;
X
X		  case 'A':
X			pfetch(markline(m));
X			m = (m & ~(BLKSIZE - 1)) + plen;
X			break;
X
X		  case 'O':
X			m &= ~(BLKSIZE - 1);
X			add(m, "\n");
X			above = TRUE;
X			break;
X
X		  case 'o':
X			m = (m & ~(BLKSIZE - 1)) + BLKSIZE;
X			add(m, "\n");
X			break;
X		}
X
X		/* insert the same text once or more */
X		for (reps = cnt, wasdot = doingdot; reps > 0; reps--, doingdot = TRUE)
X		{
X			m = input(m, m, WHEN_VIINP, above) + 1;
X		}
X		if (markidx(m) > 0)
X		{
X			m--;
X		}
X
X		doingdot = wasdot;
X	}
X
X#ifndef CRUNCH
X# ifndef NO_EXTENSIONS
X	if (key == 'i' && *o_inputmode && mode == MODE_VI)
X	{
X		msg("Now in command mode!  To return to input mode, hit <i>");
X	}
X# endif
X#endif
X
X	return m;
X}
X
X/* This starts input mode with some text deleted */
XMARK v_change(m, n)
X	MARK	m, n;	/* the range of text to change */
X{
X	int	lnmode;	/* is this a line-mode change? */
X
X	/* swap them if they're in reverse order */
X	if (m > n)
X	{
X		MARK	tmp;
X		tmp = m;
X		m = n;
X		n = tmp;
X	}
X
X	/* for line mode, retain the last newline char */
X	lnmode = (markidx(m) == 0 && markidx(n) == 0 && m != n);
X	if (lnmode)
X	{
X		n -= BLKSIZE;
X		pfetch(markline(n));
X		n = (n & ~(BLKSIZE - 1)) + plen;
X	}
X
X	ChangeText
X	{
X		cut(m, n);
X		m = input(m, n, WHEN_VIINP, FALSE);
X	}
X
X	return m;
X}
X
X/* This function replaces a given number of characters with input */
XMARK v_subst(m, cnt)
X	MARK	m;	/* where substitutions start */
X	long	cnt;	/* number of chars to replace */
X{
X	DEFAULT(1);
X
X	/* make sure we don't try replacing past EOL */
X	pfetch(markline(m));
X	if (markidx(m) + cnt > plen)
X	{
X		cnt = plen - markidx(m);
X	}
X
X	/* Go for it! */
X	ChangeText
X	{
X		cut(m, m + cnt);
X		m = input(m, m + cnt, WHEN_VIINP, FALSE);
X	}
X	return m;
X}
X
X/* This calls the ex "join" command to join some lines together */
XMARK v_join(m, cnt)
X	MARK	m;	/* the first line to be joined */
X	long	cnt;	/* number of other lines to join */
X{
X	MARK	joint;	/* where the lines were joined */
X
X	DEFAULT(1);
X
X	/* figure out where the joint will be */
X	pfetch(markline(m));
X	joint = (m & ~(BLKSIZE - 1)) + plen;
X
X	/* join the lines */
X	cmd_join(m, m + MARK_AT_LINE(cnt), CMD_JOIN, 0, "");
X
X	/* the cursor should be left at the joint */
X	return joint;
X}
X
X
X/* This calls the ex "<" command to shift some lines left */
XMARK v_lshift(m, n)
X	MARK	m, n;	/* range of lines to shift */
X{
X	/* adjust for inclusive endmarks in ex */
X	n -= BLKSIZE;
X
X	cmd_shift(m, n, CMD_SHIFTL, FALSE, (char *)0);
X
X	return m;
X}
X
X/* This calls the ex ">" command to shift some lines right */
XMARK v_rshift(m, n)
X	MARK	m, n;	/* range of lines to shift */
X{
X	/* adjust for inclusive endmarks in ex */
X	n -= BLKSIZE;
X
X	cmd_shift(m, n, CMD_SHIFTR, FALSE, (char *)0);
X
X	return m;
X}
X
X/* This filters some lines through a preset program, to reformat them */
XMARK v_reformat(m, n)
X	MARK	m, n;	/* range of lines to shift */
X{
X	/* adjust for inclusive endmarks in ex */
X	n -= BLKSIZE;
X
X	/* run the filter command */
X	filter(m, n, o_equalprg, TRUE);
X
X	redraw(MARK_UNSET, FALSE);
X	return m;
X}
X
X
X/* This runs some lines through a filter program */
XMARK v_filter(m, n)
X	MARK	m, n;	/* range of lines to shift */
X{
X	char	cmdln[150];	/* a shell command line */
X
X	/* adjust for inclusive endmarks in ex */
X	n -= BLKSIZE;
X
X	if (vgets('!', cmdln, sizeof(cmdln)) > 0)
X	{
X		filter(m, n, cmdln, TRUE);
X	}
X
X	redraw(MARK_UNSET, FALSE);
X	return m;
X}
X
X
X/* This function runs the ex "file" command to show the file's status */
XMARK v_status()
X{
X	cmd_file(cursor, cursor, CMD_FILE, 0, "");
X	return cursor;
X}
X
X
X/* This function runs the ":&" command to repeat the previous :s// */
XMARK v_again(m, n)
X	MARK	m, n;
X{
X	cmd_substitute(m, n - BLKSIZE, CMD_SUBAGAIN, TRUE, "");
X	return cursor;
X}
X
X
X
X/* This function switches to the previous file, if possible */
XMARK v_switch()
X{
X	if (!*prevorig)
X		msg("No previous file");
X	else
X	{	strcpy(tmpblk.c, prevorig);
X		cmd_edit(cursor, cursor, CMD_EDIT, 0, tmpblk.c);
X	}
X	return cursor;
X}
X
X/* This function does a tag search on a keyword */
X/*ARGSUSED*/
XMARK v_tag(keyword, m, cnt)
X	char	*keyword;
X	MARK	m;
X	long	cnt;
X{
X	/* move the cursor to the start of the tag name, where m is */
X	cursor = m;
X
X	/* perform the tag search */
X	cmd_tag(cursor, cursor, CMD_TAG, 0, keyword);
X
X	return cursor;
X}
X
X#ifndef NO_EXTENSIONS
X/* This function looks up a keyword by calling the helpprog program */
X/*ARGSUSED*/
XMARK v_keyword(keyword, m, cnt)
X	char	*keyword;
X	MARK	m;
X	long	cnt;
X{
X	int	waswarn;
X	char	cmdline[130];
X
X	move(LINES - 1, 0);
X	addstr("---------------------------------------------------------\n");
X	clrtoeol();
X	refresh();
X	sprintf(cmdline, "%s %s", o_keywordprg, keyword);
X	waswarn = *o_warn;
X	*o_warn = FALSE;
X	suspend_curses();
X	if (system(cmdline))
X	{
X		addstr("<<< failed >>>\n");
X	}
X	resume_curses(FALSE);
X	mode = MODE_VI;
X	redraw(MARK_UNSET, FALSE);
X	*o_warn = waswarn;
X
X	return m;
X}
X
X
X
XMARK v_increment(keyword, m, cnt)
X	char	*keyword;
X	MARK	m;
X	long	cnt;
X{
X	static	sign;
X	char	newval[12];
X	long	atol();
X
X	DEFAULT(1);
X
X	/* get one more keystroke, unless doingdot */
X	if (!doingdot)
X	{
X		sign = getkey(0);
X	}
X
X	/* adjust the number, based on that second keystroke */
X	switch (sign)
X	{
X	  case '+':
X	  case '#':
X		cnt = atol(keyword) + cnt;
X		break;
X
X	  case '-':
X		cnt = atol(keyword) - cnt;
X		break;
X
X	  case '=':
X		break;
X
X	  default:
X		return MARK_UNSET;
X	}
X	sprintf(newval, "%ld", cnt);
X
X	ChangeText
X	{
X		change(m, m + strlen(keyword), newval);
X	}
X
X	return m;
X}
X#endif
X
X
X/* This function acts like the EX command "xit" */
X/*ARGSUSED*/
XMARK v_xit(m, cnt, key)
X	MARK	m;	/* ignored */
X	long	cnt;	/* ignored */
X	int	key;	/* must be a second 'Z' */
X{
X	/* if second char wasn't 'Z', fail */
X	if (key != 'Z')
X	{
X		return MARK_UNSET;
X	}
X
X	/* move the cursor to the bottom of the screen */
X	move(LINES - 1, 0);
X	clrtoeol();
X
X	/* do the xit command */
X	cmd_xit(m, m, CMD_XIT, FALSE, "");
X
X	/* return the cursor */
X	return m;
X}
X
X
X/* This function undoes changes to a single line, if possible */
XMARK v_undoline(m)
X	MARK	m;	/* where we hope to undo the change */
X{
X	/* make sure we have the right line in the buffer */
X	if (markline(m) != U_line)
X	{
X		return MARK_UNSET;
X	}
X
X	/* fix it */
X	ChangeText
X	{
X		strcat(U_text, "\n");
X		change(MARK_AT_LINE(U_line), MARK_AT_LINE(U_line + 1), U_text);
X	}
X
X	/* nothing in the buffer anymore */
X	U_line = -1L;
X
X	/* return, with the cursor at the front of the line */
X	return m & ~(BLKSIZE - 1);
X}
X
X
X#ifndef NO_ERRLIST
XMARK v_errlist(m)
X	MARK	m;
X{
X	cmd_errlist(m, m, CMD_ERRLIST, FALSE, "");
X	return cursor;
X}
X#endif
X
X
X#ifndef NO_AT
X/*ARGSUSED*/
XMARK v_at(m, cnt, key)
X	MARK	m;
X	long	cnt;
X	int	key;
X{
X	int	size;
X
X	size = cb2str(key, tmpblk.c, BLKSIZE);
X	if (size <= 0 || size == BLKSIZE)
X	{
X		return MARK_UNSET;
X	}
X
X	execmap(0, tmpblk.c, FALSE);
X	return cursor;
X}
X#endif
X
X
X#ifdef SIGTSTP
XMARK v_suspend()
X{
X	cmd_suspend(MARK_UNSET, MARK_UNSET, CMD_SUSPEND, FALSE, "");
X	return MARK_UNSET;
X}
X#endif
X
X
X#ifndef NO_VISIBLE
X/*ARGSUSED*/
XMARK v_start(m, cnt, cmd)
X	MARK	m;	/* starting point for a v or V command */
X	long	cnt;	/* ignored */
X	int	cmd;	/* either 'v' or 'V' */
X{
X	if (V_from)
X	{
X		V_from = MARK_UNSET;
X	}
X	else
X	{
X		V_from = m;
X		V_linemd = isupper(cmd);
X	}
X	return m;
X}
X#endif
X
X#ifndef NO_POPUP
X# define MENU_HEIGHT 11
X# define MENU_WIDTH  23
XMARK v_popup(m, n)
X	MARK		m, n;	/* the range of text to change */
X{
X	int		i;
X	int		y, x;	/* position where the window will pop up at */
X	int		key;	/* keystroke from the user */
X	int		sel;	/* index of the selected operation */
X	static int	dfltsel;/* default value of sel */
X	static char	*labels[11] =
X	{
X		"ESC cancel!         ",
X		" d  delete (cut)    ",
X		" y  yank (copy)     ",
X		" p  paste after     ",
X		" P  paste before    ",
X		" >  more indented   ",
X		" <  less indented   ",
X		" =  reformat        ",
X		" !  external filter ",
X		" ZZ save & exit     ",
X		" u  undo previous   "
X	};
X
X	/* try to position the menu near the cursor */
X	x = physcol - (MENU_WIDTH / 2);
X	if (x < 0)
X		x = 0;
X	else if (x + MENU_WIDTH + 2 > COLS)
X		x = COLS - MENU_WIDTH - 2;
X	if (markline(cursor) < topline || markline(cursor) > botline)
X		y = 0;
X	else if (markline(cursor) + 1L + MENU_HEIGHT > botline)
X		y = (int)(markline(cursor) - topline) - MENU_HEIGHT;
X	else
X		y = (int)(markline(cursor) - topline) + 1L;
X
X	/* draw the menu */
X	for (sel = 0; sel < MENU_HEIGHT; sel++)
X	{
X		move(y + sel, x);
X		do_POPUP();
X		if (sel == dfltsel)
X			qaddstr("-->");
X		else
X			qaddstr("   ");
X		qaddstr(labels[sel]);
X		do_SE();
X	}
X
X	/* get a selection */
X	move(y + dfltsel, x + 4);
X	for (sel = dfltsel; (key = getkey(WHEN_POPUP)) != '\\' && key != '\r'; )
X	{
X		/* erase the selection arrow */
X		move(y + sel, x);
X		do_POPUP();
X		qaddstr("   ");
X		qaddstr(labels[sel]);
X		do_SE();
X
X		/* process the user's keystroke */
X		if (key == 'j' && sel < MENU_HEIGHT - 1)
X		{
X			sel++;
X		}
X		else if (key == 'k' && sel > 0)
X		{
X			sel--;
X		}
X		else if (key == '\033')
X		{
X			sel = 0;
X			break;
X		}
X		else
X		{
X			for (i = 1; i < MENU_HEIGHT && labels[i][1] != key; i++)
X			{
X			}
X			if (i < MENU_HEIGHT)
X			{
X				sel = i;
X				break;
X			}
X		}
X
X		/* redraw the arrow, possibly in a new place */
X		move(y + sel, x);
X		do_POPUP();
X		qaddstr("-->");
X		qaddstr(labels[sel]);
X		do_SE();
X		move(y + sel, x + 4);
X	}
X
X	/* in most cases, the default selection is "paste after" */
X	dfltsel = 3;
X
X	/* perform the appropriate action */
X	switch (sel)
X	{
X	  case 0:
X		m = cursor;
X		dfltsel = 0;
X		break;
X
X	  case 1: /* delete (cut) */
X		m = v_delete(m, n);
X		break;
X
X	  case 2: /* yank (copy) */
X		m = v_yank(m, n);
X		break;
X
X	  case 3: /* paste after */
X		m = v_paste(n, 1L, 'P');
X		break;
X
X	  case 4: /* paste before */
X		m = v_paste(m, 1L, 'P');
X		dfltsel = 4;
X		break;
X
X	  case 5: /* more indented */
X		m = v_rshift(m, n);
X		dfltsel = 5;
X		break;
X
X	  case 6: /* less indented */
X		m = v_lshift(m, n);
X		dfltsel = 6;
X		break;
X
X	  case 7: /* reformat */
X		m = v_reformat(m, n);
X		dfltsel = 7;
X		break;
X
X	  case 8: /* external filter */
X		m = v_filter(m, n);
X		dfltsel = 0;
X		break;
X
X	  case 9: /* save & exit */
X		/* get confirmation first */
X		do
X		{
X			key = getkey(0);
X		} while (key != '\\' && key != 'Z' && key != '\r'    /* good */
X		      && key != '\033');			     /* bad  */
X		if (key != '\033')
X		{
X			m = v_xit(m, 0L, 'Z');
X		}
X		break;
X
X	  case 10: /* undo previous */
X		m = v_undo(m);
X		dfltsel = 9;
X		break;
X	}
X
X	/* arrange for the menu to be erased (except that "chg from kbd"
X	 * already erased it, and "save & exit" doesn't care)
X	 */
X	if (sel != 5 && sel != 9)
X		redraw(MARK_UNSET, FALSE);
X
X	return m;
X}
X#endif /* undef NO_POPUP */
/
echo x - vi.c
sed '/^X/s///' > vi.c << '/'
X/* vi.c */
X
X/* Author:
X *	Steve Kirkendall
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X#include "config.h"
X#include "ctype.h"
X#include "vi.h"
X
X
X
X/* This array describes what each key does */
X#define NO_FUNC		(MARK (*)())0
X
X#define NO_ARGS		0
X#define CURSOR		1
X#define CURSOR_CNT_KEY	2
X#define CURSOR_MOVED	3
X#define CURSOR_EOL	4
X#define ZERO		5
X#define DIGIT		6
X#define CURSOR_TEXT	7
X#define KEYWORD		8
X#define ARGSMASK	0x0f
X#define	C_C_K_REP1	(CURSOR_CNT_KEY | 0x10)
X#define C_C_K_CUT	(CURSOR_CNT_KEY | 0x20)
X#define C_C_K_MARK	(CURSOR_CNT_KEY | 0x30)
X#define C_C_K_CHAR	(CURSOR_CNT_KEY | 0x40)
X#ifndef NO_SHOWMODE
Xstatic int keymodes[] = {0, WHEN_REP1, WHEN_CUT, WHEN_MARK, WHEN_CHAR};
X# define KEYMODE(args) (keymodes[(args) >> 4])
X#else
X# define KEYMODE(args) 0
X#endif
X
Xstatic struct keystru
X{
X	MARK	(*func)();	/* the function to run */
X	uchar	args;		/* description of the args needed */
X#ifndef NO_VISIBLE
X	short	flags;
X#else
X	uchar	flags;		/* other stuff */
X#endif
X}
X	vikeys[] =
X{
X/* NUL not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X#ifndef NO_EXTENSIONS
X/* ^A  find cursor word */	{m_wsrch,	KEYWORD,	MVMT|NREL|VIZ},
X#else
X/* ^A  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X#endif
X/* ^B  page backward	*/	{m_scroll,	CURSOR,		FRNT|VIZ},
X/* ^C  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X/* ^D  scroll dn 1/2page*/	{m_scroll,	CURSOR,		NCOL|VIZ},
X/* ^E  scroll up	*/	{m_scroll,	CURSOR,		NCOL|VIZ},
X/* ^F  page forward	*/	{m_scroll,	CURSOR,		FRNT|VIZ},
X/* ^G  show file status	*/	{v_status,	NO_ARGS, 	NO_FLAGS},
X/* ^H  move left, like h*/	{m_left,	CURSOR,		MVMT|VIZ},
X/* ^I  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X/* ^J  move down	*/	{m_updnto,	CURSOR,		MVMT|LNMD|VIZ|INCL},
X/* ^K  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X/* ^L  redraw screen	*/	{v_redraw,	NO_ARGS,	NO_FLAGS|VIZ},
X/* ^M  mv front next ln */	{m_updnto,	CURSOR,		MVMT|FRNT|LNMD|VIZ|INCL},
X/* ^N  move down	*/	{m_updnto,	CURSOR,		MVMT|LNMD|VIZ|INCL|NCOL},
X/* ^O  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X/* ^P  move up		*/	{m_updnto,	CURSOR,		MVMT|LNMD|VIZ|INCL|NCOL},
X/* ^Q  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X/* ^R  redraw screen	*/	{v_redraw,	NO_ARGS,	NO_FLAGS|VIZ},
X/* ^S  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X/* ^T  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X/* ^U  scroll up 1/2page*/	{m_scroll,	CURSOR,		NCOL|VIZ},
X/* ^V  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X/* ^W  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X/* ^X  move to phys col	*/	{m_tocol,	CURSOR,		MVMT|NREL|VIZ},
X/* ^Y  scroll down	*/	{m_scroll,	CURSOR,		NCOL|VIZ},
X#ifdef SIGTSTP
X/* ^Z  suspend elvis	*/	{v_suspend,	NO_ARGS,	NO_FLAGS},
X#else
X/* ^Z  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X#endif
X/* ESC not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X/* ^\  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X/* ^]  keyword is tag	*/	{v_tag,		KEYWORD,	NO_FLAGS},
X/* ^^  previous file	*/	{v_switch,	CURSOR,		NO_FLAGS},
X/* ^_  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X/* SPC move right,like l*/	{m_right,	CURSOR,		MVMT|INCL|VIZ},
X/*  !  run thru filter	*/	{v_filter,	CURSOR_MOVED,	FRNT|LNMD|INCL|VIZ},
X/*  "  select cut buffer*/	{v_selcut,	C_C_K_CUT,	PTMV|VIZ},
X#ifndef NO_EXTENSIONS
X/*  #  increment number	*/	{v_increment,	KEYWORD,	SDOT},
X#else
X/*  #  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X#endif
X/*  $  move to rear	*/	{m_rear,	CURSOR,		MVMT|INCL|VIZ},
X/*  %  move to match	*/	{m_match,	CURSOR,		MVMT|INCL|VIZ},
X/*  &  repeat subst	*/	{v_again,	CURSOR_MOVED,	SDOT|NCOL|LNMD|INCL},
X/*  '  move to a mark	*/	{m_tomark,	C_C_K_MARK,	MVMT|FRNT|NREL|LNMD|INCL|VIZ},
X#ifndef NO_SENTENCE
X/*  (  mv back sentence	*/	{m_sentence,	CURSOR,		MVMT|VIZ},
X/*  )  mv fwd sentence	*/	{m_sentence,	CURSOR,		MVMT|VIZ},
X#else
X/*  (  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X/*  )  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X#endif
X#ifndef NO_ERRLIST
X/*  *  errlist		*/	{v_errlist,	CURSOR,		FRNT|NREL},
X#else
X/*  *  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X#endif
X/*  +  mv front next ln */	{m_updnto,	CURSOR,		MVMT|FRNT|LNMD|VIZ|INCL},
X#ifndef NO_CHARSEARCH
X/*  ,  reverse [fFtT] cmd*/	{m__ch,		CURSOR,		MVMT|INCL|VIZ},
X#else
X/*  ,  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X#endif
X/*  -  mv front prev ln	*/	{m_updnto,	CURSOR,		MVMT|FRNT|LNMD|VIZ|INCL},
X/*  .  special...	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X/*  /  forward search	*/	{m_fsrch,	CURSOR_TEXT,	MVMT|NREL|VIZ},
X/*  0  part of count?	*/	{NO_FUNC,	ZERO,		MVMT|PTMV|VIZ},
X/*  1  part of count	*/	{NO_FUNC,	DIGIT,		PTMV|VIZ},
X/*  2  part of count	*/	{NO_FUNC,	DIGIT,		PTMV|VIZ},
X/*  3  part of count	*/	{NO_FUNC,	DIGIT,		PTMV|VIZ},
X/*  4  part of count	*/	{NO_FUNC,	DIGIT,		PTMV|VIZ},
X/*  5  part of count	*/	{NO_FUNC,	DIGIT,		PTMV|VIZ},
X/*  6  part of count	*/	{NO_FUNC,	DIGIT,		PTMV|VIZ},
X/*  7  part of count	*/	{NO_FUNC,	DIGIT,		PTMV|VIZ},
X/*  8  part of count	*/	{NO_FUNC,	DIGIT,		PTMV|VIZ},
X/*  9  part of count	*/	{NO_FUNC,	DIGIT,		PTMV|VIZ},
X/*  :  run single EX cmd*/	{v_1ex,		CURSOR_TEXT,	NO_FLAGS},
X#ifndef NO_CHARSEARCH
X/*  ;  repeat [fFtT] cmd*/	{m__ch,		CURSOR,		MVMT|INCL|VIZ},
X#else
X/*  ;  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS|VIZ},
X#endif
X/*  <  shift text left	*/	{v_lshift,	CURSOR_MOVED,	SDOT|FRNT|LNMD|INCL|VIZ},
X/*  =  preset filter	*/	{v_reformat,	CURSOR_MOVED,	SDOT|FRNT|LNMD|INCL|VIZ},
X/*  >  shift text right	*/	{v_rshift,	CURSOR_MOVED,	SDOT|FRNT|LNMD|INCL|VIZ},
X/*  ?  backward search	*/	{m_bsrch,	CURSOR_TEXT,	MVMT|NREL|VIZ},
X#ifndef NO_AT
X/*  @  execute a cutbuf */	{v_at,		C_C_K_CUT,	NO_FLAGS},
X#else
X/*  @  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X#endif
X/*  A  append at EOL	*/	{v_insert,	CURSOR,		SDOT},
X/*  B  move back Word	*/	{m_bword,	CURSOR,		MVMT|VIZ},
X/*  C  change to EOL	*/	{v_change,	CURSOR_EOL,	SDOT},
X/*  D  delete to EOL	*/	{v_delete,	CURSOR_EOL,	SDOT},
X/*  E  move end of Word	*/	{m_eword,	CURSOR,		MVMT|INCL|VIZ},
X#ifndef NO_CHARSEARCH
X/*  F  move bk to char	*/	{m_Fch,		C_C_K_CHAR,	MVMT|INCL|VIZ},
X#else
X/*  F  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X#endif
X/*  G  move to line #	*/	{m_updnto,	CURSOR,		MVMT|NREL|LNMD|FRNT|INCL|VIZ},
X/*  H  move to row	*/	{m_row,		CURSOR,		MVMT|LNMD|FRNT|VIZ|INCL},
X/*  I  insert at front	*/	{v_insert,	CURSOR,		SDOT},
X/*  J  join lines	*/	{v_join,	CURSOR,		SDOT},
X#ifndef NO_EXTENSIONS
X/*  K  look up keyword	*/	{v_keyword,	KEYWORD,	NO_FLAGS},
X#else
X/*  K  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X#endif
X/*  L  move to last row	*/	{m_row,		CURSOR,		MVMT|LNMD|FRNT|VIZ|INCL},
X/*  M  move to mid row	*/	{m_row,		CURSOR,		MVMT|LNMD|FRNT|VIZ|INCL},
X/*  N  reverse prev srch*/	{m_Nsrch,	CURSOR,		MVMT|NREL|VIZ},
X/*  O  insert above line*/	{v_insert,	CURSOR,		SDOT},
X/*  P  paste before	*/	{v_paste,	CURSOR,		SDOT},
X/*  Q  quit to EX mode	*/	{v_quit,	NO_ARGS,	NO_FLAGS},
X/*  R  overtype		*/	{v_overtype,	CURSOR,		SDOT},
X/*  S  change line	*/	{v_change,	CURSOR_MOVED,	SDOT},
X#ifndef NO_CHARSEARCH
X/*  T  move bk to char	*/	{m_Tch,		C_C_K_CHAR,	MVMT|INCL|VIZ},
X#else
X/*  T  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X#endif
X/*  U  undo whole line	*/	{v_undoline,	CURSOR,		FRNT},
X#ifndef NO_VISIBLE
X/*  V  start visible	*/	{v_start,	CURSOR,		INCL|LNMD|VIZ},
X#else
X/*  V  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X#endif
X/*  W  move forward Word*/	{m_fword,	CURSOR,		MVMT|INCL|VIZ},
X/*  X  delete to left	*/	{v_xchar,	CURSOR,		SDOT},
X/*  Y  yank text	*/	{v_yank,	CURSOR_MOVED,	NCOL},
X/*  Z  save file & exit	*/	{v_xit,		CURSOR_CNT_KEY,	NO_FLAGS},
X/*  [  move back section*/	{m_paragraph,	CURSOR,		MVMT|LNMD|NREL|VIZ},
X#ifndef NO_POPUP
X/*  \  pop-up menu	*/	{v_popup,	CURSOR_MOVED,	VIZ},
X#else
X/*  \  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X#endif
X/*  ]  move fwd section */	{m_paragraph,	CURSOR,		MVMT|LNMD|NREL|VIZ},
X/*  ^  move to front	*/	{m_front,	CURSOR,		MVMT|VIZ},
X/*  _  current line	*/	{m_updnto,	CURSOR,		MVMT|LNMD|FRNT|INCL},
X/*  `  move to mark	*/	{m_tomark,	C_C_K_MARK,	MVMT|NREL|VIZ},
X/*  a  append at cursor	*/	{v_insert,	CURSOR,		SDOT},
X/*  b  move back word	*/	{m_bword,	CURSOR,		MVMT|VIZ},
X/*  c  change text	*/	{v_change,	CURSOR_MOVED,	SDOT|VIZ},
X/*  d  delete op	*/	{v_delete,	CURSOR_MOVED,	SDOT|VIZ},
X/*  e  move end word	*/	{m_eword,	CURSOR,		MVMT|INCL|VIZ},
X#ifndef NO_CHARSEARCH
X/*  f  move fwd for char*/	{m_fch,		C_C_K_CHAR,	MVMT|INCL|VIZ},
X#else
X/*  f  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X#endif
X/*  g  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X/*  h  move left	*/	{m_left,	CURSOR,		MVMT|VIZ},
X/*  i  insert at cursor	*/	{v_insert,	CURSOR,		SDOT},
X/*  j  move down	*/	{m_updnto,	CURSOR,		MVMT|NCOL|LNMD|VIZ|INCL},
X/*  k  move up		*/	{m_updnto,	CURSOR,		MVMT|NCOL|LNMD|VIZ|INCL},
X/*  l  move right	*/	{m_right,	CURSOR,		MVMT|INCL|VIZ},
X/*  m  define a mark	*/	{v_mark,	C_C_K_MARK,	NO_FLAGS},
X/*  n  repeat prev srch	*/	{m_nsrch,	CURSOR, 	MVMT|NREL|VIZ},
X/*  o  insert below line*/	{v_insert,	CURSOR,		SDOT},
X/*  p  paste after	*/	{v_paste,	CURSOR,		SDOT},
X/*  q  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X/*  r  replace chars	*/	{v_replace,	C_C_K_REP1,	SDOT},
X/*  s  subst N chars	*/	{v_subst,	CURSOR,		SDOT},
X#ifndef NO_CHARSEARCH
X/*  t  move fwd to char	*/	{m_tch,		C_C_K_CHAR,	MVMT|INCL|VIZ},
X#else
X/*  t  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X#endif
X/*  u  undo		*/	{v_undo,	CURSOR,		NO_FLAGS},
X#ifndef NO_VISIBLE
X/*  v  start visible	*/	{v_start,	CURSOR,		INCL|VIZ},
X#else
X/*  v  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
X#endif
X/*  w  move fwd word	*/	{m_fword,	CURSOR,		MVMT|INCL|VIZ},
X/*  x  delete character	*/	{v_xchar,	CURSOR,		SDOT},
X/*  y  yank text	*/	{v_yank,	CURSOR_MOVED,	NCOL|VIZ},
X/*  z  adjust scrn row	*/	{m_z, 		CURSOR_CNT_KEY,	NCOL|VIZ},
X/*  {  back paragraph	*/	{m_paragraph,	CURSOR,		MVMT|LNMD|VIZ},
X/*  |  move to column	*/	{m_tocol,	CURSOR,		MVMT|NREL|VIZ},
X/*  }  fwd paragraph	*/	{m_paragraph,	CURSOR,		MVMT|LNMD|VIZ},
X/*  ~  upper/lowercase	*/	{v_ulcase,	CURSOR,		SDOT},
X/* DEL not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS}
X};
X
X
X
Xvoid vi()
X{
X	REG int			key;	/* keystroke from user */
X	long			count;	/* numeric argument to some functions */
X	REG struct keystru	*keyptr;/* pointer to vikeys[] element */
X	MARK			tcurs;	/* temporary cursor */
X	int			prevkey;/* previous key, if d/c/y/</>/! */
X	MARK			range;	/* start of range for d/c/y/</>/! */
X	char			text[132];
X	int			dotkey;	/* last "key" of a change */
X	int			dotpkey;/* last "prevkey" of a change */
X	int			dotkey2;/* last extra "getkey()" of a change */
X	int			dotcnt;	/* last "count" of a change */
X	int			firstkey;
X	REG int			i;
X
X	/* tell the redraw() function to start from scratch */
X	redraw(MARK_UNSET, FALSE);
X
X#ifdef lint
X	/* lint says that "range" might be used before it is set.  This
X	 * can't really happen due to the way "range" and "prevkey" are used,
X	 * but lint doesn't know that.  This line is here ONLY to keep lint
X	 * happy.
X	 */
X	range = 0L;
X#endif
X
X	/* safeguard against '.' with no previous command */
X	dotkey = dotpkey = dotkey2 = dotcnt = 0;
X
X	/* go immediately into insert mode, if ":set inputmode" */
X	firstkey = 0;
X#ifndef NO_EXTENSIONS
X	if (*o_inputmode)
X	{
X		firstkey = 'i';
X	}
X#endif
X
X	/* Repeatedly handle VI commands */
X	for (count = 0, prevkey = '\0'; mode == MODE_VI; )
X	{
X		/* if we've moved off the undoable line, then we can't undo it at all */
X		if (markline(cursor) != U_line)
X		{
X			U_line = 0L;
X		}
X
X		/* report any changes from the previous command */
X		if (rptlines >= *o_report)
X		{
X			redraw(cursor, FALSE);
X			msg("%ld line%s %s", rptlines, (rptlines==1?"":"s"), rptlabel);
X		}
X		rptlines = 0L;
X
X		/* get the next command key.  It must be ASCII */
X		if (firstkey)
X		{
X			key = firstkey;
X			firstkey = 0;
X		}
X		else
X		{
X			do
X			{
X				key = getkey(WHEN_VICMD);
X			} while (key < 0 || key > 127);
X		}
X
X		/* Convert a doubled-up operator such as "dd" into "d_" */
X		if (prevkey && key == prevkey)
X		{
X			key = '_';
X		}
X
X		/* look up the structure describing this command */
X		keyptr = &vikeys[key];
X
X		/* '&' and uppercase operators always act like doubled */
X		if (!prevkey && keyptr->args == CURSOR_MOVED
X			&& (key == '&' || isupper(key)))
X		{
X			range = cursor;
X			prevkey = key;
X			key = '_';
X			keyptr = &vikeys[key];
X		}
X
X#ifndef NO_VISIBLE
X		/* if we're in the middle of a v/V command, reject commands
X		 * that aren't operators or movement commands
X		 */
X		if (V_from && !(keyptr->flags & VIZ))
X		{
X			beep();
X			prevkey = 0;
X			count = 0;
X			continue;
X		}
X#endif
X
X		/* if we're in the middle of a d/c/y/</>/! command, reject
X		 * anything but movement.
X		 */
X		if (prevkey && !(keyptr->flags & (MVMT|PTMV)))
X		{
X			beep();
X			prevkey = 0;
X			count = 0;
X			continue;
X		}
X
X		/* set the "dot" variables, if we're supposed to */
X		if (((keyptr->flags & SDOT)
X			|| (prevkey && vikeys[prevkey].flags & SDOT))
X#ifndef NO_VISIBLE
X		    && !V_from
X#endif
X		)
X		{
X			dotkey = key;
X			dotpkey = prevkey;
X			dotkey2 = '\0';
X			dotcnt = count;
X
X			/* remember the line before any changes are made */
X			if (U_line != markline(cursor))
X			{
X				U_line = markline(cursor);
X				strcpy(U_text, fetchline(U_line));
X			}
X		}
X
X		/* if this is "." then set other vars from the "dot" vars */
X		if (key == '.')
X		{
X			key = dotkey;
X			keyptr = &vikeys[key];
X			prevkey = dotpkey;
X			if (prevkey)
X			{
X				range = cursor;
X			}
X			if (count == 0)
X			{
X				count = dotcnt;
X			}
X			doingdot = TRUE;
X
X			/* remember the line before any changes are made */
X			if (U_line != markline(cursor))
X			{
X				U_line = markline(cursor);
X				strcpy(U_text, fetchline(U_line));
X			}
X		}
X		else
X		{
X			doingdot = FALSE;
X		}
X
X		/* process the key as a command */
X		tcurs = cursor;
X		force_flags = NO_FLAGS;
X		switch (keyptr->args & ARGSMASK)
X		{
X		  case ZERO:
X			if (count == 0)
X			{
X				tcurs = cursor & ~(BLKSIZE - 1);
X				break;
X			}
X			/* else fall through & treat like other digits... */
X
X		  case DIGIT:
X			count = count * 10 + key - '0';
X			break;
X
X		  case KEYWORD:
X			/* if not on a keyword, fail */
X			pfetch(markline(cursor));
X			key = markidx(cursor);
X			if (!isalnum(ptext[key]))
X			{
X				tcurs = MARK_UNSET;
X				break;
X			}
X
X			/* find the start of the keyword */
X			while (key > 0 && isalnum(ptext[key - 1]))
X			{
X				key--;
X			}
X			tcurs = (cursor & ~(BLKSIZE - 1)) + key;
X
X			/* copy it into a buffer, and NUL-terminate it */
X			i = 0;
X			do
X			{
X				text[i++] = ptext[key++];
X			} while (isalnum(ptext[key]));
X			text[i] = '\0';
X
X			/* call the function */
X			tcurs = (*keyptr->func)(text, tcurs, count);
X			count = 0L;
X			break;
X
X		  case NO_ARGS:
X			if (keyptr->func)
X			{
X				(*keyptr->func)();
X			}
X			else
X			{
X				beep();
X			}
X			count = 0L;
X			break;
X	
X		  case CURSOR:
X			tcurs = (*keyptr->func)(cursor, count, key, prevkey);
X			count = 0L;
X			break;
X
X		  case CURSOR_CNT_KEY:
X			if (doingdot)
X			{
X				tcurs = (*keyptr->func)(cursor, count, dotkey2);
X			}
X			else
X			{
X				/* get a key */
X				i = getkey(KEYMODE(keyptr->args));
X				if (i == '\033') /* ESC */
X				{
X					count = 0;
X					tcurs = MARK_UNSET;
X					break; /* exit from "case CURSOR_CNT_KEY" */
X				}
X				else if (i == ctrl('V'))
X				{
X					i = getkey(0);
X				}
X
X				/* if part of an SDOT command, remember it */
X				 if (keyptr->flags & SDOT
X				 || (prevkey && vikeys[prevkey].flags & SDOT))
X				{
X					dotkey2 = i;
X				}
X
X				/* do it */
X				tcurs = (*keyptr->func)(cursor, count, i);
X			}
X			count = 0L;
X			break;
X	
X		  case CURSOR_MOVED:
X#ifndef NO_VISIBLE
X			if (V_from)
X			{
X				range = cursor;
X				tcurs = V_from;
X				count = 0L;
X				prevkey = key;
X				key = (V_linemd ? 'V' : 'v');
X				keyptr = &vikeys[key];
X			}
X			else
X#endif
X			{
X				prevkey = key;
X				range = cursor;
X				force_flags = LNMD|INCL;
X			}
X			break;
X
X		  case CURSOR_EOL:
X			prevkey = key;
X			/* a zero-length line needs special treatment */
X			pfetch(markline(cursor));
X			if (plen == 0)
X			{
X				/* act on a zero-length section of text */
X				range = tcurs = cursor;
X				key = '0';
X			}
X			else
X			{
X				/* act like CURSOR_MOVED with '$' movement */
X				range = cursor;
X				tcurs = m_rear(cursor, 1L);
X				key = '$';
X			}
X			count = 0L;
X			keyptr = &vikeys[key];
X			break;
X
X		  case CURSOR_TEXT:
X		  	do
X		  	{	
X				text[0] = key;
X				if (vgets(key, text + 1, sizeof text - 1) >= 0)
X				{
X					/* reassure user that <CR> was hit */
X					qaddch('\r');
X					refresh();
X
X					/* call the function with the text */
X					tcurs = (*keyptr->func)(cursor, text);
X				}
X				else
X				{
X					if (exwrote || mode == MODE_COLON)
X					{
X						redraw(MARK_UNSET, FALSE);
X					}
X					mode = MODE_VI;
X				}
X			} while (mode == MODE_COLON);
X			count = 0L;
X			break;
X		}
X
X		/* if that command took us out of vi mode, then exit the loop
X		 * NOW, without tweaking the cursor or anything.  This is very
X		 * important when mode == MODE_QUIT.
X		 */
X		if (mode != MODE_VI)
X		{
X			break;
X		}
X
X		/* now move the cursor, as appropriate */
X		if (keyptr->args == CURSOR_MOVED)
X		{
X			/* the < and > keys have FRNT,
X			 * but it shouldn't be applied yet
X			 */
X			tcurs = adjmove(cursor, tcurs, 0);
X		}
X		else
X		{
X			tcurs = adjmove(cursor, tcurs, (int)keyptr->flags | force_flags);
X		}
X
X		/* was that the end of a d/c/y/</>/! command? */
X		if (prevkey && ((keyptr->flags & MVMT)
X#ifndef NO_VISIBLE
X					       || V_from
X#endif
X				) && count == 0L)
X		{
X#ifndef NO_VISIBLE
X			/* turn off the hilight */
X			V_from = 0L;
X#endif
X
X			/* if the movement command failed, cancel operation */
X			if (tcurs == MARK_UNSET)
X			{
X				prevkey = 0;
X				count = 0;
X				continue;
X			}
X
X			/* make sure range=front and tcurs=rear.  Either way,
X			 * leave cursor=range since that's where we started.
X			 */
X			cursor = range;
X			if (tcurs < range)
X			{
X				range = tcurs;
X				tcurs = cursor;
X			}
X
X			/* The 'w' and 'W' destinations should never take us
X			 * to the front of a line.  Instead, they should take
X			 * us only to the end of the preceding line.
X			 */
X			if ((keyptr->flags & (MVMT|NREL|LNMD|FRNT|INCL)) == MVMT
X			  && markline(range) < markline(tcurs)
X			  && (markline(tcurs) > nlines || tcurs == m_front(tcurs, 0L)))
X			{
X				tcurs = (tcurs & ~(BLKSIZE - 1)) - BLKSIZE;
X				pfetch(markline(tcurs));
X				tcurs += plen;
X			}
X
X			/* adjust for line mode & inclusion of last char/line */
X			i = (keyptr->flags | vikeys[prevkey].flags);
X			switch ((i | force_flags) & (INCL|LNMD))
X			{
X			  case INCL:
X				tcurs++;
X				break;
X
X			  case INCL|LNMD:
X				tcurs += BLKSIZE;
X				/* fall through... */
X
X			  case LNMD:
X				range &= ~(BLKSIZE - 1);
X				tcurs &= ~(BLKSIZE - 1);
X				break;
X			}
X
X			/* run the function */
X			tcurs = (*vikeys[prevkey].func)(range, tcurs);
X			if (mode == MODE_VI)
X			{
X				(void)adjmove(cursor, cursor, 0);
X				cursor = adjmove(cursor, tcurs, (int)vikeys[prevkey].flags);
X			}
X
X			/* cleanup */
X			prevkey = 0;
X		}
X		else if (!prevkey)
X		{
X			if (tcurs != MARK_UNSET)
X				cursor = tcurs;
X		}
X	}
X}
X
X/* This function adjusts the MARK value that they return; here we make sure
X * it isn't past the end of the line, and that the column hasn't been
X * *accidentally* changed.
X */
XMARK adjmove(old, new, flags)
X	MARK		old;	/* the cursor position before the command */
X	REG MARK	new;	/* the cursor position after the command */
X	int		flags;	/* various flags regarding cursor mvmt */
X{
X	static int	colno;	/* the column number that we want */
X	REG char	*text;	/* used to scan through the line's text */
X	REG int		i;
X
X#ifdef DEBUG
X	watch();
X#endif
X
X	/* if the command failed, bag it! */
X	if (new == MARK_UNSET)
X	{
X		beep();
X		return old;
X	}
X
X	/* if this is a non-relative movement, set the '' mark */
X	if (flags & NREL)
X	{
X		mark[26] = old;
X	}
X
X	/* make sure it isn't past the end of the file */
X	if (markline(new) < 1)
X	{
X		new = MARK_FIRST;
X	}
X	else if (markline(new) > nlines)
X	{
X		new = MARK_LAST;
X	}
X
X	/* fetch the new line */
X	pfetch(markline(new));
X
X	/* move to the front, if we're supposed to */
X	if (flags & FRNT)
X	{
X		new = m_front(new, 1L);
X	}
X
X	/* change the column#, or change the mark to suit the column# */
X	if (!(flags & NCOL))
X	{
X		/* change the column# */
X		i = markidx(new);
X		if (i == BLKSIZE - 1)
X		{
X			new &= ~(BLKSIZE - 1);
X			if (plen > 0)
X			{
X				new += plen - 1;
X			}
X			colno = BLKSIZE * 8; /* one heck of a big colno */
X		}
X		else if (plen > 0)
X		{
X			if (i >= plen)
X			{
X				new = (new & ~(BLKSIZE - 1)) + plen - 1;
X			}
X			colno = idx2col(new, ptext, FALSE);
X		}
X		else
X		{
X			new &= ~(BLKSIZE - 1);
X			colno = 0;
X		}
X	}
X	else
X	{
X		/* adjust the mark to get as close as possible to column# */
X		for (i = 0, text = ptext; i <= colno && *text; text++)
X		{
X			if (*text == '\t' && !*o_list)
X			{
X				i += *o_tabstop - (i % *o_tabstop);
X			}
X			else if (UCHAR(*text) < ' ' || *text == 127)
X			{
X				i += 2;
X			}
X#ifndef NO_CHARATTR
X			else if (*o_charattr && text[0] == '\\' && text[1] == 'f' && text[2])
X			{
X				text += 2; /* plus one more in "for()" stmt */
X			}
X#endif
X			else
X			{
X				i++;
X			}
X		}
X		if (text > ptext)
X		{
X			text--;
X		}
X		new = (new & ~(BLKSIZE - 1)) + (int)(text - ptext);
X	}
X
X	return new;
X}
X
X
X#ifdef DEBUG
Xwatch()
X{
X	static wasset;
X
X	if (*origname)
X	{
X		wasset = TRUE;
X	}
X	else if (wasset)
X	{
X		mode = MODE_EX;
X		msg("origname was clobbered");
X		endwin();
X		abort();
X	}
X
X	if (wasset && nlines == 0)
X	{
X		mode = MODE_EX;
X		msg("nlines=0");
X		endwin();
X		abort();
X	}
X}
X#endif
/
echo x - vi.h
sed '/^X/s///' > vi.h << '/'
X/* vi.h */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X#define VERSION "ELVIS 1.5, by Steve Kirkendall (23 March 1992)"
X#define COPYING	"This version of ELVIS is freely redistributable."
X
X#include <errno.h>
Xextern int errno;
X#if TOS && !defined(__GNUC__)
X#define ENOENT (-AEFILNF)
X#endif
X
X#if TOS || VMS
X# include <types.h>
X# define O_RDONLY	0
X# define O_WRONLY	1
X# define O_RDWR		2
X# ifdef __GNUC__
X#  define S_IJDIR	S_IFDIR
X# endif
X#else
X# if OSK
X#  include <modes.h>
X#  define O_RDONLY	S_IREAD
X#  define O_WRONLY	S_IWRITE
X#  define O_RDWR	(S_IREAD | S_IWRITE)
X#  define ENOENT	E_PNNF
X#  define sprintf	Sprintf
X# else
X#  include <sys/types.h>
X#  if COHERENT
X#   include <sys/fcntl.h>
X#  else
X#   include <fcntl.h>
X#  endif
X# endif
X#endif
X
X#ifndef O_BINARY
X# define O_BINARY	0
X#endif
X
X#include "curses.h"
X
X#include <signal.h>
X
X/*------------------------------------------------------------------------*/
X/* Miscellaneous constants.						  */
X
X#define INFINITY	2000000001L	/* a very large integer */
X#define LONGKEY		10		/* longest possible raw :map key */
X#ifndef MAXRCLEN
X# define MAXRCLEN	1000		/* longest possible :@ command */
X#endif
X
X/*------------------------------------------------------------------------*/
X/* These describe how temporary files are divided into blocks             */
X
X#define MAXBLKS	(BLKSIZE / sizeof(unsigned short))
Xtypedef union
X{
X	char		c[BLKSIZE];	/* for text blocks */
X	unsigned short	n[MAXBLKS];	/* for the header block */
X}
X	BLK;
X
X/*------------------------------------------------------------------------*/
X/* These are used manipulate BLK buffers.                                 */
X
Xextern BLK	hdr;		/* buffer for the header block */
Xextern BLK	*blkget();	/* given index into hdr.c[], reads block */
Xextern BLK	*blkadd();	/* inserts a new block into hdr.c[] */
X
X/*------------------------------------------------------------------------*/
X/* These are used to keep track of various flags                          */
Xextern struct _viflags
X{
X	short	file;		/* file flags */
X}
X	viflags;
X
X/* file flags */
X#define NEWFILE		0x0001	/* the file was just created */
X#define READONLY	0x0002	/* the file is read-only */
X#define HADNUL		0x0004	/* the file contained NUL characters */
X#define MODIFIED	0x0008	/* the file has been modified, but not saved */
X#define NOFILE		0x0010	/* no name is known for the current text */
X#define ADDEDNL		0x0020	/* newlines were added to the file */
X#define HADBS		0x0040	/* backspace chars were lost from the file */
X#define UNDOABLE	0x0080	/* file has been modified */
X#define NOTEDITED	0x0100	/* the :file command has been used */
X
X/* macros used to set/clear/test flags */
X#define setflag(x,y)	viflags.x |= y
X#define clrflag(x,y)	viflags.x &= ~y
X#define tstflag(x,y)	(viflags.x & y)
X#define initflags()	viflags.file = 0;
X
X/* The options */
Xextern char	o_autoindent[1];
Xextern char	o_autoprint[1];
Xextern char	o_autotab[1];
Xextern char	o_autowrite[1];
Xextern char	o_columns[3];
Xextern char	o_directory[30];
Xextern char	o_edcompatible[1];
Xextern char	o_equalprg[80];
Xextern char	o_errorbells[1];
Xextern char	o_exrefresh[1];
Xextern char	o_ignorecase[1];
Xextern char	o_keytime[3];
Xextern char	o_keywordprg[80];
Xextern char	o_lines[3];
Xextern char	o_list[1];
Xextern char	o_number[1];
Xextern char	o_readonly[1];
Xextern char	o_remap[1];
Xextern char	o_report[3];
Xextern char	o_scroll[3];
Xextern char	o_shell[60];
Xextern char	o_shiftwidth[3];
Xextern char	o_sidescroll[3];
Xextern char	o_sync[1];
Xextern char	o_tabstop[3];
Xextern char	o_term[30];
Xextern char	o_flash[1];
Xextern char	o_warn[1];
Xextern char	o_wrapscan[1];
X
X#ifndef CRUNCH
Xextern char	o_beautify[1];
Xextern char	o_exrc[1];
Xextern char	o_mesg[1];
Xextern char	o_more[1];
Xextern char	o_novice[1];
Xextern char	o_prompt[1];
Xextern char	o_taglength[3];
Xextern char	o_terse[1];
Xextern char	o_window[3];
Xextern char	o_wrapmargin[3];
Xextern char	o_writeany[1];
X#endif
X
X#ifndef NO_ERRLIST
Xextern char	o_cc[30];
Xextern char	o_make[30];
X#endif
X
X#ifndef NO_CHARATTR
Xextern char	o_charattr[1];
X#endif
X
X#ifndef NO_DIGRAPH
Xextern char	o_digraph[1];
Xextern char	o_flipcase[80];
X#endif
X
X#ifndef NO_SENTENCE
Xextern char	o_hideformat[1];
X#endif
X
X#ifndef NO_EXTENSIONS
Xextern char	o_inputmode[1];
Xextern char	o_ruler[1];
X#endif
X
X#ifndef NO_MAGIC
Xextern char	o_magic[1];
X#endif
X
X#ifndef NO_MODELINES
Xextern char	o_modelines[1];
X#endif
X
X#ifndef NO_SENTENCE
Xextern char	o_paragraphs[30];
Xextern char	o_sections[30];
X#endif
X
X#if MSDOS
Xextern char	o_pcbios[1];
X#endif
X
X#ifndef NO_SHOWMATCH
Xextern char	o_showmatch[1];
X#endif
X
X#ifndef	NO_SHOWMODE
Xextern char	o_smd[1];
X#endif
X
X/*------------------------------------------------------------------------*/
X/* These help support the single-line multi-change "undo" -- shift-U      */
X
Xextern char	U_text[BLKSIZE];
Xextern long	U_line;
X
X/*------------------------------------------------------------------------*/
X/* These are used to refer to places in the text 			  */
X
Xtypedef long	MARK;
X#define markline(x)	(long)((x) / BLKSIZE)
X#define markidx(x)	(int)((x) & (BLKSIZE - 1))
X#define MARK_UNSET	((MARK)0)
X#define MARK_FIRST	((MARK)BLKSIZE)
X#define MARK_LAST	((MARK)(nlines * BLKSIZE))
X#define MARK_AT_LINE(x)	((MARK)(x) * BLKSIZE)
X
X#define NMARKS	29
Xextern MARK	mark[NMARKS];	/* marks a-z, plus mark ' and two temps */
Xextern MARK	cursor;		/* mark where line is */
X
X/*------------------------------------------------------------------------*/
X/* These are used to keep track of the current & previous files.	  */
X
Xextern long	origtime;	/* modification date&time of the current file */
Xextern char	origname[256];	/* name of the current file */
Xextern char	prevorig[256];	/* name of the preceding file */
Xextern long	prevline;	/* line number from preceding file */
X
X/*------------------------------------------------------------------------*/
X/* misc housekeeping variables & functions				  */
X
Xextern int	tmpfd;		/* fd used to access the tmp file */
Xextern int	tmpnum;		/* counter used to generate unique filenames */
Xextern long	lnum[MAXBLKS];	/* last line# of each block */
Xextern long	nlines;		/* number of lines in the file */
Xextern char	args[BLKSIZE];	/* file names given on the command line */
Xextern int	argno;		/* the current element of args[] */
Xextern int	nargs;		/* number of filenames in args */
Xextern long	changes;	/* counts changes, to prohibit short-cuts */
Xextern int	significant;	/* boolean: was a *REAL* change made? */
Xextern BLK	tmpblk;		/* a block used to accumulate changes */
Xextern long	topline;	/* file line number of top line */
Xextern int	leftcol;	/* column number of left col */
X#define		botline	 (topline + LINES - 2)
X#define		rightcol (leftcol + COLS - (*o_number ? 9 : 1))
Xextern int	physcol;	/* physical column number that cursor is on */
Xextern int	physrow;	/* physical row number that cursor is on */
Xextern int	exwrote;	/* used to detect verbose ex commands */
Xextern int	doingdot;	/* boolean: are we doing the "." command? */
Xextern int	doingglobal;	/* boolean: are doing a ":g" command? */
Xextern long	rptlines;	/* number of lines affected by a command */
Xextern char	*rptlabel;	/* description of how lines were affected */
Xextern char	*fetchline();	/* read a given line from tmp file */
Xextern char	*parseptrn();	/* isolate a regexp in a line */
Xextern MARK	paste();	/* paste from cut buffer to a given point */
Xextern char	*wildcard();	/* expand wildcards in filenames */
Xextern MARK	input();	/* inserts characters from keyboard */
Xextern char	*linespec();	/* finds the end of a /regexp/ string */
X#define		ctrl(ch) ((ch)&037)
X#ifndef NO_RECYCLE
Xextern long	allocate();	/* allocate a free block of the tmp file */
X#endif
Xextern int	trapint();	/* trap handler for SIGINT */
Xextern int	deathtrap();	/* trap handler for deadly signals */
Xextern void	blkdirty();	/* marks a block as being "dirty" */
Xextern void	blkflush();	/* writes a single dirty block to the disk */
Xextern void	blksync();	/* forces all "dirty" blocks to disk */
Xextern void	blkinit();	/* resets the block cache to "empty" state */
Xextern void	beep();		/* rings the terminal's bell */
Xextern void	exrefresh();	/* writes text to the screen */
Xextern void	msg();		/* writes a printf-style message to the screen */
Xextern void	endmsgs();	/* if "manymsgs" is set, then scroll up 1 line */
Xextern void	garbage();	/* reclaims any garbage blocks */
Xextern void	redraw();	/* updates the screen after a change */
Xextern void	resume_curses();/* puts the terminal in "cbreak" mode */
Xextern void	beforedo();	/* saves current revision before a new change */
Xextern void	afterdo();	/* marks end of a beforedo() change */
Xextern void	abortdo();	/* like "afterdo()" followed by "undo()" */
Xextern int	undo();		/* restores file to previous undo() */
Xextern void	dumpkey();	/* lists key mappings to the screen */
Xextern void	mapkey();	/* defines a new key mapping */
Xextern void	savekeys();	/* lists key mappings to a file */
Xextern void	redrawrange();	/* records clues from modify.c */
Xextern void	cut();		/* saves text in a cut buffer */
Xextern void	delete();	/* deletes text */
Xextern void	add();		/* adds text */
Xextern void	change();	/* deletes text, and then adds other text */
Xextern void	cutswitch();	/* updates cut buffers when we switch files */
Xextern void	do_abbr();	/* defines or lists abbreviations */
Xextern void	do_digraph();	/* defines or lists digraphs */
Xextern void	exstring();	/* execute a string as EX commands */
Xextern void	dumpopts();
Xextern void	setopts();
Xextern void	saveopts();
Xextern void	savedigs();
Xextern void	saveabbr();
Xextern void	savecolor();
Xextern void	cutname();
Xextern void	cutname();
Xextern void	initopts();
Xextern void	cutend();
X#ifndef CRUNCH
Xextern int	wset;		/* boolean: has the "window" size been set? */
X#endif
X
X/*------------------------------------------------------------------------*/
X/* macros that are used as control structures                             */
X
X#define BeforeAfter(before, after) for((before),bavar=1;bavar;(after),bavar=0)
X#define ChangeText	BeforeAfter(beforedo(FALSE),afterdo())
X
Xextern int	bavar;		/* used only in BeforeAfter macros */
X
X/*------------------------------------------------------------------------*/
X/* These are the movement commands.  Each accepts a mark for the starting */
X/* location & number and returns a mark for the destination.		  */
X
Xextern MARK	m_updnto();		/* k j G */
Xextern MARK	m_right();		/* h */
Xextern MARK	m_left();		/* l */
Xextern MARK	m_tocol();		/* | */
Xextern MARK	m_front();		/* ^ */
Xextern MARK	m_rear();		/* $ */
Xextern MARK	m_fword();		/* w */
Xextern MARK	m_bword();		/* b */
Xextern MARK	m_eword();		/* e */
Xextern MARK	m_paragraph();		/* { } [[ ]] */
Xextern MARK	m_match();		/* % */
X#ifndef NO_SENTENCE
X extern MARK	m_sentence();		/* ( ) */
X#endif
Xextern MARK	m_tomark();		/* 'm */
X#ifndef NO_EXTENSIONS
Xextern MARK	m_wsrch();		/* ^A */
X#endif
Xextern MARK	m_nsrch();		/* n */
Xextern MARK	m_Nsrch();		/* N */
Xextern MARK	m_fsrch();		/* /regexp */
Xextern MARK	m_bsrch();		/* ?regexp */
X#ifndef NO_CHARSEARCH
X extern MARK	m__ch();		/* ; , */
X extern MARK	m_fch();		/* f */
X extern MARK	m_tch();		/* t */
X extern MARK	m_Fch();		/* F */
X extern MARK	m_Tch();		/* T */
X#endif
Xextern MARK	m_row();		/* H L M */
Xextern MARK	m_z();			/* z */
Xextern MARK	m_scroll();		/* ^B ^F ^E ^Y ^U ^D */
X
X/* Some stuff that is used by movement functions... */
X
Xextern MARK	adjmove();		/* a helper fn, used by move fns */
X
X/* This macro is used to set the default value of cnt */
X#define DEFAULT(val)	if (cnt < 1) cnt = (val)
X
X/* These are used to minimize calls to fetchline() */
Xextern int	plen;	/* length of the line */
Xextern long	pline;	/* line number that len refers to */
Xextern long	pchgs;	/* "changes" level that len refers to */
Xextern char	*ptext;	/* text of previous line, if valid */
Xextern void	pfetch();
Xextern char	digraph();
X
X/* This is used to build a MARK that corresponds to a specific point in the
X * line that was most recently pfetch'ed.
X */
X#define buildmark(text)	(MARK)(BLKSIZE * pline + (int)((text) - ptext))
X
X
X/*------------------------------------------------------------------------*/
X/* These are used to handle EX commands.				  */
X
X#define  CMD_NULL	0	/* NOT A VALID COMMAND */
X#define  CMD_ABBR	1	/* "define an abbreviation" */
X#define  CMD_ARGS	2	/* "show me the args" */
X#define  CMD_APPEND	3	/* "insert lines after this line" */
X#define  CMD_AT		4	/* "execute a cut buffer's contents via EX" */
X#define  CMD_BANG	5	/* "run a single shell command" */
X#define  CMD_CC		6	/* "run `cc` and then do CMD_ERRLIST" */
X#define  CMD_CD		7	/* "change directories" */
X#define  CMD_CHANGE	8	/* "change some lines" */
X#define	 CMD_COLOR	9	/* "change the default colors" */
X#define  CMD_COPY	10	/* "copy the selected text to a given place" */
X#define  CMD_DELETE	11	/* "delete the selected text" */
X#define  CMD_DIGRAPH	12	/* "add a digraph, or display them all" */
X#define  CMD_EDIT	13	/* "switch to a different file" */
X#define  CMD_EQUAL	14	/* "display a line number" */
X#define  CMD_ERRLIST	15	/* "locate the next error in a list" */
X#define  CMD_FILE	16	/* "show the file's status" */
X#define  CMD_GLOBAL	17	/* "globally search & do a command" */
X#define  CMD_INSERT	18	/* "insert lines before the current line" */
X#define  CMD_JOIN	19	/* "join the selected line & the one after" */
X#define  CMD_LIST	20	/* "print lines, making control chars visible" */
X#define  CMD_MAKE	21	/* "run `make` and then do CMD_ERRLIST" */
X#define  CMD_MAP	22	/* "adjust the keyboard map" */
X#define  CMD_MARK	23	/* "mark this line" */
X#define  CMD_MKEXRC	24	/* "make a .exrc file" */
X#define  CMD_MOVE	25	/* "move the selected text to a given place" */
X#define  CMD_NEXT	26	/* "switch to next file in args" */
X#define  CMD_NUMBER	27	/* "print lines from the file w/ line numbers" */
X#define  CMD_PRESERVE	28	/* "act as though vi crashed" */
X#define  CMD_PREVIOUS	29	/* "switch to the previous file in args" */
X#define  CMD_PRINT	30	/* "print the selected text" */
X#define  CMD_PUT	31	/* "insert any cut lines before this line" */
X#define  CMD_QUIT	32	/* "quit without writing the file" */
X#define  CMD_READ	33	/* "append the given file after this line */
X#define  CMD_RECOVER	34	/* "recover file after vi crashes" - USE -r FLAG */
X#define  CMD_REWIND	35	/* "rewind to first file" */
X#define  CMD_SET	36	/* "set a variable's value" */
X#define  CMD_SHELL	37	/* "run some lines through a command" */
X#define  CMD_SHIFTL	38	/* "shift lines left" */
X#define  CMD_SHIFTR	39	/* "shift lines right" */
X#define  CMD_SOURCE	40	/* "interpret a file's contents as ex commands" */
X#define  CMD_STOP	41	/* same as CMD_SUSPEND */
X#define  CMD_SUBAGAIN	42	/* "repeat the previous substitution" */
X#define  CMD_SUBSTITUTE	43	/* "substitute text in this line" */
X#define  CMD_SUSPEND	44	/* "suspend the vi session" */
X#define  CMD_TR		45	/* "transliterate chars in the selected lines" */
X#define  CMD_TAG	46	/* "go to a particular tag" */
X#define  CMD_UNABBR	47	/* "remove an abbreviation definition" */
X#define  CMD_UNDO	48	/* "undo the previous command" */
X#define  CMD_UNMAP	49	/* "remove a key sequence map */
X#define  CMD_VERSION	50	/* "describe which version this is" */
X#define  CMD_VGLOBAL	51	/* "apply a cmd to lines NOT containing an RE" */
X#define  CMD_VISUAL	52	/* "go into visual mode" */
X#define  CMD_WQUIT	53	/* "write this file out (any case) & quit" */
X#define  CMD_WRITE	54	/* "write the selected(?) text to a given file" */
X#define  CMD_XIT	55	/* "write this file out (if modified) & quit" */
X#define  CMD_YANK	56	/* "copy the selected text into the cut buffer" */
X#ifdef DEBUG
X# define CMD_DEBUG	57	/* access to internal data structures */
X# define CMD_VALIDATE	58	/* check for internal consistency */
X#endif
Xtypedef int CMD;
X
Xextern void	ex();
Xextern void	vi();
Xextern void	doexcmd();
X
Xextern void	cmd_append();
Xextern void	cmd_args();
X#ifndef NO_AT
Xextern void	cmd_at();
X#endif
Xextern void	cmd_cd();
X#ifndef NO_COLOR
Xextern void	cmd_color();
X#endif
Xextern void	cmd_delete();
X#ifndef NO_DIGRAPH
Xextern void	cmd_digraph();
X#endif
Xextern void	cmd_edit();
X#ifndef NO_ERRLIST
Xextern void	cmd_errlist();
X#endif
Xextern void	cmd_file();
Xextern void	cmd_global();
Xextern void	cmd_join();
Xextern void	cmd_mark();
X#ifndef NO_ERRLIST
Xextern void	cmd_make();
X#endif
Xextern void	cmd_map();
X#ifndef NO_MKEXRC
Xextern void	cmd_mkexrc();
X#endif
Xextern void	cmd_next();
Xextern void	cmd_print();
Xextern void	cmd_put();
Xextern void	cmd_read();
Xextern void	cmd_set();
Xextern void	cmd_shell();
Xextern void	cmd_shift();
Xextern void	cmd_source();
Xextern void	cmd_substitute();
Xextern void	cmd_tag();
Xextern void	cmd_undo();
Xextern void	cmd_version();
Xextern void	cmd_write();
Xextern void	cmd_xit();
Xextern void	cmd_move();
X#ifdef DEBUG
Xextern void	cmd_debug();
Xextern void	cmd_validate();
X#endif
X#ifdef SIGTSTP
Xextern void	cmd_suspend();
X#endif
X
X/*----------------------------------------------------------------------*/
X/* These are used to handle VI commands 				*/
X
Xextern MARK	v_1ex();	/* : */
Xextern MARK	v_mark();	/* m */
Xextern MARK	v_quit();	/* Q */
Xextern MARK	v_redraw();	/* ^L ^R */
Xextern MARK	v_ulcase();	/* ~ */
Xextern MARK	v_undo();	/* u */
Xextern MARK	v_xchar();	/* x X */
Xextern MARK	v_replace();	/* r */
Xextern MARK	v_overtype();	/* R */
Xextern MARK	v_selcut();	/* " */
Xextern MARK	v_paste();	/* p P */
Xextern MARK	v_yank();	/* y Y */
Xextern MARK	v_delete();	/* d D */
Xextern MARK	v_join();	/* J */
Xextern MARK	v_insert();	/* a A i I o O */
Xextern MARK	v_change();	/* c C */
Xextern MARK	v_subst();	/* s */
Xextern MARK	v_lshift();	/* < */
Xextern MARK	v_rshift();	/* > */
Xextern MARK	v_reformat();	/* = */
Xextern MARK	v_filter();	/* ! */
Xextern MARK	v_status();	/* ^G */
Xextern MARK	v_switch();	/* ^^ */
Xextern MARK	v_tag();	/* ^] */
Xextern MARK	v_xit();	/* ZZ */
Xextern MARK	v_undoline();	/* U */
Xextern MARK	v_again();	/* & */
X#ifndef NO_EXTENSIONS
X extern MARK	v_keyword();	/* K */
X extern MARK	v_increment();	/* * */
X#endif
X#ifndef NO_ERRLIST
X extern MARK	v_errlist();	/* * */
X#endif
X#ifndef NO_AT
X extern MARK	v_at();		/* @ */
X#endif
X#ifdef SIGTSTP
X extern MARK	v_suspend();	/* ^Z */
X#endif
X#ifndef NO_POPUP
X extern MARK	v_popup();	/* \ */
X#endif
X
X/*----------------------------------------------------------------------*/
X/* These flags describe the quirks of the individual visual commands */
X#define NO_FLAGS	0x00
X#define	MVMT		0x01	/* this is a movement command */
X#define PTMV		0x02	/* this can be *part* of a movement command */
X#define FRNT		0x04	/* after move, go to front of line */
X#define INCL		0x08	/* include last char when used with c/d/y */
X#define LNMD		0x10	/* use line mode of c/d/y */
X#define NCOL		0x20	/* this command can't change the column# */
X#define NREL		0x40	/* this is "non-relative" -- set the '' mark */
X#define SDOT		0x80	/* set the "dot" variables, for the "." cmd */
X#ifndef NO_VISIBLE
X# define VIZ		0x100	/* commands which can be used with 'v' */
X#else
X# define VIZ		0
X#endif
X
X/* This variable is zeroed before a command executes, and later ORed with the
X * command's flags after the command has been executed.  It is used to force
X * certain flags to be TRUE for *some* invocations of a particular command.
X * For example, "/regexp/+offset" forces the LNMD flag, and sometimes a "p"
X * or "P" command will force FRNT.
X */
Xextern int	force_flags;
X
X/*----------------------------------------------------------------------*/
X/* These describe what mode we're in */
X
X#define MODE_EX		1	/* executing ex commands */
X#define	MODE_VI		2	/* executing vi commands */
X#define	MODE_COLON	3	/* executing an ex command from vi mode */
X#define	MODE_QUIT	4
Xextern int	mode;
X
X#define WHEN_VICMD	1	/* getkey: we're reading a VI command */
X#define WHEN_VIINP	2	/* getkey: we're in VI's INPUT mode */
X#define WHEN_VIREP	4	/* getkey: we're in VI's REPLACE mode */
X#define WHEN_EX		8	/* getkey: we're in EX mode */
X#define WHEN_MSG	16	/* getkey: we're at a "more" prompt */
X#define WHEN_POPUP	32	/* getkey: we're in the pop-up menu */
X#define WHEN_REP1	64	/* getkey: we're getting a single char for 'r' */
X#define WHEN_CUT	128	/* getkey: we're getting a cut buffer name */
X#define WHEN_MARK	256	/* getkey: we're getting a mark name */
X#define WHEN_CHAR	512	/* getkey: we're getting a destination for f/F/t/T */
X#define WHEN_INMV	4096	/* in input mode, interpret the key in VICMD mode */
X#define WHEN_FREE	8192	/* free the keymap after doing it once */
X#define WHENMASK	(WHEN_VICMD|WHEN_VIINP|WHEN_VIREP|WHEN_REP1|WHEN_CUT|WHEN_MARK|WHEN_CHAR)
X
X#ifndef NO_VISIBLE
Xextern MARK	V_from;
Xextern int	V_linemd;
Xextern MARK	v_start();
X#endif
X
X#ifdef DEBUG
X# define malloc(size)	dbmalloc(size, __FILE__, __LINE__)
X# define free(ptr)	dbfree(ptr, __FILE__, __LINE__)
Xextern char *dbmalloc();
X#endif
/
