new public version of nvi

This commit is contained in:
cgd 1994-08-17 16:17:19 +00:00
parent 8743009944
commit 1588279d5e
71 changed files with 36896 additions and 118 deletions

View File

@ -0,0 +1,18 @@
# @(#)Makefile 8.1 (Berkeley) 6/8/93
DIR= usd/11.edit
SRCS= edittut.ms
MACROS= -msU
paper.ps: ${SRCS}
${TBL} ${SRCS} | ${ROFF} > ${.TARGET}
# index for versatec is different from the one in edit.tut
# because the fonts are different and entries reference page
# rather than section numbers. if you have a typesetter
# you should just use the index in edit.tut, and ignore editvindex.
editvindex:
${TROFF} ${MACROS} -n22 edit.vindex
.include <bsd.doc.mk>

View File

@ -0,0 +1,115 @@
.\" Copyright (c) 1980, 1993
.\" The Regents of the University of California. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. All advertising materials mentioning features or use of this software
.\" must display the following acknowledgement:
.\" This product includes software developed by the University of
.\" California, Berkeley and its contributors.
.\" 4. Neither the name of the University nor the names of its contributors
.\" may be used to endorse or promote products derived from this software
.\" without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" @(#)edit.vindex 8.1 (Berkeley) 6/8/93
.\"
.bd I
.ND
.TL
Index
.sp 3
.2C
.nf
addressing, \fIsee\fR line numbers
append mode, 4
backslash (\\), 18
buffer, 2
command mode, 4
context search, 8, 10, 13, 18
control characters (``^'' notation), 8
control-d, 6
current filename, 19, 20
current line (.), 9, 15
diagnostic messages, 4
disk, 2
documentation, 21
edit (to begin editing session), 3, 7
editing commands:
.in +2
append (a), 4, 7
change (c), 16
copy (co), 13
delete (d), 13-14
edit (e), 12
file (f), 19
global (g), 18-19
move (m), 12-13
number (nu), 9
preserve (pre), 20-21
print (p), 8
quit (q), 5, 11
quit! (q!), 11
read (r), 20
recover (rec), 20
substitute (s), 9-10, 17, 18
undo (u), 14, 17
write (w), 5-6, 11, 19-20
z, 11
.sp 10i
! (shell escape), 19
$= , 15
+, 15
\-, 15
//, 8, 18
??, 18
\&\fB.\fR, 9, 15
\&\fB.\fR=, 9, 15
.in -2
erasing
.ti +2
characters (#), 8
.ti +2
lines (@), 8
ex (text editor), 21
\fIEx Reference Manual\fR, 21
file, 1
file recovery, 20
filename, 2
Interrupt (message), 7
line numbers, \fIsee also\fR current line
.ti +2
dollar sign ($), 8, 12-13, 15
.ti +2
dot (.), 9, 15
.ti +2
relative (+ and \-), 15, 16
logging out, 6
login procedure, 2
``magic'' characters, 21
non-printing characters, 8
``not found'' (message), 3
program, 1
recovery \fIsee\fR file recovery
shell, 18
shell escape (!), 19
special characters (^, $, \e), 18
text input mode, 4
UNIX, 1

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,14 @@
# @(#)Makefile 8.1 (Berkeley) 6/8/93
DIR= usd/13.ex
SRCS= ex.rm
MACROS= -msU
CLEANFILES=summary.*
paper.ps: ${SRCS} summary.ps
${ROFF} ${SRCS} > ${.TARGET}
summary.ps: ex.summary
${TBL} ex.summary | ${ROFF} > ${.TARGET}
.include <bsd.doc.mk>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,734 @@
.\" Copyright (c) 1980, 1993
.\" The Regents of the University of California. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. All advertising materials mentioning features or use of this software
.\" must display the following acknowledgement:
.\" This product includes software developed by the University of
.\" California, Berkeley and its contributors.
.\" 4. Neither the name of the University nor the names of its contributors
.\" may be used to endorse or promote products derived from this software
.\" without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" @(#)ex.summary 8.1 (Berkeley) 6/8/93
.\"
.ds p \v'-0.2'.\v'+0.2'
.ds U \s-2UNIX\s+2
.ds c \v'-0.2':\v'+0.2'
.nr PO .25i
.nr LL 6.75i
.lt 6.75i
.ll 6.75i
.ds CH
.ds LF Computing Services, U.C. Berkeley
.ds RF April 3, 1979
.de SP
.sp 1v
..
.nr PI 3n
.nr PD 0
.ND
.ps 12
.ft B
.ce 1
Ex/Edit Command Summary (Version 2.0)
.ft R
.nr VS 11
.nr PS 9
.nr HM 0.5i
.nr CW
.2C
.PP
.I Ex
and
.I edit
are text editors, used for creating
and modifying files of text on the \*U
computer system.
.I Edit
is a variant of
.I ex
with features designed to
make it less complicated
to learn and use.
In terms of command syntax and effect
the editors are essentially identical,
and this command summary applies to both.
.PP
The summary is meant as a quick reference
for users already acquainted
with
.I edit
or \fIex\fP.
Fuller explanations of the editors are available
in the documents
.I
Edit: A Tutorial
.R
(a self-teaching introduction) and the
.I
Ex Reference Manual
.R
(the comprehensive reference source for
both \fIedit\fP and \fIex\fP).
Both of these writeups are available in the
Computing Services Library.
.PP
In the examples included with the
summary, commands and text entered by
the user are printed in \fBboldface\fR to
distinguish them from responses printed
by the computer.
.sp 0.45v
.LP
.B
The Editor Buffer
.PP
In order to perform its tasks
the editor sets aside a temporary
work space,
called a \fIbuffer\fR,
separate from the user's permanent
file.
Before starting to work on an existing
file the editor makes a copy of it in the
buffer, leaving the original untouched.
All editing changes are made to the
buffer copy, which must then
be written back to the permanent
file in order to update the
old version.
The buffer disappears
at the end of the editing session.
.sp 0.45v
.LP
.B
Editing: Command and Text Input Modes
.PP
.R
During an editing session there are
two usual modes of operation:
\fIcommand\fP mode and \fItext input\fP
mode.
(This disregards, for the moment,
.I open
and
.I visual
modes, discussed below.)
In command mode, the editor issues a
colon prompt (:)
to show that it is ready to
accept and execute a command.
In text input mode, on the other hand, there is
no prompt and the editor merely accepts text to
be added to the buffer.
Text input mode is initiated by the commands
\fIappend\fP, \fIinsert\fP, and \fIchange\fP,
and is terminated by typing a period as the
first and only character on a line.
.sp 0.45v
.LP
.B
Line Numbers and Command Syntax
.PP
.R
The editor keeps track of lines of text
in the buffer by numbering them consecutively
starting with 1 and renumbering
as lines are added or deleted.
At any given time the editor is positioned
at one of these lines; this position is
called the \fIcurrent line\fP.
Generally, commands that change the
contents of the buffer print the
new current line at the end of their
execution.
.PP
Most commands can be preceded by one or two
line-number addresses which indicate the lines
to be affected.
If one number is given the command operates on
that line only; if two, on an inclusive range
of lines.
Commands that can take line-number prefixes also
assume default prefixes if none are given.
The default assumed by each command is designed
to make it convenient to use in many instances
without any line-number prefix.
For the most part, a command used without a
prefix operates on the current line,
though exceptions to this rule should be noted.
The \fIprint\fP command
by itself, for instance, causes
one line, the current line, to be
printed at the terminal.
.PP
The summary shows the number of line addresses
that can be
prefixed to each command as well as
the defaults assumed if they are omitted.
For example,
.I (.,.)
means that up to 2 line-numbers may be given,
and that if none is given the
command operates on the current line.
(In the address prefix notation, ``.'' stands
for the current line and ``$'' stands for
the last line of the buffer.)
If no such notation appears, no
line-number prefix may be used.
.PP
Some commands take trailing
information;
only
the more important instances of this
are mentioned in the summary.
.sp 0.25v
.LP
.B
Open and Visual Modes
.PP
.R
Besides command and text input modes,
.I ex
and
.I edit
provide on some CRT terminals other modes of editing,
.I open
and
.I visual .
In these modes the cursor can
be moved to individual words
or characters in a line.
The commands then given are very different
from the standard editor commands; most do not appear on the screen when
typed.
.I
An Introduction to Display Editing with Vi
.R
provides a full discussion.
.sp 0.25v
.LP
.B
Special Characters
.PP
.R
.fi
Some characters take on special meanings
when used in context searches
and in patterns given to the \fIsubstitute\fP command.
For \fIedit\fR, these are ``^'' and ``$'',
meaning the beginning and end of a line,
respectively.
.I Ex
has the following additional special characters:
.B
.ce 1
\&. & * [ ] ~
.R
To use one of the special characters as its
simple graphic representation
rather than with its special meaning,
precede it by a backslash (\\).
The backslash always has a special meaning.
.1C
.rm LF
.rm RF
.rm CF
.nr FM 0.4
.TS
cp10 cp10 cp10 cp10
ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
Name Abbr Description Examples
.sp 1.75
(.)\fBappend a T{
Begins text input mode,
adding lines to the buffer after
the line specified. Appending continues
until ``.'' is typed alone at the
beginning of a new line, followed by
a carriage return. \fI0a\fR places
lines at the beginning of the buffer.
T} T{
.nf
\fR:\fBa
Three lines of text
are added to the buffer
after the current line.
\*p
.R
\*c
.fi
T}
.SP
\fR(.,.)\fBchange c T{
Deletes indicated line(s) and
initiates text input mode to
replace them with new text which follows.
New text is terminated the same way
as with \fIappend\fR.
T} T{
.nf
:\fB5,6c
Lines 5 and 6 are
deleted and replaced by
these three lines.
\*p
.R
\*c
.fi
T}
.SP
\fR(.,.)\fBcopy \fIaddr co T{
Places a copy of the specified lines
after the line indicated by \fIaddr\fR.
The example places a copy of lines 8 through
12, inclusive, after line 25.
T} T{
.nf
\fR:\fB8,12co 25
\fRLast line copied is printed
\fR\*c
.fi
T}
.SP
\fR(.,.)\fBdelete d T{
Removes lines from the buffer
and prints the current line after the deletion.
T} T{
.nf
\fR:\fB13,15d
\fRNew current line is printed
\*c
.fi
T}
.TE
.sp 0.5v
.TS
ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
T{
\fBedit \fIfile\fP
.br
\fBedit! \fIfile\fP
T} T{
e
.br
e!
T} T{
.fi
\fRClears the editor buffer and then
copies into it the named \fIfile\fR,
which becomes the current file.
This is a way of shifting to a different
file
without leaving the editor.
The editor issues a warning
message if this command is used before
saving changes
made to the file already in the buffer;
using the form \fBe!\fR overrides this protective mechanism.
T} T{
.nf
\fR:\fBe ch10\fR
No write since last change
:\fBe! ch10\fR
"ch10" 3 lines, 62 characters
\*c
.fi
T}
.SP
\fBfile \fIname\fR f T{
\fRIf followed by a \fIname\fR, renames
the current file to \fIname\fR.
If used without \fIname\fR, prints
the name of the current file.
T} T{
.nf
\fR:\fBf ch9
\fR"ch9" [Modified] 3 lines ...
:\fBf
\fR"ch9" [Modified] 3 lines ...
\*c
.fi
T}
.SP
(1,$)\fBglobal g \fBglobal/\fIpattern\fB/\fIcommands T{
.nf
:\fBg/nonsense/d
\fR\*c
.fi
T}
\fR(1,$)\fBglobal! g!\fR or \fBv T{
Searches the entire buffer (unless a smaller
range is specified by line-number prefixes) and
executes \fIcommands\fR on every line with
an expression matching \fIpattern\fR.
The second form, abbreviated
either \fBg!\fR or \fBv\fR,
executes \fIcommands\fR on lines that \fIdo
not\fR contain the expression \fIpattern\fR.
T} \^
.SP
\fR(.)\fBinsert i T{
Inserts new lines of text immediately before the specified line.
Differs from
.I append
only in that text is placed before, rather than after, the indicated line.
In other words, \fB1i\fR has the same effect as \fB0a\fR.
T} T{
.nf
:\fB1i
These lines of text will
be added prior to line 1.
\&.
\fR:
.fi
T} \^
.SP
\fR(.,.+1)\fBjoin j T{
Join lines together, adjusting white space (spaces
and tabs) as necessary.
T} T{
.nf
:\fB2,5j\fR
Resulting line is printed
:
.fi
T} \^
.TE
.bp
.TS
cp10 cp10 cp10 cp10
ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
Name Abbr Description Examples
.sp 1.75
\fR(.,.)\fBlist l T{
\fRPrints lines in a more
unambiguous way than the \fIprint\fR
command does. The end of a line,
for example, is marked with a ``$'',
and tabs printed as ``^I''.
T} T{
.nf
:\fB9l
\fRThis is line 9$
\*c
.fi
T}
.TE
.sp 0.5v
.TS
ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
\fR(.,.)\fBmove \fIaddr\fB m T{
\fRMoves the specified lines
to a position after the line
indicated by \fIaddr\fR.
T} T{
.nf
\fR:\fB12,15m 25\fR
New current line is printed
\*c
.fi
T}
.SP
\fR(.,.)\fBnumber nu T{
Prints each line preceded
by its buffer line number.
T} T{
.nf
\fR:\fBnu
\0\0\fR10\0 This is line 10
\*c
.fi
T}
.SP
\fR(.)\fBopen o T{
Too involved to discuss here,
but if you enter open mode
accidentally, press
the \s-2ESC\s0 key followed by
\fBq\fR to
get back into normal editor
command mode.
\fIEdit\fP is designed to
prevent accidental use of
the open command.
T}
.SP
\fBpreserve pre T{
Saves a copy of the current buffer contents as though the system had
just crashed. This is for use in an emergency when a
.I write
command has failed and you don't know how else to save your work.\(dg
T} T{
.nf
:\fBpreserve\fR
File preserved.
:
.fi
T}
.SP
\fR(.,.)\fBprint p Prints the text of line(s). T{
.nf
:\fB+2,+3p\fR
The second and third lines
after the current line
:
.fi
T}
.TE
.FS
\(dg Seek assistance from a consultant as soon as possible
after saving a file with the
.I preserve
command, because the file is saved on system storage space for only one week.
.FE
.SP
.nf
.TS
ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
T{
.nf
\fBquit
quit!
.fi
T} T{
.nf
q
q!
T} T{
.fi
\fREnds the editing session.
You will receive a
warning if you have changed the buffer
since last writing its contents
to the file. In this event you
must either type \fBw\fR to write,
or type \fBq!\fR to exit from
the editor without saving your changes.
T} T{
.nf
\fR:\fBq
\fRNo write since last change
:\fBq!
\fR%
.fi
T}
.SP
\fR(.)\fBread \fIfile\fP r T{
.fi
\fRPlaces a copy of \fIfile\fR in the
buffer after the specified line.
Address 0 is permissible and causes
the copy of \fIfile\fR to be placed
at the beginning of the buffer.
The \fIread\fP command does not
erase any text already in the buffer.
If no line number is specified,
\fIfile\fR is placed after the
current line.
T} T{
.nf
\fR:\fB0r newfile
\fR"newfile" 5 lines, 86 characters
\*c
.fi
T}
.SP
\fBrecover \fIfile\fP rec T{
.fi
Retrieves a copy of the editor buffer
after a system crash, editor crash,
phone line disconnection, or
\fIpreserve\fR command.
T}
.SP
\fR(.,.)\fBsubstitute s T{
.nf
\fBsubstitute/\fIpattern\fB/\fIreplacement\fB/
substitute/\fIpattern\fB/\fIreplacement\fB/gc
.fi
\fRReplaces the first occurrence of \fIpattern\fR
on a line
with \fIreplacement\fP.
Including a \fBg\fR after the command
changes all occurrences of \fIpattern\fP
on the line.
The \fBc\fR option allows the user to
confirm each substitution before it is
made; see the manual for details.
T} T{
.nf
:\fB3p
\fRLine 3 contains a misstake
:\fBs/misstake/mistake/
\fRLine 3 contains a mistake
\*c
.fi
T}
.TE
.bp
.TS
cp10 cp10 cp10 cp10
ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
Name Abbr Description Examples
.sp 1.75
\fBundo u T{
.fi
\fRReverses the changes made in
the buffer by the last buffer-editing
command.
Note that this example contains
a notification about the number of
lines affected.
T} T{
.nf
\fR:\fB1,15d
\fR15 lines deleted
new line number 1 is printed
:\fBu
\fR15 more lines in file ...
old line number 1 is printed
\*c
.fi
T}
.SP
\fR(1,$)\fBwrite \fIfile\fR w T{
.fi
\fRCopies data from the buffer onto
a permanent file. If no \fIfile\fR
is named, the current filename
is used.
The file is automatically created
if it does not yet exist.
A response containing the number of
lines and characters in the file
indicates that the write
has been completed successfully.
The editor's built-in protections
against overwriting existing files
will in some circumstances
inhibit a write.
The form \fBw!\fR forces the
write, confirming that
an existing file is to be overwritten.
T} T{
.nf
\fR:\fBw
\fR"file7" 64 lines, 1122 characters
:\fBw file8
\fR"file8" File exists ...
:\fBw! file8
\fR"file8" 64 lines, 1122 characters
\*c
.fi
T}
\fR(1,$)\fBwrite! \fIfile\fP w! \^ \^
.TE
.sp 0.5v
.TS
ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
\fR(.)\fBz \fIcount\fP z T{
.fi
\fRPrints a screen full of text starting
with the line indicated;
or, if \fIcount\fR is specified,
prints that number of lines.
Variants of the \fIz\fR command
are described in the manual.
T}
.SP
\fB!\fIcommand T{
.fi
Executes the remainder of the line
after \fB!\fR as a \*U command.
The buffer is unchanged by this, and
control is returned to the editor when
the execution of \fIcommand\fR is complete.
T} T{
.nf
\fR:\fB!date
\fRFri Jun 9 12:15:11 PDT 1978
!
\*c
.fi
T}
.SP
\fRcontrol-d T{
.fi
Prints the next \fIscroll\fR of text,
normally half of a screen. See the
manual for details of the \fIscroll\fR
option.
T}
.SP
\fR(.+1)<cr> T{
.fi
An address alone followed by a carriage
return causes the line to be printed.
A carriage return by itself prints the
line following the current line.
T} T{
.nf
:\fR<cr>
the line after the current line
\*c
.fi
T}
.TE
.sp 0.5v
.TS
ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
\fB/\fIpattern\fB/ T{
.fi
\fRSearches for the next line in which
\fIpattern\fR occurs and prints it.
T} T{
.nf
\fR:\fB/This pattern/
\fRThis pattern next occurs here.
\*c
.fi
T}
.SP
\fB// T{
Repeats the most recent search.
T} T{
.nf
\fR:\fB//
\fRThis pattern also occurs here.
\*c
.fi
T}
.SP
\fB?\fIpattern\fB? T{
Searches in the reverse direction
for \fIpattern\fP.
T}
.SP
\fB?? T{
Repeats the most recent search,
moving in the reverse direction
through the buffer.
T}
.TE

View File

@ -0,0 +1,14 @@
# @(#)Makefile 8.5 (Berkeley) 7/16/94
SRCS= vi.1
DOCS= vi.0 vi.0.ps
all: ${DOCS}
vi.0: vi.1
groff -man -Tascii < vi.1 > $@
vi.0.ps: vi.1
groff -man < vi.1 > $@
clean:
rm -f ${DOCS}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,25 @@
# @(#)Makefile 8.16 (Berkeley) 8/15/94
DIR= usd/13.viref
SRCS= vi.ref ex.cmd.roff set.opt.roff vi.cmd.roff ref.so
MACROS= -me
CLEANFILES+=vi.ref.txt index index.so
paper.ps: vi.ref index.so
soelim vi.ref | ${TBL} | ${ROFF} > ${.TARGET}
vi.ref.txt: vi.ref index.so
soelim vi.ref | ${TBL} | groff ${MACROS} -Tascii > $@
index.so: vi.ref
# Build index.so, side-effect of building the paper.
soelim vi.ref | ${TBL} | ${ROFF} > /dev/null
sed -e 's/MINUSSIGN/\\-/' \
-e 's/DOUBLEQUOTE/""/' \
-e "s/SQUOTE/'/" \
-e 's/ /__SPACE/g' < index | \
sort -u '-t ' +0 -1 +1n | awk -f merge.awk | \
sed -e 's/__SPACE/ /g' > index.so
rm -f index
.include <bsd.doc.mk>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
# @(#)merge.awk 8.3 (Berkeley) 5/26/94
#
# merge index entries into one line per label
$1 == prev {
printf ", %s", $2;
next;
}
{
if (NR != 1)
printf "\n";
printf "%s \t%s", $1, $2;
prev = $1;
}
END {
printf "\n"
}

View File

@ -0,0 +1,949 @@
.\" Copyright (c) 1994
.\" The Regents of the University of California. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. All advertising materials mentioning features or use of this software
.\" must display the following acknowledgement:
.\" This product includes software developed by the University of
.\" California, Berkeley and its contributors.
.\" 4. Neither the name of the University nor the names of its contributors
.\" may be used to endorse or promote products derived from this software
.\" without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" @(#)set.opt.roff 8.26 (Berkeley) 8/11/94
.\"
.SH 1 "Set Options"
.pp
There are a large number of options that may be set (or unset) to
change the editor's behavior.
This section describes the options, their abbreviations and their
default values.
.pp
In each entry below, the first part of the tag line is the full name
of the option, followed by any equivalent abbreviations.
(Regardless of the abbreviations, it is only necessary to use the
minimum number of characters necessary to distinguish an abbreviation
from all other commands for it to be accepted, in
.EV nex nvi .
Historically, only the full name and the official abbreviations
were accepted by
.EV ex vi .
Using full names in your startup files and environmental variables will
probably make them more portable.)
The part in square brackets is the default value of the option.
Most of the options are boolean, i.e. they are either on or off,
and do not have an associated value.
.pp
Options apply to both
.CO ex
and
.CO vi
modes, unless otherwise specified.
.pp
For information on modifying the options or to display the options and
their current values, see the
.QQ set
command in the section entitled
.QB "Ex Commands" .
.KY altwerase
.IP "altwerase [off]"
.CO Vi
only.
Change how
.CO vi
does word erase during text input.
When this option is set, text is broken up into three classes:
alphabetic, numeric and underscore characters, other nonblank
characters, and blank characters.
Changing from one class to another marks the end of a word.
In addition, the class of the first character erased is ignored
(which is exactly what you want when erasing pathname components).
.KY autoindent
.IP "autoindent, ai [off]"
If this option is set, whenever you create a new line (using the
.CO vi
.CO A ,
.CO a ,
.CO C ,
.CO c ,
.CO I ,
.CO i ,
.CO O ,
.CO o ,
.CO R ,
.CO r ,
.CO S ,
and
.CO s
commands, or the
.CO ex
.CO append ,
.CO change ,
and
.CO insert
commands) the new line is automatically indented to align the cursor with
the first nonblank character of the line from which you created it.
Lines are indented using tab characters to the extent possible (based on
the value of the
.OP tabstop
option) and then using space characters as necessary.
For commands inserting text into the middle of a line, any blank characters
to the right of the cursor are discarded, and the first nonblank character
to the right of the cursor is aligned as described above.
.sp
The indent characters are themselves somewhat special.
If you do not enter more characters on the new line before moving to
another line, or entering
.LI <escape> ,
the indent character will be deleted and the line will be empty.
For example, if you enter
.LI <carriage-return>
twice in succession,
the line created by the first
.LI <carriage-return>
will not have any characters in it,
regardless of the indentation of the previous or subsequent line.
.sp
Indent characters also require that you enter additional erase characters
to delete them.
For example,
if you have an indented line, containing only blanks, the first
.LI <word-erase>
character you enter will erase up to end of the indent characters,
and the second will erase back to the beginning of the line.
(Historically, only the
.CO <control-D>
key would erase the indent characters.
Both the
.CO <control-D>
key and the usual erase keys work in
.CO nvi .)
In addition, if the cursor is positioned at the end of the indent
characters, the keys
.QT 0<control-D>
will erase all of the indent characters for the current line,
resetting the indentation level to 0.
Similarly, the keys
.QT ^<control-D>
will erase all of the indent characters for the current line,
leaving the indentation level for future created lines unaffected.
.sp
Finally, if the
.OP autoindent
option is set, the
.CO S
and
.CO cc
commands change from the first nonblank of the line to the end of the
line, instead of from the beginning of the line to the end of the line.
.KY autoprint
.IP "autoprint, ap [off]"
.CO Ex
only.
Cause the current line to be automatically displayed after the
.CO ex
commands
.CO < ,
.CO > ,
.CO copy ,
.CO delete ,
.CO join ,
.CO move ,
.CO put ,
.CO t ,
.CO Undo ,
and
.CO undo .
This automatic display is suppressed during
.CO global
and
.CO vglobal
commands, and for any command where optional flags are used to explicitly
display the line.
.KY autowrite
.IP "autowrite, aw [off]"
If this option is set, the
.CO vi
.CO ! ,
.CO ^^ ,
.CO ^]
and
.CO <control-Z>
commands, and the
.CO ex
.CO edit ,
.CO next ,
.CO rewind ,
.CO stop ,
.CO suspend ,
.CO tag ,
.CO tagpop ,
and
.CO tagtop
commands automatically write the current file back to the current file name
if it has been modified since it was last written.
If the write fails, the command fails and goes no further.
.sp
Appending the optional force flag character
.QT !
to the
.CO ex
commands
.CO next ,
.CO rewind ,
.CO stop ,
.CO suspend ,
.CO tag ,
.CO tagpop ,
and
.CO tagtop
stops the automatic write from being attempted.
.sp
(Historically, the
.CO next
command ignored the optional force flag.)
Note, the
.CO ex
commands
.CO edit ,
.CO quit ,
.CO shell ,
and
.CO xit
are
.i not
affected by the
.OP autowrite
option.
.KY beautify
.IP "beautify, bf [off]"
If this option is set, all control characters that are not currently being
specially interpreted, other than
.LI <tab> ,
.LI <newline> ,
and
.LI <form-feed> ,
are
discarded from commands read in by
.CO ex
from command files, and from input text entered to
.CO vi
(either into the file or to the colon command line).
Text files read by
.EV ex vi
are
.i not
affected by the
.OP beautify
option.
.KY cdpath
.IP "cdpath [environment variable CDPATH, or current directory]"
This option is used to specify a colon separated list of directories
which are used as path prefixes for any relative path names used as
arguments for the
.CO cd
command.
The value of this option defaults to the value of the environmental
variable
.LI CDPATH
if it is set, otherwise to the current directory.
For compatibility with the POSIX 1003.2 shell, the
.CO cd
command does
.i not
check the current directory as a path prefix for relative path names
unless it is explicitly specified.
It may be so specified by entering an empty string or a
.QT \&.
character into the
.LI CDPATH
variable or the option value.
.KY columns
.IP "columns, co [80]"
The number of columns in the screen.
Setting this option causes
.EV ex vi
to set (or reset) the environmental variable
.LI COLUMNS .
See the section entitled
.QB "Sizing the Screen"
more information.
.KY comment
.IP "comment [off]"
.CO Vi
only.
If the first non-empty line of the file begins with the string
.QT /\&* ,
this option causes
.CO vi
to skip to the end of that C-language comment (probably a terribly boring
legal notice) before displaying the file.
.KY directory
.IP "directory, dir [environment variable TMPDIR, or /tmp]"
The directory where temporary files are created.
The environmental variable
.LI TMPDIR
is used as the default value if it exists, otherwise
.LI /tmp
is used.
.KY edcompatible
.IP "edcompatible, ed [off]"
Remember the values of the
.QQ c
and
.QQ g
suffices to the
.CO substitute
commands, instead of initializing them as unset for each new
command.
Specifying pattern and replacement strings to the
.CO substitute
command unsets the
.QQ c
and
.QQ g
suffices as well.
.KY errorbells
.IP "errorbells, eb [off]"
.CO Ex
only.
.CO Ex
error messages are normally presented in inverse video.
If that is not possible for the terminal, setting this option causes
error messages to be announced by ringing the terminal bell.
.KY exrc
.IP "exrc, ex [off]"
If this option is turned off in the system or $HOME startup files,
the local startup files are never read (unless they are the same
as the system or $HOME startup files).
Turning it on has no effect, i.e. the normal checks for local startup
files are performed, regardless.
See the section entitled
.QB "Startup Information"
for more information.
.KY extended
.IP "extended [off]"
This option causes all regular expressions to be treated as POSIX
1003.2 Extended Regular Expressions (which are similar to historic
.XR egrep 1
style expressions).
.KY flash
.IP "flash [on]"
This option causes the screen to flash instead of beeping the keyboard,
on error, if the terminal has the capability.
.KY hardtabs
.IP "hardtabs, ht [8]"
This option defines the spacing between hardware tab settings, i.e.
the tab expansion done by the operating system and/or the terminal
itself.
As
.EV nex nvi
never writes
.LI <tab>
characters to the terminal, unlike historic versions of
.EV ex vi ,
this option does not currently have any affect.
.KY ignorecase
.IP "ignorecase, ic [off]"
This option causes regular expressions, both in
.CO ex
commands and in searches,
to be evaluated in a case-insensitive manner.
.KY keytime
.IP "keytime [6]"
The 10th's of a second
.EV ex vi
waits for a subsequent key to complete a key mapping.
.KY leftright
.IP "leftright [off]"
.CO Vi
only.
This option causes the screen to be scrolled left-right to view
lines longer than the screen, instead of the traditional
.CO vi
screen interface which folds long lines at the right-hand margin
of the terminal.
.KY lines
.IP "lines, li [24]"
.CO Vi
only.
The number of lines in the screen.
Setting this option causes
.EV ex vi
to set (or reset) the environmental variable
.LI LINES .
See the section entitled
.QB "Sizing the Screen"
for more information.
.KY lisp
.IP "lisp [off]"
.CO Vi
only.
This option changes the behavior of the
.CO vi
.CO ( ,
.CO ) ,
.CO { ,
.CO } ,
.CO [[
and
.CO ]]
commands to match the Lisp language.
Also, the
.OP autoindent
option's behavior is changed to be appropriate for Lisp.
.sp
.i "This option is not yet implemented."
.KY list
.IP "list [off]"
This option causes lines to be displayed in an unambiguous fashion.
Specifically, tabs are displayed as control characters, i.e.
.QT ^I ,
and the ends of lines are marked with a
.QT $
character.
.KY magic
.IP "magic [on]"
This option is on by default.
Turning the
.OP magic
option off causes all regular expression characters except for
.QT ^
and
.QT $ ,
to be treated as ordinary characters.
To re-enable characters individually, when the
.OP magic
option is off,
precede them with a backslash
.QT \e
character.
See the section entitled
.QB "Regular Expressions and Replacement Strings"
for more information.
.KY matchtime
.IP "matchtime [7]"
.CO Vi
only.
The 10th's of a second
.EV ex vi
pauses on the matching character when the
.OP showmatch
option is set.
.KY mesg
.IP "mesg [on]"
This option allows other users to contact you using the
.XR talk 1
and
.XR write 1
utilities, while you are editing.
.EV Ex vi
does not turn message on, i.e. if messages were turned off when the
editor was invoked, they will stay turned off.
This option only permits you to disallow messages for the edit session.
See the
.XR mesg 1
utility for more information.
.KY modelines
.IP "modelines, modeline [off]"
If the
.OP modelines
option is set,
.EV ex vi
has historically scanned the first and last five lines of each file as
it is read for editing, looking for any
.CO ex
commands that have been placed in those lines.
After the startup information has been processed, and before the user
starts editing the file, any commands embedded in the file are executed.
.sp
Commands were recognized by the letters
.QQ e
or
.QQ v
followed by
.QQ x
or
.QQ i ,
at the beginning of a line or following a tab or space character,
and followed by a
.QQ : ,
an
.CO ex
command, and another
.QQ : .
.sp
This option is a security problem of immense proportions,
and should not be used under any circumstances.
.sp
.i "This option will never be implemented."
.KY number
.IP "number, nu [off]"
Precede each line displayed with its current line number.
.KY octal
.IP "octal [off]"
Display unknown characters as octal numbers, instead of the default
hexadecimal.
.KY open
.IP "open [on]"
.CO Ex
only.
If this option is not set, the
.CO open
and
.CO visual
commands are disallowed.
.KY optimize
.IP "optimize, opt [on]"
.CO Vi
only.
Throughput of text is expedited by setting the terminal not to do automatic
carriage returns when printing more than one (logical) line of output,
greatly speeding output on terminals without addressable cursors when text
with leading white space is printed.
.sp
.i "This option is not yet implemented."
.KY paragraphs
.IP "paragraphs, para [IPLPPPQPP LIpplpipbp]"
.CO Vi
only.
Define additional paragraph boundaries for the
.CO {
and
.CO }
commands.
The value of this option must be a character string consisting
of zero or more character pairs.
.sp
In the text to be edited, the character string
.LI "<newline>.<char-pair>" ,
(where
.LI <char-pair>
is one of the character pairs in the option's value)
defines a paragraph boundary.
For example, if the option were set to
.LI "LaA<space>##" ,
then all of the following additional paragraph boundaries would be
recognized:
.sp
.(l
<newline>.La
<newline>.A<space>
<newline>.##
.)l
.KY prompt
.IP "prompt [on]"
.CO Ex
only.
This option causes
.CO ex
to prompt for command input with a
.QT :
character; when it is not set, no prompt is displayed.
.KY readonly
.IP "readonly, ro [off]"
This option causes a force flag to be required to attempt to write
the file back to the original file name.
Setting this option is equivalent to using the
.b \-R
command line option, or editing a file which lacks write permission.
.KY recdir
.IP "recdir [/var/tmp/vi.recover]"
The directory where recovery files are stored.
.sp
If you change the value of
.CO recdir ,
be careful to choose a directory whose contents are not regularly
deleted.
Bad choices include directories in memory based filesystems,
or
.LI /tmp ,
on most systems,
as their contents are removed when the machine is rebooted.
.sp
Public directories like
.LI /usr/tmp
and
.LI /var/tmp
are usually safe, although some sites periodically prune old files
from them.
There is no requirement that you use a public directory,
e.g. a sub-directory of your home directory will work fine.
.sp
Finally, if you change the value of
.CO recdir ,
you must modify the recovery script to operate in your chosen recovery
area.
.sp
See the section entitled
.QB "Recovery"
for further information.
.KY redraw
.IP "redraw, re [off]"
.CO Vi
only.
The editor simulates (using great amounts of output), an intelligent
terminal on a dumb terminal (e.g. during insertions in
.CO vi
the characters to the right of the cursor are refreshed as each input
character is typed).
.sp
.i "This option is not yet implemented."
.KY remap
.IP "remap [on]"
If this option is set,
it is possible to define macros in terms of other macros.
Otherwise, each key is only remapped up to one time.
For example, if
.QT A
is mapped to
.QT B ,
and
.QT B
is mapped to
.QT C ,
The keystroke
.QT A
will be mapped to
.QT C
if the
.OP remap
option is set, and to
.QT B
if it is not set.
.KY report
.IP "report [5]"
Set the threshold of the number of lines that need to be changed or
yanked before a message will be displayed to the user.
For everything but the yank command, the value is the largest value
about which the editor is silent, i.e. by default, 6 lines must be
deleted before the user is notified.
However, if the number of lines yanked is greater than
.i "or equal to"
the set value, it is reported to the user.
.KY ruler
.IP "ruler [off]"
.CO Vi
only.
Display a row/column ruler on the colon command line.
.KY scroll
.IP "scroll, scr [window / 2]"
Set the number of lines scrolled by the
.CO vi
.CO <control-D>
and
.CO <control-U>
commands.
.sp
Historically, the
.CO ex
.CO z
command, when specified without a count, used two times the size of the
scroll value; the POSIX 1003.2 standard specified the window size, which
is a better choice.
.KY sections
.IP "sections, sect [NHSHH HUnhsh]"
.CO Vi
only.
Define additional section boundaries for the
.CO [[
and
.CO ]]
commands.
The
.OP sections
option should be set to a character string consisting of zero or
more character pairs.
In the text to be edited, the character string
.LI "<newline>.<char-pair>" ,
(where
.LI <char-pair>
is one of the character pairs in the option's value),
defines a section boundary in the same manner that
.OP paragraph
option boundaries are defined.
.KY shell
.IP "shell, sh [environment variable SHELL, or /bin/sh]"
Select the shell used by the editor.
The specified path is the pathname of the shell invoked by the
.CO vi
.CO !
shell escape command and by the
.CO ex
.CO shell
command.
This program is also used to resolve any shell meta-characters in
.CO ex
commands.
.KY shiftwidth
.IP "shiftwidth, sw [8]"
Set the autoindent and shift command indentation width.
This width is used by the
.OP autoindent
option and by the
.CO < ,
.CO > ,
and
.CO shift
commands.
.KY showdirty
.IP "showdirty [off]"
.CO Vi
only.
Display an asterisk on the colon command line if the file has been modified.
.KY showmatch
.IP "showmatch, sm [off]"
.CO Vi
only.
This option causes
.CO vi ,
when a
.QT }
or
.QT )
is entered, to briefly move the cursor the matching
.QT {
or
.QT ( .
See the
.OP matchtime
option for more information.
.KY showmode
.IP "showmode [off]"
.CO Vi
only.
This option causes
.CO vi
to display a string identifying the current editor mode on the
colon command line.
.KY sidescroll
.IP "sidescroll [16]"
.CO Vi
only.
Sets the number of columns that are shifted to the left or right,
when
.CO vi
is doing left-right scrolling and the left or right margin is
crossed.
See the
.OP leftright
option for more information.
.KY slowopen
.IP "slowopen, slow [off]"
This option affects the display algorithm used by
.CO vi ,
holding off display updating during input of new text to improve
throughput when the terminal in use is slow and unintelligent.
.sp
.i "This option is not yet implemented."
.KY sourceany
.IP "sourceany [off]"
If this option is turned on,
.CO vi
historically read startup files that were owned by someone other than
the editor user.
See the section entitled
.QB "Startup Information"
for more information.
This option is a security problem of immense proportions,
and should not be used under any circumstances.
.sp
.i "This option will never be implemented."
.KY tabstop
.IP "tabstop, ts [8]"
This option sets tab widths for the editor display.
.KY taglength
.IP "taglength, tl [0]"
This option sets the maximum number of characters that are considered
significant in a tag name.
Setting the value to 0 makes all of the characters in the tag name
significant.
.KY tags
.IP "tags, tag [tags /var/db/libc.tags /sys/kern/tags]"
Sets the list of tags files, in search order,
which are used when the editor searches for a tag.
.KY term
.IP "term, ttytype, tty [environment variable TERM]"
Set the terminal type.
Setting this option causes
.EV ex vi
to set (or reset) the environmental variable
.LI TERM .
.KY terse
.IP "terse [off]"
This option has historically made editor messages less verbose.
It has no effect in this implementation.
See the
.OP verbose
option for more information.
.KY tildeop
.IP "tildeop"
Modify the
.CO ~
command to take an associated motion.
.KY timeout
.IP "timeout, to [on]"
If this option is set,
.EV ex vi
waits for a specific period for a subsequent key to complete a key
mapping (see the
.OP keytime
option).
If the option is not set, the editor waits until enough keys are
entered to resolve the ambiguity, regardless of how long it takes.
.KY ttywerase
.IP "ttywerase [off]"
.CO Vi
only.
This option changes how
.CO vi
does word erase during text input.
If this option is set, text is broken up into two classes,
blank characters and nonblank characters.
Changing from one class to another marks the end of a word.
.KY verbose
.IP "verbose [off]"
.CO Vi
only.
.CO Vi
historically bells the terminal for many obvious mistakes, e.g. trying
to move past the left-hand margin, or past the end of the file.
If this option is set, an error message is displayed for all errors.
.KY w300
.IP "w300 [no default]"
.CO Vi
only.
Set the window size if the baud rate is less than 1200 baud.
See the
.OP window
option for more information.
.KY w1200
.IP "w1200 [no default]"
.CO Vi
only.
Set the window size if the baud rate is equal to 1200 baud.
See the
.OP window
option for more information.
.KY w9600
.IP "w9600 [no default]"
.CO Vi
only.
Set the window size if the baud rate is greater than 1200 baud.
See the
.OP window
option for more information.
.KY warn
.IP "warn [on]"
.CO Ex
only.
This option causes a warning message to the terminal if the file has
been modified, since it was last written, before a
.CO !
command.
.KY window
.IP "window, w, wi [environment variable LINES]"
This option determines the default number of lines in a screenful,
as written by the
.CO z
command.
It also determines the number of lines scrolled by the
.CO vi
commands
.CO <control-F>
and
.CO <control-B> .
The value of window can be unrelated to the real screen size,
although it starts out as the number of lines on the screen (see
the section entitled
.QB "Sizing the Screen"
for more information).
Setting the value of the
.OP window
option is the same as using the
.b \-w
command line option.
.sp
If the value of the
.OP window
option (as set by the
.OP window ,
.OP w300 ,
.OP w1200
or
.OP w9600
options) is smaller than the actual size of the screen, large screen
movements will result in displaying only that smaller number of lines
on the screen.
(Further movements in that same area will result in the screen being
filled.)
This can provide a performance improvement when viewing different
places in one or more files over a slow link.
.KY wrapmargin
.IP "wrapmargin, wm [0]"
.CO Vi
only.
If the value of the
.OP wrapmargin
option is non-zero,
.CO vi
will split lines so that they end at least that number of characters
before the right-hand margin of the screen.
(Note, the value of
.OP wrapmargin
is
.i not
a text length.
In a screen that is 80 columns wide, the command
.QT ":set wrapmargin=8"
attempts to keep the lines less than or equal to 72 columns wide.)
.sp
Lines are split at the previous whitespace character closest to the
number.
Any trailing whitespace characters before that character are deleted.
If the line is split because of an inserted
.LI <space>
or
.LI <tab>
character, and you then enter another
.LI <space>
character, it is discarded.
.sp
If wrapmargin is set to 0,
or if there is no blank character upon which to split the line,
the line is not broken.
.KY wrapscan
.IP "wrapscan, ws [on]"
This option causes searches to wrap around the end or the beginning
of the file, and back to the starting point.
Otherwise, the end or beginning of the file terminates the search.
.KY writeany
.IP "writeany, wa [off]"
If this option is set, file-overwriting checks that would usually be
made before the
.CO write
and
.CO xit
commands, or before an automatic write (see the
.OP autowrite
option), are not made.
This allows a write to any file, provided the file permissions allow it.

View File

@ -0,0 +1,270 @@
Amir
Autoprint
BRE's
Bostic
Bourne
DOUBLEQUOTE
Dq
Ds
ERE's
EXINIT
Englar
Ev
FF
Fa
Fl
HUnhsh
IPLPPPQPP
Kirkendall
Korn
LIpplpipbp
LaA
Li
Lowercase
MINUSSIGN
Makefiles
NEX
NEXINIT
NHSHH
NVI
Nex
Nvi
OS
POSIX
PostScript
RE's
README
RECDIR
Reference''USD:13
SIGHUP
SIGWINCH
SQUOTE
Se
Std
Std1003.2
Sy
TANDARDS
TIOCGWINSZ
TMPDIR
Todo
USD.doc
USD:13
UUNET
Vx
Whitespace
XOFF
XON
XOptions
XXCOLUMNS
XXXX
XXXXXX
XXb
ZZ
ab
abbrev
ags
ai
al
altwerase
arg
args
autoindent
autoprint
autowrite
aw
bbrev
bf
bigword
bigwords
bostic
brev
bugs.current
c2w
carat
cdy
changelog
chd
cmd
count1
count2
creens
cs.berkeley.edu
db
dbopen
def
di
dir
dit
docs
eFlRsv
eFlRv
ead
eb
edcompatible
elete
elvis
email
enum
eof
errorbells
esc
ex.cmd.roff
exrc
ext
exu
exusage
fi
filesystem
filesystems
ftp.cs.berkeley.edu
ftp.uu.net
gdb
gdb.script
gs
gzip'd
halfbyte
hange
hangup
hardtabs
ht
ic
ifdef
ignorecase
ile
ind
ious
ir
ist
ize
keystroke
keystrokes
keytime
leftright
lhs
li
lib
libc.tags
lobal
lowercase
lp
matchtime
mber
meta
mk
mkexrc
modeful
modeline
modelines
ndo
nex
nexrc
nk
nomagic
nooption
nsert
nul
nvi
nvi.tar.Z
nvi.tar.z
nz
oin
op
ove
para
pathname
pathnames
ppend
pu
py
rc.local
readonly
rec
recdir
recfile
recover.XXXX
recover.XXXXXX
recover.c
recover.script
remapmax
res
rew
rhs
ript
rk
ro
roff
rsion
sc
scr
screeen
se
set.opt.roff
shiftwidth
showmatch
showmode
sidescroll
slowopen
sm
sourceany
sp
spell.ok
st
sual
svi
sw
ta
tabstop
taglength
tagp
tagpop
tagstring
tagt
tagtop
terminfo
th
tildeop
tl
tmp
ts
ttytype
ttywerase
uR
ubstitute
ucb
uffers
uit
una
unabbrev
unm
uppercase
urce
uunet
var
ve
vglobal
vi.0.ps
vi.0.txt
vi.1
vi.XXXX
vi.XXXXXX
vi.cmd.roff
vi.exrc
vi.recover
vibackup
virecovery
viu
viusage
wa
whitespace
wi
wm
wn
wq
wrapmargin
wrapscan
writeany
ws
xaw
xit
ya
yy

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
# @(#)Makefile 8.1 (Berkeley) 8/14/93
DIR= usd/12.vi
SRCS= vi.in vi.chars
MACROS= -msU
CLEANFILES+=summary.* viapwh.*
paper.ps: ${SRCS} summary.ps viapwh.ps
${TBL} ${SRCS} | ${ROFF} > ${.TARGET}
summary.ps: vi.summary
${TBL} vi.summary | ${ROFF} > ${.TARGET}
viapwh.ps: vi.apwh.ms
${ROFF} vi.apwh.ms > ${.TARGET}
.include <bsd.doc.mk>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,644 @@
.\" Copyright (c) 1980, 1993
.\" The Regents of the University of California. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. All advertising materials mentioning features or use of this software
.\" must display the following acknowledgement:
.\" This product includes software developed by the University of
.\" California, Berkeley and its contributors.
.\" 4. Neither the name of the University nor the names of its contributors
.\" may be used to endorse or promote products derived from this software
.\" without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" @(#)vi.chars 8.1 (Berkeley) 6/8/93
.\"
.bd S 3
..pn 21
.de iP
.IP "\fB\\$1\fR" \\$2
..
.SH
Appendix: character functions
.PP
This appendix gives the uses the editor makes of each character. The
characters are presented in their order in the \s-2ASCII\s0 character
set: Control characters come first, then most special characters, then
the digits, upper and then lower case characters.
.PP
For each character we tell a meaning it has as a command and any meaning it
has during an insert.
If it has only meaning as a command, then only this is discussed.
Section numbers in parentheses indicate where the character is discussed;
a `f' after the section number means that the character is mentioned
in a footnote.
.iP "^@" 15
Not a command character.
If typed as the first character of an insertion it is replaced with the
last text inserted, and the insert terminates. Only 128 characters are
saved from the last insert; if more characters were inserted the mechanism
is not available.
A \fB^@\fR cannot be part of the file due to the editor implementation
(7.5f).
.iP "^A" 15
Unused.
.iP "^B" 15
Backward window.
A count specifies repetition.
Two lines of continuity are kept if possible (2.1, 6.1, 7.2).
.iP "^C" 15
Unused.
.iP "^D" 15
As a command, scrolls down a half-window of text.
A count gives the number of (logical) lines to scroll, and is remembered
for future \fB^D\fR and \fB^U\fR commands (2.1, 7.2).
During an insert, backtabs over \fIautoindent\fR white space at the beginning
of a line (6.6, 7.5); this white space cannot be backspaced over.
.iP "^E" 15
Exposes one more line below the current screen in the file, leaving
the cursor where it is if possible.
(Version 3 only.)
.iP "^F" 15
Forward window. A count specifies repetition.
Two lines of continuity are kept if possible (2.1, 6.1, 7.2).
.iP "^G" 15
Equivalent to \fB:f\fR\s-2CR\s0, printing the current file, whether
it has been modified, the current line number and the number of lines
in the file, and the percentage of the way through the file that you
are.
.iP "^H (\fR\s-2BS\s0\fP)" 15
Same as
.B "left arrow" .
(See
.B h ).
During an insert, eliminates the last input character, backing over it
but not erasing it; it remains so you can see what you typed if you
wish to type something only slightly different (3.1, 7.5).
.iP "^I\ (\fR\s-2TAB\s0\fP)" 15
Not a command character.
When inserted it prints as some
number of spaces.
When the cursor is at a tab character it rests at the last of the spaces
which represent the tab.
The spacing of tabstops is controlled by the \fItabstop\fR option (4.1, 6.6).
.iP "^J\ (\fR\s-2LF\s0\fP)" 15
Same as
.B "down arrow"
(see
.B j ).
.iP "^K" 15
Unused.
.iP "^L" 15
The \s-2ASCII\s0 formfeed character, this causes the screen to be cleared
and redrawn. This is useful after a transmission error, if characters
typed by a program other than the editor scramble the screen,
or after output is stopped by an interrupt (5.4, 7.2f).
.iP "^M\ (\fR\s-2CR\s0\fP)" 15
A carriage return advances to the next line, at the first non-white position
in the line. Given a count, it advances that many lines (2.3).
During an insert, a \s-2CR\s0 causes the insert to continue onto
another line (3.1).
.iP "^N" 15
Same as
.B "down arrow"
(see
.B j ).
.iP "^O" 15
Unused.
.iP "^P" 15
Same as
.B "up arrow"
(see
.B k ).
.iP "^Q" 15
Not a command character.
In input mode,
.B ^Q
quotes the next character, the same as
.B ^V ,
except that some teletype drivers will eat the
.B ^Q
so that the editor never sees it.
.iP "^R" 15
Redraws the current screen, eliminating logical lines not corresponding
to physical lines (lines with only a single @ character on them).
On hardcopy terminals in \fIopen\fR mode, retypes the current line
(5.4, 7.2, 7.8).
.iP "^S" 15
Unused. Some teletype drivers use
.B ^S
to suspend output until
.B ^Q is pressed.
.iP "^T" 15
Not a command character.
During an insert, with \fIautoindent\fR set and at the beginning of the
line, inserts \fIshiftwidth\fR white space.
.iP "^U" 15
Scrolls the screen up, inverting \fB^D\fR which scrolls down. Counts work as
they do for \fB^D\fR, and the previous scroll amount is common to both.
On a dumb terminal, \fB^U\fR will often necessitate clearing and redrawing
the screen further back in the file (2.1, 7.2).
.iP "^V" 15
Not a command character.
In input mode, quotes the next character so that it is possible
to insert non-printing and special characters into the file (4.2, 7.5).
.iP "^W" 15
Not a command character.
During an insert, backs up as \fBb\fR would in command mode; the deleted
characters remain on the display (see \fB^H\fR) (7.5).
.iP "^X" 15
Unused.
.iP "^Y" 15
Exposes one more line above the current screen, leaving the cursor where
it is if possible. (No mnemonic value for this key; however, it is next
to \fB^U\fR which scrolls up a bunch.)
(Version 3 only.)
.iP "^Z" 15
If supported by the Unix system,
stops the editor, exiting to the top level shell.
Same as \fB:stop\fP\s-2CR\s0.
Otherwise, unused.
.iP "^[\ (\fR\s-2ESC\s0\fP)" 15
Cancels a partially formed command, such as a \fBz\fR when no following
character has yet been given; terminates inputs on the last line (read
by commands such as \fB: /\fR and \fB?\fR); ends insertions of new text
into the buffer.
If an \s-2ESC\s0 is given when quiescent in command state, the editor
rings the bell or flashes the screen. You can thus hit \s-2ESC\s0 if
you don't know what is happening till the editor rings the bell.
If you don't know if you are in insert mode you can type \s-2ESC\s0\fBa\fR,
and then material to be input; the material will be inserted correctly
whether or not you were in insert mode when you started (1.5, 3.1, 7.5).
.iP "^\e" 15
Unused.
.iP "^]" 15
Searches for the word which is after the cursor as a tag. Equivalent
to typing \fB:ta\fR, this word, and then a \s-2CR\s0.
Mnemonically, this command is ``go right to'' (7.3).
.iP "^\(ua" 15
Equivalent to \fB:e #\fR\s-2CR\s0, returning to the previous position
in the last edited file, or editing a file which you specified if you
got a `No write since last change diagnostic' and do not want to have
to type the file name again (7.3).
(You have to do a \fB:w\fR before \fB^\(ua\fR
will work in this case. If you do not wish to write the file you should
do \fB:e!\ #\fR\s-2CR\s0 instead.)
.iP "^_" 15
Unused.
Reserved as the command character for the
Tektronix 4025 and 4027 terminal.
.iP "\fR\s-2SPACE\s0\fP" 15
Same as
.B "right arrow"
(see
.B l ).
.iP "!" 15
An operator, which processes lines from the buffer with reformatting commands.
Follow \fB!\fR with the object to be processed, and then the command name
terminated by \s-2CR\s0. Doubling \fB!\fR and preceding it by a count
causes count lines to be filtered; otherwise the count
is passed on to the object after the \fB!\fR. Thus \fB2!}\fR\fIfmt\fR\s-2CR\s0
reformats the next two paragraphs by running them through the program
\fIfmt\fR. If you are working on \s-2LISP\s0,
the command \fB!%\fR\fIgrind\fR\s-2CR\s0,*
.FS
*Both
.I fmt
and
.I grind
are Berkeley programs and may not be present at all installations.
.FE
given at the beginning of a
function, will run the text of the function through the \s-2LISP\s0 grinder
(6.7, 7.3).
To read a file or the output of a command into the buffer use \fB:r\fR (7.3).
To simply execute a command use \fB:!\fR (7.3).
.tr "
.iP  15
Precedes a named buffer specification. There are named buffers \fB1\-9\fR
used for saving deleted text and named buffers \fBa\-z\fR into which you can
place text (4.3, 6.3)
.tr 
.iP "#" 15
The macro character which, when followed by a number, will substitute
for a function key on terminals without function keys (6.9).
In input mode,
if this is your erase character, it will delete the last character
you typed in input mode, and must be preceded with a \fB\e\fR to insert
it, since it normally backs over the last input character you gave.
.iP "$" 15
Moves to the end of the current line. If you \fB:se list\fR\s-2CR\s0,
then the end of each line will be shown by printing a \fB$\fR after the
end of the displayed text in the line. Given a count, advances to the
count'th following end of line; thus \fB2$\fR advances to the end of the
following line.
.iP "%" 15
Moves to the parenthesis or brace \fB{ }\fR which balances the parenthesis
or brace at the current cursor position.
.iP "&" 15
A synonym for \fB:&\fR\s-2CR\s0, by analogy with the
.I ex
.B &
command.
.iP "\(aa" 15
When followed by a \fB\(aa\fR returns to the previous context at the
beginning of a line. The previous context is set whenever the current
line is moved in a non-relative way.
When followed by a letter \fBa\fR\-\fBz\fR, returns to the line which
was marked with this letter with a \fBm\fR command, at the first non-white
character in the line. (2.2, 5.3).
When used with an operator such as \fBd\fR, the operation takes place
over complete lines; if you use \fB\(ga\fR, the operation takes place
from the exact marked place to the current cursor position within the
line.
.iP "(" 15
Retreats to the beginning of a
sentence, or to the beginning of a \s-2LISP\s0 s-expression
if the \fIlisp\fR option is set.
A sentence ends at a \fB. !\fR or \fB?\fR which is followed by either
the end of a line or by two spaces. Any number of closing \fB) ] "\fR
and \fB\(aa\fR characters may appear after the \fB. !\fR or \fB?\fR,
and before the spaces or end of line. Sentences also begin
at paragraph and section boundaries
(see \fB{\fR and \fB[[\fR below).
A count advances that many sentences (4.2, 6.8).
.iP ")" 15
Advances to the beginning of a sentence.
A count repeats the effect.
See \fB(\fR above for the definition of a sentence (4.2, 6.8).
.iP "*" 15
Unused.
.iP "+" 15
Same as \s-2CR\s0 when used as a command.
.iP "," 15
Reverse of the last \fBf F t\fR or \fBT\fR command, looking the other way
in the current line. Especially useful after hitting too many \fB;\fR
characters. A count repeats the search.
.iP "\-" 15
Retreats to the previous line at the first non-white character.
This is the inverse of \fB+\fR and \s-2RETURN\s0.
If the line moved to is not on the screen, the screen is scrolled, or
cleared and redrawn if this is not possible.
If a large amount of scrolling would be required the screen is also cleared
and redrawn, with the current line at the center (2.3).
.iP "\&." 15
Repeats the last command which changed the buffer. Especially useful
when deleting words or lines; you can delete some words/lines and then
hit \fB.\fR to delete more and more words/lines.
Given a count, it passes it on to the command being repeated. Thus after
a \fB2dw\fR, \fB3.\fR deletes three words (3.3, 6.3, 7.2, 7.4).
.iP "/" 15
Reads a string from the last line on the screen, and scans forward for
the next occurrence of this string. The normal input editing sequences may
be used during the input on the bottom line; an returns to command state
without ever searching.
The search begins when you hit \s-2CR\s0 to terminate the pattern;
the cursor moves to the beginning of the last line to indicate that the search
is in progress; the search may then
be terminated with a \s-2DEL\s0 or \s-2RUB\s0, or by backspacing when
at the beginning of the bottom line, returning the cursor to
its initial position.
Searches normally wrap end-around to find a string
anywhere in the buffer.
.IP
When used with an operator the enclosed region is normally affected.
By mentioning an
offset from the line matched by the pattern you can force whole lines
to be affected. To do this give a pattern with a closing
a closing \fB/\fR and then an offset \fB+\fR\fIn\fR or \fB\-\fR\fIn\fR.
.IP
To include the character \fB/\fR in the search string, you must escape
it with a preceding \fB\e\fR.
A \fB\(ua\fR at the beginning of the pattern forces the match to occur
at the beginning of a line only; this speeds the search. A \fB$\fR at
the end of the pattern forces the match to occur at the end of a line
only.
More extended pattern matching is available, see section 7.4;
unless you set \fBnomagic\fR in your \fI\&.exrc\fR file you will have
to preceed the characters \fB. [ *\fR and \fB~\fR in the search pattern
with a \fB\e\fR to get them to work as you would naively expect (1.5, 2,2,
6.1, 7.2, 7.4).
.iP "0" 15
Moves to the first character on the current line.
Also used, in forming numbers, after an initial \fB1\fR\-\fB9\fR.
.iP "1\-9" 15
Used to form numeric arguments to commands (2.3, 7.2).
.iP ":" 15
A prefix to a set of commands for file and option manipulation and escapes
to the system. Input is given on the bottom line and terminated with
an \s-2CR\s0, and the command then executed. You can return to where
you were by hitting \s-2DEL\s0 or \s-2RUB\s0 if you hit \fB:\fR accidentally
(see primarily 6.2 and 7.3).
.iP ";" 15
Repeats the last single character find which used \fBf F t\fR or \fBT\fR.
A count iterates the basic scan (4.1).
.iP "<" 15
An operator which shifts lines left one \fIshiftwidth\fR, normally 8
spaces. Like all operators, affects lines when repeated, as in
\fB<<\fR. Counts are passed through to the basic object, thus \fB3<<\fR
shifts three lines (6.6, 7.2).
.iP "=" 15
Reindents line for \s-2LISP\s0, as though they were typed in with \fIlisp\fR
and \fIautoindent\fR set (6.8).
.iP ">" 15
An operator which shifts lines right one \fIshiftwidth\fR, normally 8
spaces. Affects lines when repeated as in \fB>>\fR. Counts repeat the
basic object (6.6, 7.2).
.iP "?" 15
Scans backwards, the opposite of \fB/\fR. See the \fB/\fR description
above for details on scanning (2.2, 6.1, 7.4).
.iP "@" 15
A macro character (6.9). If this is your kill character, you must escape it with a \e
to type it in during input mode, as it normally backs over the input you
have given on the current line (3.1, 3.4, 7.5).
.iP "A" 15
Appends at the end of line, a synonym for \fB$a\fR (7.2).
.iP "B" 15
Backs up a word, where words are composed of non-blank sequences, placing
the cursor at the beginning of the word. A count repeats the effect
(2.4).
.iP "C" 15
Changes the rest of the text on the current line; a synonym for \fBc$\fR.
.iP "D" 15
Deletes the rest of the text on the current line; a synonym for \fBd$\fR.
.iP "E" 15
Moves forward to the end of a word, defined as blanks and non-blanks,
like \fBB\fR and \fBW\fR. A count repeats the effect.
.iP "F" 15
Finds a single following character, backwards in the current line.
A count repeats this search that many times (4.1).
.iP "G" 15
Goes to the line number given as preceding argument, or the end of the
file if no preceding count is given. The screen is redrawn with the
new current line in the center if necessary (7.2).
.iP "H" 15
.B "Home arrow" .
Homes the cursor to the top line on the screen. If a count is given,
then the cursor is moved to the count'th line on the screen.
In any case the cursor is moved to the first non-white character on the
line. If used as the target of an operator, full lines are affected
(2.3, 3.2).
.iP "I" 15
Inserts at the beginning of a line; a synonym for \fB\(uai\fR.
.iP "J" 15
Joins together lines, supplying appropriate white space: one space between
words, two spaces after a \fB.\fR, and no spaces at all if the first
character of the joined on line is \fB)\fR. A count causes that many
lines to be joined rather than the default two (6.5, 7.1f).
.iP "K" 15
Unused.
.iP "L" 15
Moves the cursor to the first non-white character of the last line on
the screen. With a count, to the first non-white of the count'th line
from the bottom. Operators affect whole lines when used with \fBL\fR
(2.3).
.iP "M" 15
Moves the cursor to the middle line on the screen, at the first non-white
position on the line (2.3).
.iP "N" 15
Scans for the next match of the last pattern given to
\fB/\fR or \fB?\fR, but in the reverse direction; this is the reverse
of \fBn\fR.
.iP "O" 15
Opens a new line above the current line and inputs text there up to an
\s-2ESC\s0. A count can be used on dumb terminals to specify a number
of lines to be opened; this is generally obsolete, as the \fIslowopen\fR
option works better (3.1).
.iP "P" 15
Puts the last deleted text back before/above the cursor. The text goes
back as whole lines above the cursor if it was deleted as whole lines.
Otherwise the text is inserted between the characters before and at the
cursor. May be preceded by a named buffer specification \fB"\fR\fIx\fR
to retrieve the contents of the buffer; buffers \fB1\fR\-\fB9\fR contain
deleted material, buffers \fBa\fR\-\fBz\fR are available for general
use (6.3).
.iP "Q" 15
Quits from \fIvi\fR to \fIex\fR command mode. In this mode, whole lines
form commands, ending with a \s-2RETURN\s0. You can give all the \fB:\fR
commands; the editor supplies the \fB:\fR as a prompt (7.7).
.iP "R" 15
Replaces characters on the screen with characters you type (overlay fashion).
Terminates with an \s-2ESC\s0.
.iP "S" 15
Changes whole lines, a synonym for \fBcc\fR. A count substitutes for
that many lines. The lines are saved in the numeric buffers, and erased
on the screen before the substitution begins.
.iP "T" 15
Takes a single following character, locates the character before the
cursor in the current line, and places the cursor just after that character.
A count repeats the effect. Most useful with operators such as \fBd\fR
(4.1).
.iP "U" 15
Restores the current line to its state before you started changing it
(3.5).
.iP "V" 15
Unused.
.iP "W" 15
Moves forward to the beginning of a word in the current line,
where words are defined as sequences of blank/non-blank characters.
A count repeats the effect (2.4).
.iP "X" 15
Deletes the character before the cursor. A count repeats the effect,
but only characters on the current line are deleted.
.iP "Y" 15
Yanks a copy of the current line into the unnamed buffer, to be put back
by a later \fBp\fR or \fBP\fR; a very useful synonym for \fByy\fR.
A count yanks that many lines. May be preceded by a buffer name to put
lines in that buffer (7.4).
.iP "ZZ" 15
Exits the editor.
(Same as \fB:x\fP\s-2CR\s0.)
If any changes have been made, the buffer is written out to the current file.
Then the editor quits.
.iP "[[" 15
Backs up to the previous section boundary. A section begins at each
macro in the \fIsections\fR option,
normally a `.NH' or `.SH' and also at lines which which start
with a formfeed \fB^L\fR. Lines beginning with \fB{\fR also stop \fB[[\fR;
this makes it useful for looking backwards, a function at a time, in C
programs. If the option \fIlisp\fR is set, stops at each \fB(\fR at the
beginning of a line, and is thus useful for moving backwards at the top
level \s-2LISP\s0 objects. (4.2, 6.1, 6.6, 7.2).
.iP "\e" 15
Unused.
.iP "]]" 15
Forward to a section boundary, see \fB[[\fR for a definition (4.2, 6.1,
6.6, 7.2).
.iP "\(ua" 15
Moves to the first non-white position on the current line (4.4).
.iP "_" 15
Unused.
.iP "\(ga" 15
When followed by a \fB\(ga\fR returns to the previous context.
The previous context is set whenever the current
line is moved in a non-relative way.
When followed by a letter \fBa\fR\-\fBz\fR, returns to the position which
was marked with this letter with a \fBm\fR command.
When used with an operator such as \fBd\fR, the operation takes place
from the exact marked place to the current position within the line;
if you use \fB\(aa\fR, the operation takes place over complete lines
(2.2, 5.3).
.iP "a" 15
Appends arbitrary text after the current cursor position; the insert
can continue onto multiple lines by using \s-2RETURN\s0 within the insert.
A count causes the inserted text to be replicated, but only if the inserted
text is all on one line.
The insertion terminates with an \s-2ESC\s0 (3.1, 7.2).
.iP "b" 15
Backs up to the beginning of a word in the current line. A word is a
sequence of alphanumerics, or a sequence of special characters.
A count repeats the effect (2.4).
.iP "c" 15
An operator which changes the following object, replacing it with the
following input text up to an \s-2ESC\s0. If more than part of a single
line is affected, the text which is changed away is saved in the numeric named
buffers. If only part of the current line is affected, then the last
character to be changed away is marked with a \fB$\fR.
A count causes that many objects to be affected, thus both
\fB3c)\fR and \fBc3)\fR change the following three sentences (7.4).
.iP "d" 15
An operator which deletes the following object. If more than part of
a line is affected, the text is saved in the numeric buffers.
A count causes that many objects to be affected; thus \fB3dw\fR is the
same as \fBd3w\fR (3.3, 3.4, 4.1, 7.4).
.iP "e" 15
Advances to the end of the next word, defined as for \fBb\fR and \fBw\fR.
A count repeats the effect (2.4, 3.1).
.iP "f" 15
Finds the first instance of the next character following the cursor on
the current line. A count repeats the find (4.1).
.iP "g" 15
Unused.
.sp
Arrow keys
.B h ,
.B j ,
.B k ,
.B l ,
and
.B H .
.iP "h" 15
.B "Left arrow" .
Moves the cursor one character to the left.
Like the other arrow keys, either
.B h ,
the
.B "left arrow"
key, or one of the synonyms (\fB^H\fP) has the same effect.
On v2 editors, arrow keys on certain kinds of terminals
(those which send escape sequences, such as vt52, c100, or hp)
cannot be used.
A count repeats the effect (3.1, 7.5).
.iP "i" 15
Inserts text before the cursor, otherwise like \fBa\fR (7.2).
.iP "j" 15
.B "Down arrow" .
Moves the cursor one line down in the same column.
If the position does not exist,
.I vi
comes as close as possible to the same column.
Synonyms include
.B ^J
(linefeed) and
.B ^N .
.iP "k" 15
.B "Up arrow" .
Moves the cursor one line up.
.B ^P
is a synonym.
.iP "l" 15
.B "Right arrow" .
Moves the cursor one character to the right.
\s-2SPACE\s0 is a synonym.
.iP "m" 15
Marks the current position of the cursor in the mark register which is
specified by the next character \fBa\fR\-\fBz\fR. Return to this position
or use with an operator using \fB\(ga\fR or \fB\(aa\fR (5.3).
.iP "n" 15
Repeats the last \fB/\fR or \fB?\fR scanning commands (2.2).
.iP "o" 15
Opens new lines below the current line; otherwise like \fBO\fR (3.1).
.iP "p" 15
Puts text after/below the cursor; otherwise like \fBP\fR (6.3).
.iP "q" 15
Unused.
.iP "r" 15
Replaces the single character at the cursor with a single character you
type. The new character may be a \s-2RETURN\s0; this is the easiest
way to split lines. A count replaces each of the following count characters
with the single character given; see \fBR\fR above which is the more
usually useful iteration of \fBr\fR (3.2).
.iP "s" 15
Changes the single character under the cursor to the text which follows
up to an \s-2ESC\s0; given a count, that many characters from the current
line are changed. The last character to be changed is marked with \fB$\fR
as in \fBc\fR (3.2).
.iP "t" 15
Advances the cursor upto the character before the next character typed.
Most useful with operators such as \fBd\fR and \fBc\fR to delete the
characters up to a following character. You can use \fB.\fR to delete
more if this doesn't delete enough the first time (4.1).
.iP "u" 15
Undoes the last change made to the current buffer. If repeated, will
alternate between these two states, thus is its own inverse. When used
after an insert which inserted text on more than one line, the lines are
saved in the numeric named buffers (3.5).
.iP "v" 15
Unused.
.iP "w" 15
Advances to the beginning of the next word, as defined by \fBb\fR (2.4).
.iP "x" 15
Deletes the single character under the cursor. With a count deletes
deletes that many characters forward from the cursor position, but only
on the current line (6.5).
.iP "y" 15
An operator, yanks the following object into the unnamed temporary buffer.
If preceded by a named buffer specification, \fB"\fR\fIx\fR, the text
is placed in that buffer also. Text can be recovered by a later \fBp\fR
or \fBP\fR (7.4).
.iP "z" 15
Redraws the screen with the current line placed as specified by the following
character: \s-2RETURN\s0 specifies the top of the screen, \fB.\fR the
center of the screen, and \fB\-\fR at the bottom of the screen.
A count may be given after the \fBz\fR and before the following character
to specify the new screen size for the redraw.
A count before the \fBz\fR gives the number of the line to place in the
center of the screen instead of the default current line. (5.4)
.iP "{" 15
Retreats to the beginning of the beginning of the preceding paragraph.
A paragraph begins at each macro in the \fIparagraphs\fR option, normally
`.IP', `.LP', `.PP', `.QP' and `.bp'.
A paragraph also begins after a completely
empty line, and at each section boundary (see \fB[[\fR above) (4.2, 6.8,
7.6).
.iP "|" 15
Places the cursor on the character in the column specified
by the count (7.1, 7.2).
.iP "}" 15
Advances to the beginning of the next paragraph. See \fB{\fR for the
definition of paragraph (4.2, 6.8, 7.6).
.iP "~" 15
Unused.
.iP "^?\ (\s-2\fRDEL\fP\s0)" 15
Interrupts the editor, returning it to command accepting state (1.5,
7.5)
.bp
\&.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,468 @@
.\" Copyright (c) 1980, 1993
.\" The Regents of the University of California. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. All advertising materials mentioning features or use of this software
.\" must display the following acknowledgement:
.\" This product includes software developed by the University of
.\" California, Berkeley and its contributors.
.\" 4. Neither the name of the University nor the names of its contributors
.\" may be used to endorse or promote products derived from this software
.\" without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" @(#)vi.summary 8.1 (Berkeley) 6/8/93
.\"
.ds CH
.ds CF
.de TS
.br
.if !\\n(1T .RT
.ul 0
.ti \\n(.iu
.if t .sp 0.25
.if n .sp
.if \\$1H .TQ
.nr IX 1
..
.nr PS 9
.ps 9
.nr VS 11
.vs 11
.nr HM .50i
.nr FM .25i
.nr PO 0
.po 0
.nr LL 3.5i
.ll 3.5i
.de nc
.bp
..
.de h
.LG
.B
\\$1
.R
.NL
..
.LG
.LG
.B
.ce
Ex Quick Reference
.R
.NL
.LP
.LP
.h "Entering/leaving ex"
.TS
aw(1.4i)b aw(1.8i).
% ex \fIname\fP edit \fIname\fP, start at end
% ex +\fIn\fP \fIname\fP ... at line \fIn\fP
% ex \-t \fItag\fP start at \fItag\fP
% ex \-r list saved files
% ex \-r \fIname\fP recover file \fIname\fP
% ex \fIname\fP ... edit first; rest via \fB:n\fP
% ex \-R \fIname\fP read only mode
: x exit, saving changes
: q! exit, discarding changes
.TE
.h "Ex states"
.TS
lw(1i) lw(2.0i).
Command T{
Normal and initial state. Input prompted for by \fB:\fP.
Your kill character cancels partial command.
T}
Insert T{
Entered by \fBa\fP \fBi\fP and \fBc\fP.
Arbitrary text then terminates with line having only \fB.\fP
character on it or abnormally with interrupt.
T}
Open/visual T{
Entered by \fBopen\fP or \fBvi\fP, terminates with \fBQ\fP
or ^\e.
T}
.TE
.h "Ex commands"
.TS
lw(.45i) lw(.08i)b lw(.45i) lw(.08i)b lw(.45i) lw(.08i)b.
abbrev ab next n unabbrev una
append a number nu undo u
args ar open o unmap unm
change c preserve pre version ve
copy co print p visual vi
delete d put pu write w
edit e quit q xit x
file f read re yank ya
global g recover rec \fIwindow\fP z
insert i rewind rew \fIescape\fP !
join j set se \fIlshift\fP <
list l shell sh \fIprint next\fP \fRCR\fP
map source so \fIresubst\fP &
mark ma stop st \fIrshift\fP >
move m substitute s \fIscroll\fP ^D
.TE
.h "Ex command addresses"
.TS
lw(.3i)b lw(0.8i) lw(.3i)b lw(0.8i).
\fIn\fP line \fIn\fP /\fIpat\fP next with \fIpat\fP
\&. current ?\fIpat\fP previous with \fIpat\fP
$ last \fIx\fP-\fIn\fP \fIn\fP before \fIx\fP
+ next \fIx\fP,\fIy\fP \fIx\fP through \fIy\fP
\- previous \(aa\fIx\fP marked with \fIx\fP
+\fIn\fP \fIn\fP forward \(aa\(aa previous context
% 1,$
.TE
.nc
.h "Specifying terminal type"
.TS
aw(1.7i)b aw(1.5i).
% setenv TERM \fItype\fP \fIcsh\fP and all version 6
$ TERM=\fItype\fP; export TERM \fIsh\fP in Version 7
See also \fItset\fR(1)
.TE
.h "Some terminal types"
.TS
lw(.4i) lw(.4i) lw(.4i) lw(.4i) lw(.4i).
2621 43 adm31 dw1 h19
2645 733 adm3a dw2 i100
300s 745 c100 gt40 mime
33 act4 dm1520 gt42 owl
37 act5 dm2500 h1500 t1061
4014 adm3 dm3025 h1510 vt52
.TE
.h "Initializing options"
.TS
lw(.9i)b aw(1.5i).
EXINIT place \fBset\fP's here in environment var.
set \fIx\fP enable option
set no\fIx\fP disable option
set \fIx\fP=\fIval\fP give value \fIval\fP
set show changed options
set all show all options
set \fIx\fP? show value of option \fIx\fP
.TE
.h "Useful options"
.TS
lw(.9i)b lw(.3i) lw(1.0i).
autoindent ai supply indent
autowrite aw write before changing files
ignorecase ic in scanning
lisp \fB( ) { }\fP are s-exp's
list print ^I for tab, $ at end
magic \fB. [ *\fP special in patterns
number nu number lines
paragraphs para macro names which start ...
redraw simulate smart terminal
scroll command mode lines
sections sect macro names ...
shiftwidth sw for \fB< >\fP, and input \fB^D\fP
showmatch sm to \fB)\fP and \fB}\fP as typed
slowopen slow choke updates during insert
window visual mode lines
wrapscan ws around end of buffer?
wrapmargin wm automatic line splitting
.TE
.LP
.h "Scanning pattern formation"
.TS
aw(.9i)b aw(1.0i).
\(ua beginning of line
$ end of line
\fB.\fR any character
\e< beginning of word
\e> end of word
[\fIstr\fP] any char in \fIstr\fP
[\(ua\fIstr\fP] ... not in \fIstr\fP
[\fIx\-y\fP] ... between \fIx\fP and \fIy\fP
* any number of preceding
.TE
.nc
.LP
.LG
.LG
.B
.ce
Vi Quick Reference
.NL
.R
.LP
.LP
.h "Entering/leaving vi"
.TS
aw(1.4i)b aw(1.8i).
% vi \fIname\fP edit \fIname\fP at top
% vi +\fIn\fP \fIname\fP ... at line \fIn\fP
% vi + \fIname\fP ... at end
% vi \-r list saved files
% vi \-r \fIname\fP recover file \fIname\fP
% vi \fIname\fP ... edit first; rest via \fB:n\fP
% vi \-t \fItag\fP start at \fItag\fP
% vi +/\fIpat\fP \fIname\fP search for \fIpat\fP
% view \fIname\fP read only mode
ZZ exit from vi, saving changes
^Z stop vi for later resumption
.TE
.h "The display"
.TS
lw(.75i) lw(2.2i).
Last line T{
Error messages, echoing input to \fB: / ?\fP and \fB!\fR,
feedback about i/o and large changes.
T}
@ lines On screen only, not in file.
~ lines Lines past end of file.
^\fIx\fP Control characters, ^? is delete.
tabs Expand to spaces, cursor at last.
.TE
.LP
.h "Vi states"
.TS
lw(.75i) lw(2.2i).
Command T{
Normal and initial state. Others return here.
ESC (escape) cancels partial command.
T}
Insert T{
Entered by \fBa i A I o O c C s S\fP \fBR\fP.
Arbitrary text then terminates with ESC character,
or abnormally with interrupt.
T}
Last line T{
Reading input for \fB: / ?\fP or \fB!\fP; terminate
with ESC or CR to execute, interrupt to cancel.
T}
.TE
.h "Counts before vi commands"
.TS
lw(1.5i) lw(1.7i)b.
line/column number z G |
scroll amount ^D ^U
replicate insert a i A I
repeat effect \fRmost rest\fP
.TE
.h "Simple commands"
.TS
lw(1.5i)b lw(1.7i).
dw delete a word
de ... leaving punctuation
dd delete a line
3dd ... 3 lines
i\fItext\fP\fRESC\fP insert text \fIabc\fP
cw\fInew\fP\fRESC\fP change word to \fInew\fP
ea\fIs\fP\fRESC\fP pluralize word
xp transpose characters
.TE
.nc
.h "Interrupting, cancelling"
.TS
aw(0.75i)b aw(1.6i).
ESC end insert or incomplete cmd
^? (delete or rubout) interrupts
^L reprint screen if \fB^?\fR scrambles it
.TE
.h "File manipulation"
.TS
aw(0.75i)b aw(1.6i).
:w write back changes
:wq write and quit
:q quit
:q! quit, discard changes
:e \fIname\fP edit file \fIname\fP
:e! reedit, discard changes
:e + \fIname\fP edit, starting at end
:e +\fIn\fR edit starting at line \fIn\fR
:e # edit alternate file
^\(ua synonym for \fB:e #\fP
:w \fIname\fP write file \fIname\fP
:w! \fIname\fP overwrite file \fIname\fP
:sh run shell, then return
:!\fIcmd\fP run \fIcmd\fR, then return
:n edit next file in arglist
:n \fIargs\fP specify new arglist
:f show current file and line
^G synonym for \fB:f\fP
:ta \fItag\fP to tag file entry \fItag\fP
^] \fB:ta\fP, following word is \fItag\fP
.TE
.h "Positioning within file"
.TS
aw(0.75i)b aw(1.6i).
^F forward screenfull
^B backward screenfull
^D scroll down half screen
^U scroll up half screen
G goto line (end default)
/\fIpat\fR next line matching \fIpat\fR
?\fIpat\fR prev line matching \fIpat\fR
n repeat last \fB/\fR or \fB?\fR
N reverse last \fB/\fR or \fB?\fR
/\fIpat\fP/+\fIn\fP n'th line after \fIpat\fR
?\fIpat\fP?\-\fIn\fP n'th line before \fIpat\fR
]] next section/function
[[ previous section/function
% find matching \fB( ) {\fP or \fB}\fP
.TE
.h "Adjusting the screen"
.TS
aw(0.75i)b aw(1.6i).
^L clear and redraw
^R retype, eliminate @ lines
z\fRCR\fP redraw, current at window top
z\- ... at bottom
z\|. ... at center
/\fIpat\fP/z\- \fIpat\fP line at bottom
z\fIn\fP\|. use \fIn\fP line window
^E scroll window down 1 line
^Y scroll window up 1 line
.TE
.nc
.h "Marking and returning
.TS
aw(0.5i)b aw(2.0i).
\(ga\(ga previous context
\(aa\(aa ... at first non-white in line
m\fIx\fP mark position with letter \fIx\fP
\(ga\fIx\fP to mark \fIx\fP
\(aa\fIx\fP ... at first non-white in line
.TE
.h "Line positioning"
.TS
aw(0.5i)b aw(2.0i).
H home window line
L last window line
M middle window line
+ next line, at first non-white
\- previous line, at first non-white
\fRCR\fP return, same as +
\(da \fRor\fP j next line, same column
\(ua \fRor\fP k previous line, same column
.TE
.h "Character positioning"
.TS
aw(0.5i)b aw(2.0i).
\(ua first non white
0 beginning of line
$ end of line
h \fRor\fP \(-> forward
l \fRor\fP \(<- backwards
^H same as \fB\(<-\fP
\fRspace\fP same as \fB\(->\fP
f\fIx\fP find \fIx\fP forward
F\fIx\fP \fBf\fR backward
t\fIx\fP upto \fIx\fP forward
T\fIx\fP back upto \fIx\fP
; repeat last \fBf F t\fP or \fBT\fP
, inverse of \fB;\fP
| to specified column
% find matching \fB( { )\fP or \fB}\fR
.TE
.h "Words, sentences, paragraphs"
.TS
aw(0.5i)b aw(2.0i).
w word forward
b back word
e end of word
) to next sentence
} to next paragraph
( back sentence
{ back paragraph
W blank delimited word
B back \fBW\fP
E to end of \fBW\fP
.TE
.h "Commands for \s-2LISP\s0"
.TS
aw(0.5i)b aw(2.0i).
) Forward s-expression
} ... but don't stop at atoms
( Back s-expression
{ ... but don't stop at atoms
.TE
.nc
.h "Corrections during insert"
.TS
aw(.5i)b aw(2.0i).
^H erase last character
^W erases last word
\fRerase\fP your erase, same as \fB^H\fP
\fRkill\fP your kill, erase input this line
\e escapes \fB^H\fR, your erase and kill
\fRESC\fP ends insertion, back to command
^? interrupt, terminates insert
^D backtab over \fIautoindent\fP
\(ua^D kill \fIautoindent\fP, save for next
0^D ... but at margin next also
^V quote non-printing character
.TE
.h "Insert and replace"
.TS
aw(.5i)b aw(2.0i).
a append after cursor
i insert before
A append at end of line
I insert before first non-blank
o open line below
O open above
r\fIx\fP replace single char with \fIx\fP
R replace characters
.TE
.h "Operators (double to affect lines)"
.TS
aw(0.5i)b aw(2.0i).
d delete
c change
< left shift
> right shift
! filter through command
\&\= indent for \s-2LISP\s0
y yank lines to buffer
.TE
.h "Miscellaneous operations"
.TS
aw(0.5i)b aw(2.0i).
C change rest of line
D delete rest of line
s substitute chars
S substitute lines
J join lines
x delete characters
X ... before cursor
Y yank lines
.TE
.h "Yank and put"
.TS
aw(0.5i)b aw(2.0i).
p put back lines
P put before
"\fIx\fPp put from buffer \fIx\fP
"\fIx\fPy yank to buffer \fIx\fP
"\fIx\fPd delete into buffer \fIx\fP
.TE
.h "Undo, redo, retrieve"
.TS
aw(0.5i)b aw(2.0i).
u undo last change
U restore current line
\fB.\fP repeat last change
"\fId\fP\|p retrieve \fId\fP'th last delete
.TE

103
usr.bin/vi/common/Makefile Normal file
View File

@ -0,0 +1,103 @@
# @(#)Makefile 8.48 (Berkeley) 8/16/94
#
# This Makefile is ONLY correct for 4.4BSD style source trees. If you're
# not using a system with that setup, it's not going to work. Read the
# files ../README and ../PORT/README for information on building nvi for
# other systems.
PROG= nvi
#CFLAGS=-g -DDEBUG
#CFLAGS+=-pg
CFLAGS+=-I. -I${.CURDIR}
#STRIP=
.PATH: ${.CURDIR}/../common ${.CURDIR}/../ex ${.CURDIR}/../sex \
${.CURDIR}/../vi ${.CURDIR}/../svi ${.CURDIR}/../xaw
CLEANFILES+=nex
# General sources.
SRCS= cut.c delete.c exf.c line.c log.c main.c mark.c msg.c options.c \
options_f.c put.c screen.c search.c seq.c signal.c recover.c \
term.c trace.c util.c
# Ex source.
SRCS+= ex.c ex_abbrev.c ex_append.c ex_args.c ex_argv.c ex_at.c ex_bang.c \
ex_cd.c ex_delete.c ex_digraph.c ex_display.c ex_edit.c ex_equal.c \
ex_exit.c ex_file.c ex_global.c ex_init.c ex_join.c ex_map.c \
ex_mark.c ex_mkexrc.c ex_move.c ex_open.c ex_preserve.c ex_print.c \
ex_put.c ex_read.c ex_screen.c ex_script.c ex_set.c ex_shell.c \
ex_shift.c ex_source.c ex_stop.c ex_subst.c ex_tag.c ex_undo.c \
ex_usage.c ex_util.c ex_version.c ex_visual.c ex_write.c ex_yank.c \
ex_z.c excmd.c filter.c
# Ex screen source.
SRCS+= sex_confirm.c sex_get.c sex_refresh.c sex_screen.c sex_term.c \
sex_util.c sex_window.c
# Vi source.
SRCS+= getc.c v_ch.c v_delete.c v_ex.c v_increment.c v_init.c v_left.c \
v_mark.c v_match.c v_ntext.c v_paragraph.c v_put.c v_redraw.c \
v_replace.c v_right.c v_screen.c v_scroll.c v_search.c v_section.c \
v_sentence.c v_status.c v_stop.c v_text.c v_ulcase.c v_undo.c \
v_util.c v_word.c v_xchar.c v_yank.c v_z.c v_zexit.c vcmd.c vi.c
# Vi curses screen source.
SRCS+= svi_confirm.c svi_curses.c svi_ex.c svi_get.c svi_line.c \
svi_refresh.c svi_relative.c svi_screen.c svi_smap.c svi_split.c \
svi_term.c svi_util.c
# Athena widget set screen source.
SRCS+= xaw_screen.c
#LDADD+=-pg
DPADD+= ${LIBCURSES} ${LIBTERM} ${LIBUTIL}
LDADD+= -lcurses -ltermlib -lutil
SPECHDR=compat.h excmd.h options.h
CLEANFILES+=${SPECHDR}
LINKS= ${BINDIR}/nvi ${BINDIR}/nex
all: nvi nex vi.0
nex: nvi
rm -f nex
ln nvi nex
nvi: compat.h options.h excmd.h
compat.h:
:> compat.h
options.h: options.h.stub options.c options.awk
rm -f options.h
cat ${.CURDIR}/options.h.stub > options.h
awk -f ${.CURDIR}/options.awk ${.CURDIR}/options.c >> options.h
excmd.h: excmd.h.stub excmd.c excmd.awk
rm -f excmd.h
cat ${.CURDIR}/../ex/excmd.h.stub > excmd.h
awk -f ${.CURDIR}/../ex/excmd.awk ${.CURDIR}/../ex/excmd.c >> excmd.h
vi.0:
rm -f vi.0
ln -s ${.CURDIR}/../USD.doc/vi.man/vi.0 vi.0
tags::
-(cd ${.CURDIR} && rm -f tags && \
ctags ../common/*.[ch] ../common/*.stub ../ex/*.[ch] ../ex/*.stub \
../vi/*.[ch] ../sex/*.[ch] ../svi/*.[ch] ../xaw/*.[ch])
warn:: ${SRCS}
-(cd ${.CURDIR} && gcc -Wall -O4 -DDEBUG \
-Iobj -I. ${.ALLSRC} -lcurses -ltermlib 2>&1 | \
sed -e "/warning: .*sccsid.*defined but not used/d" \
-e "/warning: suggest parentheses around/d" \
-e "/In function /d" \
-e "/At top level:/d" \
-e "/warning: .*inline call to/d" \
-e "/warning: comparison is always 1 due /d") > \
${.CURDIR}/WARN.OUT
MAN= ${.CURDIR}/../USD.doc/vi.man
REF= ${.CURDIR}/../USD.doc/vi.ref
.include <bsd.prog.mk>
.depend: ${SPECHDR}

53
usr.bin/vi/common/args.h Normal file
View File

@ -0,0 +1,53 @@
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)args.h 8.5 (Berkeley) 7/17/94
*/
/*
* Structure for building "argc/argv" vector of arguments.
*
* !!!
* All arguments are nul terminated as well as having an associated length.
* The argument vector is NOT necessarily NULL terminated. The proper way
* to check the number of arguments is to use the argc value in the EXCMDARG
* structure or to walk the array until an ARGS structure with a length of 0
* is found.
*/
typedef struct _args {
CHAR_T *bp; /* Argument. */
size_t blen; /* Buffer length. */
size_t len; /* Argument length. */
#define A_ALLOCATED 0x01 /* If allocated space. */
u_int8_t flags;
} ARGS;

366
usr.bin/vi/common/cut.c Normal file
View File

@ -0,0 +1,366 @@
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char sccsid[] = "@(#)cut.c 8.33 (Berkeley) 8/17/94";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include "compat.h"
#include <db.h>
#include <regex.h>
#include "vi.h"
static int cb_rotate __P((SCR *));
/*
* cut --
* Put a range of lines/columns into a TEXT buffer.
*
* There are two buffer areas, both found in the global structure. The first
* is the linked list of all the buffers the user has named, the second is the
* unnamed buffer storage. There is a pointer, too, which is the current
* default buffer, i.e. it may point to the unnamed buffer or a named buffer
* depending on into what buffer the last text was cut. Logically, in both
* delete and yank operations, if the user names a buffer, the text is cut
* into it. If it's a delete of information on more than a single line, the
* contents of the numbered buffers are rotated up one, the contents of the
* buffer named '9' are discarded, and the text is cut into the buffer named
* '1'. The text is always cut into the unnamed buffer.
*
* In all cases, upper-case buffer names are the same as lower-case names,
* with the exception that they cause the buffer to be appended to instead
* of replaced. Note, however, that if text is appended to a buffer, the
* default buffer only contains the appended text, not the entire contents
* of the buffer.
*
* !!!
* The contents of the default buffer would disappear after most operations
* in historic vi. It's unclear that this is useful, so we don't bother.
*
* When users explicitly cut text into the numeric buffers, historic vi became
* genuinely strange. I've never been able to figure out what was supposed to
* happen. It behaved differently if you deleted text than if you yanked text,
* and, in the latter case, the text was appended to the buffer instead of
* replacing the contents. Hopefully it's not worth getting right, and here
* we just treat the numeric buffers like any other named buffer.
*/
int
cut(sp, ep, namep, fm, tm, flags)
SCR *sp;
EXF *ep;
CHAR_T *namep;
int flags;
MARK *fm, *tm;
{
CB *cbp;
CHAR_T name;
recno_t lno;
int append, copy_one, copy_def;
/*
* If the user specified a buffer, put it there. (This may require
* a copy into the numeric buffers. We do the copy so that we don't
* have to reference count and so we don't have to deal with things
* like appends to buffers that are used multiple times.)
*
* Otherwise, if it's supposed to be put in a numeric buffer (usually
* a delete) put it there. The rules for putting things in numeric
* buffers were historically a little strange. There were three cases.
*
* 1: Some motions are always line mode motions, which means
* that the cut always goes into the numeric buffers.
* 2: Some motions aren't line mode motions, e.g. d10w, but
* can cross line boundaries. For these commands, if the
* cut crosses a line boundary, it goes into the numeric
* buffers. This includes most of the commands.
* 3: Some motions aren't line mode motions, e.g. d`<char>,
* but always go into the numeric buffers, regardless. This
* was the commands: % ` / ? ( ) N n { } -- and nvi adds ^A.
*
* Otherwise, put it in the unnamed buffer.
*/
append = copy_one = copy_def = 0;
if (namep != NULL) {
name = *namep;
if (LF_ISSET(CUT_NUMREQ) || LF_ISSET(CUT_NUMOPT) &&
(LF_ISSET(CUT_LINEMODE) || fm->lno != tm->lno)) {
copy_one = 1;
cb_rotate(sp);
}
if ((append = isupper(name)) == 1) {
if (!copy_one)
copy_def = 1;
name = tolower(name);
}
namecb: CBNAME(sp, cbp, name);
} else if (LF_ISSET(CUT_NUMREQ) || LF_ISSET(CUT_NUMOPT) &&
(LF_ISSET(CUT_LINEMODE) || fm->lno != tm->lno)) {
name = '1';
cb_rotate(sp);
goto namecb;
} else
cbp = &sp->gp->dcb_store;
copyloop:
/*
* If this is a new buffer, create it and add it into the list.
* Otherwise, if it's not an append, free its current contents.
*/
if (cbp == NULL) {
CALLOC_RET(sp, cbp, CB *, 1, sizeof(CB));
cbp->name = name;
CIRCLEQ_INIT(&cbp->textq);
LIST_INSERT_HEAD(&sp->gp->cutq, cbp, q);
} else if (!append) {
text_lfree(&cbp->textq);
cbp->len = 0;
cbp->flags = 0;
}
#define ENTIRE_LINE 0
/* In line mode, it's pretty easy, just cut the lines. */
if (LF_ISSET(CUT_LINEMODE)) {
cbp->flags |= CB_LMODE;
for (lno = fm->lno; lno <= tm->lno; ++lno)
if (cut_line(sp, ep, lno, 0, 0, cbp))
goto cut_line_err;
} else {
/*
* Get the first line. A length of 0 causes cut_line
* to cut from the MARK to the end of the line.
*/
if (cut_line(sp, ep, fm->lno, fm->cno, fm->lno != tm->lno ?
ENTIRE_LINE : (tm->cno - fm->cno) + 1, cbp))
goto cut_line_err;
/* Get the intermediate lines. */
for (lno = fm->lno; ++lno < tm->lno;)
if (cut_line(sp, ep, lno, 0, ENTIRE_LINE, cbp))
goto cut_line_err;
/* Get the last line. */
if (tm->lno != fm->lno &&
cut_line(sp, ep, lno, 0, tm->cno + 1, cbp)) {
cut_line_err: text_lfree(&cbp->textq);
cbp->len = 0;
cbp->flags = 0;
return (1);
}
}
append = 0; /* Only append to the named buffer. */
sp->gp->dcbp = cbp; /* Repoint the default buffer on each pass. */
if (copy_one) { /* Copy into numeric buffer 1. */
name = '1';
CBNAME(sp, cbp, name);
copy_one = 0;
goto copyloop;
}
if (copy_def) { /* Copy into the default buffer. */
cbp = &sp->gp->dcb_store;
copy_def = 0;
goto copyloop;
}
return (0);
}
/*
* cb_rotate --
* Rotate the numbered buffers up one.
*/
static int
cb_rotate(sp)
SCR *sp;
{
CB *cbp, *del_cbp;
del_cbp = NULL;
for (cbp = sp->gp->cutq.lh_first; cbp != NULL; cbp = cbp->q.le_next)
switch(cbp->name) {
case '1':
cbp->name = '2';
break;
case '2':
cbp->name = '3';
break;
case '3':
cbp->name = '4';
break;
case '4':
cbp->name = '5';
break;
case '5':
cbp->name = '6';
break;
case '6':
cbp->name = '7';
break;
case '7':
cbp->name = '8';
break;
case '8':
cbp->name = '9';
break;
case '9':
del_cbp = cbp;
break;
}
if (del_cbp != NULL) {
LIST_REMOVE(del_cbp, q);
text_lfree(&del_cbp->textq);
FREE(del_cbp, sizeof(CB));
}
return (0);
}
/*
* cut_line --
* Cut a portion of a single line.
*/
int
cut_line(sp, ep, lno, fcno, clen, cbp)
SCR *sp;
EXF *ep;
recno_t lno;
size_t fcno, clen;
CB *cbp;
{
TEXT *tp;
size_t len;
char *p;
/* Get the line. */
if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
GETLINE_ERR(sp, lno);
return (1);
}
/* Create a TEXT structure that can hold the entire line. */
if ((tp = text_init(sp, NULL, 0, len)) == NULL)
return (1);
/*
* If the line isn't empty and it's not the entire line,
* copy the portion we want, and reset the TEXT length.
*/
if (len != 0) {
if (clen == 0)
clen = len - fcno;
memmove(tp->lb, p + fcno, clen);
tp->len = clen;
}
/* Append to the end of the cut buffer. */
CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q);
cbp->len += tp->len;
return (0);
}
/*
* text_init --
* Allocate a new TEXT structure.
*/
TEXT *
text_init(sp, p, len, total_len)
SCR *sp;
const char *p;
size_t len, total_len;
{
TEXT *tp;
CALLOC(sp, tp, TEXT *, 1, sizeof(TEXT));
if (tp == NULL)
return (NULL);
/* ANSI C doesn't define a call to malloc(2) for 0 bytes. */
if ((tp->lb_len = total_len) != 0) {
MALLOC(sp, tp->lb, CHAR_T *, tp->lb_len);
if (tp->lb == NULL) {
free(tp);
return (NULL);
}
if (p != NULL && len != 0)
memmove(tp->lb, p, len);
}
tp->len = len;
return (tp);
}
/*
* text_lfree --
* Free a chain of text structures.
*/
void
text_lfree(headp)
TEXTH *headp;
{
TEXT *tp;
while ((tp = headp->cqh_first) != (void *)headp) {
CIRCLEQ_REMOVE(headp, tp, q);
text_free(tp);
}
}
/*
* text_free --
* Free a text structure.
*/
void
text_free(tp)
TEXT *tp;
{
if (tp->lb != NULL)
FREE(tp->lb, tp->lb_len);
if (tp->wd != NULL)
FREE(tp->wd, tp->wd_len);
FREE(tp, sizeof(TEXT));
}

96
usr.bin/vi/common/cut.h Normal file
View File

@ -0,0 +1,96 @@
/*-
* Copyright (c) 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)cut.h 8.19 (Berkeley) 7/28/94
*/
typedef struct _texth TEXTH; /* TEXT list head structure. */
CIRCLEQ_HEAD(_texth, _text);
/* Cut buffers. */
struct _cb {
LIST_ENTRY(_cb) q; /* Linked list of cut buffers. */
TEXTH textq; /* Linked list of TEXT structures. */
CHAR_T name; /* Cut buffer name. */
size_t len; /* Total length of cut text. */
#define CB_LMODE 0x01 /* Cut was in line mode. */
u_int8_t flags;
};
/* Lines/blocks of text. */
struct _text { /* Text: a linked list of lines. */
CIRCLEQ_ENTRY(_text) q; /* Linked list of text structures. */
char *lb; /* Line buffer. */
size_t lb_len; /* Line buffer length. */
size_t len; /* Line length. */
/* These fields are used by the vi text input routine. */
recno_t lno; /* 1-N: line number. */
size_t ai; /* 0-N: autoindent bytes. */
size_t insert; /* 0-N: bytes to insert (push). */
size_t offset; /* 0-N: initial, unerasable chars. */
size_t owrite; /* 0-N: chars to overwrite. */
size_t R_erase; /* 0-N: 'R' erase count. */
size_t sv_cno; /* 0-N: Saved line cursor. */
size_t sv_len; /* 0-N: Saved line length. */
/* These fields are used by the ex text input routine. */
u_char *wd; /* Width buffer. */
size_t wd_len; /* Width buffer length. */
};
/*
* Get named buffer 'name'.
* Translate upper-case buffer names to lower-case buffer names.
*/
#define CBNAME(sp, cbp, nch) { \
CHAR_T __name; \
__name = isupper(nch) ? tolower(nch) : (nch); \
for (cbp = sp->gp->cutq.lh_first; \
cbp != NULL; cbp = cbp->q.le_next) \
if (cbp->name == __name) \
break; \
}
#define CUT_LINEMODE 0x01 /* Cut in line mode. */
#define CUT_NUMOPT 0x02 /* Numeric buffer: optional. */
#define CUT_NUMREQ 0x04 /* Numeric buffer: required. */
int cut __P((SCR *, EXF *, CHAR_T *, MARK *, MARK *, int));
int cut_line __P((SCR *, EXF *, recno_t, size_t, size_t, CB *));
int delete __P((SCR *, EXF *, MARK *, MARK *, int));
int put __P((SCR *, EXF *, CB *, CHAR_T *, MARK *, MARK *, int));
void text_free __P((TEXT *));
TEXT *text_init __P((SCR *, const char *, size_t, size_t));
void text_lfree __P((TEXTH *));

193
usr.bin/vi/common/delete.c Normal file
View File

@ -0,0 +1,193 @@
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char sccsid[] = "@(#)delete.c 8.11 (Berkeley) 8/17/94";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include "compat.h"
#include <db.h>
#include <regex.h>
#include "vi.h"
/*
* delete --
* Delete a range of text.
*/
int
delete(sp, ep, fm, tm, lmode)
SCR *sp;
EXF *ep;
MARK *fm, *tm;
int lmode;
{
recno_t lno;
size_t blen, len, nlen, tlen;
char *bp, *p;
int eof;
bp = NULL;
/* Case 1 -- delete in line mode. */
if (lmode) {
for (lno = tm->lno; lno >= fm->lno; --lno)
if (file_dline(sp, ep, lno))
return (1);
goto vdone;
}
/*
* Case 2 -- delete to EOF. This is a special case because it's
* easier to pick it off than try and find it in the other cases.
*/
if (file_lline(sp, ep, &lno))
return (1);
if (tm->lno >= lno) {
if (tm->lno == lno) {
if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
GETLINE_ERR(sp, lno);
return (1);
}
eof = tm->cno >= len ? 1 : 0;
} else
eof = 1;
if (eof) {
for (lno = tm->lno; lno > fm->lno; --lno) {
if (file_dline(sp, ep, lno))
return (1);
++sp->rptlines[L_DELETED];
}
if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
GETLINE_ERR(sp, fm->lno);
return (1);
}
GET_SPACE_RET(sp, bp, blen, fm->cno);
memmove(bp, p, fm->cno);
if (file_sline(sp, ep, fm->lno, bp, fm->cno))
return (1);
goto done;
}
}
/* Case 3 -- delete within a single line. */
if (tm->lno == fm->lno) {
if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
GETLINE_ERR(sp, fm->lno);
return (1);
}
GET_SPACE_RET(sp, bp, blen, len);
if (fm->cno != 0)
memmove(bp, p, fm->cno);
memmove(bp + fm->cno, p + (tm->cno + 1), len - (tm->cno + 1));
if (file_sline(sp, ep, fm->lno,
bp, len - ((tm->cno - fm->cno) + 1)))
goto err;
goto done;
}
/*
* Case 4 -- delete over multiple lines.
*
* Copy the start partial line into place.
*/
if ((tlen = fm->cno) != 0) {
if ((p = file_gline(sp, ep, fm->lno, NULL)) == NULL) {
GETLINE_ERR(sp, fm->lno);
return (1);
}
GET_SPACE_RET(sp, bp, blen, tlen + 256);
memmove(bp, p, tlen);
}
/* Copy the end partial line into place. */
if ((p = file_gline(sp, ep, tm->lno, &len)) == NULL) {
GETLINE_ERR(sp, tm->lno);
goto err;
}
if (len != 0 && tm->cno != len - 1) {
/*
* XXX
* We can overflow memory here, if the total length is greater
* than SIZE_T_MAX. The only portable way I've found to test
* is depending on the overflow being less than the value.
*/
nlen = (len - (tm->cno + 1)) + tlen;
if (tlen > nlen) {
msgq(sp, M_ERR, "Error: line length overflow");
goto err;
}
if (tlen == 0) {
GET_SPACE_RET(sp, bp, blen, nlen);
} else
ADD_SPACE_RET(sp, bp, blen, nlen);
memmove(bp + tlen, p + (tm->cno + 1), len - (tm->cno + 1));
tlen += len - (tm->cno + 1);
}
/* Set the current line. */
if (file_sline(sp, ep, fm->lno, bp, tlen))
goto err;
/* Delete the last and intermediate lines. */
for (lno = tm->lno; lno > fm->lno; --lno)
if (file_dline(sp, ep, lno))
goto err;
/* Reporting. */
vdone: sp->rptlines[L_DELETED] += tm->lno - fm->lno + 1;
done: if (bp != NULL)
FREE_SPACE(sp, bp, blen);
return (0);
/* Free memory. */
err: if (bp != NULL)
FREE_SPACE(sp, bp, blen);
return (1);
}

837
usr.bin/vi/common/exf.c Normal file
View File

@ -0,0 +1,837 @@
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char sccsid[] = "@(#)exf.c 8.96 (Berkeley) 8/17/94";
#endif /* not lint */
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <sys/time.h>
/*
* We include <sys/file.h>, because the flock(2) and open(2) #defines
* were found there on historical systems. We also include <fcntl.h>
* because the open(2) #defines are found there on newer systems.
*/
#include <sys/file.h>
#include <bitstring.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "compat.h"
#include <db.h>
#include <regex.h>
#include <pathnames.h>
#include "vi.h"
#include "excmd.h"
/*
* file_add --
* Insert a file name into the FREF list, if it doesn't already
* appear in it.
*
* !!!
* The "if it doesn't already appear" changes vi's semantics slightly. If
* you do a "vi foo bar", and then execute "next bar baz", the edit of bar
* will reflect the line/column of the previous edit session. Historic nvi
* did not do this. The change is a logical extension of the change where
* vi now remembers the last location in any file that it has ever edited,
* not just the previously edited file.
*/
FREF *
file_add(sp, name)
SCR *sp;
CHAR_T *name;
{
FREF *frp;
/*
* Return it if it already exists. Note that we test against the
* user's name, whatever that happens to be, including if it's a
* temporary file.
*/
if (name != NULL)
for (frp = sp->frefq.cqh_first;
frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next)
if (!strcmp(frp->name, name))
return (frp);
/* Allocate and initialize the FREF structure. */
CALLOC(sp, frp, FREF *, 1, sizeof(FREF));
if (frp == NULL)
return (NULL);
/*
* If no file name specified, or if the file name is a request
* for something temporary, file_init() will allocate the file
* name. Temporary files are always ignored.
*/
if (name != NULL && strcmp(name, TEMPORARY_FILE_STRING) &&
(frp->name = strdup(name)) == NULL) {
FREE(frp, sizeof(FREF));
msgq(sp, M_SYSERR, NULL);
return (NULL);
}
/* Append into the chain of file names. */
CIRCLEQ_INSERT_TAIL(&sp->frefq, frp, q);
return (frp);
}
/*
* file_init --
* Start editing a file, based on the FREF structure. If successsful,
* let go of any previous file. Don't release the previous file until
* absolutely sure we have the new one.
*/
int
file_init(sp, frp, rcv_name, force)
SCR *sp;
FREF *frp;
char *rcv_name;
int force;
{
EXF *ep;
RECNOINFO oinfo;
struct stat sb;
size_t psize;
int fd;
char *oname, tname[MAXPATHLEN];
/*
* If the file is a recovery file, let the recovery code handle it.
* Clear the FR_RECOVER flag first -- the recovery code does set up,
* and then calls us! If the recovery call fails, it's probably
* because the named file doesn't exist. So, move boldly forward,
* presuming that there's an error message the user will get to see.
*/
if (F_ISSET(frp, FR_RECOVER)) {
F_CLR(frp, FR_RECOVER);
return (rcv_read(sp, frp));
}
/*
* Required FRP initialization; the only flag we keep is the
* cursor information.
*/
F_CLR(frp, ~FR_CURSORSET);
/*
* Required EXF initialization:
* Flush the line caches.
* Default recover mail file fd to -1.
* Set initial EXF flag bits.
*/
CALLOC_RET(sp, ep, EXF *, 1, sizeof(EXF));
ep->c_lno = ep->c_nlines = OOBLNO;
ep->rcv_fd = ep->fcntl_fd = -1;
LIST_INIT(&ep->marks);
F_SET(ep, F_FIRSTMODIFY);
/*
* If no name or backing file, create a backing temporary file, saving
* the temp file name so we can later unlink it. If the user never
* named this file, copy the temporary file name to the real name (we
* display that until the user renames it).
*/
if ((oname = frp->name) == NULL || stat(oname, &sb)) {
(void)snprintf(tname,
sizeof(tname), "%s/vi.XXXXXX", O_STR(sp, O_DIRECTORY));
if ((fd = mkstemp(tname)) == -1) {
msgq(sp, M_SYSERR, "Temporary file");
goto err;
}
(void)close(fd);
if (frp->name == NULL)
F_SET(frp, FR_TMPFILE);
if ((frp->tname = strdup(tname)) == NULL ||
frp->name == NULL && (frp->name = strdup(tname)) == NULL) {
if (frp->tname != NULL)
free(frp->tname);
msgq(sp, M_SYSERR, NULL);
(void)unlink(tname);
goto err;
}
oname = frp->tname;
psize = 4 * 1024;
F_SET(frp, FR_NEWFILE);
} else {
/*
* Try to keep it at 10 pages or less per file. This
* isn't friendly on a loaded machine, btw.
*/
if (sb.st_size < 40 * 1024)
psize = 4 * 1024;
else if (sb.st_size < 320 * 1024)
psize = 32 * 1024;
else
psize = 64 * 1024;
ep->mtime = sb.st_mtime;
if (!S_ISREG(sb.st_mode))
msgq(sp, M_ERR,
"Warning: %s is not a regular file", oname);
}
/* Set up recovery. */
memset(&oinfo, 0, sizeof(RECNOINFO));
oinfo.bval = '\n'; /* Always set. */
oinfo.psize = psize;
oinfo.flags = F_ISSET(sp->gp, G_SNAPSHOT) ? R_SNAPSHOT : 0;
if (rcv_name == NULL) {
if (!rcv_tmp(sp, ep, frp->name))
oinfo.bfname = ep->rcv_path;
} else {
if ((ep->rcv_path = strdup(rcv_name)) == NULL) {
msgq(sp, M_SYSERR, NULL);
goto err;
}
oinfo.bfname = ep->rcv_path;
F_SET(ep, F_MODIFIED);
}
/* Open a db structure. */
if ((ep->db = dbopen(rcv_name == NULL ? oname : NULL,
O_NONBLOCK | O_RDONLY, DEFFILEMODE, DB_RECNO, &oinfo)) == NULL) {
msgq(sp, M_SYSERR, rcv_name == NULL ? oname : rcv_name);
goto err;
}
/*
* Do the remaining things that can cause failure of the new file,
* mark and logging initialization.
*/
if (mark_init(sp, ep) || log_init(sp, ep))
goto err;
/*
* Close the previous file; if that fails, close the new one and
* run for the border.
*
* !!!
* There's a nasty special case. If the user edits a temporary file,
* and then does an ":e! %", we need to re-initialize the backing
* file, but we can't change the name. (It's worse -- we're dealing
* with *names* here, we can't even detect that it happened.) Set a
* flag so that the file_end routine ignores the backing information
* of the old file if it happens to be the same as the new one.
*
* !!!
* Side-effect: after the call to file_end(), sp->frp may be NULL.
*/
F_SET(frp, FR_DONTDELETE);
if (sp->ep != NULL && file_end(sp, sp->ep, force)) {
(void)file_end(sp, ep, 1);
goto err;
}
F_CLR(frp, FR_DONTDELETE);
/*
* Lock the file; if it's a recovery file, it should already be
* locked. Note, we acquire the lock after the previous file
* has been ended, so that we don't get an "already locked" error
* for ":edit!".
*
* XXX
* While the user can't interrupt us between the open and here,
* there's a race between the dbopen() and the lock. Not much
* we can do about it.
*
* XXX
* We don't make a big deal of not being able to lock the file. As
* locking rarely works over NFS, and often fails if the file was
* mmap(2)'d, it's far too common to do anything like print an error
* message, let alone make the file readonly. At some future time,
* when locking is a little more reliable, this should change to be
* an error.
*/
if (rcv_name == NULL)
switch (file_lock(oname,
&ep->fcntl_fd, ep->db->fd(ep->db), 0)) {
case LOCK_FAILED:
F_SET(frp, FR_UNLOCKED);
break;
case LOCK_UNAVAIL:
msgq(sp, M_INFO,
"%s already locked, session is read-only", oname);
F_SET(frp, FR_RDONLY);
break;
case LOCK_SUCCESS:
break;
}
/*
* The -R flag, or doing a "set readonly" during a session causes
* all files edited during the session (using an edit command, or
* even using tags) to be marked read-only. Changing the file name
* (see ex/ex_file.c), clears this flag.
*
* Otherwise, try and figure out if a file is readonly. This is a
* dangerous thing to do. The kernel is the only arbiter of whether
* or not a file is writeable, and the best that a user program can
* do is guess. Obvious loopholes are files that are on a file system
* mounted readonly (access catches this one on a few systems), or
* alternate protection mechanisms, ACL's for example, that we can't
* portably check. Lots of fun, and only here because users whined.
*
* !!!
* Historic vi displayed the readonly message if none of the file
* write bits were set, or if an an access(2) call on the path
* failed. This seems reasonable. If the file is mode 444, root
* users may want to know that the owner of the file did not expect
* it to be written.
*
* Historic vi set the readonly bit if no write bits were set for
* a file, even if the access call would have succeeded. This makes
* the superuser force the write even when vi expects that it will
* succeed. I'm less supportive of this semantic, but it's historic
* practice and the conservative approach to vi'ing files as root.
*
* It would be nice if there was some way to update this when the user
* does a "^Z; chmod ...". The problem is that we'd first have to
* distinguish between readonly bits set because of file permissions
* and those set for other reasons. That's not too hard, but deciding
* when to reevaluate the permissions is trickier. An alternative
* might be to turn off the readonly bit if the user forces a write
* and it succeeds.
*
* XXX
* Access(2) doesn't consider the effective uid/gid values. This
* probably isn't a problem for vi when it's running standalone.
*/
if (O_ISSET(sp, O_READONLY) || !F_ISSET(frp, FR_NEWFILE) &&
(!(sb.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) ||
access(frp->name, W_OK)))
F_SET(frp, FR_RDONLY);
/*
* Set the alternate file name to be the file we've just discarded.
*
* !!!
* If the current file was a temporary file, the call to file_end()
* unlinked it and free'd the name. So, there is no previous file,
* and there is no alternate file name. This matches historical
* practice, although in historical vi it could only happen as the
* result of the initial command, i.e. if vi was executed without a
* file name.
*/
set_alt_name(sp, sp->frp == NULL ? NULL : sp->frp->name);
/*
* Switch...
*
* !!!
* Note, because the EXF structure is examine at interrupt time,
* the underlying DB structures have to be consistent as soon as
* it's assigned to an SCR structure.
*/
++ep->refcnt;
sp->ep = ep;
sp->frp = frp;
return (0);
err: if (frp->name != NULL) {
free(frp->name);
frp->name = NULL;
}
if (frp->tname != NULL) {
(void)unlink(frp->tname);
free(frp->tname);
frp->tname = NULL;
}
if (ep->rcv_path != NULL) {
free(ep->rcv_path);
ep->rcv_path = NULL;
}
if (ep->db != NULL)
(void)ep->db->close(ep->db);
FREE(ep, sizeof(EXF));
return (1);
}
/*
* file_end --
* Stop editing a file.
*/
int
file_end(sp, ep, force)
SCR *sp;
EXF *ep;
int force;
{
FREF *frp;
/*
* Clean up the FREF structure.
*
* Save the cursor location.
*
* XXX
* It would be cleaner to do this somewhere else, but by the time
* ex or vi knows that we're changing files it's already happened.
*/
frp = sp->frp;
frp->lno = sp->lno;
frp->cno = sp->cno;
F_SET(frp, FR_CURSORSET);
/*
* We may no longer need the temporary backing file, so clean it
* up. We don't need the FREF structure either, if the file was
* never named, so lose it.
*
* !!!
* Re: FR_DONTDELETE, see the comment above in file_init().
*/
if (!F_ISSET(frp, FR_DONTDELETE) && frp->tname != NULL) {
if (unlink(frp->tname))
msgq(sp, M_SYSERR, "%s: remove", frp->tname);
free(frp->tname);
frp->tname = NULL;
if (F_ISSET(frp, FR_TMPFILE)) {
CIRCLEQ_REMOVE(&sp->frefq, frp, q);
free(frp->name);
free(frp);
}
sp->frp = NULL;
}
/*
* Clean up the EXF structure.
*
* sp->ep MAY NOT BE THE SAME AS THE ARGUMENT ep, SO DON'T USE IT!
*
* If multiply referenced, just decrement the count and return.
*/
if (--ep->refcnt != 0)
return (0);
/* Close the db structure. */
if (ep->db->close != NULL && ep->db->close(ep->db) && !force) {
msgq(sp, M_ERR, "%s: close: %s", frp->name, strerror(errno));
++ep->refcnt;
return (1);
}
/* COMMITTED TO THE CLOSE. THERE'S NO GOING BACK... */
/* Stop logging. */
(void)log_end(sp, ep);
/* Free up any marks. */
(void)mark_end(sp, ep);
/*
* Delete recovery files, close the open descriptor, free recovery
* memory. See recover.c for a description of the protocol.
*
* XXX
* Unlink backup file first, we can detect that the recovery file
* doesn't reference anything when the user tries to recover it.
* There's a race, here, obviously, but it's fairly small.
*/
if (!F_ISSET(ep, F_RCV_NORM)) {
if (ep->rcv_path != NULL && unlink(ep->rcv_path))
msgq(sp, M_ERR,
"%s: remove: %s", ep->rcv_path, strerror(errno));
if (ep->rcv_mpath != NULL && unlink(ep->rcv_mpath))
msgq(sp, M_ERR,
"%s: remove: %s", ep->rcv_mpath, strerror(errno));
}
if (ep->fcntl_fd != -1)
(void)close(ep->fcntl_fd);
if (ep->rcv_fd != -1)
(void)close(ep->rcv_fd);
if (ep->rcv_path != NULL)
free(ep->rcv_path);
if (ep->rcv_mpath != NULL)
free(ep->rcv_mpath);
FREE(ep, sizeof(EXF));
return (0);
}
/*
* file_write --
* Write the file to disk. Historic vi had fairly convoluted
* semantics for whether or not writes would happen. That's
* why all the flags.
*/
int
file_write(sp, ep, fm, tm, name, flags)
SCR *sp;
EXF *ep;
MARK *fm, *tm;
char *name;
int flags;
{
struct stat sb;
FILE *fp;
FREF *frp;
MARK from, to;
u_long nlno, nch;
int btear, fd, noname, oflags, rval;
char *msg;
frp = sp->frp;
if (name == NULL) {
noname = 1;
name = frp->name;
} else
noname = 0;
/* Can't write files marked read-only, unless forced. */
if (!LF_ISSET(FS_FORCE) && noname && F_ISSET(frp, FR_RDONLY)) {
if (LF_ISSET(FS_POSSIBLE))
msgq(sp, M_ERR,
"Read-only file, not written; use ! to override");
else
msgq(sp, M_ERR, "Read-only file, not written");
return (1);
}
/* If not forced, not appending, and "writeany" not set ... */
if (!LF_ISSET(FS_FORCE | FS_APPEND) && !O_ISSET(sp, O_WRITEANY)) {
/* Don't overwrite anything but the original file. */
if ((!noname || F_ISSET(frp, FR_NAMECHANGE)) &&
!stat(name, &sb)) {
if (LF_ISSET(FS_POSSIBLE))
msgq(sp, M_ERR,
"%s exists, not written; use ! to override", name);
else
msgq(sp, M_ERR, "%s exists, not written", name);
return (1);
}
/*
* Don't write part of any existing file. Only test for the
* original file, the previous test catches anything else.
*/
if (!LF_ISSET(FS_ALL) && noname && !stat(name, &sb)) {
if (LF_ISSET(FS_POSSIBLE))
msgq(sp, M_ERR,
"Use ! to write a partial file");
else
msgq(sp, M_ERR, "Partial file, not written");
return (1);
}
}
/*
* Figure out if the file already exists -- if it doesn't, we display
* the "new file" message. The stat might not be necessary, but we
* just repeat it because it's easier than hacking the previous tests.
* The information is only used for the user message and modification
* time test, so we can ignore the obvious race condition.
*
* If the user is overwriting a file other than the original file, and
* O_WRITEANY was what got us here (neither force nor append was set),
* display the "existing file" messsage. Since the FR_NAMECHANGE flag
* is cleared on a successful write, the message only appears once when
* the user changes a file name. This is historic practice.
*
* One final test. If we're not forcing or appending, and we have a
* saved modification time, stop the user if it's been written since
* we last edited or wrote it, and make them force it.
*/
if (stat(name, &sb))
msg = ": new file";
else {
msg = "";
if (!LF_ISSET(FS_FORCE | FS_APPEND)) {
if (ep->mtime && sb.st_mtime > ep->mtime) {
msgq(sp, M_ERR,
"%s: file modified more recently than this copy%s",
name, LF_ISSET(FS_POSSIBLE) ?
"; use ! to override" : "");
return (1);
}
if (!noname || F_ISSET(frp, FR_NAMECHANGE))
msg = ": existing file";
}
}
/* Set flags to either append or truncate. */
oflags = O_CREAT | O_WRONLY;
if (LF_ISSET(FS_APPEND))
oflags |= O_APPEND;
else
oflags |= O_TRUNC;
/* Open the file. */
if ((fd = open(name, oflags, DEFFILEMODE)) < 0) {
msgq(sp, M_SYSERR, name);
return (1);
}
/* Use stdio for buffering. */
if ((fp = fdopen(fd, "w")) == NULL) {
(void)close(fd);
msgq(sp, M_SYSERR, name);
return (1);
}
/* Build fake addresses, if necessary. */
if (fm == NULL) {
from.lno = 1;
from.cno = 0;
fm = &from;
if (file_lline(sp, ep, &to.lno))
return (1);
to.cno = 0;
tm = &to;
}
/* Turn on the busy message. */
btear = F_ISSET(sp, S_EXSILENT) ? 0 : !busy_on(sp, "Writing...");
rval = ex_writefp(sp, ep, name, fp, fm, tm, &nlno, &nch);
if (btear)
busy_off(sp);
/*
* Save the new last modification time -- even if the write fails
* we re-init the time. That way the user can clean up the disk
* and rewrite without having to force it.
*/
ep->mtime = stat(name, &sb) ? 0 : sb.st_mtime;
/* If the write failed, complain loudly. */
if (rval) {
if (!LF_ISSET(FS_APPEND))
msgq(sp, M_ERR, "%s: WARNING: file truncated!", name);
return (1);
}
/*
* Once we've actually written the file, it doesn't matter that the
* file name was changed -- if it was, we've already whacked it.
*/
F_CLR(frp, FR_NAMECHANGE);
/*
* If wrote the entire file clear the modified bit. If the file was
* written back to the original file name and the file is a temporary,
* set the "no exit" bit. This permits the user to write the file and
* use it in the context of the file system, but still keeps them from
* losing their changes by exiting.
*/
if (LF_ISSET(FS_ALL)) {
F_CLR(ep, F_MODIFIED);
if (F_ISSET(frp, FR_TMPFILE))
if (noname)
F_SET(frp, FR_TMPEXIT);
else
F_CLR(frp, FR_TMPEXIT);
}
msgq(sp, M_INFO, "%s%s%s: %lu line%s, %lu characters",
INTERRUPTED(sp) ? "Interrupted write: " : "",
name, msg, nlno, nlno == 1 ? "" : "s", nch);
return (0);
}
/*
* file_m1 --
* First modification check routine. The :next, :prev, :rewind, :tag,
* :tagpush, :tagpop, ^^ modifications check.
*/
int
file_m1(sp, ep, force, flags)
SCR *sp;
EXF *ep;
int force, flags;
{
/*
* If the file has been modified, we'll want to write it back or
* fail. If autowrite is set, we'll write it back automatically,
* unless force is also set. Otherwise, we fail unless forced or
* there's another open screen on this file.
*/
if (F_ISSET(ep, F_MODIFIED))
if (O_ISSET(sp, O_AUTOWRITE)) {
if (!force &&
file_write(sp, ep, NULL, NULL, NULL, flags))
return (1);
} else if (ep->refcnt <= 1 && !force) {
msgq(sp, M_ERR,
"File modified since last complete write; write or use %s to override",
LF_ISSET(FS_POSSIBLE) ? "!" : ":edit!");
return (1);
}
return (file_m3(sp, ep, force));
}
/*
* file_m2 --
* Second modification check routine. The :edit, :quit, :recover
* modifications check.
*/
int
file_m2(sp, ep, force)
SCR *sp;
EXF *ep;
int force;
{
/*
* If the file has been modified, we'll want to fail, unless forced
* or there's another open screen on this file.
*/
if (F_ISSET(ep, F_MODIFIED) && ep->refcnt <= 1 && !force) {
msgq(sp, M_ERR,
"File modified since last complete write; write or use ! to override");
return (1);
}
return (file_m3(sp, ep, force));
}
/*
* file_m3 --
* Third modification check routine.
*/
int
file_m3(sp, ep, force)
SCR *sp;
EXF *ep;
int force;
{
/*
* Don't exit while in a temporary files if the file was ever modified.
* The problem is that if the user does a ":wq", we write and quit,
* unlinking the temporary file. Not what the user had in mind at all.
* We permit writing to temporary files, so that user maps using file
* system names work with temporary files.
*/
if (F_ISSET(sp->frp, FR_TMPEXIT) && ep->refcnt <= 1 && !force) {
msgq(sp, M_ERR,
"File is a temporary; exit will discard modifications");
return (1);
}
return (0);
}
/*
* file_lock --
* Get an exclusive lock on a file.
*
* XXX
* The default locking is flock(2) style, not fcntl(2). The latter is
* known to fail badly on some systems, and its only advantage is that
* it occasionally works over NFS.
*
* Furthermore, the semantics of fcntl(2) are wrong. The problems are
* two-fold: you can't close any file descriptor associated with the file
* without losing all of the locks, and you can't get an exclusive lock
* unless you have the file open for writing. Someone ought to be shot,
* but it's probably too late, they may already have reproduced. To get
* around these problems, nvi opens the files for writing when it can and
* acquires a second file descriptor when it can't. The recovery files
* are examples of the former, they're always opened for writing. The DB
* files can't be opened for writing because the semantics of DB are that
* files opened for writing are flushed back to disk when the DB session
* is ended. So, in that case we have to acquire an extra file descriptor.
*/
enum lockt
file_lock(name, fdp, fd, iswrite)
char *name;
int fd, *fdp, iswrite;
{
#if !defined(USE_FCNTL) && defined(LOCK_EX)
/* Hurrah! We've got flock(2). */
/*
* !!!
* We need to distinguish a lock not being available for the file
* from the file system not supporting locking. Flock is documented
* as returning EWOULDBLOCK; add EAGAIN for good measure, and assume
* they are the former. There's no portable way to do this.
*/
errno = 0;
return (flock(fd, LOCK_EX | LOCK_NB) ?
errno == EAGAIN || errno == EWOULDBLOCK ?
LOCK_UNAVAIL : LOCK_FAILED : LOCK_SUCCESS);
#else /* Gag me. We've got fcntl(2). */
struct flock arg;
int didopen, sverrno;
arg.l_type = F_WRLCK;
arg.l_whence = 0; /* SEEK_SET */
arg.l_start = arg.l_len = 0;
arg.l_pid = 0;
/* If the file descriptor isn't opened for writing, it must fail. */
if (!iswrite) {
if (name == NULL || fdp == NULL)
return (LOCK_FAILED);
if ((fd = open(name, O_RDWR, 0)) == -1)
return (LOCK_FAILED);
*fdp = fd;
didopen = 1;
}
errno = 0;
if (!fcntl(fd, F_SETLK, &arg))
return (LOCK_SUCCESS);
if (didopen) {
sverrno = errno;
(void)close(fd);
errno = sverrno;
}
/*
* !!!
* We need to distinguish a lock not being available for the file
* from the file system not supporting locking. Fcntl is documented
* as returning EACCESS and EAGAIN; add EWOULDBLOCK for good measure,
* and assume they are the former. There's no portable way to do this.
*/
return (errno == EACCES || errno == EAGAIN || errno == EWOULDBLOCK ?
LOCK_UNAVAIL : LOCK_FAILED);
#endif
}

128
usr.bin/vi/common/exf.h Normal file
View File

@ -0,0 +1,128 @@
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)exf.h 8.35 (Berkeley) 8/4/94
*/
/* Undo direction. */
/*
* exf --
* The file structure.
*/
struct _exf {
int refcnt; /* Reference count. */
/* Underlying database state. */
DB *db; /* File db structure. */
char *c_lp; /* Cached line. */
size_t c_len; /* Cached line length. */
recno_t c_lno; /* Cached line number. */
recno_t c_nlines; /* Cached lines in the file. */
DB *log; /* Log db structure. */
char *l_lp; /* Log buffer. */
size_t l_len; /* Log buffer length. */
recno_t l_high; /* Log last + 1 record number. */
recno_t l_cur; /* Log current record number. */
MARK l_cursor; /* Log cursor position. */
enum direction lundo; /* Last undo direction. */
LIST_HEAD(_markh, _lmark) marks;/* Linked list of file MARK's. */
time_t mtime; /* Last modification time. */
int fcntl_fd; /* Fcntl locking fd; see exf.c. */
/*
* Recovery in general, and these fields specifically, are described
* in recover.c.
*/
#define RCV_PERIOD 120 /* Sync every two minutes. */
char *rcv_path; /* Recover file name. */
char *rcv_mpath; /* Recover mail file name. */
int rcv_fd; /* Locked mail file descriptor. */
struct timeval rcv_tod; /* ITIMER_REAL: recovery time-of-day. */
#define F_FIRSTMODIFY 0x001 /* File not yet modified. */
#define F_MODIFIED 0x002 /* File is currently dirty. */
#define F_MULTILOCK 0x004 /* Multiple processes running, lock. */
#define F_NOLOG 0x008 /* Logging turned off. */
#define F_RCV_NORM 0x010 /* Don't delete recovery files. */
#define F_RCV_ON 0x020 /* Recovery is possible. */
#define F_UNDO 0x040 /* No change since last undo. */
u_int8_t flags;
};
#define GETLINE_ERR(sp, lno) { \
msgq(sp, M_ERR, \
"Error: %s/%d: unable to retrieve line %u", \
tail(__FILE__), __LINE__, lno); \
}
/* EXF routines. */
FREF *file_add __P((SCR *, CHAR_T *));
int file_end __P((SCR *, EXF *, int));
int file_init __P((SCR *, FREF *, char *, int));
int file_m1 __P((SCR *, EXF *, int, int));
int file_m2 __P((SCR *, EXF *, int));
int file_m3 __P((SCR *, EXF *, int));
enum lockt { LOCK_FAILED, LOCK_SUCCESS, LOCK_UNAVAIL };
enum lockt
file_lock __P((char *, int *, int, int));
#define FS_ALL 0x01 /* Write the entire file. */
#define FS_APPEND 0x02 /* Append to the file. */
#define FS_FORCE 0x04 /* Force is set. */
#define FS_POSSIBLE 0x08 /* Force could be set. */
int file_write __P((SCR *, EXF *, MARK *, MARK *, char *, int));
/* Recovery routines. */
int rcv_init __P((SCR *, EXF *));
int rcv_list __P((SCR *));
int rcv_on __P((SCR *, EXF *));
int rcv_read __P((SCR *, FREF *));
#define RCV_EMAIL 0x01 /* Send the user email, IFF file modified. */
#define RCV_ENDSESSION 0x02 /* End the file session. */
#define RCV_PRESERVE 0x04 /* Preserve backup file, IFF file modified. */
#define RCV_SNAPSHOT 0x08 /* Snapshot the recovery, and send email. */
int rcv_sync __P((SCR *, EXF *, u_int));
int rcv_tmp __P((SCR *, EXF *, char *));
/* DB interface routines */
int file_aline __P((SCR *, EXF *, int, recno_t, char *, size_t));
int file_dline __P((SCR *, EXF *, recno_t));
char *file_gline __P((SCR *, EXF *, recno_t, size_t *));
int file_iline __P((SCR *, EXF *, recno_t, char *, size_t));
int file_lline __P((SCR *, EXF *, recno_t *));
char *file_rline __P((SCR *, EXF *, recno_t, size_t *));
int file_sline __P((SCR *, EXF *, recno_t, char *, size_t));

107
usr.bin/vi/common/gs.h Normal file
View File

@ -0,0 +1,107 @@
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)gs.h 8.39 (Berkeley) 7/23/94
*/
struct _gs {
CIRCLEQ_HEAD(_dqh, _scr) dq; /* Displayed screens. */
CIRCLEQ_HEAD(_hqh, _scr) hq; /* Hidden screens. */
mode_t origmode; /* Original terminal mode. */
struct termios
original_termios; /* Original terminal values. */
MSGH msgq; /* User message list. */
char *tmp_bp; /* Temporary buffer. */
size_t tmp_blen; /* Size of temporary buffer. */
sigset_t blockset; /* Signal mask. */
#ifdef DEBUG
FILE *tracefp; /* Trace file pointer. */
#endif
/* INFORMATION SHARED BY ALL SCREENS. */
IBUF *tty; /* Key input buffer. */
CB *dcbp; /* Default cut buffer pointer. */
CB dcb_store; /* Default cut buffer storage. */
LIST_HEAD(_cuth, _cb) cutq; /* Linked list of cut buffers. */
#define MAX_BIT_SEQ 128 /* Max + 1 fast check character. */
LIST_HEAD(_seqh, _seq) seqq; /* Linked list of maps, abbrevs. */
bitstr_t bit_decl(seqb, MAX_BIT_SEQ);
#define MAX_FAST_KEY 254 /* Max fast check character.*/
#define KEY_LEN(sp, ch) \
((ch) <= MAX_FAST_KEY ? \
sp->gp->cname[ch].len : __key_len(sp, ch))
#define KEY_NAME(sp, ch) \
((ch) <= MAX_FAST_KEY ? \
sp->gp->cname[ch].name : __key_name(sp, ch))
struct {
CHAR_T name[MAX_CHARACTER_COLUMNS + 1];
u_int8_t len;
} cname[MAX_FAST_KEY + 1]; /* Fast lookup table. */
#define KEY_VAL(sp, ch) \
((ch) <= MAX_FAST_KEY ? sp->gp->special_key[ch] : \
(ch) > sp->gp->max_special ? 0 : __key_val(sp, ch))
CHAR_T max_special; /* Max special character. */
u_char /* Fast lookup table. */
special_key[MAX_FAST_KEY + 1];
/* Interrupt macros. */
#define INTERRUPTED(sp) \
(F_ISSET((sp), S_INTERRUPTED) || F_ISSET((sp)->gp, G_SIGINT))
#define CLR_INTERRUPT(sp) { \
F_CLR((sp), S_INTERRUPTED | S_INTERRUPTIBLE); \
F_CLR((sp)->gp, G_SIGINT); \
}
#define G_ABBREV 0x0001 /* If have abbreviations. */
#define G_BELLSCHED 0x0002 /* Bell scheduled. */
#define G_RECOVER_SET 0x0004 /* Recover system initialized. */
#define G_SETMODE 0x0008 /* Tty mode changed. */
#define G_SIGALRM 0x0010 /* SIGALRM arrived. */
#define G_SIGINT 0x0020 /* SIGINT arrived. */
#define G_SIGWINCH 0x0040 /* SIGWINCH arrived. */
#define G_SNAPSHOT 0x0080 /* Always snapshot files. */
#define G_STDIN_TTY 0x0100 /* Standard input is a tty. */
#define G_TERMIOS_SET 0x0200 /* Termios structure is valid. */
#define G_TMP_INUSE 0x0400 /* Temporary buffer in use. */
u_int16_t flags;
};
extern GS *__global_list; /* List of screens. */

492
usr.bin/vi/common/line.c Normal file
View File

@ -0,0 +1,492 @@
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char sccsid[] = "@(#)line.c 8.31 (Berkeley) 8/17/94";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include "compat.h"
#include <db.h>
#include <regex.h>
#include "vi.h"
#include "excmd.h"
static __inline int scr_update
__P((SCR *, EXF *, recno_t, enum operation, int));
/*
* file_gline --
* Look in the text buffers for a line; if it's not there
* call file_rline to retrieve it from the database.
*/
char *
file_gline(sp, ep, lno, lenp)
SCR *sp;
EXF *ep;
recno_t lno; /* Line number. */
size_t *lenp; /* Length store. */
{
TEXT *tp;
recno_t l1, l2;
/*
* The underlying recno stuff handles zero by returning NULL, but
* have to have an oob condition for the look-aside into the input
* buffer anyway.
*/
if (lno == 0)
return (NULL);
/*
* Look-aside into the TEXT buffers and see if the line we want
* is there.
*/
if (F_ISSET(sp, S_INPUT)) {
l1 = ((TEXT *)sp->tiqp->cqh_first)->lno;
l2 = ((TEXT *)sp->tiqp->cqh_last)->lno;
if (l1 <= lno && l2 >= lno) {
for (tp = sp->tiqp->cqh_first;
tp->lno != lno; tp = tp->q.cqe_next);
if (lenp)
*lenp = tp->len;
return (tp->lb);
}
/*
* Adjust the line number for the number of lines used
* by the text input buffers.
*/
if (lno > l2)
lno -= l2 - l1;
}
return (file_rline(sp, ep, lno, lenp));
}
/*
* file_rline --
* Look in the cache for a line; if it's not there retrieve
* it from the file.
*/
char *
file_rline(sp, ep, lno, lenp)
SCR *sp;
EXF *ep;
recno_t lno; /* Line number. */
size_t *lenp; /* Length store. */
{
DBT data, key;
/* Check the cache. */
if (lno == ep->c_lno) {
if (lenp)
*lenp = ep->c_len;
return (ep->c_lp);
}
ep->c_lno = OOBLNO;
/* Get the line from the underlying database. */
key.data = &lno;
key.size = sizeof(lno);
switch (ep->db->get(ep->db, &key, &data, 0)) {
case -1:
msgq(sp, M_ERR,
"Error: %s/%d: unable to get line %u: %s",
tail(__FILE__), __LINE__, lno, strerror(errno));
/* FALLTHROUGH */
case 1:
return (NULL);
/* NOTREACHED */
}
if (lenp)
*lenp = data.size;
/* Fill the cache. */
ep->c_lno = lno;
ep->c_len = data.size;
ep->c_lp = data.data;
return (data.data);
}
/*
* file_dline --
* Delete a line from the file.
*/
int
file_dline(sp, ep, lno)
SCR *sp;
EXF *ep;
recno_t lno;
{
DBT key;
#if defined(DEBUG) && 0
TRACE(sp, "delete line %lu\n", lno);
#endif
/*
* XXX
* Marks and global commands have to know when lines are
* inserted or deleted.
*/
mark_insdel(sp, ep, LINE_DELETE, lno);
global_insdel(sp, ep, LINE_DELETE, lno);
/* Log change. */
log_line(sp, ep, lno, LOG_LINE_DELETE);
/* Update file. */
key.data = &lno;
key.size = sizeof(lno);
SIGBLOCK(sp->gp);
if (ep->db->del(ep->db, &key, 0) == 1) {
msgq(sp, M_ERR,
"Error: %s/%d: unable to delete line %u: %s",
tail(__FILE__), __LINE__, lno, strerror(errno));
return (1);
}
SIGUNBLOCK(sp->gp);
/* Flush the cache, update line count, before screen update. */
if (lno <= ep->c_lno)
ep->c_lno = OOBLNO;
if (ep->c_nlines != OOBLNO)
--ep->c_nlines;
/* File now dirty. */
if (F_ISSET(ep, F_FIRSTMODIFY))
(void)rcv_init(sp, ep);
F_SET(ep, F_MODIFIED);
/* Update screen. */
return (scr_update(sp, ep, lno, LINE_DELETE, 1));
}
/*
* file_aline --
* Append a line into the file.
*/
int
file_aline(sp, ep, update, lno, p, len)
SCR *sp;
EXF *ep;
int update;
recno_t lno;
char *p;
size_t len;
{
DBT data, key;
recno_t lline;
#if defined(DEBUG) && 0
TRACE(sp, "append to %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
#endif
/*
* XXX
* Very nasty special case. The historic vi code displays a single
* space (or a '$' if the list option is set) for the first line in
* an "empty" file. If we "insert" a line, that line gets scrolled
* down, not repainted, so it's incorrect when we refresh the the
* screen. This is really hard to find and fix in the vi code -- the
* text input functions detect it explicitly and don't insert a new
* line. The hack here is to repaint the screen if we're appending
* to an empty file. The reason that the test is in file_aline, and
* not in file_iline or file_sline, is that all of the ex commands
* that work in empty files end up here.
*/
if (lno == 0) {
if (file_lline(sp, ep, &lline))
return (1);
if (lline == 0)
F_SET(sp, S_REDRAW);
}
/* Update file. */
key.data = &lno;
key.size = sizeof(lno);
data.data = p;
data.size = len;
SIGBLOCK(sp->gp);
if (ep->db->put(ep->db, &key, &data, R_IAFTER) == -1) {
msgq(sp, M_ERR,
"Error: %s/%d: unable to append to line %u: %s",
tail(__FILE__), __LINE__, lno, strerror(errno));
return (1);
}
SIGUNBLOCK(sp->gp);
/* Flush the cache, update line count, before screen update. */
if (lno < ep->c_lno)
ep->c_lno = OOBLNO;
if (ep->c_nlines != OOBLNO)
++ep->c_nlines;
/* File now dirty. */
if (F_ISSET(ep, F_FIRSTMODIFY))
(void)rcv_init(sp, ep);
F_SET(ep, F_MODIFIED);
/* Log change. */
log_line(sp, ep, lno + 1, LOG_LINE_APPEND);
/*
* XXX
* Marks and global commands have to know when lines are
* inserted or deleted.
*
* XXX
* See comment above about empty files. If the file was empty,
* then we're adding the first line, which is a replacement, not
* an append. So, we shouldn't whack the marks.
*/
if (lno != 0) {
mark_insdel(sp, ep, LINE_INSERT, lno + 1);
global_insdel(sp, ep, LINE_INSERT, lno + 1);
}
/*
* Update screen.
*
* XXX
* Nasty hack. If multiple lines are input by the user, they aren't
* committed until an <ESC> is entered. The problem is the screen was
* updated/scrolled as each line was entered. So, when this routine
* is called to copy the new lines from the cut buffer into the file,
* it has to know not to update the screen again.
*/
return (scr_update(sp, ep, lno, LINE_APPEND, update));
}
/*
* file_iline --
* Insert a line into the file.
*/
int
file_iline(sp, ep, lno, p, len)
SCR *sp;
EXF *ep;
recno_t lno;
char *p;
size_t len;
{
DBT data, key;
recno_t lline;
#if defined(DEBUG) && 0
TRACE(sp,
"insert before %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
#endif
/* Very nasty special case. See comment in file_aline(). */
if (lno == 1) {
if (file_lline(sp, ep, &lline))
return (1);
if (lline == 0)
F_SET(sp, S_REDRAW);
}
/* Update file. */
key.data = &lno;
key.size = sizeof(lno);
data.data = p;
data.size = len;
SIGBLOCK(sp->gp);
if (ep->db->put(ep->db, &key, &data, R_IBEFORE) == -1) {
msgq(sp, M_ERR,
"Error: %s/%d: unable to insert at line %u: %s",
tail(__FILE__), __LINE__, lno, strerror(errno));
return (1);
}
SIGUNBLOCK(sp->gp);
/* Flush the cache, update line count, before screen update. */
if (lno >= ep->c_lno)
ep->c_lno = OOBLNO;
if (ep->c_nlines != OOBLNO)
++ep->c_nlines;
/* File now dirty. */
if (F_ISSET(ep, F_FIRSTMODIFY))
(void)rcv_init(sp, ep);
F_SET(ep, F_MODIFIED);
/* Log change. */
log_line(sp, ep, lno, LOG_LINE_INSERT);
/*
* XXX
* Marks and global commands have to know when lines are
* inserted or deleted.
*/
mark_insdel(sp, ep, LINE_INSERT, lno);
global_insdel(sp, ep, LINE_INSERT, lno);
/* Update screen. */
return (scr_update(sp, ep, lno, LINE_INSERT, 1));
}
/*
* file_sline --
* Store a line in the file.
*/
int
file_sline(sp, ep, lno, p, len)
SCR *sp;
EXF *ep;
recno_t lno;
char *p;
size_t len;
{
DBT data, key;
#if defined(DEBUG) && 0
TRACE(sp,
"replace line %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
#endif
/* Log before change. */
log_line(sp, ep, lno, LOG_LINE_RESET_B);
/* Update file. */
key.data = &lno;
key.size = sizeof(lno);
data.data = p;
data.size = len;
SIGBLOCK(sp->gp);
if (ep->db->put(ep->db, &key, &data, 0) == -1) {
msgq(sp, M_ERR,
"Error: %s/%d: unable to store line %u: %s",
tail(__FILE__), __LINE__, lno, strerror(errno));
return (1);
}
SIGUNBLOCK(sp->gp);
/* Flush the cache, before logging or screen update. */
if (lno == ep->c_lno)
ep->c_lno = OOBLNO;
/* File now dirty. */
if (F_ISSET(ep, F_FIRSTMODIFY))
(void)rcv_init(sp, ep);
F_SET(ep, F_MODIFIED);
/* Log after change. */
log_line(sp, ep, lno, LOG_LINE_RESET_F);
/* Update screen. */
return (scr_update(sp, ep, lno, LINE_RESET, 1));
}
/*
* file_lline --
* Return the number of lines in the file.
*/
int
file_lline(sp, ep, lnop)
SCR *sp;
EXF *ep;
recno_t *lnop;
{
DBT data, key;
recno_t lno;
/* Check the cache. */
if (ep->c_nlines != OOBLNO) {
*lnop = (F_ISSET(sp, S_INPUT) &&
((TEXT *)sp->tiqp->cqh_last)->lno > ep->c_nlines ?
((TEXT *)sp->tiqp->cqh_last)->lno : ep->c_nlines);
return (0);
}
key.data = &lno;
key.size = sizeof(lno);
switch (ep->db->seq(ep->db, &key, &data, R_LAST)) {
case -1:
msgq(sp, M_ERR,
"Error: %s/%d: unable to get last line: %s",
tail(__FILE__), __LINE__, strerror(errno));
*lnop = 0;
return (1);
case 1:
*lnop = 0;
return (0);
default:
break;
}
/* Fill the cache. */
memmove(&lno, key.data, sizeof(lno));
ep->c_nlines = ep->c_lno = lno;
ep->c_len = data.size;
ep->c_lp = data.data;
/* Return the value. */
*lnop = (F_ISSET(sp, S_INPUT) &&
((TEXT *)sp->tiqp->cqh_last)->lno > lno ?
((TEXT *)sp->tiqp->cqh_last)->lno : lno);
return (0);
}
/*
* scr_update --
* Update all of the screens that are backed by the file that
* just changed.
*/
static __inline int
scr_update(sp, ep, lno, op, current)
SCR *sp;
EXF *ep;
recno_t lno;
enum operation op;
int current;
{
SCR *tsp;
if (ep->refcnt != 1)
for (tsp = sp->gp->dq.cqh_first;
tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next)
if (sp != tsp && tsp->ep == ep)
(void)sp->s_change(tsp, ep, lno, op);
return (current && sp->s_change(sp, ep, lno, op));
}

698
usr.bin/vi/common/log.c Normal file
View File

@ -0,0 +1,698 @@
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char sccsid[] = "@(#)log.c 8.17 (Berkeley) 8/17/94";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <bitstring.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include "compat.h"
#include <db.h>
#include <regex.h>
#include "vi.h"
/*
* The log consists of records, each containing a type byte and a variable
* length byte string, as follows:
*
* LOG_CURSOR_INIT MARK
* LOG_CURSOR_END MARK
* LOG_LINE_APPEND recno_t char *
* LOG_LINE_DELETE recno_t char *
* LOG_LINE_INSERT recno_t char *
* LOG_LINE_RESET_F recno_t char *
* LOG_LINE_RESET_B recno_t char *
* LOG_MARK LMARK
*
* We do before image physical logging. This means that the editor layer
* MAY NOT modify records in place, even if simply deleting or overwriting
* characters. Since the smallest unit of logging is a line, we're using
* up lots of space. This may eventually have to be reduced, probably by
* doing logical logging, which is a much cooler database phrase.
*
* The implementation of the historic vi 'u' command, using roll-forward and
* roll-back, is simple. Each set of changes has a LOG_CURSOR_INIT record,
* followed by a number of other records, followed by a LOG_CURSOR_END record.
* LOG_LINE_RESET records come in pairs. The first is a LOG_LINE_RESET_B
* record, and is the line before the change. The second is LOG_LINE_RESET_F,
* and is the line after the change. Roll-back is done by backing up to the
* first LOG_CURSOR_INIT record before a change. Roll-forward is done in a
* similar fashion.
*
* The 'U' command is implemented by rolling backward to a LOG_CURSOR_END
* record for a line different from the current one. It should be noted that
* this means that a subsequent 'u' command will make a change based on the
* new position of the log's cursor. This is okay, and, in fact, historic vi
* behaved that way.
*/
static int log_cursor1 __P((SCR *, EXF *, int));
#if defined(DEBUG) && 0
static void log_trace __P((SCR *, char *, recno_t, u_char *));
#endif
/* Try and restart the log on failure, i.e. if we run out of memory. */
#define LOG_ERR { \
msgq(sp, M_ERR, "Error: %s/%d: put log error: %s", \
tail(__FILE__), __LINE__, strerror(errno)); \
(void)ep->log->close(ep->log); \
if (!log_init(sp, ep)) \
msgq(sp, M_ERR, "Log restarted"); \
return (1); \
}
/*
* log_init --
* Initialize the logging subsystem.
*/
int
log_init(sp, ep)
SCR *sp;
EXF *ep;
{
/*
* Initialize the buffer. The logging subsystem has its own
* buffers because the global ones are almost by definition
* going to be in use when the log runs.
*/
ep->l_lp = NULL;
ep->l_len = 0;
ep->l_cursor.lno = 1; /* XXX Any valid recno. */
ep->l_cursor.cno = 0;
ep->l_high = ep->l_cur = 1;
ep->log = dbopen(NULL, O_CREAT | O_NONBLOCK | O_RDWR,
S_IRUSR | S_IWUSR, DB_RECNO, NULL);
if (ep->log == NULL) {
msgq(sp, M_ERR, "log db: %s", strerror(errno));
F_SET(ep, F_NOLOG);
return (1);
}
return (0);
}
/*
* log_end --
* Close the logging subsystem.
*/
int
log_end(sp, ep)
SCR *sp;
EXF *ep;
{
if (ep->log != NULL) {
(void)(ep->log->close)(ep->log);
ep->log = NULL;
}
if (ep->l_lp != NULL) {
free(ep->l_lp);
ep->l_lp = NULL;
}
ep->l_len = 0;
ep->l_cursor.lno = 1; /* XXX Any valid recno. */
ep->l_cursor.cno = 0;
ep->l_high = ep->l_cur = 1;
return (0);
}
/*
* log_cursor --
* Log the current cursor position, starting an event.
*/
int
log_cursor(sp, ep)
SCR *sp;
EXF *ep;
{
/*
* If any changes were made since the last cursor init,
* put out the ending cursor record.
*/
if (ep->l_cursor.lno == OOBLNO) {
ep->l_cursor.lno = sp->lno;
ep->l_cursor.cno = sp->cno;
return (log_cursor1(sp, ep, LOG_CURSOR_END));
}
ep->l_cursor.lno = sp->lno;
ep->l_cursor.cno = sp->cno;
return (0);
}
/*
* log_cursor1 --
* Actually push a cursor record out.
*/
static int
log_cursor1(sp, ep, type)
SCR *sp;
EXF *ep;
int type;
{
DBT data, key;
BINC_RET(sp, ep->l_lp, ep->l_len, sizeof(u_char) + sizeof(MARK));
ep->l_lp[0] = type;
memmove(ep->l_lp + sizeof(u_char), &ep->l_cursor, sizeof(MARK));
key.data = &ep->l_cur;
key.size = sizeof(recno_t);
data.data = ep->l_lp;
data.size = sizeof(u_char) + sizeof(MARK);
if (ep->log->put(ep->log, &key, &data, 0) == -1)
LOG_ERR;
#if defined(DEBUG) && 0
TRACE(sp, "%lu: %s: %u/%u\n", ep->l_cur,
type == LOG_CURSOR_INIT ? "log_cursor_init" : "log_cursor_end",
sp->lno, sp->cno);
#endif
/* Reset high water mark. */
ep->l_high = ++ep->l_cur;
return (0);
}
/*
* log_line --
* Log a line change.
*/
int
log_line(sp, ep, lno, action)
SCR *sp;
EXF *ep;
recno_t lno;
u_int action;
{
DBT data, key;
size_t len;
char *lp;
if (F_ISSET(ep, F_NOLOG))
return (0);
/*
* XXX
*
* Kluge for vi. Clear the EXF undo flag so that the
* next 'u' command does a roll-back, regardless.
*/
F_CLR(ep, F_UNDO);
/* Put out one initial cursor record per set of changes. */
if (ep->l_cursor.lno != OOBLNO) {
if (log_cursor1(sp, ep, LOG_CURSOR_INIT))
return (1);
ep->l_cursor.lno = OOBLNO;
}
/*
* Put out the changes. If it's a LOG_LINE_RESET_B call, it's a
* special case, avoid the caches. Also, if it fails and it's
* line 1, it just means that the user started with an empty file,
* so fake an empty length line.
*/
if (action == LOG_LINE_RESET_B) {
if ((lp = file_rline(sp, ep, lno, &len)) == NULL) {
if (lno != 1) {
GETLINE_ERR(sp, lno);
return (1);
}
len = 0;
lp = "";
}
} else
if ((lp = file_gline(sp, ep, lno, &len)) == NULL) {
GETLINE_ERR(sp, lno);
return (1);
}
BINC_RET(sp,
ep->l_lp, ep->l_len, len + sizeof(u_char) + sizeof(recno_t));
ep->l_lp[0] = action;
memmove(ep->l_lp + sizeof(u_char), &lno, sizeof(recno_t));
memmove(ep->l_lp + sizeof(u_char) + sizeof(recno_t), lp, len);
key.data = &ep->l_cur;
key.size = sizeof(recno_t);
data.data = ep->l_lp;
data.size = len + sizeof(u_char) + sizeof(recno_t);
if (ep->log->put(ep->log, &key, &data, 0) == -1)
LOG_ERR;
#if defined(DEBUG) && 0
switch (action) {
case LOG_LINE_APPEND:
TRACE(sp, "%u: log_line: append: %lu {%u}\n",
ep->l_cur, lno, len);
break;
case LOG_LINE_DELETE:
TRACE(sp, "%lu: log_line: delete: %lu {%u}\n",
ep->l_cur, lno, len);
break;
case LOG_LINE_INSERT:
TRACE(sp, "%lu: log_line: insert: %lu {%u}\n",
ep->l_cur, lno, len);
break;
case LOG_LINE_RESET_F:
TRACE(sp, "%lu: log_line: reset_f: %lu {%u}\n",
ep->l_cur, lno, len);
break;
case LOG_LINE_RESET_B:
TRACE(sp, "%lu: log_line: reset_b: %lu {%u}\n",
ep->l_cur, lno, len);
break;
}
#endif
/* Reset high water mark. */
ep->l_high = ++ep->l_cur;
return (0);
}
/*
* log_mark --
* Log a mark position. For the log to work, we assume that there
* aren't any operations that just put out a log record -- this
* would mean that undo operations would only reset marks, and not
* cause any other change.
*/
int
log_mark(sp, ep, lmp)
SCR *sp;
EXF *ep;
LMARK *lmp;
{
DBT data, key;
if (F_ISSET(ep, F_NOLOG))
return (0);
/* Put out one initial cursor record per set of changes. */
if (ep->l_cursor.lno != OOBLNO) {
if (log_cursor1(sp, ep, LOG_CURSOR_INIT))
return (1);
ep->l_cursor.lno = OOBLNO;
}
BINC_RET(sp, ep->l_lp,
ep->l_len, sizeof(u_char) + sizeof(LMARK));
ep->l_lp[0] = LOG_MARK;
memmove(ep->l_lp + sizeof(u_char), lmp, sizeof(LMARK));
key.data = &ep->l_cur;
key.size = sizeof(recno_t);
data.data = ep->l_lp;
data.size = sizeof(u_char) + sizeof(LMARK);
if (ep->log->put(ep->log, &key, &data, 0) == -1)
LOG_ERR;
#if defined(DEBUG) && 0
TRACE(sp, "%lu: mark %c: %lu/%u\n",
ep->l_cur, lmp->name, lmp->lno, lmp->cno);
#endif
/* Reset high water mark. */
ep->l_high = ++ep->l_cur;
return (0);
}
/*
* Log_backward --
* Roll the log backward one operation.
*/
int
log_backward(sp, ep, rp)
SCR *sp;
EXF *ep;
MARK *rp;
{
DBT key, data;
LMARK lm;
MARK m;
recno_t lno;
int didop;
u_char *p;
if (F_ISSET(ep, F_NOLOG)) {
msgq(sp, M_ERR,
"Logging not being performed, undo not possible");
return (1);
}
if (ep->l_cur == 1) {
msgq(sp, M_BERR, "No changes to undo");
return (1);
}
F_SET(ep, F_NOLOG); /* Turn off logging. */
key.data = &ep->l_cur; /* Initialize db request. */
key.size = sizeof(recno_t);
for (didop = 0;;) {
--ep->l_cur;
if (ep->log->get(ep->log, &key, &data, 0))
LOG_ERR;
#if defined(DEBUG) && 0
log_trace(sp, "log_backward", ep->l_cur, data.data);
#endif
switch (*(p = (u_char *)data.data)) {
case LOG_CURSOR_INIT:
if (didop) {
memmove(rp, p + sizeof(u_char), sizeof(MARK));
F_CLR(ep, F_NOLOG);
return (0);
}
break;
case LOG_CURSOR_END:
break;
case LOG_LINE_APPEND:
case LOG_LINE_INSERT:
didop = 1;
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
if (file_dline(sp, ep, lno))
goto err;
++sp->rptlines[L_DELETED];
break;
case LOG_LINE_DELETE:
didop = 1;
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
if (file_iline(sp, ep, lno, p + sizeof(u_char) +
sizeof(recno_t), data.size - sizeof(u_char) -
sizeof(recno_t)))
goto err;
++sp->rptlines[L_ADDED];
break;
case LOG_LINE_RESET_F:
break;
case LOG_LINE_RESET_B:
didop = 1;
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
if (file_sline(sp, ep, lno, p + sizeof(u_char) +
sizeof(recno_t), data.size - sizeof(u_char) -
sizeof(recno_t)))
goto err;
if (sp->rptlchange != lno) {
sp->rptlchange = lno;
++sp->rptlines[L_CHANGED];
}
break;
case LOG_MARK:
didop = 1;
memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
m.lno = lm.lno;
m.cno = lm.cno;
if (mark_set(sp, ep, lm.name, &m, 0))
goto err;
break;
default:
abort();
}
}
err: F_CLR(ep, F_NOLOG);
return (1);
}
/*
* Log_setline --
* Reset the line to its original appearance.
*
* XXX
* There's a bug in this code due to our not logging cursor movements
* unless a change was made. If you do a change, move off the line,
* then move back on and do a 'U', the line will be restored to the way
* it was before the original change.
*/
int
log_setline(sp, ep)
SCR *sp;
EXF *ep;
{
DBT key, data;
LMARK lm;
MARK m;
recno_t lno;
u_char *p;
if (F_ISSET(ep, F_NOLOG)) {
msgq(sp, M_ERR,
"Logging not being performed, undo not possible");
return (1);
}
if (ep->l_cur == 1)
return (1);
F_SET(ep, F_NOLOG); /* Turn off logging. */
key.data = &ep->l_cur; /* Initialize db request. */
key.size = sizeof(recno_t);
for (;;) {
--ep->l_cur;
if (ep->log->get(ep->log, &key, &data, 0))
LOG_ERR;
#if defined(DEBUG) && 0
log_trace(sp, "log_setline", ep->l_cur, data.data);
#endif
switch (*(p = (u_char *)data.data)) {
case LOG_CURSOR_INIT:
memmove(&m, p + sizeof(u_char), sizeof(MARK));
if (m.lno != sp->lno || ep->l_cur == 1) {
F_CLR(ep, F_NOLOG);
return (0);
}
break;
case LOG_CURSOR_END:
memmove(&m, p + sizeof(u_char), sizeof(MARK));
if (m.lno != sp->lno) {
++ep->l_cur;
F_CLR(ep, F_NOLOG);
return (0);
}
break;
case LOG_LINE_APPEND:
case LOG_LINE_INSERT:
case LOG_LINE_DELETE:
case LOG_LINE_RESET_F:
break;
case LOG_LINE_RESET_B:
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
if (lno == sp->lno &&
file_sline(sp, ep, lno, p + sizeof(u_char) +
sizeof(recno_t), data.size - sizeof(u_char) -
sizeof(recno_t)))
goto err;
if (sp->rptlchange != lno) {
sp->rptlchange = lno;
++sp->rptlines[L_CHANGED];
}
case LOG_MARK:
memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
m.lno = lm.lno;
m.cno = lm.cno;
if (mark_set(sp, ep, lm.name, &m, 0))
goto err;
break;
default:
abort();
}
}
err: F_CLR(ep, F_NOLOG);
return (1);
}
/*
* Log_forward --
* Roll the log forward one operation.
*/
int
log_forward(sp, ep, rp)
SCR *sp;
EXF *ep;
MARK *rp;
{
DBT key, data;
LMARK lm;
MARK m;
recno_t lno;
int didop;
u_char *p;
if (F_ISSET(ep, F_NOLOG)) {
msgq(sp, M_ERR,
"Logging not being performed, roll-forward not possible");
return (1);
}
if (ep->l_cur == ep->l_high) {
msgq(sp, M_BERR, "No changes to re-do");
return (1);
}
F_SET(ep, F_NOLOG); /* Turn off logging. */
key.data = &ep->l_cur; /* Initialize db request. */
key.size = sizeof(recno_t);
for (didop = 0;;) {
++ep->l_cur;
if (ep->log->get(ep->log, &key, &data, 0))
LOG_ERR;
#if defined(DEBUG) && 0
log_trace(sp, "log_forward", ep->l_cur, data.data);
#endif
switch (*(p = (u_char *)data.data)) {
case LOG_CURSOR_END:
if (didop) {
++ep->l_cur;
memmove(rp, p + sizeof(u_char), sizeof(MARK));
F_CLR(ep, F_NOLOG);
return (0);
}
break;
case LOG_CURSOR_INIT:
break;
case LOG_LINE_APPEND:
case LOG_LINE_INSERT:
didop = 1;
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
if (file_iline(sp, ep, lno, p + sizeof(u_char) +
sizeof(recno_t), data.size - sizeof(u_char) -
sizeof(recno_t)))
goto err;
++sp->rptlines[L_ADDED];
break;
case LOG_LINE_DELETE:
didop = 1;
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
if (file_dline(sp, ep, lno))
goto err;
++sp->rptlines[L_DELETED];
break;
case LOG_LINE_RESET_B:
break;
case LOG_LINE_RESET_F:
didop = 1;
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
if (file_sline(sp, ep, lno, p + sizeof(u_char) +
sizeof(recno_t), data.size - sizeof(u_char) -
sizeof(recno_t)))
goto err;
if (sp->rptlchange != lno) {
sp->rptlchange = lno;
++sp->rptlines[L_CHANGED];
}
break;
case LOG_MARK:
didop = 1;
memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
m.lno = lm.lno;
m.cno = lm.cno;
if (mark_set(sp, ep, lm.name, &m, 0))
goto err;
break;
default:
abort();
}
}
err: F_CLR(ep, F_NOLOG);
return (1);
}
#if defined(DEBUG) && 0
static void
log_trace(sp, msg, rno, p)
SCR *sp;
char *msg;
recno_t rno;
u_char *p;
{
LMARK lm;
MARK m;
recno_t lno;
switch (*p) {
case LOG_CURSOR_INIT:
memmove(&m, p + sizeof(u_char), sizeof(MARK));
TRACE(sp, "%lu: %s: C_INIT: %u/%u\n", rno, msg, m.lno, m.cno);
break;
case LOG_CURSOR_END:
memmove(&m, p + sizeof(u_char), sizeof(MARK));
TRACE(sp, "%lu: %s: C_END: %u/%u\n", rno, msg, m.lno, m.cno);
break;
case LOG_LINE_APPEND:
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
TRACE(sp, "%lu: %s: APPEND: %lu\n", rno, msg, lno);
break;
case LOG_LINE_INSERT:
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
TRACE(sp, "%lu: %s: INSERT: %lu\n", rno, msg, lno);
break;
case LOG_LINE_DELETE:
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
TRACE(sp, "%lu: %s: DELETE: %lu\n", rno, msg, lno);
break;
case LOG_LINE_RESET_F:
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
TRACE(sp, "%lu: %s: RESET_F: %lu\n", rno, msg, lno);
break;
case LOG_LINE_RESET_B:
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
TRACE(sp, "%lu: %s: RESET_B: %lu\n", rno, msg, lno);
break;
case LOG_MARK:
memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
TRACE(sp,
"%lu: %s: MARK: %u/%u\n", rno, msg, lm.lno, lm.cno);
break;
default:
abort();
}
}
#endif

53
usr.bin/vi/common/log.h Normal file
View File

@ -0,0 +1,53 @@
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)log.h 8.5 (Berkeley) 3/16/94
*/
#define LOG_NOTYPE 0
#define LOG_CURSOR_INIT 1
#define LOG_CURSOR_END 2
#define LOG_LINE_APPEND 3
#define LOG_LINE_DELETE 4
#define LOG_LINE_INSERT 5
#define LOG_LINE_RESET_F 6
#define LOG_LINE_RESET_B 7
#define LOG_MARK 8
int log_backward __P((SCR *, EXF *, MARK *));
int log_cursor __P((SCR *, EXF *));
int log_end __P((SCR *, EXF *));
int log_forward __P((SCR *, EXF *, MARK *));
int log_init __P((SCR *, EXF *));
int log_line __P((SCR *, EXF *, recno_t, u_int));
int log_mark __P((SCR *, EXF *, LMARK *));
int log_setline __P((SCR *, EXF *));

720
usr.bin/vi/common/main.c Normal file
View File

@ -0,0 +1,720 @@
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char copyright[] =
"@(#) Copyright (c) 1992, 1993, 1994\n\
The Regents of the University of California. All rights reserved.\n";
#endif /* not lint */
#ifndef lint
static const char sccsid[] = "@(#)main.c 8.105 (Berkeley) 8/17/94";
#endif /* not lint */
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <bitstring.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#ifdef __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif
#include "compat.h"
#include <db.h>
#include <regex.h>
#include <pathnames.h>
#include "vi.h"
#include "excmd.h"
#include "../ex/tag.h"
enum rc { NOEXIST, NOPERM, OK };
static enum rc exrc_isok __P((SCR *, struct stat *, char *, int, int));
static void gs_end __P((GS *));
static GS *gs_init __P((void));
static void obsolete __P((char *[]));
static void usage __P((int));
GS *__global_list; /* GLOBAL: List of screens. */
int
main(argc, argv)
int argc;
char *argv[];
{
extern int optind;
extern char *optarg;
static int reenter; /* STATIC: Re-entrancy check. */
struct stat hsb, lsb;
GS *gp;
FREF *frp;
SCR *sp;
u_int flags, saved_vi_mode;
int ch, eval, flagchk, readonly, silent, snapshot;
char *excmdarg, *myname, *p, *tag_f, *trace_f, *wsizearg;
char path[MAXPATHLEN];
/* Stop if indirecting through a NULL pointer. */
if (reenter++)
abort();
#ifdef GDBATTACH
(void)printf("%u waiting...\n", getpid());
(void)read(0, &eval, 1);
#endif
/* Set screen type and mode based on the program name. */
readonly = 0;
if ((myname = strrchr(*argv, '/')) == NULL)
myname = *argv;
else
++myname;
if (!strcmp(myname, "ex") || !strcmp(myname, "nex"))
LF_INIT(S_EX);
else {
/* View is readonly. */
if (!strcmp(myname, "view"))
readonly = 1;
LF_INIT(S_VI_CURSES);
}
saved_vi_mode = S_VI_CURSES;
/* Convert old-style arguments into new-style ones. */
obsolete(argv);
/* Parse the arguments. */
flagchk = '\0';
excmdarg = tag_f = trace_f = wsizearg = NULL;
silent = 0;
snapshot = 1;
while ((ch = getopt(argc, argv, "c:eFRrsT:t:vw:X:")) != EOF)
switch (ch) {
case 'c': /* Run the command. */
excmdarg = optarg;
break;
case 'e': /* Ex mode. */
LF_CLR(S_SCREENS);
LF_SET(S_EX);
break;
case 'F': /* No snapshot. */
snapshot = 0;
break;
case 'R': /* Readonly. */
readonly = 1;
break;
case 'r': /* Recover. */
if (flagchk == 't')
errx(1,
"only one of -r and -t may be specified.");
flagchk = 'r';
break;
case 's':
silent = 1;
break;
case 'T': /* Trace. */
trace_f = optarg;
break;
case 't': /* Tag. */
if (flagchk == 'r')
errx(1,
"only one of -r and -t may be specified.");
if (flagchk == 't')
errx(1,
"only one tag file may be specified.");
flagchk = 't';
tag_f = optarg;
break;
case 'v': /* Vi mode. */
LF_CLR(S_SCREENS);
LF_SET(S_VI_CURSES);
break;
case 'w':
wsizearg = optarg;
break;
case 'X':
if (!strcmp(optarg, "aw")) {
LF_CLR(S_SCREENS);
LF_SET(S_VI_XAW);
saved_vi_mode = S_VI_XAW;
break;
}
/* FALLTHROUGH */
case '?':
default:
usage(LF_ISSET(S_EX));
}
argc -= optind;
argv += optind;
/* Silent is only applicable to ex. */
if (silent && !LF_ISSET(S_EX))
errx(1, "-s only applicable to ex.");
/* Build and initialize the GS structure. */
__global_list = gp = gs_init();
/*
* If not reading from a terminal, it's like -s was specified.
* Vi always reads from the terminal, so fail if it's not a
* terminal.
*/
if (!F_ISSET(gp, G_STDIN_TTY)) {
silent = 1;
if (!LF_ISSET(S_EX)) {
msgq(NULL, M_ERR,
"Vi's standard input must be a terminal");
goto err;
}
}
/*
* Build and initialize the first/current screen. This is a bit
* tricky. If an error is returned, we may or may not have a
* screen structure. If we have a screen structure, put it on a
* display queue so that the error messages get displayed.
*
* !!!
* Signals not on, no need to block them for queue manipulation.
*/
if (screen_init(NULL, &sp, flags)) {
if (sp != NULL)
CIRCLEQ_INSERT_HEAD(&__global_list->dq, sp, q);
goto err;
}
sp->saved_vi_mode = saved_vi_mode;
CIRCLEQ_INSERT_HEAD(&__global_list->dq, sp, q);
if (trace_f != NULL) {
#ifdef DEBUG
if ((gp->tracefp = fopen(trace_f, "w")) == NULL)
err(1, "%s", trace_f);
(void)fprintf(gp->tracefp, "\n===\ntrace: open %s\n", trace_f);
#else
msgq(sp, M_ERR, "-T support not compiled into this version");
#endif
}
if (opts_init(sp)) /* Options initialization. */
goto err;
if (readonly) /* Global read-only bit. */
O_SET(sp, O_READONLY);
if (silent) { /* Ex batch mode. */
O_CLR(sp, O_AUTOPRINT);
O_CLR(sp, O_PROMPT);
O_CLR(sp, O_VERBOSE);
O_CLR(sp, O_WARN);
F_SET(sp, S_EXSILENT);
}
if (wsizearg != NULL) {
ARGS *av[2], a, b;
errno = 0;
if (strtol(wsizearg, &p, 10) < 0 || errno || *p)
errx(1, "illegal window size -- %s.", wsizearg);
(void)snprintf(path, sizeof(path), "window=%s", wsizearg);
a.bp = (CHAR_T *)path;
a.len = strlen(path);
b.bp = NULL;
b.len = 0;
av[0] = &a;
av[1] = &b;
if (opts_set(sp, NULL, av))
msgq(sp, M_ERR,
"Unable to set command line window size option");
}
/* Keymaps, special keys, must follow option initializations. */
if (term_init(sp))
goto err;
#ifdef DIGRAPHS
if (digraph_init(sp)) /* Digraph initialization. */
goto err;
#endif
/*
* Source the system, environment, $HOME and local .exrc values.
* Vi historically didn't check $HOME/.exrc if the environment
* variable EXINIT was set. This is all done before the file is
* read in, because things in the .exrc information can set, for
* example, the recovery directory.
*
* !!!
* While nvi can handle any of the options settings of historic vi,
* the converse is not true. Since users are going to have to have
* files and environmental variables that work with both, we use nvi
* versions of both the $HOME and local startup files if they exist,
* otherwise the historic ones.
*
* !!!
* For a discussion of permissions and when what .exrc files are
* read, see the the comment above the exrc_isok() function below.
*
* !!!
* If the user started the historic of vi in $HOME, vi read the user's
* .exrc file twice, as $HOME/.exrc and as ./.exrc. We avoid this, as
* it's going to make some commands behave oddly, and I can't imagine
* anyone depending on it.
*/
if (!silent) {
switch (exrc_isok(sp, &hsb, _PATH_SYSEXRC, 1, 0)) {
case NOEXIST:
case NOPERM:
break;
case OK:
(void)ex_cfile(sp, NULL, _PATH_SYSEXRC, 0);
break;
}
if ((p = getenv("NEXINIT")) != NULL ||
(p = getenv("EXINIT")) != NULL)
if ((p = strdup(p)) == NULL) {
msgq(sp, M_SYSERR, NULL);
goto err;
} else {
F_SET(sp, S_VLITONLY);
(void)ex_icmd(sp, NULL, p, strlen(p), 0);
F_CLR(sp, S_VLITONLY);
free(p);
}
else if ((p = getenv("HOME")) != NULL && *p) {
(void)snprintf(path,
sizeof(path), "%s/%s", p, _PATH_NEXRC);
switch (exrc_isok(sp, &hsb, path, 0, 1)) {
case NOEXIST:
(void)snprintf(path,
sizeof(path), "%s/%s", p, _PATH_EXRC);
if (exrc_isok(sp, &hsb, path, 0, 1) == OK)
(void)ex_cfile(sp, NULL, path, 0);
break;
case NOPERM:
break;
case OK:
(void)ex_cfile(sp, NULL, path, 0);
break;
}
}
if (O_ISSET(sp, O_EXRC))
switch (exrc_isok(sp, &lsb, _PATH_NEXRC, 0, 0)) {
case NOEXIST:
if (exrc_isok(sp,
&lsb, _PATH_EXRC, 0, 0) == OK &&
(lsb.st_dev != hsb.st_dev ||
lsb.st_ino != hsb.st_ino))
(void)ex_cfile(sp, NULL, _PATH_EXRC, 0);
break;
case NOPERM:
break;
case OK:
if (lsb.st_dev != hsb.st_dev ||
lsb.st_ino != hsb.st_ino)
(void)ex_cfile(sp,
NULL, _PATH_NEXRC, 0);
break;
}
}
/* List recovery files if -r specified without file arguments. */
if (flagchk == 'r' && argv[0] == NULL)
exit(rcv_list(sp));
/* Set the file snapshot flag. */
if (snapshot)
F_SET(gp, G_SNAPSHOT);
/* Use a tag file if specified. */
if (tag_f != NULL && ex_tagfirst(sp, tag_f))
goto err;
/*
* Append any remaining arguments as file names. Files are
* recovery files if -r specified.
*/
if (*argv != NULL) {
sp->argv = sp->cargv = argv;
F_SET(sp, S_ARGNOFREE);
if (flagchk == 'r')
F_SET(sp, S_ARGRECOVER);
}
/*
* If the tag option hasn't already created a file, create one.
* If no files as arguments, use a temporary file.
*/
if (tag_f == NULL) {
if ((frp = file_add(sp,
sp->argv == NULL ? NULL : (CHAR_T *)(sp->argv[0]))) == NULL)
goto err;
if (F_ISSET(sp, S_ARGRECOVER))
F_SET(frp, FR_RECOVER);
if (file_init(sp, frp, NULL, 0))
goto err;
}
/*
* If there's an initial command, push it on the command stack.
* Historically, it was always an ex command, not vi in vi mode
* or ex in ex mode. So, make it look like an ex command to vi.
*
* !!!
* Historically, all such commands were executed with the last
* line of the file as the current line, and not the first, so
* set up vi to be at the end of the file.
*/
if (excmdarg != NULL)
if (IN_EX_MODE(sp)) {
if (term_push(sp, "\n", 1, 0))
goto err;
if (term_push(sp, excmdarg, strlen(excmdarg), 0))
goto err;
} else if (IN_VI_MODE(sp)) {
if (term_push(sp, "\n", 1, 0))
goto err;
if (term_push(sp, excmdarg, strlen(excmdarg), 0))
goto err;
if (term_push(sp, ":", 1, 0))
goto err;
if (file_lline(sp, sp->ep, &sp->frp->lno))
goto err;
F_SET(sp->frp, FR_CURSORSET);
}
/* Set up signals. */
if (sig_init(sp))
goto err;
for (;;) {
/* Ignore errors -- other screens may succeed. */
(void)sp->s_edit(sp, sp->ep);
/*
* Edit the next screen on the display queue, or, move
* a screen from the hidden queue to the display queue.
*/
if ((sp = __global_list->dq.cqh_first) ==
(void *)&__global_list->dq)
if ((sp = __global_list->hq.cqh_first) !=
(void *)&__global_list->hq) {
SIGBLOCK(__global_list);
CIRCLEQ_REMOVE(&sp->gp->hq, sp, q);
CIRCLEQ_INSERT_TAIL(&sp->gp->dq, sp, q);
SIGUNBLOCK(__global_list);
} else
break;
/*
* The screen type may have changed -- reinitialize the
* functions in case it has.
*/
switch (F_ISSET(sp, S_SCREENS)) {
case S_EX:
if (sex_screen_init(sp))
goto err;
break;
case S_VI_CURSES:
if (svi_screen_init(sp))
goto err;
break;
case S_VI_XAW:
if (xaw_screen_init(sp))
goto err;
break;
default:
abort();
}
}
eval = 0;
if (0)
err: eval = 1;
/*
* NOTE: sp may be GONE when the screen returns, so only
* the gp can be trusted.
*/
gs_end(gp);
exit(eval);
}
/*
* gs_init --
* Build and initialize the GS structure.
*/
static GS *
gs_init()
{
GS *gp;
int fd;
CALLOC_NOMSG(NULL, gp, GS *, 1, sizeof(GS));
if (gp == NULL)
err(1, NULL);
/*
* !!!
* Signals not on, no need to block them for queue manipulation.
*/
CIRCLEQ_INIT(&gp->dq);
CIRCLEQ_INIT(&gp->hq);
LIST_INIT(&gp->msgq);
/* Structures shared by screens so stored in the GS structure. */
CALLOC_NOMSG(NULL, gp->tty, IBUF *, 1, sizeof(IBUF));
if (gp->tty == NULL)
err(1, NULL);
CIRCLEQ_INIT(&gp->dcb_store.textq);
LIST_INIT(&gp->cutq);
LIST_INIT(&gp->seqq);
/* Set a flag if we're reading from the tty. */
if (isatty(STDIN_FILENO))
F_SET(gp, G_STDIN_TTY);
/*
* Set the G_STDIN_TTY flag. It's purpose is to avoid setting and
* resetting the tty if the input isn't from there.
*
* Set the G_TERMIOS_SET flag. It's purpose is to avoid using the
* original_termios information (mostly special character values)
* if it's not valid. We expect that if we've lost our controlling
* terminal that the open() (but not the tcgetattr()) will fail.
*/
if (F_ISSET(gp, G_STDIN_TTY)) {
if (tcgetattr(STDIN_FILENO, &gp->original_termios) == -1)
err(1, "tcgetattr");
F_SET(gp, G_TERMIOS_SET);
} else if ((fd = open(_PATH_TTY, O_RDONLY, 0)) != -1) {
if (tcgetattr(fd, &gp->original_termios) == -1)
err(1, "tcgetattr");
F_SET(gp, G_TERMIOS_SET);
(void)close(fd);
}
return (gp);
}
/*
* gs_end --
* End the GS structure.
*/
static void
gs_end(gp)
GS *gp;
{
MSG *mp;
SCR *sp;
char *tty;
/* Default buffer storage. */
(void)text_lfree(&gp->dcb_store.textq);
/* Reset anything that needs resetting. */
if (gp->flags & G_SETMODE) /* O_MESG */
if ((tty = ttyname(STDERR_FILENO)) == NULL)
warn("ttyname");
else if (chmod(tty, gp->origmode) < 0)
warn("%s", tty);
/* Ring the bell if scheduled. */
if (F_ISSET(gp, G_BELLSCHED))
(void)fprintf(stderr, "\07"); /* \a */
/* If there are any remaining screens, flush their messages. */
for (sp = __global_list->dq.cqh_first;
sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
for (mp = sp->msgq.lh_first;
mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next)
(void)fprintf(stderr,
"%.*s.\n", (int)mp->len, mp->mbuf);
for (sp = __global_list->hq.cqh_first;
sp != (void *)&__global_list->hq; sp = sp->q.cqe_next)
for (mp = sp->msgq.lh_first;
mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next)
(void)fprintf(stderr,
"%.*s.\n", (int)mp->len, mp->mbuf);
/* Flush messages on the global queue. */
for (mp = gp->msgq.lh_first;
mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next)
(void)fprintf(stderr, "%.*s.\n", (int)mp->len, mp->mbuf);
/*
* DON'T FREE THE GLOBAL STRUCTURE -- WE DIDN'T TURN
* OFF SIGNALS/TIMERS, SO IT MAY STILL BE REFERENCED.
*/
}
/*
* exrc_isok --
* Check a .exrc file for source-ability.
*
* !!!
* Historically, vi read the $HOME and local .exrc files if they were owned
* by the user's real ID, or the "sourceany" option was set, regardless of
* any other considerations. We no longer support the sourceany option as
* it's a security problem of mammoth proportions. We require the system
* .exrc file to be owned by root, the $HOME .exrc file to be owned by the
* user's effective ID (or that the user's effective ID be root) and the
* local .exrc files to be owned by the user's effective ID. In all cases,
* the file cannot be writeable by anyone other than its owner.
*
* In O'Reilly ("Learning the VI Editor", Fifth Ed., May 1992, page 106),
* it notes that System V release 3.2 and later has an option "[no]exrc".
* The behavior is that local .exrc files are read only if the exrc option
* is set. The default for the exrc option was off, so, by default, local
* .exrc files were not read. The problem this was intended to solve was
* that System V permitted users to give away files, so there's no possible
* ownership or writeability test to ensure that the file is safe.
*
* POSIX 1003.2-1992 standardized exrc as an option. It required the exrc
* option to be off by default, thus local .exrc files are not to be read
* by default. The Rationale noted (incorrectly) that this was a change
* to historic practice, but correctly noted that a default of off improves
* system security. POSIX also required that vi check the effective user
* ID instead of the real user ID, which is why we've switched from historic
* practice.
*
* We initialize the exrc variable to off. If it's turned on by the system
* or $HOME .exrc files, and the local .exrc file passes the ownership and
* writeability tests, then we read it. This breaks historic 4BSD practice,
* but it gives us a measure of security on systems where users can give away
* files.
*/
static enum rc
exrc_isok(sp, sbp, path, rootown, rootid)
SCR *sp;
struct stat *sbp;
char *path;
int rootown, rootid;
{
uid_t euid;
char *emsg, buf[MAXPATHLEN];
/* Check for the file's existence. */
if (stat(path, sbp))
return (NOEXIST);
/* Check ownership permissions. */
euid = geteuid();
if (!(rootown && sbp->st_uid == 0) &&
!(rootid && euid == 0) && sbp->st_uid != euid) {
emsg = rootown ?
"not owned by you or root" : "not owned by you";
goto denied;
}
/* Check writeability. */
if (sbp->st_mode & (S_IWGRP | S_IWOTH)) {
emsg = "writeable by a user other than the owner";
denied: if (strchr(path, '/') == NULL &&
getcwd(buf, sizeof(buf)) != NULL)
msgq(sp, M_ERR,
"%s/%s: not sourced: %s", buf, path, emsg);
else
msgq(sp, M_ERR,
"%s: not sourced: %s", path, emsg);
return (NOPERM);
}
return (OK);
}
static void
obsolete(argv)
char *argv[];
{
size_t len;
char *p;
/*
* Translate old style arguments into something getopt will like.
* Make sure it's not text space memory, because ex changes the
* strings.
* Change "+" into "-c$".
* Change "+<anything else>" into "-c<anything else>".
* Change "-" into "-s"
*/
while (*++argv)
if (argv[0][0] == '+') {
if (argv[0][1] == '\0') {
MALLOC_NOMSG(NULL, argv[0], char *, 4);
if (argv[0] == NULL)
err(1, NULL);
(void)strcpy(argv[0], "-c$");
} else {
p = argv[0];
len = strlen(argv[0]);
MALLOC_NOMSG(NULL, argv[0], char *, len + 2);
if (argv[0] == NULL)
err(1, NULL);
argv[0][0] = '-';
argv[0][1] = 'c';
(void)strcpy(argv[0] + 2, p + 1);
}
} else if (argv[0][0] == '-' && argv[0][1] == '\0') {
MALLOC_NOMSG(NULL, argv[0], char *, 3);
if (argv[0] == NULL)
err(1, NULL);
(void)strcpy(argv[0], "-s");
}
}
static void
usage(is_ex)
int is_ex;
{
#define EX_USAGE \
"ex [-eFRrsv] [-c command] [-t tag] [-w size] [files ...]"
#define VI_USAGE \
"vi [-eFRrv] [-c command] [-t tag] [-w size] [files ...]"
(void)fprintf(stderr, "usage: %s\n", is_ex ? EX_USAGE : VI_USAGE);
exit(1);
}

272
usr.bin/vi/common/mark.c Normal file
View File

@ -0,0 +1,272 @@
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char sccsid[] = "@(#)mark.c 8.20 (Berkeley) 8/17/94";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include "compat.h"
#include <db.h>
#include <regex.h>
#include "vi.h"
static LMARK *mark_find __P((SCR *, EXF *, ARG_CHAR_T));
/*
* Marks are maintained in a key sorted doubly linked list. We can't
* use arrays because we have no idea how big an index key could be.
* The underlying assumption is that users don't have more than, say,
* 10 marks at any one time, so this will be is fast enough.
*
* Marks are fixed, and modifications to the line don't update the mark's
* position in the line. This can be hard. If you add text to the line,
* place a mark in that text, undo the addition and use ` to move to the
* mark, the location will have disappeared. It's tempting to try to adjust
* the mark with the changes in the line, but this is hard to do, especially
* if we've given the line to v_ntext.c:v_ntext() for editing. Historic vi
* would move to the first non-blank on the line when the mark location was
* past the end of the line. This can be complicated by deleting to a mark
* that has disappeared using the ` command. Historic vi vi treated this as
* a line-mode motion and deleted the line. This implementation complains to
* the user.
*
* In historic vi, marks returned if the operation was undone, unless the
* mark had been subsequently reset. Tricky. This is hard to start with,
* but in the presence of repeated undo it gets nasty. When a line is
* deleted, we delete (and log) any marks on that line. An undo will create
* the mark. Any mark creations are noted as to whether the user created
* it or if it was created by an undo. The former cannot be reset by another
* undo, but the latter may.
*
* All of these routines translate ABSMARK2 to ABSMARK1. Setting either of
* the absolute mark locations sets both, so that "m'" and "m`" work like
* they, ah, for lack of a better word, "should".
*/
/*
* mark_init --
* Set up the marks.
*/
int
mark_init(sp, ep)
SCR *sp;
EXF *ep;
{
LMARK *lmp;
/*
* Make sure the marks have been set up. If they
* haven't, do so, and create the absolute mark.
*/
MALLOC_RET(sp, lmp, LMARK *, sizeof(LMARK));
lmp->lno = 1;
lmp->cno = 0;
lmp->name = ABSMARK1;
lmp->flags = 0;
LIST_INSERT_HEAD(&ep->marks, lmp, q);
return (0);
}
/*
* mark_end --
* Free up the marks.
*/
int
mark_end(sp, ep)
SCR *sp;
EXF *ep;
{
LMARK *lmp;
while ((lmp = ep->marks.lh_first) != NULL) {
LIST_REMOVE(lmp, q);
FREE(lmp, sizeof(LMARK));
}
return (0);
}
/*
* mark_get --
* Get the location referenced by a mark.
*/
int
mark_get(sp, ep, key, mp)
SCR *sp;
EXF *ep;
ARG_CHAR_T key;
MARK *mp;
{
LMARK *lmp;
size_t len;
if (key == ABSMARK2)
key = ABSMARK1;
lmp = mark_find(sp, ep, key);
if (lmp == NULL || lmp->name != key) {
msgq(sp, M_BERR, "Mark %s: not set", KEY_NAME(sp, key));
return (1);
}
if (F_ISSET(lmp, MARK_DELETED)) {
msgq(sp, M_BERR,
"Mark %s: the line was deleted", KEY_NAME(sp, key));
return (1);
}
if (file_gline(sp, ep, lmp->lno, &len) == NULL ||
lmp->cno > len || lmp->cno == len && len != 0) {
msgq(sp, M_BERR, "Mark %s: cursor position no longer exists",
KEY_NAME(sp, key));
return (1);
}
mp->lno = lmp->lno;
mp->cno = lmp->cno;
return (0);
}
/*
* mark_set --
* Set the location referenced by a mark.
*/
int
mark_set(sp, ep, key, value, userset)
SCR *sp;
EXF *ep;
ARG_CHAR_T key;
MARK *value;
int userset;
{
LMARK *lmp, *lmt;
if (key == ABSMARK2)
key = ABSMARK1;
/*
* The rules are simple. If the user is setting a mark (if it's a
* new mark this is always true), it always happens. If not, it's
* an undo, and we set it if it's not already set or if it was set
* by a previous undo.
*/
lmp = mark_find(sp, ep, key);
if (lmp == NULL || lmp->name != key) {
MALLOC_RET(sp, lmt, LMARK *, sizeof(LMARK));
if (lmp == NULL) {
LIST_INSERT_HEAD(&ep->marks, lmt, q);
} else
LIST_INSERT_AFTER(lmp, lmt, q);
lmp = lmt;
} else if (!userset &&
!F_ISSET(lmp, MARK_DELETED) && F_ISSET(lmp, MARK_USERSET))
return (0);
lmp->lno = value->lno;
lmp->cno = value->cno;
lmp->name = key;
lmp->flags = userset ? MARK_USERSET : 0;
return (0);
}
/*
* mark_find --
* Find the requested mark, or, the slot immediately before
* where it would go.
*/
static LMARK *
mark_find(sp, ep, key)
SCR *sp;
EXF *ep;
ARG_CHAR_T key;
{
LMARK *lmp, *lastlmp;
/*
* Return the requested mark or the slot immediately before
* where it should go.
*/
for (lastlmp = NULL, lmp = ep->marks.lh_first;
lmp != NULL; lastlmp = lmp, lmp = lmp->q.le_next)
if (lmp->name >= key)
return (lmp->name == key ? lmp : lastlmp);
return (lastlmp);
}
/*
* mark_insdel --
* Update the marks based on an insertion or deletion.
*/
void
mark_insdel(sp, ep, op, lno)
SCR *sp;
EXF *ep;
enum operation op;
recno_t lno;
{
LMARK *lmp;
switch (op) {
case LINE_APPEND:
return;
case LINE_DELETE:
for (lmp = ep->marks.lh_first;
lmp != NULL; lmp = lmp->q.le_next)
if (lmp->lno >= lno)
if (lmp->lno == lno) {
F_SET(lmp, MARK_DELETED);
(void)log_mark(sp, ep, lmp);
} else
--lmp->lno;
return;
case LINE_INSERT:
for (lmp = ep->marks.lh_first;
lmp != NULL; lmp = lmp->q.le_next)
if (lmp->lno >= lno)
++lmp->lno;
return;
case LINE_RESET:
return;
}
/* NOTREACHED */
}

73
usr.bin/vi/common/mark.h Normal file
View File

@ -0,0 +1,73 @@
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)mark.h 8.9 (Berkeley) 7/17/94
*/
/*
* The MARK and LMARK structures define positions in the file. There are
* two structures because the mark subroutines are the only places where
* anything cares about something other than line and column.
*
* Because of the different interfaces used by the db(3) package, curses,
* and users, the line number is 1 based and the column number is 0 based.
* Additionally, it is known that the out-of-band line number is less than
* any legal line number. The line number is of type recno_t, as that's
* the underlying type of the database. The column number is of type size_t,
* guaranteeing that we can malloc a line.
*/
struct _mark {
#define OOBLNO 0 /* Out-of-band line number. */
recno_t lno; /* Line number. */
size_t cno; /* Column number. */
};
struct _lmark {
LIST_ENTRY(_lmark) q; /* Linked list of marks. */
recno_t lno; /* Line number. */
size_t cno; /* Column number. */
CHAR_T name; /* Mark name. */
#define MARK_DELETED 0x01 /* Mark was deleted. */
#define MARK_USERSET 0x02 /* User set this mark. */
u_int8_t flags;
};
#define ABSMARK1 '\'' /* Absolute mark name. */
#define ABSMARK2 '`' /* Absolute mark name. */
/* Mark routines. */
int mark_end __P((SCR *, EXF *));
int mark_get __P((SCR *, EXF *, ARG_CHAR_T, MARK *));
int mark_init __P((SCR *, EXF *));
void mark_insdel __P((SCR *, EXF *, enum operation, recno_t));
int mark_set __P((SCR *, EXF *, ARG_CHAR_T, MARK *, int));

194
usr.bin/vi/common/mem.h Normal file
View File

@ -0,0 +1,194 @@
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)mem.h 8.8 (Berkeley) 8/16/94
*/
/* Increase the size of a malloc'd buffer. Two versions, one that
* returns, one that jumps to an error label.
*/
#define BINC_GOTO(sp, lp, llen, nlen) { \
void *__bincp; \
if ((nlen) > llen) { \
if ((__bincp = binc(sp, lp, &(llen), nlen)) == NULL) \
goto binc_err; \
/* \
* !!! \
* Possible pointer conversion. \
*/ \
lp = __bincp; \
} \
}
#define BINC_RET(sp, lp, llen, nlen) { \
void *__bincp; \
if ((nlen) > llen) { \
if ((__bincp = binc(sp, lp, &(llen), nlen)) == NULL) \
return (1); \
/* \
* !!! \
* Possible pointer conversion. \
*/ \
lp = __bincp; \
} \
}
/*
* Get some temporary space, preferably from the global temporary buffer,
* from a malloc'd buffer otherwise. Two versions, one that returns, one
* that jumps to an error label.
*/
#define GET_SPACE_GOTO(sp, bp, blen, nlen) { \
GS *__gp = (sp)->gp; \
if (F_ISSET(__gp, G_TMP_INUSE)) { \
bp = NULL; \
blen = 0; \
BINC_GOTO(sp, bp, blen, nlen); \
} else { \
BINC_GOTO(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \
bp = __gp->tmp_bp; \
blen = __gp->tmp_blen; \
F_SET(__gp, G_TMP_INUSE); \
} \
}
#define GET_SPACE_RET(sp, bp, blen, nlen) { \
GS *__gp = (sp)->gp; \
if (F_ISSET(__gp, G_TMP_INUSE)) { \
bp = NULL; \
blen = 0; \
BINC_RET(sp, bp, blen, nlen); \
} else { \
BINC_RET(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \
bp = __gp->tmp_bp; \
blen = __gp->tmp_blen; \
F_SET(__gp, G_TMP_INUSE); \
} \
}
/*
* Add space to a GET_SPACE returned buffer. Two versions, one that
* returns, one that jumps to an error label.
*/
#define ADD_SPACE_GOTO(sp, bp, blen, nlen) { \
GS *__gp = (sp)->gp; \
if (bp == __gp->tmp_bp) { \
F_CLR(__gp, G_TMP_INUSE); \
BINC_GOTO(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \
bp = __gp->tmp_bp; \
blen = __gp->tmp_blen; \
F_SET(__gp, G_TMP_INUSE); \
} else \
BINC_GOTO(sp, bp, blen, nlen); \
}
#define ADD_SPACE_RET(sp, bp, blen, nlen) { \
GS *__gp = (sp)->gp; \
if (bp == __gp->tmp_bp) { \
F_CLR(__gp, G_TMP_INUSE); \
BINC_RET(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \
bp = __gp->tmp_bp; \
blen = __gp->tmp_blen; \
F_SET(__gp, G_TMP_INUSE); \
} else \
BINC_RET(sp, bp, blen, nlen); \
}
/* Free memory, optionally making pointers unusable. */
#ifdef DEBUG
#define FREE(p, sz) { \
memset(p, 0xff, sz); \
free(p); \
}
#else
#define FREE(p, sz) free(p);
#endif
/* Free a GET_SPACE returned buffer. */
#define FREE_SPACE(sp, bp, blen) { \
if (bp == sp->gp->tmp_bp) \
F_CLR(sp->gp, G_TMP_INUSE); \
else \
FREE(bp, blen); \
}
/*
* Malloc a buffer, casting the return pointer. Various versions.
*
* !!!
* The cast should be unnecessary, malloc(3) and friends return void *'s,
* which is all we need. However, some systems that nvi needs to run on
* don't do it right yet, resulting in the compiler printing out roughly
* a million warnings. After awhile, it seemed easier to put the casts
* in instead of explaining it all the time.
*/
#define CALLOC_NOMSG(sp, p, cast, nmemb, size) { \
p = (cast)calloc(nmemb, size); \
}
#define CALLOC(sp, p, cast, nmemb, size) { \
if ((p = (cast)calloc(nmemb, size)) == NULL) \
msgq(sp, M_SYSERR, NULL); \
}
#define CALLOC_RET(sp, p, cast, nmemb, size) { \
if ((p = (cast)calloc(nmemb, size)) == NULL) { \
msgq(sp, M_SYSERR, NULL); \
return (1); \
} \
}
#define MALLOC_NOMSG(sp, p, cast, size) { \
p = (cast)malloc(size); \
}
#define MALLOC(sp, p, cast, size) { \
if ((p = (cast)malloc(size)) == NULL) \
msgq(sp, M_SYSERR, NULL); \
}
#define MALLOC_RET(sp, p, cast, size) { \
if ((p = (cast)malloc(size)) == NULL) { \
msgq(sp, M_SYSERR, NULL); \
return (1); \
} \
}
/*
* XXX
* Don't depend on realloc(NULL, size) working.
*/
#define REALLOC(sp, p, cast, size) { \
if ((p = (cast)(p == NULL ? \
malloc(size) : realloc(p, size))) == NULL) \
msgq(sp, M_SYSERR, NULL); \
}
/*
* Versions of memmove(3) and memset(3) that use the size of the
* initial pointer to figure out how much memory to manipulate.
*/
#define MEMMOVE(p, t, len) memmove(p, t, (len) * sizeof(*(p)))
#define MEMSET(p, value, len) memset(p, value, (len) * sizeof(*(p)))
void *binc __P((SCR *, void *, size_t *, size_t));

428
usr.bin/vi/common/msg.c Normal file
View File

@ -0,0 +1,428 @@
/*-
* Copyright (c) 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char sccsid[] = "@(#)msg.c 8.11 (Berkeley) 8/17/94";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#ifdef __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif
#include "compat.h"
#include <db.h>
#include <regex.h>
#include "vi.h"
/*
* msgq --
* Display a message.
*/
void
#ifdef __STDC__
msgq(SCR *sp, enum msgtype mt, const char *fmt, ...)
#else
msgq(sp, mt, fmt, va_alist)
SCR *sp;
enum msgtype mt;
char *fmt;
va_dcl
#endif
{
va_list ap;
size_t len;
char msgbuf[1024];
#ifdef __STDC__
va_start(ap, fmt);
#else
va_start(ap);
#endif
/*
* It's possible to enter msg when there's no screen to hold
* the message. If sp is NULL, ignore the special cases and
* just build the message, using __global_list.
*/
if (sp == NULL)
goto nullsp;
switch (mt) {
case M_BERR:
if (!F_ISSET(sp, S_EXSILENT) &&
F_ISSET(sp->gp, G_STDIN_TTY) && !O_ISSET(sp, O_VERBOSE)) {
F_SET(sp, S_BELLSCHED);
return;
}
mt = M_ERR;
break;
case M_VINFO:
if (!O_ISSET(sp, O_VERBOSE))
return;
mt = M_INFO;
/* FALLTHROUGH */
case M_INFO:
if (F_ISSET(sp, S_EXSILENT))
return;
break;
case M_ERR:
case M_SYSERR:
break;
default:
abort();
}
nullsp: len = 0;
#define EPREFIX "Error: "
if (mt == M_SYSERR) {
memmove(msgbuf, EPREFIX, sizeof(EPREFIX) - 1);
len += sizeof(EPREFIX) - 1;
}
if (sp != NULL && sp->if_name != NULL) {
len += snprintf(msgbuf + len, sizeof(msgbuf) - len,
"%s, %d: ", sp->if_name, sp->if_lno);
if (len >= sizeof(msgbuf))
goto err;
}
if (fmt != NULL) {
len += vsnprintf(msgbuf + len, sizeof(msgbuf) - len, fmt, ap);
if (len >= sizeof(msgbuf))
goto err;
}
if (mt == M_SYSERR) {
len += snprintf(msgbuf + len,
sizeof(msgbuf) - len, ": %s", strerror(errno));
if (len >= sizeof(msgbuf))
goto err;
}
/*
* If len >= the size, some characters were discarded.
* Ignore trailing nul.
*/
err: if (len >= sizeof(msgbuf))
len = sizeof(msgbuf) - 1;
#ifdef DEBUG
if (sp != NULL)
TRACE(sp, "%.*s\n", len, msgbuf);
#endif
msg_app(__global_list, sp, mt == M_ERR ? 1 : 0, msgbuf, len);
}
/*
* msg_app --
* Append a message into the queue. This can fail, but there's
* nothing we can do if it does.
*/
void
msg_app(gp, sp, inv_video, p, len)
GS *gp;
SCR *sp;
int inv_video;
char *p;
size_t len;
{
static int reenter; /* STATIC: Re-entrancy check. */
MSG *mp, *nmp;
/*
* It's possible to reenter msg when it allocates space.
* We're probably dead anyway, but no reason to drop core.
*/
if (reenter)
return;
reenter = 1;
/*
* We can be entered as the result of a signal arriving, trying
* to sync the file and failing. This shouldn't be a hot spot,
* block the signals.
*/
SIGBLOCK(gp);
/*
* Find an empty structure, or allocate a new one. Use the
* screen structure if it exists, otherwise the global one.
*/
if (sp != NULL) {
if ((mp = sp->msgq.lh_first) == NULL) {
CALLOC(sp, mp, MSG *, 1, sizeof(MSG));
if (mp == NULL)
goto ret;
LIST_INSERT_HEAD(&sp->msgq, mp, q);
goto store;
}
} else if ((mp = gp->msgq.lh_first) == NULL) {
CALLOC(sp, mp, MSG *, 1, sizeof(MSG));
if (mp == NULL)
goto ret;
LIST_INSERT_HEAD(&gp->msgq, mp, q);
goto store;
}
while (!F_ISSET(mp, M_EMPTY) && mp->q.le_next != NULL)
mp = mp->q.le_next;
if (!F_ISSET(mp, M_EMPTY)) {
CALLOC(sp, nmp, MSG *, 1, sizeof(MSG));
if (nmp == NULL)
goto ret;
LIST_INSERT_AFTER(mp, nmp, q);
mp = nmp;
}
/* Get enough memory for the message. */
store: if (len > mp->blen &&
(mp->mbuf = binc(sp, mp->mbuf, &mp->blen, len)) == NULL)
goto ret;
/* Store the message. */
memmove(mp->mbuf, p, len);
mp->len = len;
mp->flags = inv_video ? M_INV_VIDEO : 0;
ret: reenter = 0;
SIGUNBLOCK(gp);
}
/*
* msg_rpt --
* Report on the lines that changed.
*
* !!!
* Historic vi documentation (USD:15-8) claimed that "The editor will also
* always tell you when a change you make affects text which you cannot see."
* This isn't true -- edit a large file and do "100d|1". We don't implement
* this semantic as it would require that we track each line that changes
* during a command instead of just keeping count.
*
* Line counts weren't right in historic vi, either. For example, given the
* file:
* abc
* def
* the command 2d}, from the 'b' would report that two lines were deleted,
* not one.
*/
int
msg_rpt(sp, is_message)
SCR *sp;
int is_message;
{
static char * const action[] = {
"added", "changed", "deleted", "joined", "moved",
"left shifted", "right shifted", "yanked",
NULL,
};
recno_t total;
u_long rptval;
int first, cnt;
size_t blen, len;
char * const *ap;
char *bp, *p, number[40];
if (F_ISSET(sp, S_EXSILENT))
return (0);
if ((rptval = O_VAL(sp, O_REPORT)) == 0)
goto norpt;
GET_SPACE_RET(sp, bp, blen, 512);
p = bp;
total = 0;
for (ap = action, cnt = 0, first = 1; *ap != NULL; ++ap, ++cnt)
if (sp->rptlines[cnt] != 0) {
total += sp->rptlines[cnt];
len = snprintf(number, sizeof(number),
"%s%lu lines %s",
first ? "" : "; ", sp->rptlines[cnt], *ap);
memmove(p, number, len);
p += len;
first = 0;
}
/*
* If nothing to report, return.
*
* !!!
* And now, a special vi clone test. Historically, vi reported if
* the number of changed lines was > than the value, not >=. Which
* means that users can't report on single line changes, btw.) In
* any case, if it was a yank command, it was >=, not >. No lie. I
* got complaints, so we do it right.
*/
if (total > rptval || sp->rptlines[L_YANKED] >= rptval) {
*p = '\0';
if (is_message)
msgq(sp, M_INFO, "%s", bp);
else
ex_printf(EXCOOKIE, "%s\n", bp);
}
FREE_SPACE(sp, bp, blen);
/* Clear after each report. */
norpt: sp->rptlchange = OOBLNO;
memset(sp->rptlines, 0, sizeof(sp->rptlines));
return (0);
}
/*
* msg_status --
* Report on the file's status.
*/
int
msg_status(sp, ep, lno, showlast)
SCR *sp;
EXF *ep;
recno_t lno;
int showlast;
{
recno_t last;
char *mo, *nc, *nf, *pid, *ro, *ul;
#ifdef DEBUG
char pbuf[50];
(void)snprintf(pbuf, sizeof(pbuf), " (pid %u)", getpid());
pid = pbuf;
#else
pid = "";
#endif
/*
* See nvi/exf.c:file_init() for a description of how and
* when the read-only bit is set.
*
* !!!
* The historic display for "name changed" was "[Not edited]".
*/
if (F_ISSET(sp->frp, FR_NEWFILE)) {
F_CLR(sp->frp, FR_NEWFILE);
nf = "new file";
mo = nc = "";
} else {
nf = "";
if (F_ISSET(sp->frp, FR_NAMECHANGE)) {
nc = "name changed";
mo = F_ISSET(ep, F_MODIFIED) ?
", modified" : ", unmodified";
} else {
nc = "";
mo = F_ISSET(ep, F_MODIFIED) ?
"modified" : "unmodified";
}
}
ro = F_ISSET(sp->frp, FR_RDONLY) ? ", readonly" : "";
ul = F_ISSET(sp->frp, FR_UNLOCKED) ? ", UNLOCKED" : "";
if (showlast) {
if (file_lline(sp, ep, &last))
return (1);
if (last >= 1)
msgq(sp, M_INFO,
"%s: %s%s%s%s%s: line %lu of %lu [%ld%%]%s",
sp->frp->name, nf, nc, mo, ul, ro, lno,
last, (lno * 100) / last, pid);
else
msgq(sp, M_INFO, "%s: %s%s%s%s%s: empty file%s",
sp->frp->name, nf, nc, mo, ul, ro, pid);
} else
msgq(sp, M_INFO, "%s: %s%s%s%s%s: line %lu%s",
sp->frp->name, nf, nc, mo, ul, ro, lno, pid);
return (0);
}
#ifdef MSG_CATALOG
/*
* get_msg --
* Return a format based on a message number.
*/
char *
get_msg(sp, msgno)
SCR *sp;
char *s_msgno;
{
DBT data, key;
GS *gp;
recno_t msgno;
char *msg, *p;
gp = sp == NULL ? __global_list : sp->gp;
if (gp->msgdb == NULL) {
p = sp == NULL ? _PATH_MSGDEF : O_STR(sp, O_CATALOG);
if ((gp->msgdb = dbopen(p,
O_NONBLOCK | O_RDONLY, 444, DB_RECNO, NULL)) == NULL) {
if ((fmt = malloc(256)) == NULL)
return ("");
(void)snprintf(fmt,
"unable to open %s: %s", p, strerror(errno));
return (fmt);
}
}
msgno = atoi(s_msgno);
key.data = &msgno;
key.size = sizeof(recno_t);
switch (gp->msgdb->get(gp->msgdb, &key, &data, 0)) {
case 0:
return (data.data);
case 1:
p = "no catalog record %ls";
break;
case -1:
p = "catalog record %s: %s";
break;
}
if ((fmt = malloc(256)) == NULL)
return ("");
(void)snprintf(fmt, p, msgno, strerror(errno));
return (fmt);
}
#endif

82
usr.bin/vi/common/msg.h Normal file
View File

@ -0,0 +1,82 @@
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)msg.h 8.13 (Berkeley) 8/8/94
*/
/*
* Message types.
*
* !!!
* In historical vi, O_VERBOSE didn't exist, and O_TERSE made the error
* messages shorter. In this implementation, O_TERSE has no effect and
* O_VERBOSE results in informational displays about common errors for
* naive users.
*
* M_BERR Error: M_ERR if O_VERBOSE, else bell.
* M_ERR Error: Display in inverse video.
* M_INFO Info: Display in normal video.
* M_SYSERR Error: M_ERR, using strerror(3) message.
* M_VINFO Info: M_INFO if O_VERBOSE, else ignore.
*/
enum msgtype { M_BERR, M_ERR, M_INFO, M_SYSERR, M_VINFO };
typedef struct _msgh MSGH; /* MESG list head structure. */
LIST_HEAD(_msgh, _msg);
struct _msg {
LIST_ENTRY(_msg) q; /* Linked list of messages. */
char *mbuf; /* Message buffer. */
size_t blen; /* Message buffer length. */
size_t len; /* Message length. */
#define M_EMPTY 0x01 /* No message. */
#define M_INV_VIDEO 0x02 /* Inverse video. */
u_int8_t flags;
};
/*
* Define MSG_CATALOG for the Makefile compile command
* line to enable message catalogs.
*/
#ifdef MSG_CATALOG
#define M(number, fmt) number
char *get_msg __P((SCR *, char *));
#else
#define M(number, fmt) fmt
#endif
/* Messages. */
void msg_app __P((GS *, SCR *, int, char *, size_t));
int msg_rpt __P((SCR *, int));
int msg_status __P((SCR *, EXF *, recno_t, int));
void msgq __P((SCR *, enum msgtype, const char *, ...));

View File

@ -0,0 +1,9 @@
# @(#)options.awk 8.1 (Berkeley) 4/17/94
/^\/\* O_[0-9A-Z_]*/ {
printf("#define %s %d\n", $2, cnt++);
next;
}
END {
printf("#define O_OPTIONCOUNT %d\n", cnt);
}

890
usr.bin/vi/common/options.c Normal file
View File

@ -0,0 +1,890 @@
/*-
* Copyright (c) 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char sccsid[] = "@(#)options.c 8.65 (Berkeley) 8/17/94";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <bitstring.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "compat.h"
#include <db.h>
#include <regex.h>
#include <pathnames.h>
#include "vi.h"
#include "excmd.h"
static int opts_abbcmp __P((const void *, const void *));
static int opts_cmp __P((const void *, const void *));
static OPTLIST const *opts_prefix __P((char *));
static int opts_print __P((SCR *, OPTLIST const *));
/*
* O'Reilly noted options and abbreviations are from "Learning the VI Editor",
* Fifth Edition, May 1992. There's no way of knowing what systems they are
* actually from.
*
* HPUX noted options and abbreviations are from "The Ultimate Guide to the
* VI and EX Text Editors", 1990.
*/
static OPTLIST const optlist[] = {
/* O_ALTWERASE 4.4BSD */
{"altwerase", f_altwerase, OPT_0BOOL, 0},
/* O_AUTOINDENT 4BSD */
{"autoindent", NULL, OPT_0BOOL, 0},
/* O_AUTOPRINT 4BSD */
{"autoprint", NULL, OPT_1BOOL, 0},
/* O_AUTOWRITE 4BSD */
{"autowrite", NULL, OPT_0BOOL, 0},
/* O_BEAUTIFY 4BSD */
{"beautify", NULL, OPT_0BOOL, 0},
/* O_CDPATH 4.4BSD */
{"cdpath", f_cdpath, OPT_STR, 0},
/* O_COLUMNS 4.4BSD */
{"columns", f_columns, OPT_NUM, OPT_NOSAVE},
/* O_COMMENT 4.4BSD */
{"comment", NULL, OPT_0BOOL, 0},
/* O_DIGRAPH XXX: Elvis */
{"digraph", NULL, OPT_0BOOL, 0},
/* O_DIRECTORY 4BSD */
{"directory", NULL, OPT_STR, 0},
/* O_EDCOMPATIBLE 4BSD */
{"edcompatible",NULL, OPT_0BOOL, 0},
/* O_ERRORBELLS 4BSD */
{"errorbells", NULL, OPT_0BOOL, 0},
/* O_EXRC System V (undocumented) */
{"exrc", NULL, OPT_0BOOL, 0},
/* O_EXTENDED 4.4BSD */
{"extended", NULL, OPT_0BOOL, 0},
/* O_FLASH HPUX */
{"flash", NULL, OPT_1BOOL, 0},
/* O_HARDTABS 4BSD */
{"hardtabs", NULL, OPT_NUM, 0},
/* O_IGNORECASE 4BSD */
{"ignorecase", NULL, OPT_0BOOL, 0},
/* O_KEYTIME 4.4BSD */
{"keytime", NULL, OPT_NUM, 0},
/* O_LEFTRIGHT 4.4BSD */
{"leftright", f_leftright, OPT_0BOOL, 0},
/* O_LINES 4.4BSD */
{"lines", f_lines, OPT_NUM, OPT_NOSAVE},
/* O_LISP 4BSD */
/*
* XXX
* When the lisp option is implemented, delete
* the OPT_NOSAVE flag, so that :mkexrc dumps it.
*/
{"lisp", f_lisp, OPT_0BOOL, OPT_NOSAVE},
/* O_LIST 4BSD */
{"list", f_list, OPT_0BOOL, 0},
/* O_MAGIC 4BSD */
{"magic", NULL, OPT_1BOOL, 0},
/* O_MATCHTIME 4.4BSD */
{"matchtime", NULL, OPT_NUM, 0},
/* O_MESG 4BSD */
{"mesg", f_mesg, OPT_1BOOL, 0},
/* O_META 4.4BSD */
{"meta", NULL, OPT_STR, 0},
/* O_MODELINE 4BSD */
{"modeline", f_modeline, OPT_0BOOL, 0},
/* O_NUMBER 4BSD */
{"number", f_number, OPT_0BOOL, 0},
/* O_OCTAL 4.4BSD */
{"octal", f_octal, OPT_0BOOL, 0},
/* O_OPEN 4BSD */
{"open", NULL, OPT_1BOOL, 0},
/* O_OPTIMIZE 4BSD */
{"optimize", NULL, OPT_1BOOL, 0},
/* O_PARAGRAPHS 4BSD */
{"paragraphs", f_paragraph, OPT_STR, 0},
/* O_PROMPT 4BSD */
{"prompt", NULL, OPT_1BOOL, 0},
/* O_READONLY 4BSD (undocumented) */
{"readonly", f_readonly, OPT_0BOOL, 0},
/* O_RECDIR 4.4BSD */
{"recdir", NULL, OPT_STR, 0},
/* O_REDRAW 4BSD */
{"redraw", NULL, OPT_0BOOL, 0},
/* O_REMAP 4BSD */
{"remap", NULL, OPT_1BOOL, 0},
/* O_REPORT 4BSD */
{"report", NULL, OPT_NUM, OPT_NOSTR},
/* O_RULER 4.4BSD */
{"ruler", NULL, OPT_0BOOL, 0},
/* O_SCROLL 4BSD */
{"scroll", NULL, OPT_NUM, 0},
/* O_SECTIONS 4BSD */
{"sections", f_section, OPT_STR, 0},
/* O_SHELL 4BSD */
{"shell", NULL, OPT_STR, 0},
/* O_SHIFTWIDTH 4BSD */
{"shiftwidth", f_shiftwidth, OPT_NUM, 0},
/* O_SHOWDIRTY 4.4BSD */
{"showdirty", NULL, OPT_0BOOL, 0},
/* O_SHOWMATCH 4BSD */
{"showmatch", NULL, OPT_0BOOL, 0},
/* O_SHOWMODE 4.4BSD */
{"showmode", NULL, OPT_0BOOL, 0},
/* O_SIDESCROLL 4.4BSD */
{"sidescroll", NULL, OPT_NUM, 0},
/* O_SLOWOPEN 4BSD */
{"slowopen", NULL, OPT_0BOOL, 0},
/* O_SOURCEANY 4BSD (undocumented) */
{"sourceany", f_sourceany, OPT_0BOOL, 0},
/* O_TABSTOP 4BSD */
{"tabstop", f_tabstop, OPT_NUM, 0},
/* O_TAGLENGTH 4BSD */
{"taglength", NULL, OPT_NUM, OPT_NOSTR},
/* O_TAGS 4BSD */
{"tags", f_tags, OPT_STR, 0},
/* O_TERM 4BSD */
{"term", f_term, OPT_STR, OPT_NOSAVE},
/* O_TERSE 4BSD */
{"terse", NULL, OPT_0BOOL, 0},
/* O_TILDEOP 4.4BSD */
{"tildeop", NULL, OPT_0BOOL, 0},
/* O_TIMEOUT 4BSD (undocumented) */
{"timeout", NULL, OPT_1BOOL, 0},
/* O_TTYWERASE 4.4BSD */
{"ttywerase", f_ttywerase, OPT_0BOOL, 0},
/* O_VERBOSE 4.4BSD */
{"verbose", NULL, OPT_0BOOL, 0},
/* O_W1200 4BSD */
{"w1200", f_w1200, OPT_NUM, OPT_NEVER|OPT_NOSAVE},
/* O_W300 4BSD */
{"w300", f_w300, OPT_NUM, OPT_NEVER|OPT_NOSAVE},
/* O_W9600 4BSD */
{"w9600", f_w9600, OPT_NUM, OPT_NEVER|OPT_NOSAVE},
/* O_WARN 4BSD */
{"warn", NULL, OPT_1BOOL, 0},
/* O_WINDOW 4BSD */
{"window", f_window, OPT_NUM, 0},
/* O_WRAPMARGIN 4BSD */
{"wrapmargin", NULL, OPT_NUM, OPT_NOSTR},
/* O_WRAPSCAN 4BSD */
{"wrapscan", NULL, OPT_1BOOL, 0},
/* O_WRITEANY 4BSD */
{"writeany", NULL, OPT_0BOOL, 0},
{NULL},
};
typedef struct abbrev {
char *name;
int offset;
} OABBREV;
static OABBREV const abbrev[] = {
{"ai", O_AUTOINDENT}, /* 4BSD */
{"ap", O_AUTOPRINT}, /* 4BSD */
{"aw", O_AUTOWRITE}, /* 4BSD */
{"bf", O_BEAUTIFY}, /* 4BSD */
{"co", O_COLUMNS}, /* 4.4BSD */
{"dir", O_DIRECTORY}, /* 4BSD */
{"eb", O_ERRORBELLS}, /* 4BSD */
{"ed", O_EDCOMPATIBLE}, /* 4BSD */
{"ex", O_EXRC}, /* System V (undocumented) */
{"ht", O_HARDTABS}, /* 4BSD */
{"ic", O_IGNORECASE}, /* 4BSD */
{"li", O_LINES}, /* 4.4BSD */
{"modelines", O_MODELINE}, /* HPUX */
{"nu", O_NUMBER}, /* 4BSD */
{"opt", O_OPTIMIZE}, /* 4BSD */
{"para", O_PARAGRAPHS}, /* 4BSD */
{"re", O_REDRAW}, /* O'Reilly */
{"ro", O_READONLY}, /* 4BSD (undocumented) */
{"scr", O_SCROLL}, /* 4BSD (undocumented) */
{"sect", O_SECTIONS}, /* O'Reilly */
{"sh", O_SHELL}, /* 4BSD */
{"slow", O_SLOWOPEN}, /* 4BSD */
{"sm", O_SHOWMATCH}, /* 4BSD */
{"sw", O_SHIFTWIDTH}, /* 4BSD */
{"tag", O_TAGS}, /* 4BSD (undocumented) */
{"tl", O_TAGLENGTH}, /* 4BSD */
{"to", O_TIMEOUT}, /* 4BSD (undocumented) */
{"ts", O_TABSTOP}, /* 4BSD */
{"tty", O_TERM}, /* 4BSD (undocumented) */
{"ttytype", O_TERM}, /* 4BSD (undocumented) */
{"w", O_WINDOW}, /* O'Reilly */
{"wa", O_WRITEANY}, /* 4BSD */
{"wi", O_WINDOW}, /* 4BSD (undocumented) */
{"wm", O_WRAPMARGIN}, /* 4BSD */
{"ws", O_WRAPSCAN}, /* 4BSD */
{NULL},
};
/*
* opts_init --
* Initialize some of the options. Since the user isn't really
* "setting" these variables, don't set their OPT_SET bits.
*/
int
opts_init(sp)
SCR *sp;
{
ARGS *argv[2], a, b;
OPTLIST const *op;
u_long v;
int cnt;
char *s, b1[1024];
a.bp = b1;
a.len = 0;
b.bp = NULL;
b.len = 0;
argv[0] = &a;
argv[1] = &b;
#define SET_DEF(opt, str) { \
if (str != b1) /* GCC puts strings in text-space. */ \
(void)strcpy(b1, str); \
a.len = strlen(b1); \
if (opts_set(sp, NULL, argv)) { \
msgq(sp, M_ERR, \
"Unable to set default %s option", optlist[opt]); \
return (1); \
} \
F_CLR(&sp->opts[opt], OPT_SET); \
}
/* Set default values. */
for (op = optlist, cnt = 0; op->name != NULL; ++op, ++cnt)
if (op->type == OPT_0BOOL)
O_CLR(sp, cnt);
else if (op->type == OPT_1BOOL)
O_SET(sp, cnt);
(void)snprintf(b1, sizeof(b1), "cdpath=%s",
(s = getenv("CDPATH")) == NULL ? ":" : s);
SET_DEF(O_CDPATH, b1);
/*
* !!!
* Vi historically stored temporary files in /var/tmp. We store them
* in /tmp by default, hoping it's a memory based file system. There
* are two ways to change this -- the user can set either the directory
* option or the TMPDIR environmental variable.
*/
(void)snprintf(b1, sizeof(b1), "directory=%s",
(s = getenv("TMPDIR")) == NULL ? _PATH_TMP : s);
SET_DEF(O_DIRECTORY, b1);
SET_DEF(O_KEYTIME, "keytime=6");
SET_DEF(O_MATCHTIME, "matchtime=7");
SET_DEF(O_META, "meta=~{[*?$`'\"\\");
SET_DEF(O_REPORT, "report=5");
SET_DEF(O_PARAGRAPHS, "paragraphs=IPLPPPQPP LIpplpipbp");
(void)snprintf(b1, sizeof(b1), "recdir=%s", _PATH_PRESERVE);
SET_DEF(O_RECDIR, b1);
SET_DEF(O_SECTIONS, "sections=NHSHH HUnhsh");
(void)snprintf(b1, sizeof(b1), "shell=%s",
(s = getenv("SHELL")) == NULL ? _PATH_BSHELL : s);
SET_DEF(O_SHELL, b1);
SET_DEF(O_SHIFTWIDTH, "shiftwidth=8");
SET_DEF(O_SIDESCROLL, "sidescroll=16");
SET_DEF(O_TABSTOP, "tabstop=8");
(void)snprintf(b1, sizeof(b1), "tags=%s", _PATH_TAGS);
SET_DEF(O_TAGS, b1);
(void)snprintf(b1, sizeof(b1), "term=%s",
(s = getenv("TERM")) == NULL ? "unknown" : s);
SET_DEF(O_TERM, b1);
/*
* XXX
* Initialize ^D, ^U scrolling value here, after TERM. (We didn't
* have the options information when the screen was initialized.)
* Initializing term should have created a LINES/COLUMNS value.
*/
sp->defscroll = O_VAL(sp, O_LINES) / 2;
(void)snprintf(b1, sizeof(b1), "scroll=%ld", sp->defscroll);
SET_DEF(O_SCROLL, b1);
/*
* The default window option values are:
* 8 if baud rate <= 600
* 16 if baud rate <= 1200
* LINES - 1 if baud rate > 1200
*/
v = baud_from_bval(sp);
if (v <= 600)
v = 8;
else if (v <= 1200)
v = 16;
else
v = O_VAL(sp, O_LINES) - 1;
(void)snprintf(b1, sizeof(b1), "window=%lu", v);
SET_DEF(O_WINDOW, b1);
SET_DEF(O_WRAPMARGIN, "wrapmargin=0");
/*
* By default, the historic vi always displayed information
* about two options, redraw and term. Term seems sufficient.
*/
F_SET(&sp->opts[O_TERM], OPT_SET);
return (0);
}
/*
* opts_set --
* Change the values of one or more options.
*/
int
opts_set(sp, usage, argv)
SCR *sp;
char *usage;
ARGS *argv[];
{
enum optdisp disp;
OABBREV atmp, *ap;
OPTLIST const *op;
OPTLIST otmp;
OPTION *spo;
u_long value, turnoff;
int ch, equals, offset, qmark, rval;
char *endp, *name, *p, *sep;
disp = NO_DISPLAY;
for (rval = 0; argv[0]->len != 0; ++argv) {
/*
* The historic vi dumped the options for each occurrence of
* "all" in the set list. Puhleeze.
*/
if (!strcmp(argv[0]->bp, "all")) {
disp = ALL_DISPLAY;
continue;
}
/* Find equals sign or question mark. */
for (sep = NULL, equals = qmark = 0,
p = name = argv[0]->bp; (ch = *p) != '\0'; ++p)
if (ch == '=' || ch == '?') {
if (p == name) {
if (usage != NULL)
msgq(sp,
M_ERR, "Usage: %s", usage);
return (1);
}
sep = p;
if (ch == '=')
equals = 1;
else
qmark = 1;
break;
}
turnoff = 0;
op = NULL;
if (sep != NULL)
*sep++ = '\0';
/* Check list of abbreviations. */
atmp.name = name;
if ((ap = bsearch(&atmp, abbrev,
sizeof(abbrev) / sizeof(OABBREV) - 1,
sizeof(OABBREV), opts_abbcmp)) != NULL) {
op = optlist + ap->offset;
goto found;
}
/* Check list of options. */
otmp.name = name;
if ((op = bsearch(&otmp, optlist,
sizeof(optlist) / sizeof(OPTLIST) - 1,
sizeof(OPTLIST), opts_cmp)) != NULL)
goto found;
/* Try the name without any leading "no". */
if (name[0] == 'n' && name[1] == 'o') {
turnoff = 1;
name += 2;
} else
goto prefix;
/* Check list of abbreviations. */
atmp.name = name;
if ((ap = bsearch(&atmp, abbrev,
sizeof(abbrev) / sizeof(OABBREV) - 1,
sizeof(OABBREV), opts_abbcmp)) != NULL) {
op = optlist + ap->offset;
goto found;
}
/* Check list of options. */
otmp.name = name;
if ((op = bsearch(&otmp, optlist,
sizeof(optlist) / sizeof(OPTLIST) - 1,
sizeof(OPTLIST), opts_cmp)) != NULL)
goto found;
/* Check for prefix match. */
prefix: op = opts_prefix(name);
found: if (op == NULL) {
msgq(sp, M_ERR,
"no %s option: 'set all' gives all option values",
name);
continue;
}
/* Find current option values. */
offset = op - optlist;
spo = sp->opts + offset;
/*
* !!!
* Historically, the question mark could be a separate
* argument.
*/
if (!equals && !qmark &&
argv[1]->len == 1 && argv[1]->bp[0] == '?') {
++argv;
qmark = 1;
}
/* Set name, value. */
switch (op->type) {
case OPT_0BOOL:
case OPT_1BOOL:
if (equals) {
msgq(sp, M_ERR,
"set: [no]%s option doesn't take a value",
name);
break;
}
if (qmark) {
if (!disp)
disp = SELECT_DISPLAY;
F_SET(spo, OPT_SELECTED);
break;
}
if (op->func != NULL) {
if (op->func(sp, spo, NULL, turnoff)) {
rval = 1;
break;
}
} else if (turnoff)
O_CLR(sp, offset);
else
O_SET(sp, offset);
goto change;
case OPT_NUM:
/*
* !!!
* Extension to historic vi. If the OPT_NOSTR flag is
* set, a numeric option may be turned off by using a
* "no" prefix, e.g. "nowrapmargin". (We assume that
* setting the value to 0 turns a numeric option off.)
*/
if (turnoff) {
if (F_ISSET(op, OPT_NOSTR)) {
value = 0;
goto nostr;
}
msgq(sp, M_ERR,
"set: %s option isn't a boolean", name);
break;
}
if (qmark || !equals) {
if (!disp)
disp = SELECT_DISPLAY;
F_SET(spo, OPT_SELECTED);
break;
}
value = strtol(sep, &endp, 10);
if (*endp && !isblank(*endp)) {
msgq(sp, M_ERR,
"set %s: illegal number %s", name, sep);
break;
}
nostr: if (op->func != NULL) {
if (op->func(sp, spo, sep, value)) {
rval = 1;
break;
}
} else
O_VAL(sp, offset) = value;
goto change;
case OPT_STR:
if (turnoff) {
msgq(sp, M_ERR,
"set: %s option isn't a boolean", name);
break;
}
if (qmark || !equals) {
if (!disp)
disp = SELECT_DISPLAY;
F_SET(spo, OPT_SELECTED);
break;
}
if (op->func != NULL) {
if (op->func(sp, spo, sep, (u_long)0)) {
rval = 1;
break;
}
} else {
if (F_ISSET(&sp->opts[offset], OPT_ALLOCATED))
free(O_STR(sp, offset));
if ((O_STR(sp, offset) = strdup(sep)) == NULL) {
msgq(sp, M_SYSERR, NULL);
rval = 1;
break;
} else
F_SET(&sp->opts[offset], OPT_ALLOCATED);
}
change: if (sp->s_optchange != NULL)
(void)sp->s_optchange(sp, offset);
F_SET(&sp->opts[offset], OPT_SET);
break;
default:
abort();
}
}
if (disp != NO_DISPLAY)
opts_dump(sp, disp);
return (rval);
}
/*
* opts_dump --
* List the current values of selected options.
*/
void
opts_dump(sp, type)
SCR *sp;
enum optdisp type;
{
OPTLIST const *op;
int base, b_num, cnt, col, colwidth, curlen, s_num;
int numcols, numrows, row;
int b_op[O_OPTIONCOUNT], s_op[O_OPTIONCOUNT];
char nbuf[20];
/*
* XXX
* It's possible to get here by putting "set option" in the
* .exrc file. I can't think of a clean way to layer this,
* or a reasonable check to make, so we block it here.
*/
if (sp->stdfp == NULL) {
msgq(sp, M_ERR,
"Option display requires that the screen be initialized");
return;
}
/*
* Options are output in two groups -- those that fit in a column and
* those that don't. Output is done on 6 character "tab" boundaries
* for no particular reason. (Since we don't output tab characters,
* we can ignore the terminal's tab settings.) Ignore the user's tab
* setting because we have no idea how reasonable it is.
*
* Find a column width we can live with.
*/
for (cnt = 6; cnt > 1; --cnt) {
colwidth = (sp->cols - 1) / cnt & ~(STANDARD_TAB - 1);
if (colwidth >= 10) {
colwidth =
(colwidth + STANDARD_TAB) & ~(STANDARD_TAB - 1);
break;
}
colwidth = 0;
}
/*
* Get the set of options to list, entering them into
* the column list or the overflow list.
*/
for (b_num = s_num = 0, op = optlist; op->name != NULL; ++op) {
cnt = op - optlist;
/* If OPT_NEVER set, it's never displayed. */
if (F_ISSET(op, OPT_NEVER))
continue;
switch (type) {
case ALL_DISPLAY: /* Display all. */
break;
case CHANGED_DISPLAY: /* Display changed. */
if (!F_ISSET(&sp->opts[cnt], OPT_SET))
continue;
break;
case SELECT_DISPLAY: /* Display selected. */
if (!F_ISSET(&sp->opts[cnt], OPT_SELECTED))
continue;
break;
default:
case NO_DISPLAY:
abort();
/* NOTREACHED */
}
F_CLR(&sp->opts[cnt], OPT_SELECTED);
curlen = strlen(op->name);
switch (op->type) {
case OPT_0BOOL:
case OPT_1BOOL:
if (!O_ISSET(sp, cnt))
curlen += 2;
break;
case OPT_NUM:
(void)snprintf(nbuf,
sizeof(nbuf), "%ld", O_VAL(sp, cnt));
curlen += strlen(nbuf);
break;
case OPT_STR:
curlen += strlen(O_STR(sp, cnt)) + 3;
break;
}
/* Offset by two so there's a gap. */
if (curlen < colwidth - 2)
s_op[s_num++] = cnt;
else
b_op[b_num++] = cnt;
}
if (s_num > 0) {
/* Figure out the number of columns. */
numcols = (sp->cols - 1) / colwidth;
if (s_num > numcols) {
numrows = s_num / numcols;
if (s_num % numcols)
++numrows;
} else
numrows = 1;
/* Display the options in sorted order. */
for (row = 0; row < numrows;) {
for (base = row, col = 0; col < numcols; ++col) {
cnt = opts_print(sp, &optlist[s_op[base]]);
if ((base += numrows) >= s_num)
break;
(void)ex_printf(EXCOOKIE,
"%*s", (int)(colwidth - cnt), "");
}
if (++row < numrows || b_num)
(void)ex_printf(EXCOOKIE, "\n");
}
}
for (row = 0; row < b_num;) {
(void)opts_print(sp, &optlist[b_op[row]]);
if (++row < b_num)
(void)ex_printf(EXCOOKIE, "\n");
}
(void)ex_printf(EXCOOKIE, "\n");
}
/*
* opts_print --
* Print out an option.
*/
static int
opts_print(sp, op)
SCR *sp;
OPTLIST const *op;
{
int curlen, offset;
curlen = 0;
offset = op - optlist;
switch (op->type) {
case OPT_0BOOL:
case OPT_1BOOL:
curlen += ex_printf(EXCOOKIE,
"%s%s", O_ISSET(sp, offset) ? "" : "no", op->name);
break;
case OPT_NUM:
curlen += ex_printf(EXCOOKIE,
"%s=%ld", op->name, O_VAL(sp, offset));
break;
case OPT_STR:
curlen += ex_printf(EXCOOKIE,
"%s=\"%s\"", op->name, O_STR(sp, offset));
break;
}
return (curlen);
}
/*
* opts_save --
* Write the current configuration to a file.
*/
int
opts_save(sp, fp)
SCR *sp;
FILE *fp;
{
OPTLIST const *op;
int ch, cnt;
char *p;
for (op = optlist; op->name != NULL; ++op) {
if (F_ISSET(op, OPT_NOSAVE))
continue;
cnt = op - optlist;
switch (op->type) {
case OPT_0BOOL:
case OPT_1BOOL:
if (O_ISSET(sp, cnt))
(void)fprintf(fp, "set %s\n", op->name);
else
(void)fprintf(fp, "set no%s\n", op->name);
break;
case OPT_NUM:
(void)fprintf(fp,
"set %s=%-3d\n", op->name, O_VAL(sp, cnt));
break;
case OPT_STR:
(void)fprintf(fp, "set ");
for (p = op->name; (ch = *p) != '\0'; ++p) {
if (isblank(ch) || ch == '\\')
(void)putc('\\', fp);
(void)putc(ch, fp);
}
(void)putc('=', fp);
for (p = O_STR(sp, cnt); (ch = *p) != '\0'; ++p) {
if (isblank(ch) || ch == '\\')
(void)putc('\\', fp);
(void)putc(ch, fp);
}
(void)putc('\n', fp);
break;
}
if (ferror(fp)) {
msgq(sp, M_ERR, "I/O error: %s", strerror(errno));
return (1);
}
}
return (0);
}
/*
* opts_prefix --
* Check to see if the name is the prefix of one (and only one)
* option. If so, return the option.
*/
static OPTLIST const *
opts_prefix(name)
char *name;
{
OPTLIST const *op, *save_op;
size_t len;
save_op = NULL;
len = strlen(name);
for (op = optlist; op->name != NULL; ++op) {
if (op->name[0] < name[0])
continue;
if (op->name[0] > name[0])
break;
if (!memcmp(op->name, name, len)) {
if (save_op != NULL)
return (NULL);
save_op = op;
}
}
return (save_op);
}
static int
opts_abbcmp(a, b)
const void *a, *b;
{
return(strcmp(((OABBREV *)a)->name, ((OABBREV *)b)->name));
}
static int
opts_cmp(a, b)
const void *a, *b;
{
return(strcmp(((OPTLIST *)a)->name, ((OPTLIST *)b)->name));
}
/*
* opts_free --
* Free all option strings
*/
void
opts_free(sp)
SCR *sp;
{
int cnt;
char *p;
for (cnt = 0; cnt < O_OPTIONCOUNT; ++cnt)
if (F_ISSET(&sp->opts[cnt], OPT_ALLOCATED)) {
p = O_STR(sp, cnt);
FREE(p, strlen(p) + 1);
}
}
/*
* opts_copy --
* Copy a screen's OPTION array.
*/
int
opts_copy(orig, sp)
SCR *orig, *sp;
{
OPTION *op;
int cnt;
/* Copy most everything without change. */
memmove(sp->opts, orig->opts, sizeof(orig->opts));
/*
* Allocate copies of the strings -- keep trying to reallocate
* after ENOMEM failure, otherwise end up with more than one
* screen referencing the original memory.
*/
for (op = sp->opts, cnt = 0; cnt < O_OPTIONCOUNT; ++cnt, ++op)
if (F_ISSET(&sp->opts[cnt], OPT_ALLOCATED) &&
(O_STR(sp, cnt) = strdup(O_STR(sp, cnt))) == NULL) {
msgq(orig, M_SYSERR, NULL);
return (1);
}
return (0);
}

View File

@ -0,0 +1,108 @@
/*-
* Copyright (c) 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)options.h.stub 8.22 (Berkeley) 8/8/94
*/
struct _option {
union {
u_long val; /* Value or boolean. */
char *str; /* String. */
} o_u;
size_t len; /* String length. */
#define OPT_ALLOCATED 0x01 /* Allocated space. */
#define OPT_SELECTED 0x02 /* Selected for display. */
#define OPT_SET 0x04 /* Set (display for the user). */
u_char flags;
};
struct _optlist {
char *name; /* Name. */
/* Change function. */
int (*func) __P((SCR *, OPTION *, char *, u_long));
/* Type of object. */
enum { OPT_0BOOL, OPT_1BOOL, OPT_NUM, OPT_STR } type;
#define OPT_NEVER 0x01 /* Never display the option. */
#define OPT_NOSAVE 0x02 /* Mkexrc command doesn't save. */
#define OPT_NOSTR 0x04 /* String that takes a "no". */
u_int flags;
};
/* Clear, set, test boolean options. */
#define O_SET(sp, o) (sp)->opts[(o)].o_u.val = 1
#define O_CLR(sp, o) (sp)->opts[(o)].o_u.val = 0
#define O_ISSET(sp, o) ((sp)->opts[(o)].o_u.val)
/* Get option values. */
#define O_LEN(sp, o) (sp)->opts[(o)].len
#define O_STR(sp, o) (sp)->opts[(o)].o_u.str
#define O_VAL(sp, o) (sp)->opts[(o)].o_u.val
/* Option routines. */
u_long baud_from_bval __P((SCR *));
int opts_copy __P((SCR *, SCR *));
void opts_free __P((SCR *));
int opts_init __P((SCR *));
int opts_save __P((SCR *, FILE *));
int opts_set __P((SCR *, char *, ARGS *[]));
enum optdisp { NO_DISPLAY, ALL_DISPLAY, CHANGED_DISPLAY, SELECT_DISPLAY };
void opts_dump __P((SCR *, enum optdisp));
/* Per-option change routines. */
int f_altwerase __P((SCR *, OPTION *, char *, u_long));
int f_cdpath __P((SCR *, OPTION *, char *, u_long));
int f_columns __P((SCR *, OPTION *, char *, u_long));
int f_leftright __P((SCR *, OPTION *, char *, u_long));
int f_lines __P((SCR *, OPTION *, char *, u_long));
int f_lisp __P((SCR *, OPTION *, char *, u_long));
int f_list __P((SCR *, OPTION *, char *, u_long));
int f_mesg __P((SCR *, OPTION *, char *, u_long));
int f_modeline __P((SCR *, OPTION *, char *, u_long));
int f_number __P((SCR *, OPTION *, char *, u_long));
int f_octal __P((SCR *, OPTION *, char *, u_long));
int f_paragraph __P((SCR *, OPTION *, char *, u_long));
int f_readonly __P((SCR *, OPTION *, char *, u_long));
int f_section __P((SCR *, OPTION *, char *, u_long));
int f_shiftwidth __P((SCR *, OPTION *, char *, u_long));
int f_sourceany __P((SCR *, OPTION *, char *, u_long));
int f_tabstop __P((SCR *, OPTION *, char *, u_long));
int f_tags __P((SCR *, OPTION *, char *, u_long));
int f_term __P((SCR *, OPTION *, char *, u_long));
int f_ttywerase __P((SCR *, OPTION *, char *, u_long));
int f_w1200 __P((SCR *, OPTION *, char *, u_long));
int f_w300 __P((SCR *, OPTION *, char *, u_long));
int f_w9600 __P((SCR *, OPTION *, char *, u_long));
int f_window __P((SCR *, OPTION *, char *, u_long));

View File

@ -0,0 +1,518 @@
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char sccsid[] = "@(#)options_f.c 8.35 (Berkeley) 8/17/94";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <bitstring.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "compat.h"
#include <db.h>
#include <regex.h>
#include "vi.h"
#include "../ex/tag.h"
static int opt_dup __P((SCR *, int, char *));
static int opt_putenv __P((char *));
#define DECL(f) \
int \
f(sp, op, str, val) \
SCR *sp; \
OPTION *op; \
char *str; \
u_long val;
#define CALL(f) \
f(sp, op, str, val)
#define turnoff val
DECL(f_altwerase)
{
if (turnoff)
O_CLR(sp, O_ALTWERASE);
else {
O_SET(sp, O_ALTWERASE);
O_CLR(sp, O_TTYWERASE);
}
return (0);
}
DECL(f_cdpath)
{
return (opt_dup(sp, O_CDPATH, str));
}
DECL(f_columns)
{
char buf[25];
/* Validate the number. */
if (val < MINIMUM_SCREEN_COLS) {
msgq(sp, M_ERR, "Screen columns too small, less than %d",
MINIMUM_SCREEN_COLS);
return (1);
}
/* Set the columns value in the environment for curses. */
(void)snprintf(buf, sizeof(buf), "COLUMNS=%lu", val);
if (opt_putenv(buf))
return (1);
/* This is expensive, don't do it unless it's necessary. */
if (O_VAL(sp, O_COLUMNS) == val)
return (0);
/* Set the value. */
O_VAL(sp, O_COLUMNS) = val;
F_SET(sp, S_RESIZE);
return (0);
}
DECL(f_leftright)
{
if (turnoff)
O_CLR(sp, O_LEFTRIGHT);
else
O_SET(sp, O_LEFTRIGHT);
F_SET(sp, S_REFORMAT | S_REDRAW);
return (0);
}
DECL(f_lines)
{
char buf[25];
/* Validate the number. */
if (val < MINIMUM_SCREEN_ROWS) {
msgq(sp, M_ERR, "Screen lines too small, less than %d",
MINIMUM_SCREEN_ROWS);
return (1);
}
/* Set the rows value in the environment for curses. */
(void)snprintf(buf, sizeof(buf), "ROWS=%lu", val);
if (opt_putenv(buf))
return (1);
/* This is expensive, don't do it unless it's necessary. */
if (O_VAL(sp, O_LINES) == val)
return (0);
/* Set the value. */
O_VAL(sp, O_LINES) = val;
/*
* If no window value set, set a new default window and,
* optionally, a new scroll value.
*/
if (!F_ISSET(&sp->opts[O_WINDOW], OPT_SET)) {
O_VAL(sp, O_WINDOW) = val - 1;
if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET))
O_VAL(sp, O_SCROLL) = val / 2;
}
F_SET(sp, S_RESIZE);
return (0);
}
DECL(f_lisp)
{
msgq(sp, M_ERR, "The lisp option is not implemented");
return (0);
}
DECL(f_list)
{
if (turnoff)
O_CLR(sp, O_LIST);
else
O_SET(sp, O_LIST);
F_SET(sp, S_REFORMAT | S_REDRAW);
return (0);
}
DECL(f_mesg)
{
struct stat sb;
char *tty;
/* Find the tty. */
if ((tty = ttyname(STDERR_FILENO)) == NULL) {
msgq(sp, M_ERR, "ttyname: %s", strerror(errno));
return (1);
}
/* Save the tty mode for later; only save it once. */
if (!F_ISSET(sp->gp, G_SETMODE)) {
F_SET(sp->gp, G_SETMODE);
if (stat(tty, &sb) < 0) {
msgq(sp, M_ERR, "%s: %s", tty, strerror(errno));
return (1);
}
sp->gp->origmode = sb.st_mode;
}
if (turnoff) {
if (chmod(tty, sp->gp->origmode & ~S_IWGRP) < 0) {
msgq(sp, M_ERR, "messages not turned off: %s: %s",
tty, strerror(errno));
return (1);
}
O_CLR(sp, O_MESG);
} else {
if (chmod(tty, sp->gp->origmode | S_IWGRP) < 0) {
msgq(sp, M_ERR, "messages not turned on: %s: %s",
tty, strerror(errno));
return (1);
}
O_SET(sp, O_MESG);
}
return (0);
}
/*
* f_modeline --
* This has been documented in historical systems as both "modeline"
* and as "modelines". Regardless of the name, this option represents
* a security problem of mammoth proportions, not to mention a stunning
* example of what your intro CS professor referred to as the perils of
* mixing code and data. Don't add it, or I will kill you.
*/
DECL(f_modeline)
{
if (!turnoff)
msgq(sp, M_ERR, "The modeline(s) option may never be set");
return (0);
}
DECL(f_number)
{
if (turnoff)
O_CLR(sp, O_NUMBER);
else
O_SET(sp, O_NUMBER);
F_SET(sp, S_REFORMAT | S_REDRAW);
return (0);
}
DECL(f_octal)
{
if (turnoff)
O_CLR(sp, O_OCTAL);
else
O_SET(sp, O_OCTAL);
key_init(sp);
F_SET(sp, S_REFORMAT | S_REDRAW);
return (0);
}
DECL(f_paragraph)
{
if (strlen(str) & 1) {
msgq(sp, M_ERR,
"Paragraph options must be in sets of two characters");
return (1);
}
return (opt_dup(sp, O_PARAGRAPHS, str));
}
DECL(f_readonly)
{
if (turnoff) {
O_CLR(sp, O_READONLY);
if (sp->frp != NULL)
F_CLR(sp->frp, FR_RDONLY);
} else {
O_SET(sp, O_READONLY);
if (sp->frp != NULL)
F_SET(sp->frp, FR_RDONLY);
}
return (0);
}
DECL(f_section)
{
if (strlen(str) & 1) {
msgq(sp, M_ERR,
"Section options must be in sets of two characters");
return (1);
}
return (opt_dup(sp, O_SECTIONS, str));
}
DECL(f_shiftwidth)
{
if (val == 0) {
msgq(sp, M_ERR, "The shiftwidth can't be set to 0");
return (1);
}
O_VAL(sp, O_SHIFTWIDTH) = val;
return (0);
}
/*
* f_sourceany --
* Historic vi, on startup, source'd $HOME/.exrc and ./.exrc, if they
* were owned by the user. The sourceany option was an undocumented
* feature of historic vi which permitted the startup source'ing of
* .exrc files the user didn't own. This is an obvious security problem,
* and we ignore the option.
*/
DECL(f_sourceany)
{
if (!turnoff)
msgq(sp, M_ERR, "The sourceany option may never be set");
return (0);
}
DECL(f_tabstop)
{
if (val == 0) {
msgq(sp, M_ERR, "Tab stops can't be set to 0");
return (1);
}
#define MAXTABSTOP 20
if (val > MAXTABSTOP) {
msgq(sp, M_ERR,
"Tab stops can't be larger than %d", MAXTABSTOP);
return (1);
}
O_VAL(sp, O_TABSTOP) = val;
F_SET(sp, S_REFORMAT | S_REDRAW);
return (0);
}
DECL(f_tags)
{
return (opt_dup(sp, O_TAGS, str));
}
DECL(f_term)
{
char buf[256];
if (opt_dup(sp, O_TERM, str))
return (1);
/* Set the terminal value in the environment for curses. */
(void)snprintf(buf, sizeof(buf), "TERM=%s", str);
if (opt_putenv(buf))
return (1);
return (0);
}
DECL(f_ttywerase)
{
if (turnoff)
O_CLR(sp, O_TTYWERASE);
else {
O_SET(sp, O_TTYWERASE);
O_CLR(sp, O_ALTWERASE);
}
return (0);
}
DECL(f_w300)
{
/* Historical behavior for w300 was < 1200. */
if (baud_from_bval(sp) >= 1200)
return (0);
if (CALL(f_window))
return (1);
if (val > O_VAL(sp, O_LINES) - 1)
val = O_VAL(sp, O_LINES) - 1;
O_VAL(sp, O_W300) = val;
return (0);
}
DECL(f_w1200)
{
u_long v;
/* Historical behavior for w1200 was == 1200. */
v = baud_from_bval(sp);
if (v < 1200 || v > 4800)
return (0);
if (CALL(f_window))
return (1);
if (val > O_VAL(sp, O_LINES) - 1)
val = O_VAL(sp, O_LINES) - 1;
O_VAL(sp, O_W1200) = val;
return (0);
}
DECL(f_w9600)
{
speed_t v;
/* Historical behavior for w9600 was > 1200. */
v = baud_from_bval(sp);
if (v <= 4800)
return (0);
if (CALL(f_window))
return (1);
if (val > O_VAL(sp, O_LINES) - 1)
val = O_VAL(sp, O_LINES) - 1;
O_VAL(sp, O_W9600) = val;
return (0);
}
DECL(f_window)
{
if (val < MINIMUM_SCREEN_ROWS) {
msgq(sp, M_ERR, "Window too small, less than %d",
MINIMUM_SCREEN_ROWS);
return (1);
}
if (val > O_VAL(sp, O_LINES) - 1)
val = O_VAL(sp, O_LINES) - 1;
O_VAL(sp, O_WINDOW) = val;
O_VAL(sp, O_SCROLL) = val / 2;
return (0);
}
/*
* opt_dup --
* Copy a string value for user display.
*/
static int
opt_dup(sp, opt, str)
SCR *sp;
int opt;
char *str;
{
char *p;
/* Copy for user display. */
if ((p = strdup(str)) == NULL) {
msgq(sp, M_SYSERR, NULL);
return (1);
}
/* Free the old contents. */
if (F_ISSET(&sp->opts[opt], OPT_ALLOCATED))
free(O_STR(sp, opt));
/* Note that it's set and allocated. */
F_SET(&sp->opts[opt], OPT_ALLOCATED | OPT_SET);
/* Assign new contents. */
O_STR(sp, opt) = p;
return (0);
}
/*
* opt_putenv --
* Put a value into the environment. We use putenv(3) because it's
* more portable. The following hack is because some moron decided
* to keep a reference to the memory passed to putenv(3), instead of
* having it allocate its own. Someone clearly needs to get promoted
* into management.
*/
static int
opt_putenv(s)
char *s;
{
char *t;
/*
* XXX
* Memory leak.
*/
if ((t = strdup(s)) == NULL)
return (1);
return (putenv(t));
}
/*
* baud_from_bval --
* Return the baud rate using the standard defines.
*/
u_long
baud_from_bval(sp)
SCR *sp;
{
if (!F_ISSET(sp->gp, G_TERMIOS_SET))
return (9600);
/*
* XXX
* There's no portable way to get a "baud rate" -- cfgetospeed(3)
* returns the value associated with some #define, which we may
* never have heard of, or which may be a purely local speed. Vi
* only cares if it's SLOW (w300), slow (w1200) or fast (w9600).
* Try and detect the slow ones, and default to fast.
*/
switch (cfgetospeed(&sp->gp->original_termios)) {
case B50:
case B75:
case B110:
case B134:
case B150:
case B200:
case B300:
case B600:
return (600);
case B1200:
return (1200);
}
return (9600);
}

View File

@ -0,0 +1,45 @@
/*-
* Copyright (c) 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)pathnames.h 8.7 (Berkeley) 3/28/94
*/
#define _PATH_BSHELL "/bin/sh"
#define _PATH_DEVNULL "/dev/null"
#define _PATH_EXRC ".exrc"
#define _PATH_NEXRC ".nexrc"
#define _PATH_PRESERVE "/var/tmp/vi.recover"
#define _PATH_SENDMAIL "/usr/sbin/sendmail"
#define _PATH_SYSEXRC "/etc/vi.exrc"
#define _PATH_TAGS "tags"
#define _PATH_TMP "/tmp"
#define _PATH_TTY "/dev/tty"

254
usr.bin/vi/common/put.c Normal file
View File

@ -0,0 +1,254 @@
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char sccsid[] = "@(#)put.c 8.10 (Berkeley) 8/17/94";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <ctype.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include "compat.h"
#include <db.h>
#include <regex.h>
#include "vi.h"
/*
* put --
* Put text buffer contents into the file.
*/
int
put(sp, ep, cbp, namep, cp, rp, append)
SCR *sp;
EXF *ep;
CB *cbp;
CHAR_T *namep;
MARK *cp, *rp;
int append;
{
CHAR_T name;
TEXT *ltp, *tp;
recno_t lno;
size_t blen, clen, len;
int rval;
char *bp, *p, *t;
if (cbp == NULL)
if (namep == NULL) {
cbp = sp->gp->dcbp;
if (cbp == NULL) {
msgq(sp, M_ERR, "The default buffer is empty");
return (1);
}
} else {
name = *namep;
CBNAME(sp, cbp, name);
if (cbp == NULL) {
msgq(sp, M_ERR,
"Buffer %s is empty", KEY_NAME(sp, name));
return (1);
}
}
tp = cbp->textq.cqh_first;
/*
* It's possible to do a put into an empty file, meaning that the cut
* buffer simply becomes the file. It's a special case so that we can
* ignore it in general.
*
* !!!
* Historically, pasting into a file with no lines in vi would preserve
* the single blank line. This is surely a result of the fact that the
* historic vi couldn't deal with a file that had no lines in it. This
* implementation treats that as a bug, and does not retain the blank
* line.
*
* Historical practice is that the cursor ends at the first character
* in the file.
*/
if (cp->lno == 1) {
if (file_lline(sp, ep, &lno))
return (1);
if (lno == 0) {
for (; tp != (void *)&cbp->textq;
++lno, ++sp->rptlines[L_ADDED], tp = tp->q.cqe_next)
if (file_aline(sp, ep, 1, lno, tp->lb, tp->len))
return (1);
rp->lno = 1;
rp->cno = 0;
return (0);
}
}
/* If a line mode buffer, append each new line into the file. */
if (F_ISSET(cbp, CB_LMODE)) {
lno = append ? cp->lno : cp->lno - 1;
rp->lno = lno + 1;
for (; tp != (void *)&cbp->textq;
++lno, ++sp->rptlines[L_ADDED], tp = tp->q.cqe_next)
if (file_aline(sp, ep, 1, lno, tp->lb, tp->len))
return (1);
rp->cno = 0;
(void)nonblank(sp, ep, rp->lno, &rp->cno);
return (0);
}
/*
* If buffer was cut in character mode, replace the current line with
* one built from the portion of the first line to the left of the
* split plus the first line in the CB. Append each intermediate line
* in the CB. Append a line built from the portion of the first line
* to the right of the split plus the last line in the CB.
*
* Get the first line.
*/
lno = cp->lno;
if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
GETLINE_ERR(sp, lno);
return (1);
}
GET_SPACE_RET(sp, bp, blen, tp->len + len + 1);
t = bp;
/* Original line, left of the split. */
if (len > 0 && (clen = cp->cno + (append ? 1 : 0)) > 0) {
memmove(bp, p, clen);
p += clen;
t += clen;
}
/* First line from the CB. */
memmove(t, tp->lb, tp->len);
t += tp->len;
/* Calculate length left in original line. */
clen = len ? len - cp->cno - (append ? 1 : 0) : 0;
/*
* If no more lines in the CB, append the rest of the original
* line and quit. Otherwise, build the last line before doing
* the intermediate lines, because the line changes will lose
* the cached line.
*/
rval = 0;
if (tp->q.cqe_next == (void *)&cbp->textq) {
/*
* Historical practice is that if a non-line mode put
* is inside a single line, the cursor ends up on the
* last character inserted.
*/
rp->lno = lno;
rp->cno = (t - bp) - 1;
if (clen > 0) {
memmove(t, p, clen);
t += clen;
}
if (file_sline(sp, ep, lno, bp, t - bp))
goto mem;
if (sp->rptlchange != lno) {
sp->rptlchange = lno;
++sp->rptlines[L_CHANGED];
}
} else {
/*
* Have to build both the first and last lines of the
* put before doing any sets or we'll lose the cached
* line. Build both the first and last lines in the
* same buffer, so we don't have to have another buffer
* floating around.
*
* Last part of original line; check for space, reset
* the pointer into the buffer.
*/
ltp = cbp->textq.cqh_last;
len = t - bp;
ADD_SPACE_RET(sp, bp, blen, ltp->len + clen);
t = bp + len;
/* Add in last part of the CB. */
memmove(t, ltp->lb, ltp->len);
if (clen)
memmove(t + ltp->len, p, clen);
clen += ltp->len;
/*
* Now: bp points to the first character of the first
* line, t points to the last character of the last
* line, t - bp is the length of the first line, and
* clen is the length of the last. Just figured you'd
* want to know.
*
* Output the line replacing the original line.
*/
if (file_sline(sp, ep, lno, bp, t - bp))
goto mem;
if (sp->rptlchange != lno) {
sp->rptlchange = lno;
++sp->rptlines[L_CHANGED];
}
/*
* Historical practice is that if a non-line mode put
* covers multiple lines, the cursor ends up on the
* first character inserted. (Of course.)
*/
rp->lno = lno;
rp->cno = (t - bp) - 1;
/* Output any intermediate lines in the CB. */
for (tp = tp->q.cqe_next;
tp->q.cqe_next != (void *)&cbp->textq;
++lno, ++sp->rptlines[L_ADDED], tp = tp->q.cqe_next)
if (file_aline(sp, ep, 1, lno, tp->lb, tp->len))
goto mem;
if (file_aline(sp, ep, 1, lno, t, clen))
mem: rval = 1;
++sp->rptlines[L_ADDED];
}
FREE_SPACE(sp, bp, blen);
return (rval);
}

869
usr.bin/vi/common/recover.c Normal file
View File

@ -0,0 +1,869 @@
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char sccsid[] = "@(#)recover.c 8.73 (Berkeley) 8/17/94";
#endif /* not lint */
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <sys/time.h>
/*
* We include <sys/file.h>, because the open #defines were found there
* on historical systems. We also include <fcntl.h> because the open(2)
* #defines are found there on newer systems.
*/
#include <sys/file.h>
#include <netdb.h> /* MAXHOSTNAMELEN on some systems. */
#include <bitstring.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "compat.h"
#include <db.h>
#include <regex.h>
#include <pathnames.h>
#include "vi.h"
/*
* Recovery code.
*
* The basic scheme is as follows. In the EXF structure, we maintain full
* paths of a b+tree file and a mail recovery file. The former is the file
* used as backing store by the DB package. The latter is the file that
* contains an email message to be sent to the user if we crash. The two
* simple states of recovery are:
*
* + first starting the edit session:
* the b+tree file exists and is mode 700, the mail recovery
* file doesn't exist.
* + after the file has been modified:
* the b+tree file exists and is mode 600, the mail recovery
* file exists, and is exclusively locked.
*
* In the EXF structure we maintain a file descriptor that is the locked
* file descriptor for the mail recovery file. NOTE: we sometimes have to
* do locking with fcntl(2). This is a problem because if you close(2) any
* file descriptor associated with the file, ALL of the locks go away. Be
* sure to remember that if you have to modify the recovery code. (It has
* been rhetorically asked of what the designers could have been thinking
* when they did that interface. The answer is simple: they weren't.)
*
* To find out if a recovery file/backing file pair are in use, try to get
* a lock on the recovery file.
*
* To find out if a backing file can be deleted at boot time, check for an
* owner execute bit. (Yes, I know it's ugly, but it's either that or put
* special stuff into the backing file itself, or correlate the files at
* boot time, neither or which looks like fun.) Note also that there's a
* window between when the file is created and the X bit is set. It's small,
* but it's there. To fix the window, check for 0 length files as well.
*
* To find out if a file can be recovered, check the F_RCV_ON bit. Note,
* this DOES NOT mean that any initialization has been done, only that we
* haven't yet failed at setting up or doing recovery.
*
* To preserve a recovery file/backing file pair, set the F_RCV_NORM bit.
* If that bit is not set when ending a file session:
* If the EXF structure paths (rcv_path and rcv_mpath) are not NULL,
* they are unlink(2)'d, and free(3)'d.
* If the EXF file descriptor (rcv_fd) is not -1, it is closed.
*
* The backing b+tree file is set up when a file is first edited, so that
* the DB package can use it for on-disk caching and/or to snapshot the
* file. When the file is first modified, the mail recovery file is created,
* the backing file permissions are updated, the file is sync(2)'d to disk,
* and the timer is started. Then, at RCV_PERIOD second intervals, the
* b+tree file is synced to disk. RCV_PERIOD is measured using SIGALRM, which
* means that the data structures (SCR, EXF, the underlying tree structures)
* must be consistent when the signal arrives.
*
* The recovery mail file contains normal mail headers, with two additions,
* which occur in THIS order, as the FIRST TWO headers:
*
* X-vi-recover-file: file_name
* X-vi-recover-path: recover_path
*
* Since newlines delimit the headers, this means that file names cannot have
* newlines in them, but that's probably okay. As these files aren't intended
* to be long-lived, changing their format won't be too painful.
*
* Btree files are named "vi.XXXX" and recovery files are named "recover.XXXX".
*/
#define VI_FHEADER "X-vi-recover-file: "
#define VI_PHEADER "X-vi-recover-path: "
static int rcv_copy __P((SCR *, int, char *));
static void rcv_email __P((SCR *, char *));
static char *rcv_gets __P((char *, size_t, int));
static int rcv_mailfile __P((SCR *, EXF *, int, char *));
static int rcv_mktemp __P((SCR *, char *, char *, int));
/*
* rcv_tmp --
* Build a file name that will be used as the recovery file.
*/
int
rcv_tmp(sp, ep, name)
SCR *sp;
EXF *ep;
char *name;
{
struct stat sb;
int fd;
char *dp, *p, path[MAXPATHLEN];
/*
* If the recovery directory doesn't exist, try and create it. As
* the recovery files are themselves protected from reading/writing
* by other than the owner, the worst that can happen is that a user
* would have permission to remove other user's recovery files. If
* the sticky bit has the BSD semantics, that too will be impossible.
*/
dp = O_STR(sp, O_RECDIR);
if (stat(dp, &sb)) {
if (errno != ENOENT || mkdir(dp, 0)) {
msgq(sp, M_SYSERR, "%s", dp);
goto err;
}
(void)chmod(dp, S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX);
}
/* Newlines delimit the mail messages. */
for (p = name; *p; ++p)
if (*p == '\n') {
msgq(sp, M_ERR,
"Files with newlines in the name are unrecoverable");
goto err;
}
(void)snprintf(path, sizeof(path), "%s/vi.XXXXXX", dp);
if ((fd = rcv_mktemp(sp, path, dp, S_IRWXU)) == -1)
goto err;
(void)close(fd);
if ((ep->rcv_path = strdup(path)) == NULL) {
msgq(sp, M_SYSERR, NULL);
(void)unlink(path);
err: msgq(sp, M_ERR,
"Modifications not recoverable if the session fails");
return (1);
}
/* We believe the file is recoverable. */
F_SET(ep, F_RCV_ON);
return (0);
}
/*
* rcv_init --
* Force the file to be snapshotted for recovery.
*/
int
rcv_init(sp, ep)
SCR *sp;
EXF *ep;
{
recno_t lno;
int btear;
/* Only do this once. */
F_CLR(ep, F_FIRSTMODIFY);
/* If we already know the file isn't recoverable, we're done. */
if (!F_ISSET(ep, F_RCV_ON))
return (0);
/* Turn off recoverability until we figure out if this will work. */
F_CLR(ep, F_RCV_ON);
/* Test if we're recovering a file, not editing one. */
if (ep->rcv_mpath == NULL) {
/* Build a file to mail to the user. */
if (rcv_mailfile(sp, ep, 0, NULL))
goto err;
/* Force a read of the entire file. */
if (file_lline(sp, ep, &lno))
goto err;
/* Turn on a busy message, and sync it to backing store. */
btear = F_ISSET(sp, S_EXSILENT) ? 0 :
!busy_on(sp, "Copying file for recovery...");
if (ep->db->sync(ep->db, R_RECNOSYNC)) {
msgq(sp, M_ERR, "Preservation failed: %s: %s",
ep->rcv_path, strerror(errno));
if (btear)
busy_off(sp);
goto err;
}
if (btear)
busy_off(sp);
}
/* Turn on the recovery timer, if it's not yet running. */
if (!F_ISSET(sp->gp, G_RECOVER_SET) && rcv_on(sp, ep)) {
err: msgq(sp, M_ERR,
"Modifications not recoverable if the session fails");
return (1);
}
/* Turn off the owner execute bit. */
(void)chmod(ep->rcv_path, S_IRUSR | S_IWUSR);
/* We believe the file is recoverable. */
F_SET(ep, F_RCV_ON);
return (0);
}
/*
* rcv_sync --
* Sync the file, optionally:
* flagging the backup file to be preserved
* snapshotting the backup file and send email to the user
* sending email to the user if the file was modified
* ending the file session
*/
int
rcv_sync(sp, ep, flags)
SCR *sp;
EXF *ep;
u_int flags;
{
int btear, fd, rval;
char *dp, buf[1024];
/* Make sure that there's something to recover/sync. */
if (ep == NULL || !F_ISSET(ep, F_RCV_ON))
return (0);
/* Sync the file if it's been modified. */
if (F_ISSET(ep, F_MODIFIED)) {
if (ep->db->sync(ep->db, R_RECNOSYNC)) {
F_CLR(ep, F_RCV_ON | F_RCV_NORM);
msgq(sp, M_SYSERR,
"File backup failed: %s", ep->rcv_path);
return (1);
}
/* REQUEST: don't remove backing file on exit. */
if (LF_ISSET(RCV_PRESERVE))
F_SET(ep, F_RCV_NORM);
/* REQUEST: send email. */
if (LF_ISSET(RCV_EMAIL))
rcv_email(sp, ep->rcv_mpath);
}
/*
* !!!
* Each time the user exec's :preserve, we have to snapshot all of
* the recovery information, i.e. it's like the user re-edited the
* file. We copy the DB(3) backing file, and then create a new mail
* recovery file, it's simpler than exiting and reopening all of the
* underlying files.
*
* REQUEST: snapshot the file.
*/
rval = 0;
if (LF_ISSET(RCV_SNAPSHOT)) {
btear = F_ISSET(sp, S_EXSILENT) ? 0 :
!busy_on(sp, "Copying file for recovery...");
dp = O_STR(sp, O_RECDIR);
(void)snprintf(buf, sizeof(buf), "%s/vi.XXXXXX", dp);
if ((fd = rcv_mktemp(sp, buf, dp, S_IRUSR | S_IWUSR)) == -1)
goto e1;
if (rcv_copy(sp, fd, ep->rcv_path) || close(fd))
goto e2;
if (rcv_mailfile(sp, ep, 1, buf)) {
e2: (void)unlink(buf);
e1: if (fd != -1)
(void)close(fd);
rval = 1;
}
if (btear)
busy_off(sp);
}
/* REQUEST: end the file session. */
if (LF_ISSET(RCV_ENDSESSION) && file_end(sp, ep, 1))
rval = 1;
return (rval);
}
/*
* rcv_mailfile --
* Build the file to mail to the user.
*/
static int
rcv_mailfile(sp, ep, issync, cp_path)
SCR *sp;
EXF *ep;
int issync;
char *cp_path;
{
struct passwd *pw;
size_t len;
time_t now;
uid_t uid;
int fd;
char *dp, *p, *t, buf[4096], host[MAXHOSTNAMELEN], mpath[MAXPATHLEN];
char *t1, *t2, *t3;
if ((pw = getpwuid(uid = getuid())) == NULL) {
msgq(sp, M_ERR, "Information on user id %u not found", uid);
return (1);
}
dp = O_STR(sp, O_RECDIR);
(void)snprintf(mpath, sizeof(mpath), "%s/recover.XXXXXX", dp);
if ((fd = rcv_mktemp(sp, mpath, dp, S_IRUSR | S_IWUSR)) == -1)
return (1);
/*
* XXX
* We keep an open lock on the file so that the recover option can
* distinguish between files that are live and those that need to
* be recovered. There's an obvious window between the mkstemp call
* and the lock, but it's pretty small.
*/
if (file_lock(NULL, NULL, fd, 1) != LOCK_SUCCESS)
msgq(sp, M_SYSERR, "Unable to lock recovery file");
if (!issync) {
/* Save the recover file descriptor, and mail path. */
ep->rcv_fd = fd;
if ((ep->rcv_mpath = strdup(mpath)) == NULL) {
msgq(sp, M_SYSERR, NULL);
goto err;
}
cp_path = ep->rcv_path;
}
/*
* XXX
* We can't use stdio(3) here. The problem is that we may be using
* fcntl(2), so if ANY file descriptor into the file is closed, the
* lock is lost. So, we could never close the FILE *, even if we
* dup'd the fd first.
*/
t = sp->frp->name;
if ((p = strrchr(t, '/')) == NULL)
p = t;
else
++p;
(void)time(&now);
(void)gethostname(host, sizeof(host));
len = snprintf(buf, sizeof(buf),
"%s%s\n%s%s\n%s\n%s\n%s%s\n%s%s\n%s\n\n",
VI_FHEADER, t, /* Non-standard. */
VI_PHEADER, cp_path, /* Non-standard. */
"Reply-To: root",
"From: root (Nvi recovery program)",
"To: ", pw->pw_name,
"Subject: Nvi saved the file ", p,
"Precedence: bulk"); /* For vacation(1). */
if (len > sizeof(buf) - 1)
goto lerr;
if (write(fd, buf, len) != len)
goto werr;
len = snprintf(buf, sizeof(buf), "%s%.24s%s%s%s%s%s%s%s%s%s%s%s\n\n",
"On ", ctime(&now), ", the user ", pw->pw_name,
" was editing a file named ", t, " on the machine ",
host, ", when it was saved for recovery. ",
"You can recover most, if not all, of the changes ",
"to this file using the -r option to nex or nvi:\n\n",
"\tnvi -r ", t);
if (len > sizeof(buf) - 1) {
lerr: msgq(sp, M_ERR, "recovery file buffer overrun");
goto err;
}
/*
* Format the message. (Yes, I know it's silly.)
* Requires that the message end in a <newline>.
*/
#define FMTCOLS 60
for (t1 = buf; len > 0; len -= t2 - t1, t1 = t2) {
/* Check for a short length. */
if (len <= FMTCOLS) {
t2 = t1 + (len - 1);
goto wout;
}
/* Check for a required <newline>. */
t2 = strchr(t1, '\n');
if (t2 - t1 <= FMTCOLS)
goto wout;
/* Find the closest space, if any. */
for (t3 = t2; t2 > t1; --t2)
if (*t2 == ' ') {
if (t2 - t1 <= FMTCOLS)
goto wout;
t3 = t2;
}
t2 = t3;
/* t2 points to the last character to display. */
wout: *t2++ = '\n';
/* t2 points one after the last character to display. */
if (write(fd, t1, t2 - t1) != t2 - t1) {
werr: msgq(sp, M_SYSERR, "recovery file");
goto err;
}
}
if (issync)
rcv_email(sp, mpath);
return (0);
err: if (!issync)
ep->rcv_fd = -1;
if (fd != -1)
(void)close(fd);
return (1);
}
/*
* people making love
* never exactly the same
* just like a snowflake
*
* rcv_list --
* List the files that can be recovered by this user.
*/
int
rcv_list(sp)
SCR *sp;
{
struct dirent *dp;
struct stat sb;
DIR *dirp;
FILE *fp;
int found;
char *p, *t, file[MAXPATHLEN], path[MAXPATHLEN];
/*
* XXX
* Messages aren't yet set up.
*/
if (chdir(O_STR(sp, O_RECDIR)) || (dirp = opendir(".")) == NULL) {
(void)fprintf(stderr,
"vi: %s: %s\n", O_STR(sp, O_RECDIR), strerror(errno));
return (1);
}
for (found = 0; (dp = readdir(dirp)) != NULL;) {
if (strncmp(dp->d_name, "recover.", 8))
continue;
/*
* If it's readable, it's recoverable.
*
* XXX
* Should be "r", we don't want to write the file. However,
* if we're using fcntl(2), there's no way to lock a file
* descriptor that's not open for writing.
*/
if ((fp = fopen(dp->d_name, "r+")) == NULL)
continue;
switch (file_lock(NULL, NULL, fileno(fp), 1)) {
case LOCK_FAILED:
/*
* XXX
* Assume that a lock can't be acquired, but that we
* should permit recovery anyway. If this is wrong,
* and someone else is using the file, we're going to
* die horribly.
*/
break;
case LOCK_SUCCESS:
break;
case LOCK_UNAVAIL:
/* If it's locked, it's live. */
(void)fclose(fp);
continue;
}
/* Check the headers. */
if (fgets(file, sizeof(file), fp) == NULL ||
strncmp(file, VI_FHEADER, sizeof(VI_FHEADER) - 1) ||
(p = strchr(file, '\n')) == NULL ||
fgets(path, sizeof(path), fp) == NULL ||
strncmp(path, VI_PHEADER, sizeof(VI_PHEADER) - 1) ||
(t = strchr(path, '\n')) == NULL) {
msgq(sp, M_ERR,
"%s: malformed recovery file", dp->d_name);
goto next;
}
*p = *t = '\0';
/*
* If the file doesn't exist, it's an orphaned recovery file,
* toss it.
*
* XXX
* This can occur if the backup file was deleted and we crashed
* before deleting the email file.
*/
errno = 0;
if (stat(path + sizeof(VI_PHEADER) - 1, &sb) &&
errno == ENOENT) {
(void)unlink(dp->d_name);
goto next;
}
/* Get the last modification time and display. */
(void)fstat(fileno(fp), &sb);
(void)printf("%s: %s",
file + sizeof(VI_FHEADER) - 1, ctime(&sb.st_mtime));
found = 1;
/* Close, discarding lock. */
next: (void)fclose(fp);
}
if (found == 0)
(void)printf("vi: no files to recover.\n");
(void)closedir(dirp);
return (0);
}
/*
* rcv_read --
* Start a recovered file as the file to edit.
*/
int
rcv_read(sp, frp)
SCR *sp;
FREF *frp;
{
struct dirent *dp;
struct stat sb;
DIR *dirp;
EXF *ep;
time_t rec_mtime;
int fd, found, locked, requested, sv_fd;
char *name, *p, *t, *recp, *pathp;
char file[MAXPATHLEN], path[MAXPATHLEN], recpath[MAXPATHLEN];
if ((dirp = opendir(O_STR(sp, O_RECDIR))) == NULL) {
msgq(sp, M_ERR,
"%s: %s", O_STR(sp, O_RECDIR), strerror(errno));
return (1);
}
name = frp->name;
sv_fd = -1;
rec_mtime = 0;
recp = pathp = NULL;
for (found = requested = 0; (dp = readdir(dirp)) != NULL;) {
if (strncmp(dp->d_name, "recover.", 8))
continue;
(void)snprintf(recpath, sizeof(recpath),
"%s/%s", O_STR(sp, O_RECDIR), dp->d_name);
/*
* If it's readable, it's recoverable. It would be very
* nice to use stdio(3), but, we can't because that would
* require closing and then reopening the file so that we
* could have a lock and still close the FP. Another tip
* of the hat to fcntl(2).
*
* XXX
* Should be O_RDONLY, we don't want to write it. However,
* if we're using fcntl(2), there's no way to lock a file
* descriptor that's not open for writing.
*/
if ((fd = open(recpath, O_RDWR, 0)) == -1)
continue;
switch (file_lock(NULL, NULL, fd, 1)) {
case LOCK_FAILED:
/*
* XXX
* Assume that a lock can't be acquired, but that we
* should permit recovery anyway. If this is wrong,
* and someone else is using the file, we're going to
* die horribly.
*/
locked = 0;
break;
case LOCK_SUCCESS:
locked = 1;
break;
case LOCK_UNAVAIL:
/* If it's locked, it's live. */
(void)close(fd);
continue;
}
/* Check the headers. */
if (rcv_gets(file, sizeof(file), fd) == NULL ||
strncmp(file, VI_FHEADER, sizeof(VI_FHEADER) - 1) ||
(p = strchr(file, '\n')) == NULL ||
rcv_gets(path, sizeof(path), fd) == NULL ||
strncmp(path, VI_PHEADER, sizeof(VI_PHEADER) - 1) ||
(t = strchr(path, '\n')) == NULL) {
msgq(sp, M_ERR,
"%s: malformed recovery file", recpath);
goto next;
}
*p = *t = '\0';
++found;
/*
* If the file doesn't exist, it's an orphaned recovery file,
* toss it.
*
* XXX
* This can occur if the backup file was deleted and we crashed
* before deleting the email file.
*/
errno = 0;
if (stat(path + sizeof(VI_PHEADER) - 1, &sb) &&
errno == ENOENT) {
(void)unlink(dp->d_name);
goto next;
}
/* Check the file name. */
if (strcmp(file + sizeof(VI_FHEADER) - 1, name))
goto next;
++requested;
/*
* If we've found more than one, take the most recent.
*
* XXX
* Since we're using st_mtime, for portability reasons,
* we only get a single second granularity, instead of
* getting it right.
*/
(void)fstat(fd, &sb);
if (recp == NULL || rec_mtime < sb.st_mtime) {
p = recp;
t = pathp;
if ((recp = strdup(recpath)) == NULL) {
msgq(sp, M_ERR,
"vi: Error: %s.\n", strerror(errno));
recp = p;
goto next;
}
if ((pathp = strdup(path)) == NULL) {
msgq(sp, M_ERR,
"vi: Error: %s.\n", strerror(errno));
FREE(recp, strlen(recp) + 1);
recp = p;
pathp = t;
goto next;
}
if (p != NULL) {
FREE(p, strlen(p) + 1);
FREE(t, strlen(t) + 1);
}
rec_mtime = sb.st_mtime;
if (sv_fd != -1)
(void)close(sv_fd);
sv_fd = fd;
} else
next: (void)close(fd);
}
(void)closedir(dirp);
if (recp == NULL) {
msgq(sp, M_INFO,
"No files named %s, readable by you, to recover", name);
return (1);
}
if (found) {
if (requested > 1)
msgq(sp, M_INFO,
"There are older versions of this file for you to recover");
if (found > requested)
msgq(sp, M_INFO,
"There are other files for you to recover");
}
/*
* Create the FREF structure, start the btree file.
*
* XXX
* file_init() is going to set ep->rcv_path.
*/
if (file_init(sp, frp, pathp + sizeof(VI_PHEADER) - 1, 0)) {
free(recp);
free(pathp);
(void)close(sv_fd);
return (1);
}
/*
* We keep an open lock on the file so that the recover option can
* distinguish between files that are live and those that need to
* be recovered. The lock is already acquired, just copy it.
*/
ep = sp->ep;
ep->rcv_mpath = recp;
ep->rcv_fd = sv_fd;
if (!locked)
F_SET(frp, FR_UNLOCKED);
/* We believe the file is recoverable. */
F_SET(ep, F_RCV_ON);
return (0);
}
/*
* rcv_copy --
* Copy a recovery file.
*/
static int
rcv_copy(sp, wfd, fname)
SCR *sp;
int wfd;
char *fname;
{
int nr, nw, off, rfd;
char buf[8 * 1024];
if ((rfd = open(fname, O_RDONLY, 0)) == -1)
goto err;
while ((nr = read(rfd, buf, sizeof(buf))) > 0)
for (off = 0; nr; nr -= nw, off += nw)
if ((nw = write(wfd, buf + off, nr)) < 0)
goto err;
if (nr == 0)
return (0);
err: msgq(sp, M_SYSERR, "%s", fname);
return (1);
}
/*
* rcv_gets --
* Fgets(3) for a file descriptor.
*/
static char *
rcv_gets(buf, len, fd)
char *buf;
size_t len;
int fd;
{
ssize_t nr;
char *p;
if ((nr = read(fd, buf, len - 1)) == -1)
return (NULL);
if ((p = strchr(buf, '\n')) == NULL)
return (NULL);
(void)lseek(fd, (off_t)((p - buf) + 1), SEEK_SET);
return (buf);
}
/*
* rcv_mktemp --
* Paranoid make temporary file routine.
*/
static int
rcv_mktemp(sp, path, dname, perms)
SCR *sp;
char *path, *dname;
int perms;
{
int fd;
/*
* !!!
* We expect mkstemp(3) to set the permissions correctly. On
* historic System V systems, mkstemp didn't. Do it here, on
* GP's.
*
* XXX
* The variable perms should really be a mode_t, and it would
* be nice to use fchmod(2) instead of chmod(2), here.
*/
if ((fd = mkstemp(path)) == -1)
msgq(sp, M_SYSERR, "%s", dname);
else
(void)chmod(path, perms);
return (fd);
}
/*
* rcv_email --
* Send email.
*/
static void
rcv_email(sp, fname)
SCR *sp;
char *fname;
{
struct stat sb;
char buf[MAXPATHLEN * 2 + 20];
if (stat(_PATH_SENDMAIL, &sb))
msgq(sp, M_SYSERR, "not sending email: %s", _PATH_SENDMAIL);
else {
/*
* !!!
* If you need to port this to a system that doesn't have
* sendmail, the -t flag causes sendmail to read the message
* for the recipients instead of specifying them some other
* way.
*/
(void)snprintf(buf, sizeof(buf),
"%s -t < %s", _PATH_SENDMAIL, fname);
(void)system(buf);
}
}

311
usr.bin/vi/common/screen.c Normal file
View File

@ -0,0 +1,311 @@
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char sccsid[] = "@(#)screen.c 8.66 (Berkeley) 8/17/94";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "compat.h"
#include <db.h>
#include <regex.h>
#include "vi.h"
#include "../vi/vcmd.h"
#include "excmd.h"
#include "../ex/tag.h"
/*
* screen_init --
* Do the default initialization of an SCR structure.
*/
int
screen_init(orig, spp, flags)
SCR *orig, **spp;
u_int flags;
{
SCR *sp;
size_t len;
*spp = NULL;
CALLOC_RET(orig, sp, SCR *, 1, sizeof(SCR));
*spp = sp;
/* INITIALIZED AT SCREEN CREATE. */
sp->gp = __global_list; /* All ref the GS structure. */
LIST_INIT(&sp->msgq);
CIRCLEQ_INIT(&sp->frefq);
sp->ccnt = 2; /* Anything > 1 */
FD_ZERO(&sp->rdfd);
/*
* XXX
* sp->defscroll is initialized by the opts_init() code because
* we don't have the option information yet.
*/
sp->tiqp = &sp->__tiq;
CIRCLEQ_INIT(&sp->__tiq);
/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
if (orig == NULL) {
sp->searchdir = NOTSET;
switch (flags & S_SCREENS) {
case S_EX:
if (sex_screen_init(sp))
return (1);
break;
case S_VI_CURSES:
if (svi_screen_init(sp))
return (1);
break;
case S_VI_XAW:
if (xaw_screen_init(sp))
return (1);
break;
default:
abort();
}
sp->flags = flags;
} else {
if (orig->alt_name != NULL &&
(sp->alt_name = strdup(orig->alt_name)) == NULL)
goto mem;
/* Retain all searching/substitution information. */
if (F_ISSET(orig, S_SRE_SET)) {
F_SET(sp, S_SRE_SET);
sp->sre = orig->sre;
}
if (F_ISSET(orig, S_SUBRE_SET)) {
F_SET(sp, S_SUBRE_SET);
sp->subre = orig->subre;
}
sp->searchdir = orig->searchdir == NOTSET ? NOTSET : FORWARD;
if (orig->repl_len) {
MALLOC(sp, sp->repl, char *, orig->repl_len);
if (sp->repl == NULL)
goto mem;
sp->repl_len = orig->repl_len;
memmove(sp->repl, orig->repl, orig->repl_len);
}
if (orig->newl_len) {
len = orig->newl_len * sizeof(size_t);
MALLOC(sp, sp->newl, size_t *, len);
if (sp->newl == NULL)
goto mem;
sp->newl_len = orig->newl_len;
sp->newl_cnt = orig->newl_cnt;
memmove(sp->newl, orig->newl, len);
}
sp->saved_vi_mode = orig->saved_vi_mode;
if (opts_copy(orig, sp)) {
mem: msgq(orig, M_SYSERR, "new screen attributes");
(void)screen_end(sp);
return (1);
}
sp->s_bell = orig->s_bell;
sp->s_bg = orig->s_bg;
sp->s_busy = orig->s_busy;
sp->s_change = orig->s_change;
sp->s_clear = orig->s_clear;
sp->s_colpos = orig->s_colpos;
sp->s_column = orig->s_column;
sp->s_confirm = orig->s_confirm;
sp->s_crel = orig->s_crel;
sp->s_edit = orig->s_edit;
sp->s_end = orig->s_end;
sp->s_ex_cmd = orig->s_ex_cmd;
sp->s_ex_run = orig->s_ex_run;
sp->s_ex_write = orig->s_ex_write;
sp->s_fg = orig->s_fg;
sp->s_fill = orig->s_fill;
sp->s_get = orig->s_get;
sp->s_key_read = orig->s_key_read;
sp->s_fmap = orig->s_fmap;
sp->s_optchange = orig->s_optchange;
sp->s_position = orig->s_position;
sp->s_rabs = orig->s_rabs;
sp->s_rcm = orig->s_rcm;
sp->s_refresh = orig->s_refresh;
sp->s_scroll = orig->s_scroll;
sp->s_split = orig->s_split;
sp->s_suspend = orig->s_suspend;
sp->s_window = orig->s_window;
F_SET(sp, F_ISSET(orig, S_SCREENS));
}
if (xaw_screen_copy(orig, sp)) /* Init S_VI_XAW screen. */
return (1);
if (svi_screen_copy(orig, sp)) /* Init S_VI_CURSES screen. */
return (1);
if (sex_screen_copy(orig, sp)) /* Init S_EX screen. */
return (1);
if (v_screen_copy(orig, sp)) /* Init vi. */
return (1);
if (ex_screen_copy(orig, sp)) /* Init ex. */
return (1);
*spp = sp;
return (0);
}
/*
* screen_end --
* Release a screen.
*/
int
screen_end(sp)
SCR *sp;
{
int rval;
rval = 0;
if (xaw_screen_end(sp)) /* End S_VI_XAW screen. */
rval = 1;
if (svi_screen_end(sp)) /* End S_VI_CURSES screen. */
rval = 1;
if (sex_screen_end(sp)) /* End S_EX screen. */
rval = 1;
if (v_screen_end(sp)) /* End vi. */
rval = 1;
if (ex_screen_end(sp)) /* End ex. */
rval = 1;
/* Free FREF's. */
{ FREF *frp;
while ((frp = sp->frefq.cqh_first) != (FREF *)&sp->frefq) {
CIRCLEQ_REMOVE(&sp->frefq, frp, q);
if (frp->name != NULL)
free(frp->name);
if (frp->tname != NULL)
free(frp->tname);
FREE(frp, sizeof(FREF));
}
}
/* Free file names. */
{ char **ap;
if (!F_ISSET(sp, S_ARGNOFREE) && sp->argv != NULL) {
for (ap = sp->argv; *ap != NULL; ++ap)
free(*ap);
free(sp->argv);
}
}
/* Free any text input. */
text_lfree(&sp->__tiq);
/* Free any script information. */
if (F_ISSET(sp, S_SCRIPT))
sscr_end(sp);
/* Free alternate file name. */
if (sp->alt_name != NULL)
free(sp->alt_name);
/* Free up search information. */
if (sp->repl != NULL)
FREE(sp->repl, sp->repl_len);
if (sp->newl != NULL)
FREE(sp->newl, sp->newl_len);
/* Free all the options */
opts_free(sp);
/*
* Free the message chain last, so previous failures have a place
* to put messages. Copy messages to (in order) a related screen,
* any screen, the global area.
*/
{ SCR *c_sp; MSG *mp, *next;
if ((c_sp = sp->q.cqe_prev) != (void *)&sp->gp->dq) {
if (F_ISSET(sp, S_BELLSCHED))
F_SET(c_sp, S_BELLSCHED);
} else if ((c_sp = sp->q.cqe_next) != (void *)&sp->gp->dq) {
if (F_ISSET(sp, S_BELLSCHED))
F_SET(c_sp, S_BELLSCHED);
} else if ((c_sp =
sp->gp->hq.cqh_first) != (void *)&sp->gp->hq) {
if (F_ISSET(sp, S_BELLSCHED))
F_SET(c_sp, S_BELLSCHED);
} else {
c_sp = NULL;
if (F_ISSET(sp, S_BELLSCHED))
F_SET(sp->gp, G_BELLSCHED);
}
for (mp = sp->msgq.lh_first; mp != NULL; mp = next) {
if (!F_ISSET(mp, M_EMPTY))
msg_app(sp->gp, c_sp,
mp->flags & M_INV_VIDEO, mp->mbuf, mp->len);
next = mp->q.le_next;
if (mp->mbuf != NULL)
FREE(mp->mbuf, mp->blen);
FREE(mp, sizeof(MSG));
}
}
/* Remove the screen from the displayed queue. */
SIGBLOCK(sp->gp);
CIRCLEQ_REMOVE(&sp->gp->dq, sp, q);
SIGUNBLOCK(sp->gp);
/* Free the screen itself. */
FREE(sp, sizeof(SCR));
return (rval);
}

342
usr.bin/vi/common/screen.h Normal file
View File

@ -0,0 +1,342 @@
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)screen.h 8.127 (Berkeley) 8/8/94
*/
/*
* There are minimum values that vi has to have to display a screen. The
* row minimum is fixed at 1 line for the text, and 1 line for any error
* messages. The column calculation is a lot trickier. For example, you
* have to have enough columns to display the line number, not to mention
* guaranteeing that tabstop and shiftwidth values are smaller than the
* current column value. It's a lot simpler to have a fixed value and not
* worry about it.
*
* XXX
* MINIMUM_SCREEN_COLS is probably wrong.
*/
#define MINIMUM_SCREEN_ROWS 2
#define MINIMUM_SCREEN_COLS 20
enum adjust { /* Screen adjustment operations. */
A_DECREASE, A_INCREASE, A_SET };
enum operation { /* Line operations. */
LINE_APPEND, LINE_DELETE, LINE_INSERT, LINE_RESET };
enum position { /* Position operations. */
P_BOTTOM, P_FILL, P_MIDDLE, P_TOP };
enum sctype { /* Scroll operations. */
CNTRL_B, CNTRL_D, CNTRL_E, CNTRL_F, CNTRL_U, CNTRL_Y, Z_CARAT, Z_PLUS };
/*
* Structure for holding file references. Each SCR structure contains a
* linked list of these. The structure contains the name of the file,
* along with the information that follows the name.
*
* !!!
* The read-only bit follows the file name, not the file itself.
*
* XXX
* The mtime field should be a struct timespec, but time_t is more portable.
*/
struct _fref {
CIRCLEQ_ENTRY(_fref) q; /* Linked list of file references. */
char *name; /* File name. */
char *tname; /* Backing temporary file name. */
recno_t lno; /* 1-N: file cursor line. */
size_t cno; /* 0-N: file cursor column. */
#define FR_CURSORSET 0x001 /* If lno/cno values valid. */
#define FR_DONTDELETE 0x002 /* Don't delete the temporary file. */
#define FR_FNONBLANK 0x004 /* Move to the first non-<blank>. */
#define FR_NAMECHANGE 0x008 /* If the name changed. */
#define FR_NEWFILE 0x010 /* File doesn't really exist yet. */
#define FR_RDONLY 0x020 /* File is read-only. */
#define FR_READNAMED 0x040 /* Read renamed the file. */
#define FR_RECOVER 0x080 /* File is being recovered. */
#define FR_TMPEXIT 0x100 /* Modified temporary file, no exit. */
#define FR_TMPFILE 0x200 /* If file has no name. */
#define FR_UNLOCKED 0x400 /* File couldn't be locked. */
u_int16_t flags;
};
#define TEMPORARY_FILE_STRING "/tmp" /* Default temporary file name. */
/*
* SCR --
* The screen structure. To the extent possible, all screen information
* is stored in the various private areas. The only information here
* is used by global routines or is shared by too many screens.
*/
struct _scr {
/* INITIALIZED AT SCREEN CREATE. */
CIRCLEQ_ENTRY(_scr) q; /* Screens. */
GS *gp; /* Pointer to global area. */
SCR *nextdisp; /* Next display screen. */
EXF *ep; /* Screen's current EXF structure. */
MSGH msgq; /* Message list. */
/* FREF list. */
CIRCLEQ_HEAD(_frefh, _fref) frefq;
FREF *frp; /* FREF being edited. */
char **argv; /* NULL terminated file name array. */
char **cargv; /* Current file name. */
u_long ccnt; /* Command count. */
u_long q_ccnt; /* Quit or ZZ command count. */
/* Screen's: */
size_t rows; /* 1-N: number of rows. */
size_t cols; /* 1-N: number of columns. */
size_t woff; /* 0-N: row offset in screen. */
size_t t_rows; /* 1-N: cur number of text rows. */
size_t t_maxrows; /* 1-N: max number of text rows. */
size_t t_minrows; /* 1-N: min number of text rows. */
/* Cursor's: */
recno_t lno; /* 1-N: file line. */
size_t cno; /* 0-N: file character in line. */
size_t rcm; /* Vi: 0-N: Most attractive column. */
int rcm_last; /* Cursor drawn to the last column. */
#define L_ADDED 0 /* Added lines. */
#define L_CHANGED 1 /* Changed lines. */
#define L_DELETED 2 /* Deleted lines. */
#define L_JOINED 3 /* Joined lines. */
#define L_MOVED 4 /* Moved lines. */
#define L_LSHIFT 5 /* Left shift lines. */
#define L_RSHIFT 6 /* Right shift lines. */
#define L_YANKED 7 /* Yanked lines. */
recno_t rptlchange; /* Ex/vi: last L_CHANGED lno. */
recno_t rptlines[L_YANKED + 1];/* Ex/vi: lines changed by last op. */
FILE *stdfp; /* Ex output file pointer. */
char *if_name; /* Ex input file name, for messages. */
recno_t if_lno; /* Ex input file line, for messages. */
fd_set rdfd; /* Ex/vi: read fd select mask. */
TEXTH __tiq; /* Ex/vi: text input queue. */
TEXTH *tiqp; /* Ex/vi: text input queue reference. */
SCRIPT *script; /* Vi: script mode information .*/
recno_t defscroll; /* Vi: ^D, ^U scroll information. */
struct timeval busy_tod; /* ITIMER_REAL: busy time-of-day. */
char const *busy_msg; /* ITIMER_REAL: busy message. */
/* Display character. */
CHAR_T cname[MAX_CHARACTER_COLUMNS + 1];
size_t clen; /* Length of display character. */
#define MAX_MODE_NAME 12
char *showmode; /* Mode. */
void *ex_private; /* Ex private area. */
void *sex_private; /* Ex screen private area. */
void *vi_private; /* Vi private area. */
void *svi_private; /* Vi curses screen private area. */
void *xaw_private; /* Vi XAW screen private area. */
/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
char *alt_name; /* Ex/vi: alternate file name. */
/* Ex/vi: search/substitute info. */
regex_t sre; /* Last search RE. */
regex_t subre; /* Last substitute RE. */
enum direction searchdir; /* File search direction. */
char *repl; /* Substitute replacement. */
size_t repl_len; /* Substitute replacement length.*/
size_t *newl; /* Newline offset array. */
size_t newl_len; /* Newline array size. */
size_t newl_cnt; /* Newlines in replacement. */
u_char c_suffix; /* Edcompatible 'c' suffix value. */
u_char g_suffix; /* Edcompatible 'g' suffix value. */
u_int saved_vi_mode; /* Saved vi display type. */
OPTION opts[O_OPTIONCOUNT]; /* Options. */
/*
* SCREEN SUPPORT ROUTINES.
*
* A SCR * MUST be the first argument to these routines.
*/
/* Ring the screen bell. */
void (*s_bell) __P((SCR *));
/* Background the screen. */
int (*s_bg) __P((SCR *));
/* Put up a busy message. */
int (*s_busy) __P((SCR *, char const *));
/* Change a screen line. */
int (*s_change) __P((SCR *, EXF *, recno_t, enum operation));
/* Clear the screen. */
int (*s_clear) __P((SCR *));
/* Return column close to specified. */
size_t (*s_colpos) __P((SCR *, EXF *, recno_t, size_t));
/* Return the logical cursor column. */
int (*s_column) __P((SCR *, EXF *, size_t *));
enum confirm /* Confirm an action with the user. */
(*s_confirm) __P((SCR *, EXF *, MARK *, MARK *));
/* Change the relative screen size. */
int (*s_crel) __P((SCR *, long));
/* Edit a file. */
int (*s_edit) __P((SCR *, EXF *));
/* End a screen. */
int (*s_end) __P((SCR *));
/* Run a single ex command. */
int (*s_ex_cmd) __P((SCR *, EXF *, EXCMDARG *, MARK *));
/* Run user's ex commands. */
int (*s_ex_run) __P((SCR *, EXF *, MARK *));
/* Screen's ex write function. */
int (*s_ex_write) __P((void *, const char *, int));
/* Foreground the screen. */
int (*s_fg) __P((SCR *, CHAR_T *));
/* Fill the screen's map. */
int (*s_fill) __P((SCR *, EXF *, recno_t, enum position));
enum input /* Get a line from the user. */
(*s_get) __P((SCR *, EXF *, TEXTH *, ARG_CHAR_T, u_int));
enum input /* Get a key from the user. */
(*s_key_read) __P((SCR *, int *, struct timeval *));
/* Map a function key. */
int (*s_fmap) __P((SCR *,
enum seqtype, CHAR_T *, size_t, CHAR_T *, size_t));
/* Tell the screen an option changed. */
int (*s_optchange) __P((SCR *, int));
/* Return column at screen position. */
int (*s_position) __P((SCR *, EXF *,
MARK *, u_long, enum position));
/* Change the absolute screen size. */
int (*s_rabs) __P((SCR *, long, enum adjust));
/* Return column close to selection. */
size_t (*s_rcm) __P((SCR *, EXF *, recno_t));
/* Refresh the screen. */
int (*s_refresh) __P((SCR *, EXF *));
/* Move down the screen. */
int (*s_scroll) __P((SCR *, EXF *, MARK *, recno_t, enum sctype));
/* Split the screen. */
int (*s_split) __P((SCR *, ARGS *[], int));
/* Suspend the screen. */
int (*s_suspend) __P((SCR *));
/* Set the window size. */
int (*s_window) __P((SCR *, int));
/* Editor screens. */
#define S_EX 0x0000001 /* Ex screen. */
#define S_VI_CURSES 0x0000002 /* Vi: curses screen. */
#define S_VI_XAW 0x0000004 /* Vi: Athena widgets screen. */
#define IN_EX_MODE(sp) /* If in ex mode. */ \
(F_ISSET(sp, S_EX))
#define IN_VI_MODE(sp) /* If in vi mode. */ \
(F_ISSET(sp, S_VI_CURSES | S_VI_XAW))
#define S_SCREENS /* Screens. */ \
(S_EX | S_VI_CURSES | S_VI_XAW)
/* Major screen/file changes. */
#define S_EXIT 0x0000008 /* Exiting (not forced). */
#define S_EXIT_FORCE 0x0000010 /* Exiting (forced). */
#define S_FSWITCH 0x0000020 /* Switch files. */
#define S_SSWITCH 0x0000040 /* Switch screens. */
#define S_MAJOR_CHANGE /* Screen or file changes. */ \
(S_EXIT | S_EXIT_FORCE | S_FSWITCH | S_SSWITCH)
#define S_ARGNOFREE 0x0000080 /* Argument list wasn't allocated. */
#define S_ARGRECOVER 0x0000100 /* Argument list is recovery files. */
#define S_BELLSCHED 0x0000200 /* Bell scheduled. */
#define S_CONTINUE 0x0000400 /* Need to ask the user to continue. */
#define S_EXSILENT 0x0000800 /* Ex batch script. */
#define S_GLOBAL 0x0001000 /* Doing a global command. */
#define S_INPUT 0x0002000 /* Doing text input. */
#define S_INTERRUPTED 0x0004000 /* If have been interrupted. */
#define S_INTERRUPTIBLE 0x0008000 /* If can be interrupted. */
#define S_IVIDEO 0x0010000 /* Display in inverse video. */
#define S_REDRAW 0x0020000 /* Redraw the screen. */
#define S_REFORMAT 0x0040000 /* Reformat the screen. */
#define S_REFRESH 0x0080000 /* Refresh the screen. */
#define S_RENUMBER 0x0100000 /* Renumber the screen. */
#define S_RESIZE 0x0200000 /* Resize the screen. */
#define S_SCRIPT 0x0400000 /* Window is a shell script. */
#define S_SRE_SET 0x0800000 /* The search RE has been set. */
#define S_SUBRE_SET 0x1000000 /* The substitute RE has been set. */
#define S_UPDATE_MODE 0x2000000 /* Don't repaint modeline. */
#define S_VLITONLY 0x4000000 /* ^V literal next only. */
u_int32_t flags;
};
/*
* Signals/timers have no structure, so it's all here.
*
* Block all signals that are being handled. Used to keep the underlying DB
* system calls from being interrupted and not restarted, as it could cause
* consistency problems. Also used when vi forks child processes, to avoid
* a signal arriving after the fork and before the exec, causing both parent
* and child to attempt recovery processing.
*/
#define SIGBLOCK(gp) \
(void)sigprocmask(SIG_BLOCK, &(gp)->blockset, NULL);
#define SIGUNBLOCK(gp) \
(void)sigprocmask(SIG_UNBLOCK, &(gp)->blockset, NULL);
void busy_off __P((SCR *));
int busy_on __P((SCR *, char const *));
void sig_end __P((void));
int sig_init __P((SCR *));
/* Generic routines to start/stop a screen. */
int screen_end __P((SCR *));
int screen_init __P((SCR *, SCR **, u_int));
/* Public interfaces to the underlying screens. */
int ex_screen_copy __P((SCR *, SCR *));
int ex_screen_end __P((SCR *));
int ex_screen_init __P((SCR *));
int sex_screen_copy __P((SCR *, SCR *));
int sex_screen_end __P((SCR *));
int sex_screen_init __P((SCR *));
int svi_screen_copy __P((SCR *, SCR *));
int svi_screen_end __P((SCR *));
int svi_screen_init __P((SCR *));
int v_screen_copy __P((SCR *, SCR *));
int v_screen_end __P((SCR *));
int v_screen_init __P((SCR *));
int xaw_screen_copy __P((SCR *, SCR *));
int xaw_screen_end __P((SCR *));
int xaw_screen_init __P((SCR *));

833
usr.bin/vi/common/search.c Normal file
View File

@ -0,0 +1,833 @@
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char sccsid[] = "@(#)search.c 8.47 (Berkeley) 8/17/94";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "compat.h"
#include <db.h>
#include <regex.h>
#include "vi.h"
static int check_delta __P((SCR *, EXF *, long, recno_t));
static int ctag_conv __P((SCR *, char **, int *));
static int get_delta __P((SCR *, char **, long *, u_int *));
static int resetup __P((SCR *, regex_t **, enum direction,
char *, char **, long *, u_int *));
/*
* resetup --
* Set up a search for a regular expression.
*/
static int
resetup(sp, rep, dir, ptrn, epp, deltap, flagp)
SCR *sp;
regex_t **rep;
enum direction dir;
char *ptrn, **epp;
long *deltap;
u_int *flagp;
{
u_int flags;
int delim, eval, re_flags, replaced;
char *p, *t;
/* Set return information the default. */
*deltap = 0;
/*
* Use saved pattern if no pattern supplied, or if only a delimiter
* character is supplied. Only the pattern was saved, historic vi
* did not reuse any delta supplied.
*/
flags = *flagp;
if (ptrn == NULL)
goto prev;
if (ptrn[1] == '\0') {
if (epp != NULL)
*epp = ptrn + 1;
goto prev;
}
if (ptrn[0] == ptrn[1] && ptrn[2] == '\0') {
if (epp != NULL)
*epp = ptrn + 2;
prev: if (!F_ISSET(sp, S_SRE_SET)) {
msgq(sp, M_ERR, "No previous search pattern");
return (1);
}
*rep = &sp->sre;
/* Empty patterns set the direction. */
if (LF_ISSET(SEARCH_SET)) {
F_SET(sp, S_SRE_SET);
sp->searchdir = dir;
sp->sre = **rep;
}
return (0);
}
re_flags = 0; /* Set RE flags. */
if (O_ISSET(sp, O_EXTENDED))
re_flags |= REG_EXTENDED;
if (O_ISSET(sp, O_IGNORECASE))
re_flags |= REG_ICASE;
replaced = 0;
if (LF_ISSET(SEARCH_PARSE)) { /* Parse the string. */
/* Set delimiter. */
delim = *ptrn++;
/* Find terminating delimiter, handling escaped delimiters. */
for (p = t = ptrn;;) {
if (p[0] == '\0' || p[0] == delim) {
if (p[0] == delim)
++p;
*t = '\0';
break;
}
if (p[1] == delim && p[0] == '\\')
++p;
*t++ = *p++;
}
/*
* If characters after the terminating delimiter, it may
* be an error, or may be an offset. In either case, we
* return the end of the string, whatever it may be.
*/
if (*p) {
if (get_delta(sp, &p, deltap, flagp))
return (1);
if (*p && LF_ISSET(SEARCH_TERM)) {
msgq(sp, M_ERR,
"Characters after search string and/or delta");
return (1);
}
}
if (epp != NULL)
*epp = p;
/* Check for "/ " or other such silliness. */
if (*ptrn == '\0')
goto prev;
if (re_conv(sp, &ptrn, &replaced))
return (1);
} else if (LF_ISSET(SEARCH_TAG)) {
if (ctag_conv(sp, &ptrn, &replaced))
return (1);
re_flags &= ~(REG_EXTENDED | REG_ICASE);
}
/* Compile the RE. */
if (eval = regcomp(*rep, ptrn, re_flags))
re_error(sp, eval, *rep);
else if (LF_ISSET(SEARCH_SET)) {
F_SET(sp, S_SRE_SET);
sp->searchdir = dir;
sp->sre = **rep;
}
/* Free up any extra memory. */
if (replaced)
FREE_SPACE(sp, ptrn, 0);
return (eval);
}
/*
* ctag_conv --
* Convert a tags search path into something that the POSIX
* 1003.2 RE functions can handle.
*/
static int
ctag_conv(sp, ptrnp, replacedp)
SCR *sp;
char **ptrnp;
int *replacedp;
{
size_t blen, len;
int lastdollar;
char *bp, *p, *t;
*replacedp = 0;
len = strlen(p = *ptrnp);
/* Max memory usage is 2 times the length of the string. */
GET_SPACE_RET(sp, bp, blen, len * 2);
t = bp;
/* The last character is a '/' or '?', we just strip it. */
if (p[len - 1] == '/' || p[len - 1] == '?')
p[len - 1] = '\0';
/* The next-to-last character is a '$', and it's magic. */
if (p[len - 2] == '$') {
lastdollar = 1;
p[len - 2] = '\0';
} else
lastdollar = 0;
/* The first character is a '/' or '?', we just strip it. */
if (p[0] == '/' || p[0] == '?')
++p;
/* The second character is a '^', and it's magic. */
if (p[0] == '^')
*t++ = *p++;
/*
* Escape every other magic character we can find, stripping the
* backslashes ctags inserts to escape the search delimiter
* characters.
*/
while (p[0]) {
/* Ctags escapes the search delimiter characters. */
if (p[0] == '\\' && (p[1] == '/' || p[1] == '?'))
++p;
else if (strchr("^.[]$*", p[0]))
*t++ = '\\';
*t++ = *p++;
}
if (lastdollar)
*t++ = '$';
*t++ = '\0';
*ptrnp = bp;
*replacedp = 1;
return (0);
}
#define EMPTYMSG "File empty; nothing to search"
#define EOFMSG "Reached end-of-file without finding the pattern"
#define NOTFOUND "Pattern not found"
#define SOFMSG "Reached top-of-file without finding the pattern"
#define WRAPMSG "Search wrapped"
int
f_search(sp, ep, fm, rm, ptrn, eptrn, flagp)
SCR *sp;
EXF *ep;
MARK *fm, *rm;
char *ptrn, **eptrn;
u_int *flagp;
{
regmatch_t match[1];
regex_t *re, lre;
recno_t lno;
size_t coff, len;
long delta;
u_int flags;
int btear, eval, rval, wrapped;
char *l;
if (file_lline(sp, ep, &lno))
return (1);
flags = *flagp;
if (lno == 0) {
if (LF_ISSET(SEARCH_MSG))
msgq(sp, M_INFO, EMPTYMSG);
return (1);
}
re = &lre;
if (resetup(sp, &re, FORWARD, ptrn, eptrn, &delta, flagp))
return (1);
/*
* Start searching immediately after the cursor. If at the end of the
* line, start searching on the next line. This is incompatible (read
* bug fix) with the historic vi -- searches for the '$' pattern never
* moved forward, and "-t foo" didn't work if "foo" was the first thing
* in the file.
*/
if (LF_ISSET(SEARCH_FILE)) {
lno = 1;
coff = 0;
} else {
if ((l = file_gline(sp, ep, fm->lno, &len)) == NULL) {
GETLINE_ERR(sp, fm->lno);
return (1);
}
if (fm->cno + 1 >= len) {
if (fm->lno == lno) {
if (!O_ISSET(sp, O_WRAPSCAN)) {
if (LF_ISSET(SEARCH_MSG))
msgq(sp, M_INFO, EOFMSG);
return (1);
}
lno = 1;
} else
lno = fm->lno + 1;
coff = 0;
} else {
lno = fm->lno;
coff = fm->cno + 1;
}
}
/* Turn on busy message. */
btear = F_ISSET(sp, S_EXSILENT) ? 0 : !busy_on(sp, "Searching...");
for (rval = 1, wrapped = 0;; ++lno, coff = 0) {
if (INTERRUPTED(sp)) {
msgq(sp, M_INFO, "Interrupted.");
break;
}
if (wrapped && lno > fm->lno ||
(l = file_gline(sp, ep, lno, &len)) == NULL) {
if (wrapped) {
if (LF_ISSET(SEARCH_MSG))
msgq(sp, M_INFO, NOTFOUND);
break;
}
if (!O_ISSET(sp, O_WRAPSCAN)) {
if (LF_ISSET(SEARCH_MSG))
msgq(sp, M_INFO, EOFMSG);
break;
}
lno = 0;
wrapped = 1;
continue;
}
/* If already at EOL, just keep going. */
if (len && coff == len)
continue;
/* Set the termination. */
match[0].rm_so = coff;
match[0].rm_eo = len;
#if defined(DEBUG) && 0
TRACE(sp, "F search: %lu from %u to %u\n",
lno, coff, len ? len - 1 : len);
#endif
/* Search the line. */
eval = regexec(re, l, 1, match,
(match[0].rm_so == 0 ? 0 : REG_NOTBOL) | REG_STARTEND);
if (eval == REG_NOMATCH)
continue;
if (eval != 0) {
re_error(sp, eval, re);
break;
}
/* Warn if wrapped. */
if (wrapped && O_ISSET(sp, O_WARN) && LF_ISSET(SEARCH_MSG))
msgq(sp, M_VINFO, WRAPMSG);
/*
* If an offset, see if it's legal. It's possible to match
* past the end of the line with $, so check for that case.
*/
if (delta) {
if (check_delta(sp, ep, delta, lno))
break;
rm->lno = delta + lno;
rm->cno = 0;
} else {
#if defined(DEBUG) && 0
TRACE(sp, "found: %qu to %qu\n",
match[0].rm_so, match[0].rm_eo);
#endif
rm->lno = lno;
rm->cno = match[0].rm_so;
/*
* If a change command, it's possible to move beyond
* the end of a line. Historic vi generally got this
* wrong (try "c?$<cr>"). Not all that sure this gets
* it right, there are lots of strange cases.
*/
if (!LF_ISSET(SEARCH_EOL) && rm->cno >= len)
rm->cno = len ? len - 1 : 0;
}
rval = 0;
break;
}
/* Turn off busy message, interrupts. */
if (btear)
busy_off(sp);
return (rval);
}
int
b_search(sp, ep, fm, rm, ptrn, eptrn, flagp)
SCR *sp;
EXF *ep;
MARK *fm, *rm;
char *ptrn, **eptrn;
u_int *flagp;
{
regmatch_t match[1];
regex_t *re, lre;
recno_t lno;
size_t coff, len, last;
long delta;
u_int flags;
int btear, eval, rval, wrapped;
char *l;
if (file_lline(sp, ep, &lno))
return (1);
flags = *flagp;
if (lno == 0) {
if (LF_ISSET(SEARCH_MSG))
msgq(sp, M_INFO, EMPTYMSG);
return (1);
}
re = &lre;
if (resetup(sp, &re, BACKWARD, ptrn, eptrn, &delta, flagp))
return (1);
/* If in the first column, start searching on the previous line. */
if (fm->cno == 0) {
if (fm->lno == 1) {
if (!O_ISSET(sp, O_WRAPSCAN)) {
if (LF_ISSET(SEARCH_MSG))
msgq(sp, M_INFO, SOFMSG);
return (1);
}
} else
lno = fm->lno - 1;
} else
lno = fm->lno;
/* Turn on busy message. */
btear = F_ISSET(sp, S_EXSILENT) ? 0 : !busy_on(sp, "Searching...");
for (rval = 1, wrapped = 0, coff = fm->cno;; --lno, coff = 0) {
if (INTERRUPTED(sp)) {
msgq(sp, M_INFO, "Interrupted.");
break;
}
if (wrapped && lno < fm->lno || lno == 0) {
if (wrapped) {
if (LF_ISSET(SEARCH_MSG))
msgq(sp, M_INFO, NOTFOUND);
break;
}
if (!O_ISSET(sp, O_WRAPSCAN)) {
if (LF_ISSET(SEARCH_MSG))
msgq(sp, M_INFO, SOFMSG);
break;
}
if (file_lline(sp, ep, &lno))
goto err;
if (lno == 0) {
if (LF_ISSET(SEARCH_MSG))
msgq(sp, M_INFO, EMPTYMSG);
break;
}
++lno;
wrapped = 1;
continue;
}
if ((l = file_gline(sp, ep, lno, &len)) == NULL)
goto err;
/* Set the termination. */
match[0].rm_so = 0;
match[0].rm_eo = len;
#if defined(DEBUG) && 0
TRACE(sp, "B search: %lu from 0 to %qu\n", lno, match[0].rm_eo);
#endif
/* Search the line. */
eval = regexec(re, l, 1, match,
(match[0].rm_eo == len ? 0 : REG_NOTEOL) | REG_STARTEND);
if (eval == REG_NOMATCH)
continue;
if (eval != 0) {
re_error(sp, eval, re);
break;
}
/* Check for a match starting past the cursor. */
if (coff != 0 && match[0].rm_so >= coff)
continue;
/* Warn if wrapped. */
if (wrapped && O_ISSET(sp, O_WARN) && LF_ISSET(SEARCH_MSG))
msgq(sp, M_VINFO, WRAPMSG);
if (delta) {
if (check_delta(sp, ep, delta, lno))
break;
rm->lno = delta + lno;
rm->cno = 0;
} else {
#if defined(DEBUG) && 0
TRACE(sp, "found: %qu to %qu\n",
match[0].rm_so, match[0].rm_eo);
#endif
/*
* We now have the first match on the line. Step
* through the line character by character until we
* find the last acceptable match. This is painful,
* we need a better interface to regex to make this
* work.
*/
for (;;) {
last = match[0].rm_so++;
if (match[0].rm_so >= len)
break;
match[0].rm_eo = len;
eval = regexec(re, l, 1, match,
(match[0].rm_so == 0 ? 0 : REG_NOTBOL) |
REG_STARTEND);
if (eval == REG_NOMATCH)
break;
if (eval != 0) {
re_error(sp, eval, re);
goto err;
}
if (coff && match[0].rm_so >= coff)
break;
}
rm->lno = lno;
/* See comment in f_search(). */
if (!LF_ISSET(SEARCH_EOL) && last >= len)
rm->cno = len ? len - 1 : 0;
else
rm->cno = last;
}
rval = 0;
break;
}
/* Turn off busy message, interrupts. */
err: if (btear)
busy_off(sp);
return (rval);
}
/*
* re_conv --
* Convert vi's regular expressions into something that the
* the POSIX 1003.2 RE functions can handle.
*
* There are three conversions we make to make vi's RE's (specifically
* the global, search, and substitute patterns) work with POSIX RE's.
*
* 1: If O_MAGIC is not set, strip backslashes from the magic character
* set (.[]*~) that have them, and add them to the ones that don't.
* 2: If O_MAGIC is not set, the string "\~" is replaced with the text
* from the last substitute command's replacement string. If O_MAGIC
* is set, it's the string "~".
* 3: The pattern \<ptrn\> does "word" searches, convert it to use the
* new RE escapes.
*/
int
re_conv(sp, ptrnp, replacedp)
SCR *sp;
char **ptrnp;
int *replacedp;
{
size_t blen, needlen;
int magic;
char *bp, *p, *t;
/*
* First pass through, we figure out how much space we'll need.
* We do it in two passes, on the grounds that most of the time
* the user is doing a search and won't have magic characters.
* That way we can skip the malloc and memmove's.
*/
for (p = *ptrnp, magic = 0, needlen = 0; *p != '\0'; ++p)
switch (*p) {
case '\\':
switch (*++p) {
case '<':
magic = 1;
needlen += sizeof(RE_WSTART);
break;
case '>':
magic = 1;
needlen += sizeof(RE_WSTOP);
break;
case '~':
if (!O_ISSET(sp, O_MAGIC)) {
magic = 1;
needlen += sp->repl_len;
}
break;
case '.':
case '[':
case ']':
case '*':
if (!O_ISSET(sp, O_MAGIC)) {
magic = 1;
needlen += 1;
}
break;
default:
needlen += 2;
}
break;
case '~':
if (O_ISSET(sp, O_MAGIC)) {
magic = 1;
needlen += sp->repl_len;
}
break;
case '.':
case '[':
case ']':
case '*':
if (!O_ISSET(sp, O_MAGIC)) {
magic = 1;
needlen += 2;
}
break;
default:
needlen += 1;
break;
}
if (!magic) {
*replacedp = 0;
return (0);
}
/*
* Get enough memory to hold the final pattern.
*
* XXX
* It's nul-terminated, for now.
*/
GET_SPACE_RET(sp, bp, blen, needlen + 1);
for (p = *ptrnp, t = bp; *p != '\0'; ++p)
switch (*p) {
case '\\':
switch (*++p) {
case '<':
memmove(t, RE_WSTART, sizeof(RE_WSTART) - 1);
t += sizeof(RE_WSTART) - 1;
break;
case '>':
memmove(t, RE_WSTOP, sizeof(RE_WSTOP) - 1);
t += sizeof(RE_WSTOP) - 1;
break;
case '~':
if (O_ISSET(sp, O_MAGIC))
*t++ = '~';
else {
memmove(t, sp->repl, sp->repl_len);
t += sp->repl_len;
}
break;
case '.':
case '[':
case ']':
case '*':
if (O_ISSET(sp, O_MAGIC))
*t++ = '\\';
*t++ = *p;
break;
default:
*t++ = '\\';
*t++ = *p;
}
break;
case '~':
if (O_ISSET(sp, O_MAGIC)) {
memmove(t, sp->repl, sp->repl_len);
t += sp->repl_len;
} else
*t++ = '~';
break;
case '.':
case '[':
case ']':
case '*':
if (!O_ISSET(sp, O_MAGIC))
*t++ = '\\';
*t++ = *p;
break;
default:
*t++ = *p;
break;
}
*t = '\0';
*ptrnp = bp;
*replacedp = 1;
return (0);
}
/*
* get_delta --
* Get a line delta. The trickiness is that the delta can be pretty
* complicated, i.e. "+3-2+3++- ++" is allowed.
*
* !!!
* In historic vi, if you had a delta on a search pattern which was used as
* a motion command, the command became a line mode command regardless of the
* cursor positions. A fairly common trick is to use a delta of "+0" to make
* the command a line mode command. This is the only place that knows about
* delta's, so we set the return flag information here.
*/
static int
get_delta(sp, dp, valp, flagp)
SCR *sp;
char **dp;
long *valp;
u_int *flagp;
{
char *p;
long val, tval;
for (tval = 0, p = *dp; *p != '\0'; *flagp |= SEARCH_DELTA) {
if (isblank(*p)) {
++p;
continue;
}
if (*p == '+' || *p == '-') {
if (!isdigit(*(p + 1))) {
if (*p == '+') {
if (tval == LONG_MAX)
goto overflow;
++tval;
} else {
if (tval == LONG_MIN)
goto underflow;
--tval;
}
++p;
continue;
}
} else
if (!isdigit(*p))
break;
errno = 0;
val = strtol(p, &p, 10);
if (errno == ERANGE) {
if (val == LONG_MAX)
overflow: msgq(sp, M_ERR, "Delta value overflow");
else if (val == LONG_MIN)
underflow: msgq(sp, M_ERR, "Delta value underflow");
else
msgq(sp, M_SYSERR, NULL);
return (1);
}
if (val >= 0) {
if (LONG_MAX - val < tval)
goto overflow;
} else
if (-(LONG_MIN - tval) > val)
goto underflow;
tval += val;
}
*dp = p;
*valp = tval;
return (0);
}
/*
* check_delta --
* Check a line delta to see if it's legal.
*/
static int
check_delta(sp, ep, delta, lno)
SCR *sp;
EXF *ep;
long delta;
recno_t lno;
{
/* A delta can overflow a record number. */
if (delta < 0) {
if (lno < LONG_MAX && delta >= (long)lno) {
msgq(sp, M_ERR, "Search offset before line 1");
return (1);
}
} else {
if (ULONG_MAX - lno < delta) {
msgq(sp, M_ERR, "Delta value overflow");
return (1);
}
if (file_gline(sp, ep, lno + delta, NULL) == NULL) {
msgq(sp, M_ERR, "Search offset past end-of-file");
return (1);
}
}
return (0);
}
/*
* re_error --
* Report a regular expression error.
*/
void
re_error(sp, errcode, preg)
SCR *sp;
int errcode;
regex_t *preg;
{
size_t s;
char *oe;
s = regerror(errcode, preg, "", 0);
if ((oe = malloc(s)) == NULL)
msgq(sp, M_SYSERR, NULL);
else {
(void)regerror(errcode, preg, oe, s);
msgq(sp, M_ERR, "RE error: %s", oe);
}
free(oe);
}

View File

@ -0,0 +1,54 @@
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)search.h 8.9 (Berkeley) 3/16/94
*/
#define RE_WSTART "[[:<:]]" /* Not-in-word search patterns. */
#define RE_WSTOP "[[:>:]]"
#define SEARCH_DELTA 0x001 /* A delta part of the search.*/
#define SEARCH_EOL 0x002 /* Offset past EOL is okay. */
#define SEARCH_FILE 0x004 /* Search the entire file. */
#define SEARCH_MSG 0x008 /* Display search warning messages. */
#define SEARCH_PARSE 0x010 /* Parse the search pattern. */
#define SEARCH_SET 0x020 /* Set search direction. */
#define SEARCH_TAG 0x040 /* Search pattern is a tag pattern. */
#define SEARCH_TERM 0x080 /* Search pattern should terminate. */
enum direction { NOTSET, FORWARD, BACKWARD };
/* Search functions. */
int b_search __P((SCR *, EXF *, MARK *, MARK *, char *, char **, u_int *));
int f_search __P((SCR *, EXF *, MARK *, MARK *, char *, char **, u_int *));
int re_conv __P((SCR *, char **, int *));
void re_error __P((SCR *, int, regex_t *));

351
usr.bin/vi/common/seq.c Normal file
View File

@ -0,0 +1,351 @@
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char sccsid[] = "@(#)seq.c 8.32 (Berkeley) 8/17/94";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include "compat.h"
#include <db.h>
#include <regex.h>
#include "vi.h"
#include "excmd.h"
/*
* seq_set --
* Internal version to enter a sequence.
*/
int
seq_set(sp, name, nlen, input, ilen, output, olen, stype, flags)
SCR *sp;
CHAR_T *name, *input, *output;
size_t nlen, ilen, olen;
enum seqtype stype;
int flags;
{
CHAR_T *p;
SEQ *lastqp, *qp;
int sv_errno;
/*
* An input string must always be present. The output string
* can be NULL, when set internally, that's how we throw away
* input.
*
* Just replace the output field if the string already set.
*/
if ((qp = seq_find(sp, &lastqp, input, ilen, stype, NULL)) != NULL) {
if (output == NULL || olen == 0) {
p = NULL;
olen = 0;
} else if ((p = v_strdup(sp, output, olen)) == NULL) {
sv_errno = errno;
goto mem1;
}
if (qp->output != NULL)
free(qp->output);
qp->olen = olen;
qp->output = p;
return (0);
}
/* Allocate and initialize SEQ structure. */
CALLOC(sp, qp, SEQ *, 1, sizeof(SEQ));
if (qp == NULL) {
sv_errno = errno;
goto mem1;
}
/* Name. */
if (name == NULL || nlen == 0)
qp->name = NULL;
else if ((qp->name = v_strdup(sp, name, nlen)) == NULL) {
sv_errno = errno;
goto mem2;
}
qp->nlen = nlen;
/* Input. */
if ((qp->input = v_strdup(sp, input, ilen)) == NULL) {
sv_errno = errno;
goto mem3;
}
qp->ilen = ilen;
/* Output. */
if (output == NULL) {
qp->output = NULL;
olen = 0;
} else if ((qp->output = v_strdup(sp, output, olen)) == NULL) {
sv_errno = errno;
free(qp->input);
mem3: if (qp->name != NULL)
free(qp->name);
mem2: FREE(qp, sizeof(SEQ));
mem1: errno = sv_errno;
msgq(sp, M_SYSERR, NULL);
return (1);
}
qp->olen = olen;
/* Type, flags. */
qp->stype = stype;
qp->flags = flags;
/* Link into the chain. */
if (lastqp == NULL) {
LIST_INSERT_HEAD(&sp->gp->seqq, qp, q);
} else {
LIST_INSERT_AFTER(lastqp, qp, q);
}
/* Set the fast lookup bit. */
if (qp->input[0] < MAX_BIT_SEQ)
bit_set(sp->gp->seqb, qp->input[0]);
return (0);
}
/*
* seq_delete --
* Delete a sequence.
*/
int
seq_delete(sp, input, ilen, stype)
SCR *sp;
CHAR_T *input;
size_t ilen;
enum seqtype stype;
{
SEQ *qp;
if ((qp = seq_find(sp, NULL, input, ilen, stype, NULL)) == NULL)
return (1);
return (seq_mdel(qp));
}
/*
* seq_mdel --
* Delete a map entry, without lookup.
*/
int
seq_mdel(qp)
SEQ *qp;
{
LIST_REMOVE(qp, q);
if (qp->name != NULL)
free(qp->name);
free(qp->input);
if (qp->output != NULL)
free(qp->output);
FREE(qp, sizeof(SEQ));
return (0);
}
/*
* seq_find --
* Search the sequence list for a match to a buffer, if ispartial
* isn't NULL, partial matches count.
*/
SEQ *
seq_find(sp, lastqp, input, ilen, stype, ispartialp)
SCR *sp;
SEQ **lastqp;
CHAR_T *input;
size_t ilen;
enum seqtype stype;
int *ispartialp;
{
SEQ *lqp, *qp;
int diff;
/*
* Ispartialp is a location where we return if there was a
* partial match, i.e. if the string were extended it might
* match something.
*
* XXX
* Overload the meaning of ispartialp; only the terminal key
* search doesn't want the search limited to complete matches,
* i.e. ilen may be longer than the match.
*/
if (ispartialp != NULL)
*ispartialp = 0;
for (lqp = NULL, qp = sp->gp->seqq.lh_first;
qp != NULL; lqp = qp, qp = qp->q.le_next) {
/* Fast checks on the first character and type. */
if (qp->input[0] > input[0])
break;
if (qp->input[0] < input[0] ||
qp->stype != stype || F_ISSET(qp, SEQ_FUNCMAP))
continue;
/* Check on the real comparison. */
diff = memcmp(qp->input, input, MIN(qp->ilen, ilen));
if (diff > 0)
break;
if (diff < 0)
continue;
/*
* If the entry is the same length as the string, return a
* match. If the entry is shorter than the string, return a
* match if called from the terminal key routine. Otherwise,
* keep searching for a complete match.
*/
if (qp->ilen <= ilen) {
if (qp->ilen == ilen || ispartialp != NULL) {
if (lastqp != NULL)
*lastqp = lqp;
return (qp);
}
continue;
}
/*
* If the entry longer than the string, return partial match
* if called from the terminal key routine. Otherwise, no
* match.
*/
if (ispartialp != NULL)
*ispartialp = 1;
break;
}
if (lastqp != NULL)
*lastqp = lqp;
return (NULL);
}
/*
* seq_dump --
* Display the sequence entries of a specified type.
*/
int
seq_dump(sp, stype, isname)
SCR *sp;
enum seqtype stype;
int isname;
{
CHAR_T *p;
SEQ *qp;
int cnt, len, olen;
cnt = 0;
for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next) {
if (stype != qp->stype || F_ISSET(qp, SEQ_FUNCMAP))
continue;
++cnt;
for (p = qp->input,
olen = qp->ilen, len = 0; olen > 0; --olen, ++p)
len += ex_printf(EXCOOKIE, "%s", KEY_NAME(sp, *p));
for (len = STANDARD_TAB - len % STANDARD_TAB; len > 0;)
len -= ex_printf(EXCOOKIE, " ");
if (qp->output != NULL)
for (p = qp->output,
olen = qp->olen, len = 0; olen > 0; --olen, ++p)
len +=
ex_printf(EXCOOKIE, "%s", KEY_NAME(sp, *p));
else
len = 0;
if (isname && qp->name != NULL) {
for (len = STANDARD_TAB - len % STANDARD_TAB; len > 0;)
len -= ex_printf(EXCOOKIE, " ");
for (p = qp->name,
olen = qp->nlen; olen > 0; --olen, ++p)
(void)ex_printf(EXCOOKIE,
"%s", KEY_NAME(sp, *p));
}
(void)ex_printf(EXCOOKIE, "\n");
}
return (cnt);
}
/*
* seq_save --
* Save the sequence entries to a file.
*/
int
seq_save(sp, fp, prefix, stype)
SCR *sp;
FILE *fp;
char *prefix;
enum seqtype stype;
{
CHAR_T *p;
SEQ *qp;
size_t olen;
int ch;
/* Write a sequence command for all keys the user defined. */
for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next) {
if (stype != qp->stype ||
F_ISSET(qp, SEQ_FUNCMAP) || !F_ISSET(qp, SEQ_USERDEF))
continue;
if (prefix)
(void)fprintf(fp, "%s", prefix);
for (p = qp->input, olen = qp->ilen; olen > 0; --olen) {
ch = *p++;
if (ch == CH_LITERAL || ch == '|' ||
isblank(ch) || KEY_VAL(sp, ch) == K_NL)
(void)putc(CH_LITERAL, fp);
(void)putc(ch, fp);
}
(void)putc(' ', fp);
if (qp->output != NULL)
for (p = qp->output,
olen = qp->olen; olen > 0; --olen) {
ch = *p++;
if (ch == CH_LITERAL || ch == '|' ||
KEY_VAL(sp, ch) == K_NL)
(void)putc(CH_LITERAL, fp);
(void)putc(ch, fp);
}
(void)putc('\n', fp);
}
return (0);
}

79
usr.bin/vi/common/seq.h Normal file
View File

@ -0,0 +1,79 @@
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)seq.h 8.12 (Berkeley) 8/16/94
*/
/*
* Map and abbreviation structures.
*
* The map structure is doubly linked list, sorted by input string and by
* input length within the string. (The latter is necessary so that short
* matches will happen before long matches when the list is searched.)
* Additionally, there is a bitmap which has bits set if there are entries
* starting with the corresponding character. This keeps us from walking
* the list unless it's necessary.
*
* The name and the output fields of a SEQ can be empty, i.e. NULL.
* Only the input field is required.
*
* XXX
* The fast-lookup bits are never turned off -- users don't usually unmap
* things, though, so it's probably not a big deal.
*/
/* Sequence type. */
enum seqtype { SEQ_ABBREV, SEQ_COMMAND, SEQ_INPUT };
struct _seq {
LIST_ENTRY(_seq) q; /* Linked list of all sequences. */
enum seqtype stype; /* Sequence type. */
CHAR_T *name; /* Sequence name (if any). */
size_t nlen; /* Name length. */
CHAR_T *input; /* Sequence input keys. */
size_t ilen; /* Input keys length. */
CHAR_T *output; /* Sequence output keys. */
size_t olen; /* Output keys length. */
#define SEQ_FUNCMAP 0x01 /* If unresolved function key.*/
#define SEQ_SCREEN 0x02 /* If screen specific. */
#define SEQ_USERDEF 0x04 /* If user defined. */
u_int8_t flags;
};
int seq_delete __P((SCR *, CHAR_T *, size_t, enum seqtype));
int seq_dump __P((SCR *, enum seqtype, int));
SEQ *seq_find __P((SCR *, SEQ **, CHAR_T *, size_t, enum seqtype, int *));
void seq_init __P((SCR *));
int seq_mdel __P((SEQ *));
int seq_save __P((SCR *, FILE *, char *, enum seqtype));
int seq_set __P((SCR *, CHAR_T *, size_t,
CHAR_T *, size_t, CHAR_T *, size_t, enum seqtype, int));

569
usr.bin/vi/common/signal.c Normal file
View File

@ -0,0 +1,569 @@
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char sccsid[] = "@(#)signal.c 8.33 (Berkeley) 8/17/94";
#endif /* not lint */
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include "compat.h"
#include <db.h>
#include <regex.h>
#include "vi.h"
static void h_alrm __P((int));
static void h_hup __P((int));
static void h_int __P((int));
static void h_term __P((int));
static void h_winch __P((int));
static void sig_sync __P((int, u_int));
/*
* There are seven normally asynchronous actions about which vi cares:
* SIGALRM, SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGTSTP and SIGWINCH.
*
* The assumptions:
* 1: The DB routines are not reentrant.
* 2: The curses routines may not be reentrant.
*
* SIGALRM, SIGHUP, SIGTERM
* Used for file recovery. The DB routines can't be reentered, so
* the vi routines that call DB block all three signals (see line.c).
* This means that DB routines can be called at interrupt time.
*
* SIGALRM
* Used to paint busy messages on the screen. The curses routines
* can't be reentered, so this function of SIGALRM can only be used
* in sections of code that do not use any curses functions (see
* busy_on, busy_off in signal.c). This means that curses can be
* called at interrupt time.
*
* SIGQUIT
* Disabled by the signal initialization routines. Historically,
* ^\ switched vi into ex mode, and we continue that practice.
*
* SIGWINCH:
* The interrupt routine sets a global bit which is checked by the
* key-read routine, so there are no reentrancy issues. This means
* that the screen will not resize until vi runs out of keys, but
* that doesn't seem like a problem.
*
* SIGINT and SIGTSTP are a much more difficult issue to resolve. Vi has
* to permit the user to interrupt long-running operations. Generally, a
* search, substitution or read/write is done on a large file, or, the user
* creates a key mapping with an infinite loop. This problem will become
* worse as more complex semantics are added to vi. There are four major
* solutions on the table, each of which have minor permutations.
*
* 1: Run in raw mode.
*
* The up side is that there's no asynchronous behavior to worry about,
* and obviously no reentrancy problems. The down side is that it's easy
* to misinterpret characters (e.g. :w big_file^Mi^V^C is going to look
* like an interrupt) and it's easy to get into places where we won't see
* interrupt characters (e.g. ":map a ixx^[hxxaXXX" infinitely loops in
* historic implementations of vi). Periodically reading the terminal
* input buffer might solve the latter problem, but it's not going to be
* pretty.
*
* Also, we're going to be checking for ^C's and ^Z's both, all over
* the place -- I hate to litter the source code with that. For example,
* the historic version of vi didn't permit you to suspend the screen if
* you were on the colon command line. This isn't right. ^Z isn't a vi
* command, it's a terminal event. (Dammit.)
*
* 2: Run in cbreak mode. There are two problems in this area. First, the
* current curses implementations (both System V and Berkeley) don't give
* you clean cbreak modes. For example, the IEXTEN bit is left on, turning
* on DISCARD and LNEXT. To clarify, what vi WANTS is 8-bit clean, with
* the exception that flow control and signals are turned on, and curses
* cbreak mode doesn't give you this.
*
* We can either set raw mode and twiddle the tty, or cbreak mode and
* twiddle the tty. I chose to use raw mode, on the grounds that raw
* mode is better defined and I'm less likely to be surprised by a curses
* implementation down the road. The twiddling consists of setting ISIG,
* IXON/IXOFF, and disabling some of the interrupt characters (see the
* comments in svi/svi_screen.c). This is all found in historic System
* V (SVID 3) and POSIX 1003.1-1992, so it should be fairly portable.
*
* The second problem is that vi permits you to enter literal signal
* characters, e.g. ^V^C. There are two possible solutions. First, you
* can turn off signals when you get a ^V, but that means that a network
* packet containing ^V and ^C will lose, since the ^C may take effect
* before vi reads the ^V. (This is particularly problematic if you're
* talking over a protocol that recognizes signals locally and sends OOB
* packets when it sees them.) Second, you can turn the ^C into a literal
* character in vi, but that means that there's a race between entering
* ^V<character>^C, i.e. the sequence may end up being ^V^C<character>.
* Also, the second solution doesn't work for flow control characters, as
* they aren't delivered to the program as signals.
*
* Generally, this is what historic vi did. (It didn't have the curses
* problems because it didn't use curses.) It entered signals following
* ^V characters into the input stream, (which is why there's no way to
* enter a literal flow control character).
*
* 3: Run in mostly raw mode; turn signals on when doing an operation the
* user might want to interrupt, but leave them off most of the time.
*
* This works well for things like file reads and writes. This doesn't
* work well for trying to detect infinite maps. The problem is that
* you can write the code so that you don't have to turn on interrupts
* per keystroke, but the code isn't pretty and it's hard to make sure
* that an optimization doesn't cover up an infinite loop. This also
* requires interaction or state between the vi parser and the key
* reading routines, as an infinite loop may still be returning keys
* to the parser.
*
* Also, if the user inserts an interrupt into the tty queue while the
* interrupts are turned off, the key won't be treated as an interrupt,
* and requiring the user to pound the keyboard to catch an interrupt
* window is nasty.
*
* 4: Run in mostly raw mode, leaving signals on all of the time. Done
* by setting raw mode, and twiddling the tty's termios ISIG bit.
*
* This works well for the interrupt cases, because the code only has
* to check to see if the interrupt flag has been set, and can otherwise
* ignore signals. It's also less likely that we'll miss a case, and we
* don't have to worry about synchronizing between the vi parser and the
* key read routines.
*
* The down side is that we have to turn signals off if the user wants
* to enter a literal character (e.g. ^V^C). If the user enters the
* combination fast enough, or as part of a single network packet,
* the text input routines will treat it as a signal instead of as a
* literal character. To some extent, we have this problem already,
* since we turn off flow control so that the user can enter literal
* XON/XOFF characters.
*
* This is probably the easiest to code, and provides the smoothest
* programming interface.
*
* There are a couple of other problems to consider.
*
* First, System V's curses doesn't handle SIGTSTP correctly. If you use the
* newterm() interface, the TSTP signal will leave you in raw mode, and the
* final endwin() will leave you in the correct shell mode. If you use the
* initscr() interface, the TSTP signal will return you to the correct shell
* mode, but the final endwin() will leave you in raw mode. There you have
* it: proof that drug testing is not making any significant headway in the
* computer industry. The 4BSD curses is deficient in that it does not have
* an interface to the terminal keypad. So, regardless, we have to do our
* own SIGTSTP handling.
*
* The problem with this is that if we do our own SIGTSTP handling, in either
* models #3 or #4, we're going to have to call curses routines at interrupt
* time, which means that we might be reentering curses, which is something we
* don't want to do.
*
* Second, SIGTSTP has its own little problems. It's broadcast to the entire
* process group, not sent to a single process. The scenario goes something
* like this: the shell execs the mail program, which execs vi. The user hits
* ^Z, and all three programs get the signal, in some random order. The mail
* program goes to sleep immediately (since it probably didn't have a SIGTSTP
* handler in place). The shell gets a SIGCHLD, does a wait, and finds out
* that the only child in its foreground process group (of which it's aware)
* is asleep. It then optionally resets the terminal (because the modes aren't
* how it left them), and starts prompting the user for input. The problem is
* that somewhere in the middle of all of this, vi is resetting the terminal,
* and getting ready to send a SIGTSTP to the process group in order to put
* itself to sleep. There's a solution to all of this: when vi starts, it puts
* itself into its own process group, and then only it (and possible child
* processes) receive the SIGTSTP. This permits it to clean up the terminal
* and switch back to the original process group, where it sends that process
* group a SIGTSTP, putting everyone to sleep and waking the shell.
*
* Third, handing SIGTSTP asynchronously is further complicated by the child
* processes vi may fork off. If vi calls ex, ex resets the terminal and
* starts running some filter, and SIGTSTP stops them both, vi has to know
* when it restarts that it can't repaint the screen until ex's child has
* finished running. This is solveable, but it's annoying.
*
* Well, somebody had to make a decision, and this is the way it's going to be
* (unless I get talked out of it). SIGINT is handled asynchronously, so
* that we can pretty much guarantee that the user can interrupt any operation
* at any time. SIGTSTP is handled synchronously, so that we don't have to
* reenter curses and so that we don't have to play the process group games.
* ^Z is recognized in the standard text input and command modes. (^Z should
* also be recognized during operations that may potentially take a long time.
* The simplest solution is probably to twiddle the tty, install a handler for
* SIGTSTP, and then restore normal tty modes when the operation is complete.)
*/
/*
* sig_init --
* Initialize signals.
*/
int
sig_init(sp)
SCR *sp;
{
GS *gp;
struct sigaction act;
/* Initialize the signals. */
gp = sp->gp;
(void)sigemptyset(&gp->blockset);
/*
* Use sigaction(2), not signal(3), since we don't always want to
* restart system calls. The example is when waiting for a command
* mode keystroke and SIGWINCH arrives. Try to set the restart bit
* (SA_RESTART) on SIGALRM anyway, it should result in a lot fewer
* interruptions. We also block every other signal that we can block
* when a signal arrives. This is because the signal functions call
* other nvi functions, which aren't guaranteed to be reentrant.
*/
#ifndef SA_RESTART
#define SA_RESTART 0
#endif
#define SETSIG(signal, flags, handler) { \
if (sigaddset(&gp->blockset, signal)) \
goto err; \
act.sa_handler = handler; \
sigfillset(&act.sa_mask); \
act.sa_flags = flags; \
if (sigaction(signal, &act, NULL)) \
goto err; \
}
SETSIG(SIGALRM, SA_RESTART, h_alrm);
SETSIG(SIGHUP, 0, h_hup);
SETSIG(SIGINT, 0, h_int);
SETSIG(SIGTERM, 0, h_term);
SETSIG(SIGWINCH, 0, h_winch);
return (0);
err: msgq(sp, M_SYSERR, "signal init");
return (1);
}
/*
* sig_end --
* End signal setup.
*/
void
sig_end()
{
/*
* POSIX 1003.1-1990 requires that fork (and, presumably, vfork) clear
* pending alarms, and that the exec functions clear pending signals.
* In addition, after an exec, the child continues to ignore signals
* ignored in the parent, and the child's action for signals caught in
* the parent is set to the default action. So, as we currently don't
* ignore any signals, there's no cleanup to be done. This routine is
* left here as a stub function.
*/
return;
}
/*
* busy_on --
* Set a busy message timer.
*/
int
busy_on(sp, msg)
SCR *sp;
char const *msg;
{
struct itimerval value;
struct timeval tod;
/*
* Give the oldest busy message precedence, since it's
* the longer running operation.
*/
if (sp->busy_msg != NULL)
return (1);
/* Get the current time of day, and create a target time. */
if (gettimeofday(&tod, NULL))
return (1);
#define USER_PATIENCE_USECS (8 * 100000L)
sp->busy_tod.tv_sec = tod.tv_sec;
sp->busy_tod.tv_usec = tod.tv_usec + USER_PATIENCE_USECS;
/* We depend on this being an atomic instruction. */
sp->busy_msg = msg;
/*
* Busy messages turn around fast. Reset the timer regardless
* of its current state.
*/
value.it_value.tv_sec = 0;
value.it_value.tv_usec = USER_PATIENCE_USECS;
value.it_interval.tv_sec = 0;
value.it_interval.tv_usec = 0;
if (setitimer(ITIMER_REAL, &value, NULL))
msgq(sp, M_SYSERR, "timer: setitimer");
return (0);
}
/*
* busy_off --
* Turn off a busy message timer.
*/
void
busy_off(sp)
SCR *sp;
{
/* We depend on this being an atomic instruction. */
sp->busy_msg = NULL;
}
/*
* rcv_on --
* Turn on recovery timer.
*/
int
rcv_on(sp, ep)
SCR *sp;
EXF *ep;
{
struct itimerval value;
struct timeval tod;
/* Get the current time of day. */
if (gettimeofday(&tod, NULL))
return (1);
/* Create target time of day. */
ep->rcv_tod.tv_sec = tod.tv_sec + RCV_PERIOD;
ep->rcv_tod.tv_usec = 0;
/*
* If there's a busy message happening, we're done, the
* interrupt handler will start our timer as necessary.
*/
if (sp->busy_msg != NULL)
return (0);
value.it_value.tv_sec = RCV_PERIOD;
value.it_value.tv_usec = 0;
value.it_interval.tv_sec = 0;
value.it_interval.tv_usec = 0;
if (setitimer(ITIMER_REAL, &value, NULL)) {
msgq(sp, M_SYSERR, "timer: setitimer");
return (1);
}
return (0);
}
/*
* h_alrm --
* Handle SIGALRM.
*
* There are two uses of the ITIMER_REAL timer (SIGALRM) in nvi. The first
* is to push the recovery information out to disk at periodic intervals.
* The second is to display a "busy" message if an operation takes more time
* that users are willing to wait before seeing something happen. The SCR
* structure has a wall clock timer structure for each of these. Since the
* busy timer has a much faster timeout than the recovery timer, most of the
* code ignores the recovery timer unless it's the only thing running.
*
* XXX
* It would be nice to reimplement this with two timers, a la POSIX 1003.1,
* but not many systems offer them yet.
*/
static void
h_alrm(signo)
int signo;
{
struct itimerval value;
struct timeval ntod, tod;
SCR *sp;
EXF *ep;
int sverrno;
sverrno = errno;
/* XXX: Get the current time of day; if this fails, we're dead. */
if (gettimeofday(&tod, NULL))
goto ret;
/*
* Fire any timers that are past due, or any that are due
* in a tenth of a second or less.
*/
for (ntod.tv_sec = 0, sp = __global_list->dq.cqh_first;
sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) {
/* Check the busy timer if the msg pointer is set. */
if (sp->busy_msg == NULL)
goto skip_busy;
if (sp->busy_tod.tv_sec > tod.tv_sec ||
sp->busy_tod.tv_sec == tod.tv_sec &&
sp->busy_tod.tv_usec > tod.tv_usec &&
sp->busy_tod.tv_usec - tod.tv_usec > 100000L) {
if (ntod.tv_sec == 0 ||
ntod.tv_sec > sp->busy_tod.tv_sec ||
ntod.tv_sec == sp->busy_tod.tv_sec &&
ntod.tv_usec > sp->busy_tod.tv_usec)
ntod = sp->busy_tod;
} else {
(void)sp->s_busy(sp, sp->busy_msg);
sp->busy_msg = NULL;
}
/*
* Sync the file if the recovery timer has fired. If
* the sync fails, we don't reschedule future sync's.
*/
skip_busy: ep = sp->ep;
if (ep->rcv_tod.tv_sec < tod.tv_sec ||
ep->rcv_tod.tv_sec == tod.tv_sec &&
ep->rcv_tod.tv_usec < tod.tv_usec + 100000L) {
if (rcv_sync(sp, ep, 0))
continue;
ep->rcv_tod = tod;
ep->rcv_tod.tv_sec += RCV_PERIOD;
}
if (ntod.tv_sec == 0 ||
ntod.tv_sec > ep->rcv_tod.tv_sec ||
ntod.tv_sec == ep->rcv_tod.tv_sec &&
ntod.tv_usec > ep->rcv_tod.tv_usec)
ntod = ep->rcv_tod;
}
if (ntod.tv_sec == 0)
goto ret;
/* XXX: Set the timer; if this fails, we're dead. */
value.it_value.tv_sec = ntod.tv_sec - tod.tv_sec;
value.it_value.tv_usec = ntod.tv_usec - tod.tv_usec;
value.it_interval.tv_sec = 0;
value.it_interval.tv_usec = 0;
(void)setitimer(ITIMER_REAL, &value, NULL);
ret: errno = sverrno;
}
/*
* h_hup --
* Handle SIGHUP.
*/
static void
h_hup(signo)
int signo;
{
sig_sync(SIGHUP, RCV_EMAIL);
/* NOTREACHED */
}
/*
* h_int --
* Handle SIGINT.
*
* XXX
* This isn't right if windows are independent of each other.
*/
static void
h_int(signo)
int signo;
{
F_SET(__global_list, G_SIGINT);
}
/*
* h_term --
* Handle SIGTERM.
*/
static void
h_term(signo)
int signo;
{
sig_sync(SIGTERM, 0);
/* NOTREACHED */
}
/*
* h_winch --
* Handle SIGWINCH.
*
* XXX
* This isn't right if windows are independent of each other.
*/
static void
h_winch(signo)
int signo;
{
F_SET(__global_list, G_SIGWINCH);
}
/*
* sig_sync --
*
* Sync the files based on a signal.
*/
static void
sig_sync(signo, flags)
int signo;
u_int flags;
{
SCR *sp;
/*
* Walk the lists of screens, sync'ing the files; only sync
* each file once.
*/
for (sp = __global_list->dq.cqh_first;
sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
rcv_sync(sp, sp->ep, RCV_ENDSESSION | RCV_PRESERVE | flags);
for (sp = __global_list->hq.cqh_first;
sp != (void *)&__global_list->hq; sp = sp->q.cqe_next)
rcv_sync(sp, sp->ep, RCV_ENDSESSION | RCV_PRESERVE | flags);
/*
* Die with the proper exit status. Don't bother using
* sigaction(2) 'cause we want the default behavior.
*/
(void)signal(signo, SIG_DFL);
(void)kill(getpid(), signo);
/* NOTREACHED */
exit (1);
}

732
usr.bin/vi/common/term.c Normal file
View File

@ -0,0 +1,732 @@
/*-
* Copyright (c) 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char sccsid[] = "@(#)term.c 8.80 (Berkeley) 8/17/94";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <locale.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "compat.h"
/*
* XXX
* DON'T INCLUDE <curses.h> HERE, IT BREAKS OSF1 V2.0 WHERE IT
* CHANGES THE VALUES OF VERASE/VKILL/VWERASE TO INCORRECT ONES.
*/
#include <db.h>
#include <regex.h>
#include "vi.h"
static int keycmp __P((const void *, const void *));
static enum input term_key_queue __P((SCR *));
static void term_key_set __P((GS *, int, int));
/*
* If we're reading less than 20 characters, up the size of the tty buffer.
* This shouldn't ever happen, other than the first time through, but it's
* possible if a map is large enough.
*/
#define term_read_grow(sp, tty) \
(tty)->nelem - ((tty)->cnt + (tty)->next) >= 20 ? \
0 : __term_read_grow(sp, tty, 64)
static int __term_read_grow __P((SCR *, IBUF *, int));
/*
* !!!
* Historic vi always used:
*
* ^D: autoindent deletion
* ^H: last character deletion
* ^W: last word deletion
* ^Q: quote the next character (if not used in flow control).
* ^V: quote the next character
*
* regardless of the user's choices for these characters. The user's erase
* and kill characters worked in addition to these characters. Nvi wires
* down the above characters, but in addition permits the VEOF, VERASE, VKILL
* and VWERASE characters described by the user's termios structure.
*
* Ex was not consistent with this scheme, as it historically ran in tty
* cooked mode. This meant that the scroll command and autoindent erase
* characters were mapped to the user's EOF character, and the character
* and word deletion characters were the user's tty character and word
* deletion characters. This implementation makes it all consistent, as
* described above for vi.
*
* XXX
* THIS REQUIRES THAT ALL SCREENS SHARE A SPECIAL KEY SET.
*/
KEYLIST keylist[] = {
{K_CARAT, '^'}, /* ^ */
{K_CNTRLD, '\004'}, /* ^D */
{K_CNTRLR, '\022'}, /* ^R */
{K_CNTRLT, '\024'}, /* ^T */
{K_CNTRLZ, '\032'}, /* ^Z */
{K_COLON, ':'}, /* : */
{K_CR, '\r'}, /* \r */
{K_ESCAPE, '\033'}, /* ^[ */
{K_FORMFEED, '\f'}, /* \f */
{K_HEXCHAR, '\030'}, /* ^X */
{K_NL, '\n'}, /* \n */
{K_RIGHTBRACE, '}'}, /* } */
{K_RIGHTPAREN, ')'}, /* ) */
{K_TAB, '\t'}, /* \t */
{K_VERASE, '\b'}, /* \b */
{K_VKILL, '\025'}, /* ^U */
{K_VLNEXT, '\021'}, /* ^Q */
{K_VLNEXT, '\026'}, /* ^V */
{K_VWERASE, '\027'}, /* ^W */
{K_ZERO, '0'}, /* 0 */
{K_NOTUSED, 0}, /* VEOF, VERASE, VKILL, VWERASE */
{K_NOTUSED, 0},
{K_NOTUSED, 0},
{K_NOTUSED, 0},
};
static int nkeylist = (sizeof(keylist) / sizeof(keylist[0])) - 4;
/*
* term_init --
* Initialize the special key lookup table.
*/
int
term_init(sp)
SCR *sp;
{
GS *gp;
KEYLIST *kp;
int cnt;
/*
* XXX
* 8-bit only, for now. Recompilation should get you any
* 8-bit character set, as long as nul isn't a character.
*/
(void)setlocale(LC_ALL, "");
key_init(sp);
gp = sp->gp;
#ifdef VEOF
term_key_set(gp, VEOF, K_CNTRLD);
#endif
#ifdef VERASE
term_key_set(gp, VERASE, K_VERASE);
#endif
#ifdef VKILL
term_key_set(gp, VKILL, K_VKILL);
#endif
#ifdef VWERASE
term_key_set(gp, VWERASE, K_VWERASE);
#endif
/* Sort the special key list. */
qsort(keylist, nkeylist, sizeof(keylist[0]), keycmp);
/* Initialize the fast lookup table. */
for (gp->max_special = 0, kp = keylist, cnt = nkeylist; cnt--; ++kp) {
if (gp->max_special < kp->value)
gp->max_special = kp->value;
if (kp->ch <= MAX_FAST_KEY)
gp->special_key[kp->ch] = kp->value;
}
return (0);
}
/*
* term_key_set --
* Set keys found in the termios structure. VERASE and VKILL are required
* by POSIX 1003.1-1990, VWERASE is a 4.4BSD extension. We've left three
* open slots in the keylist table, if these values exist, put them into
* place. Note, they may reset (or duplicate) values already in the table,
* so we check for that first.
*/
static void
term_key_set(gp, name, val)
GS *gp;
int name, val;
{
KEYLIST *kp;
cc_t ch;
if (!F_ISSET(gp, G_TERMIOS_SET))
return;
if ((ch = gp->original_termios.c_cc[name]) == _POSIX_VDISABLE)
return;
/* Check for duplication. */
for (kp = keylist; kp->value != K_NOTUSED; ++kp)
if (kp->ch == ch) {
kp->value = val;
return;
}
/* Add a new entry. */
if (kp->value == K_NOTUSED) {
keylist[nkeylist].ch = ch;
keylist[nkeylist].value = val;
++nkeylist;
}
}
/*
* key_init --
* Build the fast-lookup key display array.
*/
void
key_init(sp)
SCR *sp;
{
CHAR_T ch;
for (ch = 0; ch <= MAX_FAST_KEY; ++ch) {
(void)__key_name(sp, ch);
(void)memmove(sp->gp->cname[ch].name, sp->cname, sp->clen);
sp->gp->cname[ch].len = sp->clen;
}
}
/*
* __key_len --
* Return the length of the string that will display the key.
* This routine is the backup for the KEY_LEN() macro.
*/
size_t
__key_len(sp, ch)
SCR *sp;
ARG_CHAR_T ch;
{
(void)__key_name(sp, ch);
return (sp->clen);
}
/*
* __key_name --
* Return the string that will display the key. This routine
* is the backup for the KEY_NAME() macro.
*/
CHAR_T *
__key_name(sp, ach)
SCR *sp;
ARG_CHAR_T ach;
{
static const CHAR_T hexdigit[] = "0123456789abcdef";
static const CHAR_T octdigit[] = "01234567";
CHAR_T ch, *chp, mask;
size_t len;
int cnt, shift;
/*
* Historical (ARPA standard) mappings. Printable characters are left
* alone. Control characters less than '\177' are represented as '^'
* followed by the character offset from the '@' character in the ASCII
* map. '\177' is represented as '^' followed by '?'.
*
* XXX
* The following code depends on the current locale being identical to
* the ASCII map from '\100' to '\076' (\076 since that's the largest
* character for which we can offset from '@' and get something that's
* a printable character in ASCII. I'm told that this is a reasonable
* assumption...
*
* XXX
* This code will only work with CHAR_T's that are multiples of 8-bit
* bytes.
*
* XXX
* NB: There's an assumption here that all printable characters take
* up a single column on the screen. This is not always correct.
*/
ch = ach;
if (isprint(ch)) {
sp->cname[0] = ch;
len = 1;
} else if (ch <= '\076' && iscntrl(ch)) {
sp->cname[0] = '^';
sp->cname[1] = ch == '\177' ? '?' : '@' + ch;
len = 2;
} else if (O_ISSET(sp, O_OCTAL)) {
#define BITS (sizeof(CHAR_T) * 8)
#define SHIFT (BITS - BITS % 3)
#define TOPMASK (BITS % 3 == 2 ? 3 : 1) << (BITS - BITS % 3)
sp->cname[0] = '\\';
sp->cname[1] = octdigit[(ch & TOPMASK) >> SHIFT];
shift = SHIFT - 3;
for (len = 2, mask = 7 << (SHIFT - 3),
cnt = BITS / 3; cnt-- > 0; mask >>= 3, shift -= 3)
sp->cname[len++] = octdigit[(ch & mask) >> shift];
} else {
sp->cname[0] = '0';
sp->cname[1] = 'x';
for (len = 2, chp = (u_int8_t *)&ch,
cnt = sizeof(CHAR_T); cnt-- > 0; ++chp) {
sp->cname[len++] = hexdigit[(*chp & 0xf0) >> 4];
sp->cname[len++] = hexdigit[*chp & 0x0f];
}
}
sp->cname[sp->clen = len] = '\0';
return (sp->cname);
}
/*
* term_push --
* Push keys onto the front of a buffer.
*
* There is a single input buffer in ex/vi. Characters are read onto the
* end of the buffer by the terminal input routines, and pushed onto the
* front of the buffer by various other functions in ex/vi. Each key has
* an associated flag value, which indicates if it has already been quoted,
* if it is the result of a mapping or an abbreviation, as well as a count
* of the number of times it has been mapped.
*/
int
term_push(sp, s, nchars, flags)
SCR *sp;
CHAR_T *s; /* Characters. */
size_t nchars; /* Number of chars. */
u_int flags; /* CH_* flags. */
{
IBUF *tty;
size_t total;
/* If we have room, stuff the keys into the buffer. */
tty = sp->gp->tty;
if (nchars <= tty->next ||
(tty->ch != NULL && tty->cnt == 0 && nchars <= tty->nelem)) {
if (tty->cnt != 0)
tty->next -= nchars;
tty->cnt += nchars;
MEMMOVE(tty->ch + tty->next, s, nchars);
MEMSET(tty->chf + tty->next, flags, nchars);
return (0);
}
/*
* If there are currently characters in the queue, shift them up,
* leaving some extra room. Get enough space plus a little extra.
*/
#define TERM_PUSH_SHIFT 30
total = tty->cnt + tty->next + nchars + TERM_PUSH_SHIFT;
if (total >= tty->nelem && __term_read_grow(sp, tty, MAX(total, 64)))
return (1);
if (tty->cnt) {
MEMMOVE(tty->ch + TERM_PUSH_SHIFT + nchars,
tty->ch + tty->next, tty->cnt);
MEMMOVE(tty->chf + TERM_PUSH_SHIFT + nchars,
tty->chf + tty->next, tty->cnt);
}
/* Put the new characters into the queue. */
tty->next = TERM_PUSH_SHIFT;
tty->cnt += nchars;
MEMMOVE(tty->ch + TERM_PUSH_SHIFT, s, nchars);
MEMSET(tty->chf + TERM_PUSH_SHIFT, flags, nchars);
return (0);
}
/*
* Remove characters from the queue, simultaneously clearing the flag
* and map counts.
*/
#define QREM_HEAD(q, len) { \
size_t __off = (q)->next; \
if (len == 1) \
tty->chf[__off] = 0; \
else \
MEMSET(tty->chf + __off, 0, len); \
if (((q)->cnt -= len) == 0) \
(q)->next = 0; \
else \
(q)->next += len; \
}
#define QREM_TAIL(q, len) { \
size_t __off = (q)->next + (q)->cnt - 1; \
if (len == 1) \
tty->chf[__off] = 0; \
else \
MEMSET(tty->chf + __off, 0, len); \
if (((q)->cnt -= len) == 0) \
(q)->next = 0; \
}
/*
* term_key --
* Get the next key.
*
* !!!
* The flag TXT_MAPNODIGIT probably needs some explanation. First, the idea
* of mapping keys is that one or more keystrokes act like a function key.
* What's going on is that vi is reading a number, and the character following
* the number may or may not be mapped (TXT_MAPCOMMAND). For example, if the
* user is entering the z command, a valid command is "z40+", and we don't want
* to map the '+', i.e. if '+' is mapped to "xxx", we don't want to change it
* into "z40xxx". However, if the user enters "35x", we want to put all of the
* characters through the mapping code.
*
* Historical practice is a bit muddled here. (Surprise!) It always permitted
* mapping digits as long as they weren't the first character of the map, e.g.
* ":map ^A1 xxx" was okay. It also permitted the mapping of the digits 1-9
* (the digit 0 was a special case as it doesn't indicate the start of a count)
* as the first character of the map, but then ignored those mappings. While
* it's probably stupid to map digits, vi isn't your mother.
*
* The way this works is that the TXT_MAPNODIGIT causes term_key to return the
* end-of-digit without "looking" at the next character, i.e. leaving it as the
* user entered it. Presumably, the next term_key call will tell us how the
* user wants it handled.
*
* There is one more complication. Users might map keys to digits, and, as
* it's described above, the commands "map g 1G|d2g" would return the keys
* "d2<end-of-digits>1G", when the user probably wanted "d21<end-of-digits>G".
* So, if a map starts off with a digit we continue as before, otherwise, we
* pretend that we haven't mapped the character and return <end-of-digits>.
*
* Now that that's out of the way, let's talk about Energizer Bunny macros.
* It's easy to create macros that expand to a loop, e.g. map x 3x. It's
* fairly easy to detect this example, because it's all internal to term_key.
* If we're expanding a macro and it gets big enough, at some point we can
* assume it's looping and kill it. The examples that are tough are the ones
* where the parser is involved, e.g. map x "ayyx"byy. We do an expansion
* on 'x', and get "ayyx"byy. We then return the first 4 characters, and then
* find the looping macro again. There is no way that we can detect this
* without doing a full parse of the command, because the character that might
* cause the loop (in this case 'x') may be a literal character, e.g. the map
* map x "ayy"xyy"byy is perfectly legal and won't cause a loop.
*
* Historic vi tried to detect looping macros by disallowing obvious cases in
* the map command, maps that that ended with the same letter as they started
* (which wrongly disallowed "map x 'x"), and detecting macros that expanded
* too many times before keys were returned to the command parser. It didn't
* get many (most?) of the tricky cases right, however, and it was certainly
* possible to create macros that ran forever. And, even if it did figure out
* what was going on, the user was usually tossed into ex mode. Finally, any
* changes made before vi realized that the macro was recursing were left in
* place. We recover gracefully, but the only recourse the user has in an
* infinite macro loop is to interrupt.
*
* !!!
* It is historic practice that mapping characters to themselves as the first
* part of the mapped string was legal, and did not cause infinite loops, i.e.
* ":map! { {^M^T" and ":map n nz." were known to work. The initial, matching
* characters were returned instead of being remapped.
*
* XXX
* The final issue is recovery. It would be possible to undo all of the work
* that was done by the macro if we entered a record into the log so that we
* knew when the macro started, and, in fact, this might be worth doing at some
* point. Given that this might make the log grow unacceptably (consider that
* cursor keys are done with maps), for now we leave any changes made in place.
*/
enum input
term_key(sp, chp, flags)
SCR *sp;
CH *chp;
u_int flags;
{
enum input rval;
struct timeval t, *tp;
CHAR_T ch;
GS *gp;
IBUF *tty;
SEQ *qp;
int init_nomap, ispartial, nr;
/* If we've been interrupted, return an error. */
if (INTERRUPTED(sp))
return (INP_INTR);
gp = sp->gp;
tty = gp->tty;
/*
* If the queue is empty, read more keys in. Since no timeout is
* requested, s_key_read will either return an error or will read
* some number of characters.
*/
loop: if (tty->cnt == 0) {
if (term_read_grow(sp, tty))
return (INP_ERR);
if ((rval = sp->s_key_read(sp, &nr, NULL)) != INP_OK)
return (rval);
/*
* If there's something on the mode line that we wanted
* the user to see, they just entered a character so we
* can presume they saw it.
*/
if (F_ISSET(sp, S_UPDATE_MODE))
F_CLR(sp, S_UPDATE_MODE);
}
/* If the key is mappable and should be mapped, look it up. */
if (!(tty->chf[tty->next] & CH_NOMAP) &&
LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT)) {
/* Set up timeout value. */
if (O_ISSET(sp, O_TIMEOUT)) {
tp = &t;
t.tv_sec = O_VAL(sp, O_KEYTIME) / 10;
t.tv_usec = (O_VAL(sp, O_KEYTIME) % 10) * 100000L;
} else
tp = NULL;
/* Get the next key. */
newmap: ch = tty->ch[tty->next];
if (ch < MAX_BIT_SEQ && !bit_test(gp->seqb, ch))
goto nomap;
/* Search the map. */
remap: qp = seq_find(sp, NULL, &tty->ch[tty->next], tty->cnt,
LF_ISSET(TXT_MAPCOMMAND) ? SEQ_COMMAND : SEQ_INPUT,
&ispartial);
/* If we've been interrupted, return an error. */
if (INTERRUPTED(sp))
return (INP_INTR);
/*
* If get a partial match, read more characters and retry
* the map. If no characters read, return the characters
* unmapped.
*/
if (ispartial) {
if (term_read_grow(sp, tty))
return (INP_ERR);
if ((rval = sp->s_key_read(sp, &nr, tp)) != INP_OK)
return (rval);
if (nr)
goto remap;
goto nomap;
}
/* If no map, return the character. */
if (qp == NULL)
goto nomap;
/*
* If looking for the end of a digit string, and the first
* character of the map is it, pretend we haven't seen the
* character.
*/
if (LF_ISSET(TXT_MAPNODIGIT) &&
qp->output != NULL && !isdigit(qp->output[0]))
goto not_digit_ch;
/* Find out if the initial segments are identical. */
init_nomap = !memcmp(&tty->ch[tty->next], qp->output, qp->ilen);
/* Delete the mapped characters from the queue. */
QREM_HEAD(tty, qp->ilen);
/* If keys mapped to nothing, go get more. */
if (qp->output == NULL)
goto loop;
/* If remapping characters, push the character on the queue. */
if (O_ISSET(sp, O_REMAP)) {
if (init_nomap) {
if (term_push(sp, qp->output + qp->ilen,
qp->olen - qp->ilen, CH_MAPPED))
return (INP_ERR);
if (term_push(sp,
qp->output, qp->ilen, CH_NOMAP | CH_MAPPED))
return (INP_ERR);
goto nomap;
} else
if (term_push(sp,
qp->output, qp->olen, CH_MAPPED))
return (INP_ERR);
goto newmap;
}
/* Else, push the characters on the queue and return one. */
if (term_push(sp, qp->output, qp->olen, CH_MAPPED | CH_NOMAP))
return (INP_ERR);
}
nomap: ch = tty->ch[tty->next];
if (LF_ISSET(TXT_MAPNODIGIT) && !isdigit(ch)) {
not_digit_ch: chp->ch = CH_NOT_DIGIT;
chp->value = 0;
chp->flags = 0;
return (INP_OK);
}
/* Fill in the return information. */
chp->ch = ch;
chp->flags = tty->chf[tty->next];
chp->value = KEY_VAL(sp, ch);
/* Delete the character from the queue. */
QREM_HEAD(tty, 1);
return (INP_OK);
}
/*
* term_flush --
* Flush any flagged keys.
*/
void
term_flush(sp, msg, flags)
SCR *sp;
char *msg;
u_int flags;
{
IBUF *tty;
tty = sp->gp->tty;
if (!tty->cnt || !(tty->chf[tty->next] & flags))
return;
do {
QREM_HEAD(tty, 1);
} while (tty->cnt && tty->chf[tty->next] & flags);
msgq(sp, M_ERR, "%s: keys flushed", msg);
}
/*
* term_user_key --
* Get the next key, but require the user enter one.
*/
enum input
term_user_key(sp, chp)
SCR *sp;
CH *chp;
{
enum input rval;
IBUF *tty;
int nr;
/*
* Read any keys the user has waiting. Make the race
* condition as short as possible.
*/
if ((rval = term_key_queue(sp)) != INP_OK)
return (rval);
/* Wait and read another key. */
if ((rval = sp->s_key_read(sp, &nr, NULL)) != INP_OK)
return (rval);
/* Fill in the return information. */
tty = sp->gp->tty;
chp->ch = tty->ch[tty->next + (tty->cnt - 1)];
chp->flags = 0;
chp->value = KEY_VAL(sp, chp->ch);
QREM_TAIL(tty, 1);
return (INP_OK);
}
/*
* term_key_queue --
* Read the keys off of the terminal queue until it's empty.
*/
static enum input
term_key_queue(sp)
SCR *sp;
{
enum input rval;
struct timeval t;
IBUF *tty;
int nr;
t.tv_sec = 0;
t.tv_usec = 0;
for (tty = sp->gp->tty;;) {
if (term_read_grow(sp, tty))
return (INP_ERR);
if ((rval = sp->s_key_read(sp, &nr, &t)) != INP_OK)
return (rval);
if (nr == 0)
break;
}
return (INP_OK);
}
/*
* __key_val --
* Fill in the value for a key. This routine is the backup
* for the KEY_VAL() macro.
*/
int
__key_val(sp, ch)
SCR *sp;
ARG_CHAR_T ch;
{
KEYLIST k, *kp;
k.ch = ch;
kp = bsearch(&k, keylist, nkeylist, sizeof(keylist[0]), keycmp);
return (kp == NULL ? K_NOTUSED : kp->value);
}
/*
* __term_read_grow --
* Grow the terminal queue. This routine is the backup for
* the term_read_grow() macro.
*/
static int
__term_read_grow(sp, tty, add)
SCR *sp;
IBUF *tty;
int add;
{
size_t new_nelem, olen;
new_nelem = tty->nelem + add;
olen = tty->nelem * sizeof(tty->ch[0]);
BINC_RET(sp, tty->ch, olen, new_nelem * sizeof(tty->ch[0]));
olen = tty->nelem * sizeof(tty->chf[0]);
BINC_RET(sp, tty->chf, olen, new_nelem * sizeof(tty->chf[0]));
tty->nelem = olen / sizeof(tty->chf[0]);
return (0);
}
static int
keycmp(ap, bp)
const void *ap, *bp;
{
return (((KEYLIST *)ap)->ch - ((KEYLIST *)bp)->ch);
}

205
usr.bin/vi/common/term.h Normal file
View File

@ -0,0 +1,205 @@
/*-
* Copyright (c) 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)term.h 8.48 (Berkeley) 7/25/94
*/
/*
* Fundamental character types.
*
* CHAR_T An integral type that can hold any character.
* ARG_CHAR_T The type of a CHAR_T when passed as an argument using
* traditional promotion rules. It should also be able
* to be compared against any CHAR_T for equality without
* problems.
* MAX_CHAR_T The maximum value of any character.
*
* If no integral type can hold a character, don't even try the port.
*/
typedef u_char CHAR_T;
typedef u_int ARG_CHAR_T;
#define MAX_CHAR_T 0xff
/* The maximum number of columns any character can take up on a screen. */
#define MAX_CHARACTER_COLUMNS 4
/* Structure to return a character and associated information. */
struct _ch {
CHAR_T ch; /* Character. */
#define K_NOTUSED 0
#define K_CARAT 1
#define K_CNTRLD 2
#define K_CNTRLR 3
#define K_CNTRLT 4
#define K_CNTRLZ 5
#define K_COLON 6
#define K_CR 7
#define K_ESCAPE 8
#define K_FORMFEED 9
#define K_HEXCHAR 10
#define K_NL 11
#define K_RIGHTBRACE 12
#define K_RIGHTPAREN 13
#define K_TAB 14
#define K_VERASE 15
#define K_VKILL 16
#define K_VLNEXT 17
#define K_VWERASE 18
#define K_ZERO 19
u_int8_t value; /* Special character flag values. */
#define CH_ABBREVIATED 0x01 /* Character from an abbreviation. */
#define CH_MAPPED 0x02 /* Character from a map. */
#define CH_NOMAP 0x04 /* Do not map the character. */
#define CH_QUOTED 0x08 /* Character is already quoted. */
u_int8_t flags;
};
typedef struct _keylist {
u_int8_t value; /* Special value. */
CHAR_T ch; /* Key. */
} KEYLIST;
extern KEYLIST keylist[];
/* Structure for the key input buffer. */
struct _ibuf {
CHAR_T *ch; /* Array of characters. */
u_int8_t *chf; /* Array of character flags (CH_*). */
size_t cnt; /* Count of remaining characters. */
size_t nelem; /* Numer of array elements. */
size_t next; /* Offset of next array entry. */
};
/* Return if more keys in queue. */
#define KEYS_WAITING(sp) ((sp)->gp->tty->cnt)
#define MAPPED_KEYS_WAITING(sp) \
(KEYS_WAITING(sp) && sp->gp->tty->chf[sp->gp->tty->next] & CH_MAPPED)
/*
* Routines that return a key as a side-effect return:
*
* INP_OK Returning a character; must be 0.
* INP_EOF EOF.
* INP_ERR Error.
* INP_INTR Interrupted.
*
* The vi structure depends on the key routines being able to return INP_EOF
* multiple times without failing -- eventually enough things will end due to
* INP_EOF that vi will reach the command level for the screen, at which point
* the exit flags will be set and vi will exit.
*/
enum input { INP_OK=0, INP_EOF, INP_ERR, INP_INTR };
/*
* Routines that return a confirmation return:
*
* CONF_NO User answered no.
* CONF_QUIT User answered quit, eof or an error.
* CONF_YES User answered yes.
*/
enum confirm { CONF_NO, CONF_QUIT, CONF_YES };
/*
* Ex/vi commands are generally separated by whitespace characters. We
* can't use the standard isspace(3) macro because it returns true for
* characters like ^K in the ASCII character set. The 4.4BSD isblank(3)
* macro does exactly what we want, but it's not portable yet.
*
* XXX
* Note side effect, ch is evaluated multiple times.
*/
#ifndef isblank
#define isblank(ch) ((ch) == ' ' || (ch) == '\t')
#endif
/* The "standard" tab width, for displaying things to users. */
#define STANDARD_TAB 6
/* Various special characters, messages. */
#define CH_BSEARCH '?' /* Backward search prompt. */
#define CH_CURSOR ' ' /* Cursor character. */
#define CH_ENDMARK '$' /* End of a range. */
#define CH_EXPROMPT ':' /* Ex prompt. */
#define CH_FSEARCH '/' /* Forward search prompt. */
#define CH_HEX '\030' /* Leading hex character. */
#define CH_LITERAL '\026' /* ASCII ^V. */
#define CH_NO 'n' /* No. */
#define CH_NOT_DIGIT 'a' /* A non-isdigit() character. */
#define CH_QUIT 'q' /* Quit. */
#define CH_YES 'y' /* Yes. */
#define STR_CONFIRM "confirm? [ynq]"
#define STR_CMSG "Enter return to continue: "
#define STR_QMSG "Enter return to continue [q to quit]: "
/* Flags describing how input is handled. */
#define TXT_AICHARS 0x00000001 /* Leading autoindent chars. */
#define TXT_ALTWERASE 0x00000002 /* Option: altwerase. */
#define TXT_APPENDEOL 0x00000004 /* Appending after EOL. */
#define TXT_AUTOINDENT 0x00000008 /* Autoindent set this line. */
#define TXT_BACKSLASH 0x00000010 /* Backslashes escape characters. */
#define TXT_BEAUTIFY 0x00000020 /* Only printable characters. */
#define TXT_BS 0x00000040 /* Backspace returns the buffer. */
#define TXT_CNTRLD 0x00000080 /* Control-D is a special command. */
#define TXT_CNTRLT 0x00000100 /* Control-T is an indent special. */
#define TXT_CR 0x00000200 /* CR returns the buffer. */
#define TXT_DOTTERM 0x00000400 /* Leading '.' terminates the input. */
#define TXT_EMARK 0x00000800 /* End of replacement mark. */
#define TXT_ESCAPE 0x00001000 /* Escape returns the buffer. */
#define TXT_EXSUSPEND 0x00002000 /* ^Z should suspend the session. */
#define TXT_INFOLINE 0x00004000 /* Editing the info line. */
#define TXT_MAPCOMMAND 0x00008000 /* Apply the command map. */
#define TXT_MAPINPUT 0x00010000 /* Apply the input map. */
#define TXT_MAPNODIGIT 0x00020000 /* Return to a digit. */
#define TXT_NLECHO 0x00040000 /* Echo the newline. */
#define TXT_OVERWRITE 0x00080000 /* Overwrite characters. */
#define TXT_PROMPT 0x00100000 /* Display a prompt. */
#define TXT_RECORD 0x00200000 /* Record for replay. */
#define TXT_REPLACE 0x00400000 /* Replace; don't delete overwrite. */
#define TXT_REPLAY 0x00800000 /* Replay the last input. */
#define TXT_RESOLVE 0x01000000 /* Resolve the text into the file. */
#define TXT_SHOWMATCH 0x02000000 /* Option: showmatch. */
#define TXT_TTYWERASE 0x04000000 /* Option: ttywerase. */
#define TXT_WRAPMARGIN 0x08000000 /* Option: wrapmargin. */
/* Support keyboard routines. */
size_t __key_len __P((SCR *, ARG_CHAR_T));
CHAR_T *__key_name __P((SCR *, ARG_CHAR_T));
int __key_val __P((SCR *, ARG_CHAR_T));
void key_init __P((SCR *));
void term_flush __P((SCR *, char *, u_int));
enum input term_key __P((SCR *, CH *, u_int));
enum input term_user_key __P((SCR *, CH *));
int term_init __P((SCR *));
int term_push __P((SCR *, CHAR_T *, size_t, u_int));

84
usr.bin/vi/common/trace.c Normal file
View File

@ -0,0 +1,84 @@
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)trace.c 8.2 (Berkeley) 3/8/94
*/
#ifdef DEBUG
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <termios.h>
#include "compat.h"
#include <db.h>
#include <regex.h>
#include "vi.h"
#ifdef __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif
void
#ifdef __STDC__
TRACE(SCR *sp, const char *fmt, ...)
#else
TRACE(sp, fmt, va_alist)
SCR *sp;
char *fmt;
va_dcl
#endif
{
FILE *tfp;
va_list ap;
if ((tfp = sp->gp->tracefp) == NULL)
return;
#ifdef __STDC__
va_start(ap, fmt);
#else
va_start(ap);
#endif
(void)vfprintf(tfp, fmt, ap);
va_end(ap);
(void)fflush(tfp);
}
#endif

213
usr.bin/vi/common/util.c Normal file
View File

@ -0,0 +1,213 @@
/*-
* Copyright (c) 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char sccsid[] = "@(#)util.c 8.73 (Berkeley) 8/17/94";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "compat.h"
#include <curses.h>
#include <db.h>
#include <regex.h>
#include "vi.h"
/*
* binc --
* Increase the size of a buffer.
*/
void *
binc(sp, bp, bsizep, min)
SCR *sp; /* sp MAY BE NULL!!! */
void *bp;
size_t *bsizep, min;
{
size_t csize;
/* If already larger than the minimum, just return. */
if (min && *bsizep >= min)
return (bp);
csize = *bsizep + MAX(min, 256);
REALLOC(sp, bp, void *, csize);
if (bp == NULL) {
/*
* Theoretically, realloc is supposed to leave any already
* held memory alone if it can't get more. Don't trust it.
*/
*bsizep = 0;
return (NULL);
}
/*
* Memory is guaranteed to be zero-filled, various parts of
* nvi depend on this.
*/
memset((char *)bp + *bsizep, 0, csize - *bsizep);
*bsizep = csize;
return (bp);
}
/*
* nonblank --
* Set the column number of the first non-blank character
* including or after the starting column. On error, set
* the column to 0, it's safest.
*/
int
nonblank(sp, ep, lno, cnop)
SCR *sp;
EXF *ep;
recno_t lno;
size_t *cnop;
{
char *p;
size_t cnt, len, off;
/* Default. */
off = *cnop;
*cnop = 0;
/* Get the line. */
if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
if (file_lline(sp, ep, &lno))
return (1);
if (lno == 0)
return (0);
GETLINE_ERR(sp, lno);
return (1);
}
/* Set the offset. */
if (len == 0 || off >= len)
return (0);
for (cnt = off, p = &p[off],
len -= off; len && isblank(*p); ++cnt, ++p, --len);
/* Set the return. */
*cnop = len ? cnt : cnt - 1;
return (0);
}
/*
* tail --
* Return tail of a path.
*/
char *
tail(path)
char *path;
{
char *p;
if ((p = strrchr(path, '/')) == NULL)
return (path);
return (p + 1);
}
/*
* set_alt_name --
* Set the alternate file name.
*
* Swap the alternate file name. It's a routine because I wanted some place
* to hang this comment. The alternate file name (normally referenced using
* the special character '#' during file expansion) is set by many
* operations. In the historic vi, the commands "ex", and "edit" obviously
* set the alternate file name because they switched the underlying file.
* Less obviously, the "read", "file", "write" and "wq" commands set it as
* well. In this implementation, some new commands have been added to the
* list. Where it gets interesting is that the alternate file name is set
* multiple times by some commands. If an edit attempt fails (for whatever
* reason, like the current file is modified but as yet unwritten), it is
* set to the file name that the user was unable to edit. If the edit
* succeeds, it is set to the last file name that was edited. Good fun.
*
* If the user edits a temporary file, there are time when there isn't an
* alternative file name. A name argument of NULL turns it off.
*/
void
set_alt_name(sp, name)
SCR *sp;
char *name;
{
if (sp->alt_name != NULL)
free(sp->alt_name);
if (name == NULL)
sp->alt_name = NULL;
else if ((sp->alt_name = strdup(name)) == NULL)
msgq(sp, M_SYSERR, NULL);
}
/*
* v_strdup --
* Strdup for wide character strings with an associated length.
*/
CHAR_T *
v_strdup(sp, str, len)
SCR *sp;
CHAR_T *str;
size_t len;
{
CHAR_T *copy;
MALLOC(sp, copy, CHAR_T *, len + 1);
if (copy == NULL)
return (NULL);
memmove(copy, str, len * sizeof(CHAR_T));
copy[len] = '\0';
return (copy);
}
/*
* vi_putchar --
* Functional version of putchar, for tputs.
*/
void
vi_putchar(ch)
int ch;
{
(void)putchar(ch);
}

124
usr.bin/vi/common/vi.h Normal file
View File

@ -0,0 +1,124 @@
/*-
* Copyright (c) 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)vi.h 8.46 (Berkeley) 8/8/94
*/
/*
* Forward structure declarations. Not pretty, but the include files
* are far too interrelated for a clean solution.
*/
typedef struct _cb CB;
typedef struct _ch CH;
typedef struct _excmdarg EXCMDARG;
typedef struct _exf EXF;
typedef struct _fref FREF;
typedef struct _gs GS;
typedef struct _ibuf IBUF;
typedef struct _lmark LMARK;
typedef struct _mark MARK;
typedef struct _msg MSG;
typedef struct _option OPTION;
typedef struct _optlist OPTLIST;
typedef struct _scr SCR;
typedef struct _script SCRIPT;
typedef struct _seq SEQ;
typedef struct _tag TAG;
typedef struct _tagf TAGF;
typedef struct _text TEXT;
/*
* Local includes.
*/
#include "term.h" /* Required by args.h. */
#include "args.h" /* Required by options.h. */
#include "options.h" /* Required by screen.h. */
#include "search.h" /* Required by screen.h. */
#include "msg.h" /* Required by gs.h. */
#include "cut.h" /* Required by gs.h. */
#include "seq.h" /* Required by screen.h. */
#include "gs.h" /* Required by screen.h. */
#include "screen.h" /* Required by exf.h. */
#include "mark.h" /* Required by exf.h. */
#include "exf.h"
#include "log.h"
#include "mem.h"
#if FWOPEN_NOT_AVAILABLE /* See PORT/clib/fwopen.c. */
#define EXCOOKIE sp
int ex_fflush __P((SCR *));
int ex_printf __P((SCR *, const char *, ...));
FILE *fwopen __P((SCR *, void *));
#else
#define EXCOOKIE sp->stdfp
#define ex_fflush fflush
#define ex_printf fprintf
#endif
/* Macros to set/clear/test flags. */
#define F_SET(p, f) (p)->flags |= (f)
#define F_CLR(p, f) (p)->flags &= ~(f)
#define F_ISSET(p, f) ((p)->flags & (f))
#define LF_INIT(f) flags = (f)
#define LF_SET(f) flags |= (f)
#define LF_CLR(f) flags &= ~(f)
#define LF_ISSET(f) (flags & (f))
/*
* XXX
* MIN/MAX have traditionally been in <sys/param.h>. Don't
* try to get them from there, it's just not worth the effort.
*/
#ifndef MAX
#define MAX(_a,_b) ((_a)<(_b)?(_b):(_a))
#endif
#ifndef MIN
#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b))
#endif
/* Function prototypes that don't seem to belong anywhere else. */
int nonblank __P((SCR *, EXF *, recno_t, size_t *));
void set_alt_name __P((SCR *, char *));
char *tail __P((char *));
CHAR_T *v_strdup __P((SCR *, CHAR_T *, size_t));
void vi_putchar __P((int));
#ifdef DEBUG
void TRACE __P((SCR *, const char *, ...));
#endif
/* Digraphs (not currently real). */
int digraph __P((SCR *, int, int));
int digraph_init __P((SCR *));
void digraph_save __P((SCR *, int));

View File

@ -1,10 +1,10 @@
# @(#)README 8.54 (Berkeley) 3/24/94
# @(#)README 8.85 (Berkeley) 8/17/94
This is the README for version 1.11 of nex/nvi, a freely redistributable
replacement for the Berkeley ex and vi text editors. The compressed tar
archive can be retrieved by anonymous ftp from ftp.cs.berkeley.edu, from
the file ucb/4bsd/nvi.tar.Z. There is a gzip'd tar archive, nvi.tar.z,
in the same directory.
This is the README for version 1.33 of nex/nvi, a freely redistributable
replacement for the Berkeley ex and vi text editors. The compressed or
gzip'd archives for this and future versions, can be retrieved by using
anonymous ftp to ftp.cs.berkeley.edu, from the file ucb/4bsd/nvi.tar.Z,
or ucb/4bsd/nvi.tar.gz.
If you have any questions about nvi, or problems making it work, please
contact me by electronic mail at one of the following addresses:
@ -22,7 +22,7 @@ California, but may be freely redistributed (or sold, or used to line
your birdcage) under the following conditions:
/*-
* Copyright (c) 1991, 1993, 1994
* Copyright (c) 1991, 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -84,60 +84,79 @@ o And...
=-=-=-=-=-=-=-=-=-=-=
o Status:
This software is in beta test, and it's believed to be pretty stable.
Almost all of the historic functionality in ex/vi is there, the missing
pieces are fairly obscure. In particular, the edcompatible, hardtabs*,
lisp*, optimize*, redraw*, and slowopen* options are recognized, but not
implemented.
This software is in beta test, and it's pretty stable. Almost all of
the historic functionality in ex/vi is there, the only major missing
pieces are open mode and the lisp option. (Also, the options hardtabs,
optimize, redraw, and slowopen are recognized, but ignored.)
Nvi is mostly 8-bit clean. This isn't difficult to fix, and was left in
during initial development to make things easier. Wide character support
will be integrated at the same time it is made fully 8-bit clean.
Nvi is mostly 8-bit clean. This isn't difficult to fix, and was left
in during initial development to keep things simple. Wide character
support will be integrated at the same time that it is made fully 8-bit
clean.
There aren't a lot of new features in nex/nvi, but there are a few things
you might like. See the "ADDITIONAL FEATURES" section of the manual page
(docs/vi.0.txt, docs/vi.0.ps) for a list.
you might like. The "Additional Features" section of the reference page
(USD.doc/vi.ref/vi.ref.txt, USD.doc/vi.ref/vi.ref.ps) has more information.
=-=-=-=-=-=-=-=-=-=-=
o Porting information:
The directory PORT has directories per OS/machine combination, with
V7-style Makefiles which build nex/nvi. See the file PORT/README for
detailed information.
The directory "PORT" has directories for specific OS/machine combinations,
including V7-style Makefiles, for building nex/nvi on different machines.
See the file PORT/README for detailed information.
=-=-=-=-=-=-=-=-=-=-=
o Bug reports:
o Debugging:
Code fixes are appreciated, of course, but if you can't provide them,
please email me as much information as you can as to how to reproduce
the bug, and I'll try to fix it locally. In particular, the screen
routines are nasty stuff, and you probably don't want to mess with them.
Stack traces of core dumps are sometimes helpful, but an example file
with a set of keystrokes that causes the problem is far better. Also,
make sure that you include the dimensions of the screen on which the
problem occurred, your startup files (.exrc, .nexrc), and the environment
variable (EXINIT, NEXINIT) values.
the bug, and I'll try to fix it locally. Stack traces of core dumps
are only rarely helpful -- an example file with a set of keystrokes that
causes the problem is almost invariably necessary.
Please include the following in the bug report;
o The version of nvi you're running (use :version to get it).
o The row/column dimensions of the screen (80 x 32).
o Unless you're confident that they're not part of the problem,
your startup files (.exrc, .nexrc) and the environment variable
(EXININT, NEXINIT) values. (Cutting and pasting the output
of ":set all" is usually sufficient.)
If you're running a memory checker (e.g. Purify) on nvi, you will want
to recompile everything with "-DPURIFY" in the CFLAGS, first. By
default, allocated pages are not initialized by the DB code, and they
will show up as reads of uninitialized memory in the buffer write routines.
=-=-=-=-=-=-=-=-=-=-=
o Layout:
o Directory layout:
nvi:
Source files for pieces of code that are shared by all the
editors, like searching and logging code or code translating
line numbers into requests to the dbopen(3) database code.
It also has the code for adding, deleting, and changing "records"
in the underlying database.
nvi/USD.doc:
Ex/vi documentation, both historic and current.
nvi/PORT:
Porting directories, one per OS/architecture combination. See
nvi/PORT/README for porting information.
edit/ Roff source for "Edit: A tutorial", USD:14 in the
4.3BSD manuals.
ex/ Roff source for "Ex Reference Manual -- Version
3.7", USD:16 in the 4.3BSD manuals.
vi/ Roff source for "An Introduction to Display
Editing with Vi", USD:15 in the 4.3BSD manuals.
Includes the "Vi Quick Reference" card.
vi.man/ Manual page for nex/nvi; an updated version of
the document distributed with 4.4BSD-Lite.
vi.ref/ Reference document for nex/nvi; an updated version
of the document distributed with 4.4BSD-Lite.
nvi/common:
Source files for pieces of code that are shared by all the editors,
like searching and logging code or code translating line numbers
into requests to the dbopen(3) database code. It also has the
interface code for modifying "records" in the underlying database.
nvi/docs:
The nvi/docs directory has all of the nvi documentation:
Random nvi documentation:
README -- Nvi main README file.
USD.doc -- Historic vi documentation (in roff source form).
bugs.current -- Known bugs in the current nvi implementation.
bugs.current -- Major known bugs in the current nvi.
changelog -- Log of changes from version to version.
features -- Todo list, suggested features list.
internals/
@ -145,33 +164,37 @@ nvi/docs:
gdb.script -- GDB debugging scripts.
input -- Vi maps, executable buffers, and input discussion.
quoting -- Vi quoting discussion.
structures -- Nvi internal structure description.
spell.ok -- Misspellings list for README, vi.1.
tutorial -- Historic vi tutorial
vi.0.ps -- PostScript of vi.1.
vi.0.txt -- Flat text of vi.1.
vi.1 -- Nvi man page (in roff source form).
vi.ref -- Nvi reference (in roff source form).
vi.ref.ps -- PostScript of vi.ref.
vi.ref.txt -- Flat text of vi.ref.
structures -- Out-of-date nvi internal structure description.
tutorial/ -- Historic vi tutorial(s), of unknown quality.
nvi/ex:
The nvi/ex directory is the ex source code. Because vi has the
colon command, lots of this code is used by vi. Generally, if
functionality is shared by both ex and vi, it's in nvi/ex, if
it's vi only, it's in nvi/vi. Files are generally named by the
command(s) they support, but occasionally with a name that
describes their functionality.
The ex source code. Because vi has the colon command, lots of
this code is used by vi. Generally, if functionality is shared
by both ex and vi, it's in nvi/ex. If it's vi only, it's in
nvi/vi. Files are generally named by the command(s) they support,
but occasionally with a name that describes their functionality.
nvi/install:
Things to install on the local system.
recover.script -- Vi recovery script.
nvi/PORT:
Porting directories, one per OS/architecture combination. See
nvi/PORT/README for porting information.
curses/ -- 4.4BSD curses implementation
db/ -- 4.4BSD DB routines.
regex/ -- Henry Spencer's POSIX.2 RE support.
nvi/sex:
The nvi/sex directory is the screen support for the ex editor.
The screen support for the ex editor.
nvi/svi:
The nvi/svi directory is the screen support for a curses based
vi editor.
The screen support for a curses based vi editor.
nvi/vi:
The nvi/vi directory is the vi source code.
The vi source code.
nvi/xaw:
Place reserved for an X11 (Athena Widget) screen.

View File

@ -1,41 +1,37 @@
List of known bugs:
+ Large numbers of matches (e.g. %, g or v commands), with the
ignorecase option set, triggers a memory corruption bug in the
regex routines.
+ The number option doesn't display line numbers in ex append/insert
mode.
+ Autoindent doesn't work in the ex editor.
+ The option sidescroll is completely wrong, and setting it does more
harm than good.
+ When nvi edits files that don't have trailing newlines, it appends
one, regardless.
+ Open mode is not yet implemented.
+ ^C isn't passed to the shell in the script windows as an interrupt
character.
+ The command ":ab foo^J bar" prints a usage message -- non-word
characters should be quoted in the underlying terminal engine
so that the upper-level knows they're quoted and doesn't use them
as delimiters. (Note, this isn't historical practice, vi didn't
permit escaping of ^J in this type of command.)
+ The options:
+ The options edcompatible, hardtabs*, lisp*, optimize*, redraw*,
and slowopen* are recognized, but not implemented. Options with
an asterisk are unlikely to ever be implemented, so if you want
them you might want to say something! I will implement lisp if
anyone ever documents how it really worked.
hardtabs, lisp, optimize, redraw, slowopen
+ Screen repainting over slow lines, for some screen changes, is not
are recognized, but not implemented. These options are unlikely to
be implemented, so if you want them you might want to say something!
I will implement lisp if anyone ever documents how it worked.
+ Screen repainting over slow lines, for some screen changes, isn't
as good as the historic vi's.
+ If an error results during input in ex, it is not displayed until
after input mode is exited.
+ If the ex append command is used from vi, the input command buffer
is overwritten by the ex_append function, causing random errors.
+ The line movement commands ('k', 'j' are easy examples) don't find the
most attractive cursor position correctly when wrapped lines are longer
than 80 characters, and they're on the second or subsequent lines.
+ Colon commands longer than a single line cause the display to be
incorrect.
+ When switching files in a small screen (O_WINDOW) with :e, the status
message isn't displayed.
+ The usages of S_{REDRAW,REFORMAT,REFRESH,RENUMBER,RESIZE} are
inconsistent, and should be reviewed. In particular, S_REFRESH
in any screen redraws all screens.

View File

@ -1,4 +1,392 @@
1.10 -> 1.11: Thu Mar 24 16:07:45 EST 1994
1.32 -> 1.33 Wed Aug 17 09:31:41 1994 (PUBLICLY AVAILABLE VERSION)
+ Get back 5K of data space for the sccsid strings.
+ Fix bug where cG fix in version 1.31 broke cw cursor positioning
when the change command extended the line.
+ Fix core dump in map/seq code if character larger than 7 bits.
+ Block signals when manipulating the SCR chains.
+ Fix memory allocation for machines with multiple pointer sizes.
1.31 -> 1.32 Mon Aug 15 14:27:49 1994
+ Turn off recno mmap call for Solaris 2.4/SunOS 5.4.
1.30 -> 1.31 Sun Aug 14 13:13:35 1994
+ Fix bug were cG on the last line of a file wasn't done in line mode,
and where the cursor wasn't positioned correctly after exiting text
insert mode.
+ Add termcap workaround to make function keys greater than 9 work
correctly (or fail if old-style termcap support).
+ Change ex/vi to not flush mapped keys on error -- this is historic
practice, and people depended on it.
+ Rework vi parser so that no command including a mapped key ever
becomes the '.' command, matching historic practice.
+ Make <escape> cancellation in the vi parser match POSIX 1003.2.
+ Fix curses bug where standout string was written for each standout
character, and where standout mode was never exited explicitly.
Fix bugs in curses SF/sf and SR/sr scrolling, as seen on Sun and
x86 consoles.
+ The v/global commands execute the print command by default.
+ The number option historically applies to ex as well as vi.
1.29 -> 1.30 Mon Aug 8 10:30:42 1994
+ Make first read into a temporary set the file's name.
+ Permit any key to continue scrolling or ex commands -- this
allows stacked colon commands, and matches historic practice.
+ Don't output normal ! command commentary in ex silent mode.
+ Allow +/- flags after substitute commands, make line (flag)
offsets from vi mode match historic practice.
+ Return <eof> to ex immediately, even if preceded by spaces. Rework
ex parser to do erase the prompt instead of depending on the print
routines to do it. Minor fixes to the ex parser for display of
default and scrolling commands. MORE EX PARSER CHANGES.
1.28 -> 1.29 Fri Aug 5 10:18:07 1994
+ Make the abbreviated ex delete command work (:dele---###lll for
example, is historically legal.
+ When autoprint fires, multiple flags may be set, use ex_print
directly instead of the stub routines.
+ Change v/global commands to turn off autoprint while running.
+ Minor changes to make the ! command display match historic output.
+ Rework the ex parser to permit multiple command separators without
commands -- MAJOR CHANGE, likely to introduce all sorts of new bugs.
+ Fix cd command to expand argument in the context of each element
of the cdpath option, make relative paths always relative to the
current directory.
+ Rework write/quit cases for temporary files, so that user's don't
discard them accidentally.
+ Check for window size changes when continuing after a suspend.
+ Fix memory problem in svi_screen, used free'd memory.
+ Change the ex change, insert, append commands to match historic
cursor positions if no data entered by the user.
+ Change ex format flags (#, l, p) to affect future commands, not
just the current one, to match historic practice.
+ Make the user's EOF character an additional scroll character in ex.
+ Fix ex ^D scrolling to be the value of the scroll option, not half
the screen.
+ Fix buffer execution to match historic practice -- bugs where the
'*' command didn't work, and @<carriage-return> didn't work.
+ Fix doubled reporting of deleted lines in filters.
+ Rework the % ` / ? ( ) N n { and ^A commands to always cut into
numeric buffers regardless of the location or length of the cut.
This matches historic practice.
+ Fix the { command to check the current line if the cursor doesn't
start on the first character of the line.
+ Do '!' expansion in the ex read command arguments, it's historic
practice. In addition, it sets the last '!' command.
1.27 -> 1.28 Wed Jul 27 21:29:18 1994
+ Add support for scrolling using the CS and SF/sf/SR/sr termcap
strings to the 4BSD curses.
+ Rework of getkey() introduced a bug where command interrupt put
nvi into an infinite loop.
+ Piping through a filter historically cut the replaced lines into
the default buffer, although not the numeric ones.
+ Read of a filter and !! historically moved to the first nonblank
of the resulting cursor line (most of the time).
+ Rework cursor motion flags, to support '!' as a motion command.
1.26 -> 1.27 Tue Jul 26 10:27:58 1994
+ Add the meta option, to specify characters the shell will expand.
+ Fix the read command to match historic practice, the white space
and bang characters weren't getting parsed correctly.
+ Change SIGALRM handler to save and restore errno.
+ Change SunOS include/compat.h to include <vfork.h> so that the
ex/filter.c code works again.
+ Don't put lines deleted by the ex delete command into the numeric
buffers, matching historic practice.
+ Fix; if appending to a buffer, default buffer historically only
references the appended text, not the resulting text.
+ Support multiple, semi-colon separated search strings, and 'z'
commands after search strings.
+ Make previous context mark setting match historic practice (see
docs/internals/context).
+ Fix the set command to permit whitespace between the option and
the question mark, fix question marks in general.
+ Fix bug where ex error messages could be accidentally preceded
by a single space.
+ Fix bug where curses reorganization could lose screen specific
mappings as soon as any screen exited.
+ Fix bug in paragraph code where invalid macros could be matched.
Make paragraph motions stop at formfeed (^L) characters.
+ Change 'c' to match historic practice, it cut text into numeric
buffers.
1.25 -> 1.26 Tue Jul 19 17:46:24 1994
+ Ignore SIGWINCH if the screen size is unchanged; SunOS systems
deliver one when a screen is uncovered.
+ Fix: don't permit a command with a motion component to wrap due
to wrapscan and return to the original cursor position.
+ Fix: ^E wasn't beeping when reaching the bottom of the file.
+ Fix bg/fg bug where tmp file exiting caused a NULL dereference.
+ Rework file locking code to use fcntl(2) explicitly.
+ Fix bug in section code where invalid macros could be matched.
+ Fix bug where line number reset by vi's Q command.
+ Add explicit character mode designation to character mode buffers.
+ Add <sys/ioctl.h> include to sex/sex_window.c, needed by NET/2
vintage systems.
+ Change to always flush a character during suspend, 4BSD curses
has the optimization where it doesn't flush after a standend().
+ Fix bug on OSF1 where <curses.h> changes the values of VERASE,
VKILL and VWERASE to incorrect ones.
+ Fix bug where optarg used incorrectly in main.c.
+ Block all signals when acting on a signal delivery.
+ Fix recovery bug where RCV_EMAIL could fire even if there wasn't
a backing file; format recovery message.
1.24 -> 1.25 Sun Jul 17 14:33:38 1994
+ Stop allowing keyboard suspends (^Z) in insert mode, it's hard
to get autowrite correct, and it's not historic practice.
+ Fix z^, z+ to match historic practice.
+ Bug in message handling, "vi +35 non-existent_file" lost the
status message because the "+35" pushed onto the stack erased
it. For now, change so that messages aren't displayed if there
are keys waiting -- may need to add a "don't-erase" bit to the
character in the stack instead.
+ Bug in svi_msgflush(), where error messages could come out in
normal video.
1.23 -> 1.24 Sat Jul 16 18:30:18 1994
+ Fix core dump in exf.c, where editing a non-existent file and
exiting could cause already free'd memory to be free'd.
+ Clean up numerous memory errors, courtesy of Purify.
+ Change process wait code to fail if wait fails, and not attempt
to interpret the wait return information.
+ Open recovery and DB files for writing as well as reading, System
V (fcntl) won't let you acquire LOCK_EX locks otherwise.
+ Fix substitute bug where could malloc 0 bytes (AIX breaks).
+ Permit the mapping of <carriage-return>, it's historic practice.
+ Historic vi didn't eat <blank> characters before the force
flag, match historic practice.
+ Bug in ex argument parsing, corrected for literal characters
twice.
+ Delete screen specific maps when the screen closes.
+ Move to the first non-<blank> in the line on startup; historic
practice.
+ Change the ex visual command to move directly to a line if no
trailing 'z' command.
+ Fix "[[" and "]]" to match historic practice (yet again...).
+ Fix "yb" and "y{" commands to update the cursor correctly.
+ Change "~<motion>" to match the yank cursor movement semantics
exactly.
+ Move all of the curses related code into sex/svi -- major rework,
but should help in future ports.
+ Fix bug in split code caused by new file naming code, where would
drop core when a split screen exited.
+ Change svi_ex_write to do character display translation, so that
messages with file names in them are displayed correctly.
+ Display the file name on split screens instead of a divider line.
+ Fix move bug, wasn't copying lines before putting them.
+ Fix bug were :n dropped core if no arguments supplied.
+ Don't quote characters in executed buffer: "ifoo<esc>" should leave
insert mode after the buffer is executed.
+ Tagpop and tagpush should set the absolute mark in case only moving
within a file.
+ Skip leading whitespace characters before tags and cursor word
searches.
+ Fix bug in ex_global where re_conv() was allocating the temporary
buffer and not freeing it.
1.22 -> 1.23: Wed Jun 29 19:22:33 1994
+ New <sys/cdefs.h> required "inline" to change to "__inline"
+ Fix System V curses code for new ^Z support.
+ Fix off-by-one in the move code, avoid ":1,$mo$" with only one
line in the buffer.
+ Line orientation of motion commands was remembered too long,
i.e. '.' command could be incorrectly marked as line oriented.
+ Move file modification time into EXF, so it's shared across
split screens.
+ Put the prev[ious] command back in, people complained.
+ Random fixes to next/prev semantics changed in 1.22.
+ Historically vi doesn't only move to the last address if there's
ANYTHING after the addresses, e.g. ":3" moves to line 3, ":3|"
prints line 3.
1.21 -> 1.22: Mon Jun 27 11:01:41 1994
+ Make the line between split screens inverse video again.
+ Delete the prev[ious] command, it's not useful enough to keep.
+ Rework :args/file name handling from scratch -- MAJOR CHANGE,
likely to introduce all sorts of new bugs.
+ Fix RE bug where no subexpressions in the pattern but there were
subexpressions referenced in the replacement, e.g. "s/XXX/\1/g".
+ Change recovery to not leave unmodified files around after a
crash, by using the owner 'x' bit on unmodified backup files.
MAJOR CHANGE, the system recovery script has to change!
+ Change -r option to delete recovery.* files that reference non-
existent vi.* files.
+ Rework recovery locking so that fcntl(2) locking will work.
+ Fix append (upper-case) buffers, broken by cut fixes.
+ Fix | to not set the absolute motion mark.
+ Read $HOME/.exrc file on startup if the effective user ID is
root. This makes running vi while su(1)'d work correctly.
+ Use the full pathname of the file as the recovery name, not
just the last component. Matches historic practice.
+ Keep marks in empty files from being destroyed.
+ Block all caught signals before calling the DB routines.
+ Make the line change report match historic practice (yanked
lines were different than everything else).
+ Add section on multiple screens to the reference manual.
+ Display all messages at once, combine onto a single line if
possible. Delete the trailing period from all messages.
1.20 -> 1.21: Thu May 19 12:21:58 1994
+ Delete the -l flag from the recover mail.
+ Send the user email if ex command :preserve executed, this matches
historic practice. Lots of changes to the preserve and recovery
code, change preserve to snapshot files (again, historic practice).
+ Make buffers match historic practice: "add logically stores text
into buffer a, buffer 1, and the unnamed buffer.
+ Print <tab> characters as ^I on the colon command line if the
list option set.
+ Adjust ^F and ^B scroll values in the presence of split screens
and small windows.
+ Break msg* routines out from util.c into msg.c, start thinking
about message catalogs.
+ Add tildeop set option, based on stevie's option of the same name.
Changes the ~ command into "[count] ~ motion", i.e. ~ takes a
trailing motion.
+ Chose NOT to match historic practice on cursor positioning after
consecutive undo commands on a single line; see vi/v_undo.c for
the comment.
+ Add a one line cache so that multiple changes to the same line
are only counted once (e.g. "dl35p" changes one line, not 35).
+ Rework signals some more. Block file sync signals in vi routines
that interface to DB, so can sync the files at interrupt time.
Write up all of the signal handling arguments, see signal.c.
1.19 -> 1.20: Thu May 5 19:24:57 1994
+ Return ^Z to synchronous handling. See the dicussion in signal.c
and svi_screen.c:svi_curses_init().
+ Fix bug where line change report was wrong in util.c:msg_rpt().
1.18 -> 1.19: Thu May 5 12:59:51 1994
+ Block DSUSP so that ^Y isn't delivered at SIGTSTP.
+ Fix bug -- put into an empty file leaves the cursor at 1,0,
not the first nonblank.
+ Fix bug were number of lines reported for the 'P' command was
off-by-one.
+ Fix bug were 0^D wasn't being handled correctly.
+ Delete remnants of ^Z as a raw character.
+ Fix bug where if a map was an entire colon command, it may never
have been displayed.
+ Final cursor position fixes for the vi T and t commands.
+ The ex :next command took an optional ex command as it's first
argument similar to the :edit commands. Match historic practice.
1.17 -> 1.18: Wed May 4 13:57:10 1994
+ Rework curses information in the PORT/Makefile's.
+ Minor fixes to ^Z asynchronous code.
1.16 -> 1.17: Wed May 4 11:15:56 1994
+ Make ex comment handling match historic practice.
+ Make ^Z work asynchronously, we can no longer use the SIGTSTP
handler in the curses library.
1.15 -> 1.16: Mon May 2 19:42:07 1994
+ Make the 'p' and 'P' commands support counts, i.e. "Y10p" works.
+ Make characters that map to themselves as the first part of the
mapping work, it's historic practice.
+ Fix bug where "s/./\& /" discarded the space in the replacement
string.
+ Add support for up/down cursor arrows in text input mode, rework
left/right support to match industry practice.
+ Fix bug were enough character remapping could corrupt memory.
+ Delete O_REMAPMAX in favor of setting interrupts after N mapped
characters without a read, delete the map counter per character.
MAJOR CHANGE. All of the interrupt signal handling has been
reworked so that interrupts are always turned on instead of
being turned on periodically, when an interruptible operation is
pending.
+ Fix bug where vi wait() was interrupted by the recovery alarm.
+ Make +cmd's and initial commands execute with the current line
set to the last line of the file. This is historic practice.
+ Change "lock failed" error message to a file status message.
It always fails over NFS, and making all NFS files readonly
isn't going to fly.
+ Use the historic line number format, but check for overflow.
+ Fix bug where vi command parser ignored buffers specified as
part of the motion command.
+ Make [@*]buffer commands on character mode buffers match historic
practice.
+ Fix bug where the cmap/chf entries of the tty structure weren't
being cleared when new characters were read.
+ Fix bug where the default command motion flags were being set
when the command was a motion component.
+ Fix wrapmargin bug; if appending characters, and wrapmargin breaks
the line, an additional space is eaten.
1.14 -> 1.15: Fri Apr 29 07:44:57 1994
+ Make the ex delete command work in any empty file.
+ Fix bug where 't' command placed the cursor on the character
instead of to its left.
+ ^D and ^U didn't set the scroll option value historically.
Note, this change means that any user set value (e.g. 15^D)
will be lost when splitting the screen, since the split code
now resets the scroll value regardless.
+ Fix the ( command to set the absolute movement mark.
+ Only use TIOCGWINSZ for window information if SIGWINCH signal
caught.
+ Delete the -l flag, and make -r work for multiple arguments.
Add the ex "recover[!] file" command.
+ Switch into ex terminal mode and use the sex routines when
append/change/insert called from vi mode.
+ Make ^F and ^B match historic practice. This required a fairly
extensive rework of the svi scrolling code.
+ Cursor positioning in H, M, L, G (first non-blank for 1G) wasn't
being done correctly. Delete the SETLFNB flag. H, M, and L stay
logical movements (SETNNB) and G always moves to the first nonblank.
+ System V uses "lines" and "cols", not "li" and "co", change as
necessary. Check termcap function returns for errors.
+ Fix `<character> command to do start/end of line correction,
and to set line mode if starting and stopping at column 0.
+ Fix bug in delete code where dropped core if deleted in character
mode to an empty line. (Rework the delete code for efficiency.)
+ Give up on SunOS 4.1.X, and use "cc" instead of /usr/5bin/cc.
+ Protect ex_getline routine from interrupted system calls (if
possible, set SA_RESTART on SIGALRM, too).
+ Fix leftright scrolling bug, when moving to a shorter line.
+ Do validity checking on the copy, move, t command target line
numbers.
+ Change for System V % pattern broke trailing flags for empty
replacement strings.
+ Fix bug when RCM flags retained in the saved dot structure.
+ Make the ex '=' command work for empty files.
+ Fix bug where special_key array was being free'd (it's no longer
allocated).
+ Matches cut in line mode only if the starting cursor is at or
before the first nonblank in its line, and the ending cursor is
at or after the last nonblank in its line.
+ Add the :wn command, so you can write a file and switch to a new
file in one command.
+ Allow only a single key as an argument to :viusage.
+ New movement code broke filter/paragraph operations in empty
files ("!}date" in an empty file was dropping core).
1.12 -> 1.14: Mon Apr 18 11:05:10 1994 (PUBLICLY AVAILABLE VERSION, 4.4BSD)
+ Fix FILE structure leakage in the ex filter code.
+ Rework suspend code for System V curses. Nvi has to do the
the work, there's no way to get curses to do it right.
+ Revert SunOS 4.1.X ports to the distributed curses. There's
a bug in Sun's implementation that we can't live with.
+ Quit immediately if row/column values are unreasonable.
+ Fix the function keys to match vi historic behavior.
+ Replace the echo/awk magic in the Makefile's with awk scripts.
1.11 -> 1.12: Thu Apr 14 11:10:19 1994
+ Fix bug where only the first vi key was checked for validity.
+ Make 'R' continue to overwrite after a <carriage-return>.
+ Only display the "no recovery" message once.
+ Rework line backup code to restore the line to its previous
condition.
+ Don't permit :q in a .exrc file or EXINIT variable.
+ Fix wrapscan option bug where forward searches become backward
searches and do cursor correction accordingly.
+ Change "dd" to move the cursor to the first non-blank on the line.
+ Delete cursor attraction to the first non-blank, change non-blank
motions to set the most attractive cursor position instead.
+ Fix 'r' substitute option to set the RE to the last RE, not the
last substitute RE.
+ Fix 'c' and 'g' substitute options to always toggle, and fix
edcompatible option to not reset them.
+ Display ex error messages in inverse video.
+ Fix errorbells option to match historic practice.
+ Delete fixed character display table in favor of table built based
on the current locale.
+ Add ":set octal" option, that displays unknown characters as octal
values instead of the default hexadecimal.
+ Make all command and text input modes interruptible.
+ Fix ex input mode to display error messages immediately, instead
of waiting for the lines to be resolved.
+ Fix bug where vi calling append could overwrite the command.
+ Fix off-by-one in the ex print routine tab code.
+ Fix incorrect ^D test in vi text input routines.
+ Add autoindent support for ex text insert routines.
+ Add System V substitute command replacement pattern semantics,
where '%' means the last replacement pattern.
+ Fix bug that \ didn't escape newlines in ex commands.
+ Regularize the names of special characters to CH_*.
+ Change hex insert character from ^Vx<hex_char> to ^X<hex_char>
+ Integrate System V style curses, so SunOS and Solaris ports can
use the native curses implementation.
1.10 -> 1.11: Thu Mar 24 16:07:45 EST 1994 (PUBLICLY AVAILABLE VERSION)
+ Change H, M, and L to set the absolute mark, historical practice.
+ Fix bug in stepping through multiple tags files.
+ Add "remapmax" option that turns off map counts so you can remap

55
usr.bin/vi/docs/ev Normal file
View File

@ -0,0 +1,55 @@
# @(#)ev 8.4 (Berkeley) 4/29/94
Ev: Vi: Result:
<CK> <CK> (Cursor keys). Move around the file.
Meta key commands:
^A<#> <#>G Goto line #.
^A$ G Goto the end of the file.
^A/ / Prompt and execute a forward search.
^A: : Prompt and execute an ex command.
^A? ? Prompt and execute a backward search.
^Ac y'<c> Copy to mark in line mode (or copy the current line).
^AC y`<c> Copy to mark in character mode.
^Ad d'<c> Delete to mark in line mode (or delete the current line).
^AD d`<c> Delete to mark in character mode.
^Aj J Join lines.
^Am m<c> Mark the current cursor position.
^AN N Repeat search in the reverse direction.
^An ^A Search for the word under the cursor.
^Ar u Redo a command.
^Au u Undo a command.
Single key commands:
^B ^B Page up a screen.
^C ^C Interrupt long-running commands.
^D ^D Page down a half-screen.
^E $ End of line.
^F ^F Page down a screen.
^G ^G File status/information.
^H X Delete the character to the left of the cursor.
^I (TAB)
^J j Cursor down one line.
^K k Cursor up one line.
^L ^L Redraw the screen.
^M (CR) ^M In insert mode, split the line at the current cursor,
creating a new line.
In overwrite mode, cursor down one line.
^N n Repeat previous search, in previous direction.
^O (UNUSED)
^P p Paste the cut text at the cursor position.
^Q (XON/XOFF)
^R (UNUSED)
^S (XON/XOFF)
^T D Truncate the line at the cursor position.
^U ^U Page up a half-screen.
^V<c> ^V<c> Insert/overwrite with a literal next character.
^W w Move forward one whitespace separated word.
^X x Delete the current character.
^Y (UNUSED)
^Z ^Z Suspend.
New ex mode commands:
^A:set ov[erwrite] Toggle "insert" mode, so that input keys overwrite
the existing characters.

View File

@ -1,27 +1,45 @@
List of things that should be added at some point:
List of things that should be added:
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ X11 interface.
+ X11 (Tk, Motif, Xaw) interface.
+ Interpreted language (Perl5, Scheme, Tcl)
+ Ports: Windows, Windows NT, MSDOS
+ Message catalogs.
+ Forms editing package; use RE's to verify field contents.
+ Internationalization, including wide character support.
+ Make db, curses real libraries that we load against instead of
compiling directly.
+ Add a ":resize =N" command, that sets the window to a specific
size.
+ Support for single line window editing, including full editing
capability on the vi colon command line.
+ Rob Pike's sam style RE's.
List of suggested features:
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ Change tags to also attempt to find the file using the directory
where it found the tags file.
+ Filename completion. While on the subject of completion, it would be
nice to have the completion mechanism found in tcsh version >= 6.03.
For instance, the completion for the `:cd' command will be directories
only. The completion for the `:set' command will be all options not
set at that moment, and for `:set un' will be all options that are set
at that moment. The completion for `:< count' will be the flags.
+ Add a "push" command that would push a file on the tags stack.
(Essentially make tags a special case of the stack, and make
the stack more general purpose.)
+ Make :script just run a command and edit the output, and :interactive,
which allows interactive shell session, instead of just the current
:script.
+ Add versioning based on a "set version" variable, that would
create backup copies when the file was written back, i.e. the
":w" and autowrite's would copy the original.
+ Add tagging information to the man page so that users can display
the part of the man page that discusses the command in which they're
interested.
+ Add a zone option so that you can declare that top/bottom few lines
of the screen aren't filled except by accident, so that the text
you ask for is always concentrated in the center of the screen.
+ Add "set searchdir" for a list of directories to look in for
files to edit. The semantic is that ":e foo" is replaced with
the file name that is found, so there's no confusion as to
@ -36,12 +54,34 @@ List of suggested features:
of keystrokes into a named buffer for later use. Handy when
you're trying to build a semi-complex macro.
+ Put multiple messages on a single line if they fit in their entirety,
so that ":n" with autowrite set doesn't force users to hit return to
get see both the "written" and "new file status" messages.
+ The semantics of :split, :bg, and :fg aren't right. Someone needs to
rethink how they should interact. The main problem arises when users
want to get a window into a new file. Currently, the necessary sequence
is ":split newfile|^W|:bg". It would be nice if you could simply
background the current screen and edit a new one.
+ An option to turn on a ``quarter plane'' model so that you can
go as far to the right or down as you wish. The File or the
current line is only extended if you actually put down a char at
the new location. Very handy for ascii graphics and tables.
+ Some way of replacing the command bindings. For this to work
cleanly the notion of a command must be separate from that of a
key. (Simulate the Rand editor?)
+ Vertical splitting, so you can see files side by side.
+ Tracking. Two or more files are associated so that when one file
is scrolled up/down/left/right other files track by the same amount.
Tracking may be constrained such that two files only track vertically
or horizontally. This is relatively easy to implement.
+ A status file so that the next time invocation of the editor returns
to the same place, with the same number of windows etc. In case of
change of the screen size, reasonable defaults are used. For each
window size and location of the window, name of the file and position
in it, any tab settings, any other settings for the window (such as
insert/overwrite mode, auto indent etc). Last search RE and maybe
direction. If a file does not exist the next time you invoke the
editor, its window is left in the same place but with some default
message.

View File

@ -0,0 +1,32 @@
# @(#)context 8.5 (Berkeley) 7/23/94
In historic vi, the previous context mark was always set:
ex address:
any number, <question-mark>, <slash>, <dollar-sign>,
<single-quote>, <backslash>
ex commands: undo, "z.", global, vglobal
vi commands: (, ), {, }, %, [[, ]], ^]
nvi adds the vi command ^T to this list.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
In historic vi, the previous context mark was set if the
line changed:
vi commands: '<mark>, G, H, L, M, z
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
In historic vi, the previous context mark was set if the
line or column changed:
vi commands: `<mark>, /, ?, N, n
nvi adds the vi command ^A to this list.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
In historic vi, the previous context mark was set in non-visual
mode for ^R and ^L if the line changed, but I have yet to figure
out how the line could change.

View File

@ -1,4 +1,4 @@
# @(#)gdb.script 8.2 (Berkeley) 9/29/93
# @(#)gdb.script 8.3 (Berkeley) 8/16/94
# display the SVI screen map
# usage dmap(sp)
@ -39,10 +39,19 @@ define tmap
end
end
# display the SVI private structure
# display the private structures
define vip
print *((VI_PRIVATE *)sp->vi_private)
end
define svp
print *((SVI_PRIVATE *)sp->svi_private)
end
define exp
print *((EX_PRIVATE *)sp->ex_private)
end
define sxp
print *((SEX_PRIVATE *)sp->sex_private)
end
# display the marks
define markp

View File

@ -1,4 +1,4 @@
# @(#)input 5.4 (Berkeley) 8/26/93
# @(#)input 5.5 (Berkeley) 7/2/94
MAPS, EXECUTABLE BUFFERS AND INPUT IN EX/VI:
@ -65,6 +65,8 @@ the historic maps and @ buffers work. The summary is as follows:
6: If an error is encountered while executing a mapped command or buffer,
the rest of the mapped command/buffer is discarded. No user input
characters are discarded.
7: Characters in executable buffers are remapped.
8: Characters in executable buffers are not quoted.
Individual test cases follow. Note, in the test cases, control characters
are not literal and will have to be replaced to make the test cases work.
@ -312,3 +314,37 @@ line 3 foo bar baz
or 3G, it will succeed.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
7: Characters in executable buffers are remapped.
=== test file ===
abcdefghijklmnnop
ggg
=== end test file ===
:map g x
2G"ay$1G@a
The output should be:
=== output file ===
defghijklmnnop
ggg
=== end output file ===
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
8: Characters in executable buffers are not quoted.
=== test file ===
iFOO^[
=== end test file ===
1G"ay$2G@a
The output should be:
=== output file ===
iFOO^[
FOO
=== end output file ===
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

6
usr.bin/vi/ex/excmd.awk Normal file
View File

@ -0,0 +1,6 @@
# @(#)excmd.awk 8.1 (Berkeley) 4/17/94
/^\/\* C_[0-9A-Z_]* \*\/$/ {
printf("#define %s %d\n", $2, cnt++);
next;
}

View File

@ -0,0 +1,46 @@
# @(#)recover.script 8.7 (Berkeley) 8/16/94
#
# Script to recover nvi edit sessions.
#
RECDIR=/var/tmp/vi.recover
SENDMAIL=/usr/lib/sendmail
echo 'Recovering nvi editor sessions.'
# Check editor backup files.
vibackup=`echo $RECDIR/vi.*`
if [ "$vibackup" != "$RECDIR/vi.*" ]; then
for i in $vibackup; do
# Only test files that are readable.
if test ! -r $i; then
continue
fi
# Unmodified nvi editor backup files either have the
# execute bit set or are zero length. Delete them.
if test -x $i -o ! -s $i; then
rm $i
fi
done
fi
# It is possible to get incomplete recovery files, if the editor crashes
# at the right time.
virecovery=`echo $RECDIR/recover.*`
if [ "$virecovery" != "$RECDIR/recover.*" ]; then
for i in $virecovery; do
# Only test files that are readable.
if test ! -r $i; then
continue
fi
# Delete any recovery files that are zero length, corrupted,
# or that have no corresponding backup file. Else send mail
# to the user.
recfile=`awk '/^X-vi-recover-path:/{print $2}' < $i`
if test -n "$recfile" -a -s "$recfile"; then
$SENDMAIL -t < $i
else
rm $i
fi
done
fi

194
usr.bin/vi/sex/sex_window.c Normal file
View File

@ -0,0 +1,194 @@
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char sccsid[] = "@(#)sex_window.c 8.7 (Berkeley) 8/17/94";
#endif /* not lint */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "compat.h"
#include <curses.h>
#include <db.h>
#include <regex.h>
#include "vi.h"
/*
* sex_window --
* Set the window size.
*/
int
sex_window(sp, sigwinch)
SCR *sp;
int sigwinch;
{
struct winsize win;
size_t col, row;
int rval, user_set;
ARGS *argv[2], a, b;
char *s, buf[2048];
/*
* Get the screen rows and columns. If the values are wrong, it's
* not a big deal -- as soon as the user sets them explicitly the
* environment will be set and the screen package will use the new
* values.
*
* Try TIOCGWINSZ.
*/
row = col = 0;
#ifdef TIOCGWINSZ
if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) {
row = win.ws_row;
col = win.ws_col;
}
#endif
/* If here because of a signal, TIOCGWINSZ is all we trust. */
if (sigwinch) {
if (row == 0 || col == 0) {
msgq(sp, M_SYSERR, "TIOCGWINSZ");
return (1);
}
/*
* !!!
* SunOS systems deliver SIGWINCH when windows are uncovered
* as well as when they change size. In addition, we call
* here when continuing after being suspended since the window
* may have changed size. Since we don't want to background
* all of the screens just because the window was uncovered,
* ignore the signal if there's no change.
*/
if (row == O_VAL(sp, O_LINES) && col == O_VAL(sp, O_COLUMNS))
return (1);
goto sigw;
}
/*
* !!!
* If TIOCGWINSZ failed, or had entries of 0, try termcap. This
* routine is called before any termcap or terminal information
* has been set up. If there's no TERM environmental variable set,
* let it go, at least ex can run.
*/
if (row == 0 || col == 0) {
if ((s = getenv("TERM")) == NULL)
goto noterm;
#ifdef SYSV_CURSES
if (row == 0)
if ((rval = tigetnum("lines")) < 0)
msgq(sp, M_SYSERR, "tigetnum: lines");
else
row = rval;
if (col == 0)
if ((rval = tigetnum("cols")) < 0)
msgq(sp, M_SYSERR, "tigetnum: cols");
else
col = rval;
#else
switch (tgetent(buf, s)) {
case -1:
msgq(sp, M_SYSERR, "tgetent: %s", s);
return (1);
case 0:
msgq(sp, M_ERR, "%s: unknown terminal type", s);
return (1);
}
if (row == 0)
if ((rval = tgetnum("li")) < 0)
msgq(sp, M_ERR,
"no \"li\" capability for %s", s);
else
row = rval;
if (col == 0)
if ((rval = tgetnum("co")) < 0)
msgq(sp, M_ERR,
"no \"co\" capability for %s", s);
else
col = rval;
#endif
}
/* If nothing else, well, it's probably a VT100. */
noterm: if (row == 0)
row = 24;
if (col == 0)
col = 80;
/* POSIX 1003.2 requires the environment to override. */
if ((s = getenv("LINES")) != NULL)
row = strtol(s, NULL, 10);
if ((s = getenv("COLUMNS")) != NULL)
col = strtol(s, NULL, 10);
sigw: a.bp = buf;
b.bp = NULL;
b.len = 0;
argv[0] = &a;
argv[1] = &b;;
/*
* Tell the options code that the screen size has changed.
* Since the user didn't do the set, clear the set bits.
*/
user_set = F_ISSET(&sp->opts[O_LINES], OPT_SET);
a.len = snprintf(buf, sizeof(buf), "lines=%u", row);
if (opts_set(sp, NULL, argv))
return (1);
if (user_set)
F_CLR(&sp->opts[O_LINES], OPT_SET);
user_set = F_ISSET(&sp->opts[O_COLUMNS], OPT_SET);
a.len = snprintf(buf, sizeof(buf), "columns=%u", col);
if (opts_set(sp, NULL, argv))
return (1);
if (user_set)
F_CLR(&sp->opts[O_COLUMNS], OPT_SET);
F_SET(sp, S_RESIZE);
return (0);
}

252
usr.bin/vi/svi/svi_curses.c Normal file
View File

@ -0,0 +1,252 @@
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char sccsid[] = "@(#)svi_curses.c 8.4 (Berkeley) 8/17/94";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "compat.h"
#include <curses.h>
#include <db.h>
#include <regex.h>
#include "vi.h"
#include "svi_screen.h"
/*
* svi_curses_init --
* Initialize curses.
*/
int
svi_curses_init(sp)
SCR *sp;
{
struct termios t;
char *p;
#ifdef SYSV_CURSES
/*
* The SunOS/System V initscr() isn't reentrant. Don't even think
* about trying to use it. It fails in subtle ways (e.g. select(2)
* on fileno(stdin) stops working). We don't care about the SCREEN
* reference returned by newterm, we never have more than one SCREEN
* at a time.
*/
errno = 0;
if (newterm(O_STR(sp, O_TERM), stdout, stdin) == NULL) {
msgq(sp, errno ? M_SYSERR : M_ERR, "newterm failed");
return (1);
}
#else
/*
* Initscr() doesn't provide useful error values or messages. The
* reasonable guess is that either malloc failed or the terminal was
* unknown or lacking some essential feature. Try and guess so the
* user isn't even more pissed off because of the error message.
*/
errno = 0;
if (initscr() == NULL) {
char kbuf[2048];
msgq(sp, errno ? M_SYSERR : M_ERR, "initscr failed");
if ((p = getenv("TERM")) == NULL || !strcmp(p, "unknown"))
msgq(sp, M_ERR,
"No TERM environment variable set, or TERM set to \"unknown\"");
else if (tgetent(kbuf, p) != 1)
msgq(sp, M_ERR,
"%s: unknown terminal type, or terminal lacks necessary features", p);
else
msgq(sp, M_ERR,
"%s: terminal type lacks necessary features", p);
return (1);
}
#endif
/*
* We use raw mode. What we want is 8-bit clean, however, signals
* and flow control should continue to work. Admittedly, it sounds
* like cbreak, but it isn't. Using cbreak() can get you additional
* things like IEXTEN, which turns on things like DISCARD and LNEXT.
*
* !!!
* If raw isn't turning off echo and newlines, something's wrong.
* However, it doesn't hurt.
*/
noecho(); /* No character echo. */
nonl(); /* No CR/NL translation. */
raw(); /* 8-bit clean. */
idlok(stdscr, 1); /* Use hardware insert/delete line. */
/*
* XXX
* Historic implementations of curses handled SIGTSTP signals
* in one of three ways. They either:
*
* 1: Set their own handler, regardless.
* 2: Did not set a handler if a handler was already installed.
* 3: Set their own handler, but then called any previously set
* handler after completing their own cleanup.
*
* We don't try and figure out which behavior is in place, we
* just set it to SIG_DFL after initializing the curses interface.
*/
(void)signal(SIGTSTP, SIG_DFL);
/*
* If flow control was on, turn it back on. Turn signals on. ISIG
* turns on VINTR, VQUIT, VDSUSP and VSUSP. See signal.c:sig_init()
* for a discussion of what's going on here. To sum up, sig_init()
* already installed a handler for VINTR. We're going to disable the
* other three.
*
* XXX
* We want to use ^Y as a vi scrolling command. If the user has the
* DSUSP character set to ^Y (common practice) clean it up. As it's
* equally possible that the user has VDSUSP set to 'a', we disable
* it regardless. It doesn't make much sense to suspend vi at read,
* so I don't think anyone will care. Alternatively, we could look
* it up in the table of legal command characters and turn it off if
* it matches one. VDSUSP wasn't in POSIX 1003.1-1990, so we test for
* it.
*
* XXX
* We don't check to see if the user had signals enabled to start with.
* If they didn't, it's unclear what we're supposed to do here, but it
* is also pretty unlikely.
*/
if (!tcgetattr(STDIN_FILENO, &t)) {
if (sp->gp->original_termios.c_iflag & IXON)
t.c_iflag |= IXON;
if (sp->gp->original_termios.c_iflag & IXOFF)
t.c_iflag |= IXOFF;
t.c_lflag |= ISIG;
#ifdef VDSUSP
t.c_cc[VDSUSP] = _POSIX_VDISABLE;
#endif
t.c_cc[VQUIT] = _POSIX_VDISABLE;
t.c_cc[VSUSP] = _POSIX_VDISABLE;
(void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t);
}
/* Put the cursor keys into application mode. */
svi_keypad(sp, 1);
/*
* The first screen in the list gets it all. All other screens
* are hidden and lose their maps.
*/
svi_dtoh(sp, "Window resize");
/* Initialize terminal values. */
SVP(sp)->srows = O_VAL(sp, O_LINES);
/*
* Initialize screen values.
*
* Small windows: see svi/svi_refresh.c:svi_refresh, section 3b.
*
* Setup:
* t_minrows is the minimum rows to display
* t_maxrows is the maximum rows to display (rows - 1)
* t_rows is the rows currently being displayed
*/
sp->rows = SVP(sp)->srows;
sp->cols = O_VAL(sp, O_COLUMNS);
sp->woff = 0;
sp->t_rows = sp->t_minrows = O_VAL(sp, O_WINDOW);
if (sp->t_rows > sp->rows - 1) {
sp->t_minrows = sp->t_rows = sp->rows - 1;
msgq(sp, M_INFO,
"Windows option value is too large, max is %u", sp->t_rows);
}
sp->t_maxrows = sp->rows - 1;
/* Create the screen map. */
CALLOC(sp, HMAP, SMAP *, SIZE_HMAP(sp), sizeof(SMAP));
if (HMAP == NULL) {
if (endwin() == ERR)
msgq(sp, M_SYSERR, "endwin");
return (1);
}
TMAP = HMAP + (sp->t_rows - 1);
F_SET(SVP(sp), SVI_CUR_INVALID); /* Cursor is invalid. */
F_SET(SVP(sp), SVI_CURSES_INIT); /* It's initialized. */
return (0);
}
/*
* svi_curses_end --
* Move to the bottom of the screen, end curses.
*/
int
svi_curses_end(sp)
SCR *sp;
{
/*
* XXX
* By the time we get here, the screen private area (SVI_PRIVATE)
* is probably gone. Don't use it, and don't call any routines
* that do.
*
* Restore the cursor keys to normal mode.
*/
svi_keypad(sp, 0);
/* Move to the bottom of the screen. */
if (move(INFOLINE(sp), 0) == OK) {
clrtoeol();
refresh();
}
/* End curses window. */
if (endwin() == ERR)
msgq(sp, M_SYSERR, "endwin");
return (0);
}

310
usr.bin/vi/svi/svi_term.c Normal file
View File

@ -0,0 +1,310 @@
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char sccsid[] = "@(#)svi_term.c 8.6 (Berkeley) 8/17/94";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "compat.h"
#include <curses.h>
#include <db.h>
#include <regex.h>
#include "vi.h"
#include "../vi/vcmd.h"
#include "excmd.h"
#include "svi_screen.h"
/*
* XXX
* THIS REQUIRES THAT ALL SCREENS SHARE A TERMINAL TYPE.
*/
typedef struct _tklist {
char *ts; /* Key's termcap string. */
char *output; /* Corresponding vi command. */
char *name; /* Name. */
u_char value; /* Special value (for lookup). */
} TKLIST;
static TKLIST const c_tklist[] = { /* Command mappings. */
#ifdef SYSV_CURSES
{"kil1", "O", "insert line"},
{"kdch1", "x", "delete character"},
{"kcud1", "j", "cursor down"},
{"kel", "D", "delete to eol"},
{"kind", "\004", "scroll down"},
{"kll", "$", "go to eol"},
{"khome", "^", "go to sol"},
{"kich1", "i", "insert at cursor"},
{"kdl1", "dd", "delete line"},
{"kcub1", "h", "cursor left"},
{"knp", "\006", "page down"},
{"kpp", "\002", "page up"},
{"kri", "\025", "scroll up"},
{"ked", "dG", "delete to end of screen"},
{"kcuf1", "l", "cursor right"},
{"kcuu1", "k", "cursor up"},
#else
{"kA", "O", "insert line"},
{"kD", "x", "delete character"},
{"kd", "j", "cursor down"},
{"kE", "D", "delete to eol"},
{"kF", "\004", "scroll down"},
{"kH", "$", "go to eol"},
{"kh", "^", "go to sol"},
{"kI", "i", "insert at cursor"},
{"kL", "dd", "delete line"},
{"kl", "h", "cursor left"},
{"kN", "\006", "page down"},
{"kP", "\002", "page up"},
{"kR", "\025", "scroll up"},
{"kS", "dG", "delete to end of screen"},
{"kr", "l", "cursor right"},
{"ku", "k", "cursor up"},
#endif
{NULL},
};
static TKLIST const m1_tklist[] = { /* Input mappings (lookup). */
{NULL},
};
static TKLIST const m2_tklist[] = { /* Input mappings (set or delete). */
#ifdef SYSV_CURSES
{"kcud1", "\033ja", "cursor down"},
{"kcub1", "\033ha", "cursor left"},
{"kcuu1", "\033ka", "cursor up"},
{"kcuf1", "\033la", "cursor right"},
#else
{"kd", "\033ja", "cursor down"},
{"kl", "\033ha", "cursor left"},
{"ku", "\033ka", "cursor up"},
{"kr", "\033la", "cursor right"},
#endif
{NULL},
};
/*
* svi_term_init --
* Initialize the special keys defined by the termcap/terminfo entry.
*/
int
svi_term_init(sp)
SCR *sp;
{
KEYLIST *kp;
SEQ *qp;
TKLIST const *tkp;
size_t len;
char *sbp, *s, *t, sbuf[1024];
/* Command mappings. */
for (tkp = c_tklist; tkp->name != NULL; ++tkp) {
#ifdef SYSV_CURSES
if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
continue;
#else
sbp = sbuf;
if ((t = tgetstr(tkp->ts, &sbp)) == NULL)
continue;
#endif
if (seq_set(sp, tkp->name, strlen(tkp->name), t, strlen(t),
tkp->output, strlen(tkp->output), SEQ_COMMAND, SEQ_SCREEN))
return (1);
}
/* Input mappings needing to be looked up. */
for (tkp = m1_tklist; tkp->name != NULL; ++tkp) {
#ifdef SYSV_CURSES
if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
continue;
#else
sbp = sbuf;
if ((t = tgetstr(tkp->ts, &sbp)) == NULL)
continue;
#endif
for (kp = keylist;; ++kp)
if (kp->value == tkp->value)
break;
if (kp == NULL)
continue;
if (seq_set(sp, tkp->name, strlen(tkp->name),
t, strlen(t), &kp->ch, 1, SEQ_INPUT, SEQ_SCREEN))
return (1);
}
/* Input mappings that are already set or are text deletions. */
for (tkp = m2_tklist; tkp->name != NULL; ++tkp) {
#ifdef SYSV_CURSES
if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
continue;
#else
sbp = sbuf;
if ((t = tgetstr(tkp->ts, &sbp)) == NULL)
continue;
#endif
/*
* !!!
* Some terminals' <cursor_left> keys send single <backspace>
* characters. This is okay in command mapping, but not okay
* in input mapping. That combination is the only one we'll
* ever see, hopefully, so kluge it here for now.
*/
if (!strcmp(t, "\b"))
continue;
if (tkp->output == NULL) {
if (seq_set(sp, tkp->name, strlen(tkp->name),
t, strlen(t), NULL, 0, SEQ_INPUT, SEQ_SCREEN))
return (1);
} else
if (seq_set(sp, tkp->name, strlen(tkp->name),
t, strlen(t), tkp->output, strlen(tkp->output),
SEQ_INPUT, SEQ_SCREEN))
return (1);
}
/* Rework any function key mappings. */
for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next) {
if (!F_ISSET(qp, SEQ_FUNCMAP))
continue;
(void)svi_fmap(sp, qp->stype,
qp->input, qp->ilen, qp->output, qp->olen);
}
/* Set up the visual bell information. */
t = sbuf;
if (tgetstr("vb", &t) != NULL && (len = t - sbuf) != 0) {
MALLOC_RET(sp, s, char *, len);
memmove(s, sbuf, len);
if (SVP(sp)->VB != NULL)
free(SVP(sp)->VB);
SVP(sp)->VB = s;
return (0);
}
return (0);
}
/*
* svi_term_end --
* End the special keys defined by the termcap/terminfo entry.
*/
int
svi_term_end(sp)
SCR *sp;
{
SEQ *qp, *nqp;
/* Delete screen specific mappings. */
for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = nqp) {
nqp = qp->q.le_next;
if (!F_ISSET(qp, SEQ_SCREEN))
continue;
(void)seq_mdel(qp);
}
return (0);
}
/*
* svi_fmap --
* Map a function key.
*/
int
svi_fmap(sp, stype, from, flen, to, tlen)
SCR *sp;
enum seqtype stype;
CHAR_T *from, *to;
size_t flen, tlen;
{
char *t, keyname[64];
size_t nlen;
/* If the terminal isn't initialized, there's nothing to do. */
if (!F_ISSET(SVP(sp), SVI_CURSES_INIT))
return (0);
#ifdef SYSV_CURSES
(void)snprintf(keyname, sizeof(keyname), "kf%d", atoi(from + 1));
if ((t = tigetstr(keyname)) == NULL || t == (char *)-1)
t = NULL;
#else
/*
* !!!
* Historically, the 4BSD termcap code didn't support functions keys
* greater than 9. This was silently enforced -- asking for key k12
* returned the value for k1. We try and get around this by using
* the tables specified in the terminfo(TI_ENV) man page from the 3rd
* Edition SVID. This assumes that the implementors of any System V
* compatibility code or an extended termcap used those codes.
*/
{ int n; char *sbp, sbuf[1024];
static const char codes[] = {
/* 0-10 */ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ';',
/* 11-19 */ '1', '2', '3', '4', '5', '6', '7', '8', '9',
/* 20-63 */ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
};
if ((n = atoi(from + 1)) > 63) {
msgq(sp, M_ERR,
"Termcap has no code for the %s function key",
from);
return (1);
}
(void)snprintf(keyname, sizeof(keyname),
"%c%c", n <= 10 ? 'k' : 'F', codes[n]);
sbp = sbuf;
t = tgetstr(keyname, &sbp);
}
#endif
if (t == NULL) {
msgq(sp, M_ERR, "This terminal has no %s key", from);
return (1);
}
nlen = snprintf(keyname,
sizeof(keyname), "function key %d", atoi(from + 1));
return (seq_set(sp, keyname, nlen, t, strlen(t),
to, tlen, stype, SEQ_SCREEN | SEQ_USERDEF));
}

View File

@ -32,7 +32,7 @@
*/
#ifndef lint
static char sccsid[] = "@(#)v_zexit.c 8.7 (Berkeley) 3/8/94";
static const char sccsid[] = "@(#)v_zexit.c 8.12 (Berkeley) 8/17/94";
#endif /* not lint */
#include <sys/types.h>
@ -64,25 +64,18 @@ v_zexit(sp, ep, vp)
EXF *ep;
VICMDARG *vp;
{
/* Write back any modifications. */
if (F_ISSET(ep, F_MODIFIED) &&
file_write(sp, ep, NULL, NULL, NULL, FS_ALL))
return (1);
/*
* !!!
* Historic practice: quit! or two quit's done in succession
* (where ZZ counts as a quit) didn't check for other files.
*
* Check for related screens; quit if they exist, the user will
* get a message on the last screen.
*/
if (sp->ccnt != sp->q_ccnt + 1 &&
ep->refcnt <= 1 && file_unedited(sp) != NULL) {
sp->q_ccnt = sp->ccnt;
msgq(sp, M_ERR,
"More files to edit; use \":n\" to go to the next file");
/* Check to make sure it's not a temporary file. */
if (file_m3(sp, ep, 0))
return (1);
/* Check for more files to edit. */
if (ex_ncheck(sp, 0))
return (1);
}
F_SET(sp, S_EXIT);
return (0);