new public version of nvi
This commit is contained in:
parent
8743009944
commit
1588279d5e
18
usr.bin/vi/USD.doc/edit/Makefile
Normal file
18
usr.bin/vi/USD.doc/edit/Makefile
Normal 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>
|
115
usr.bin/vi/USD.doc/edit/edit.vindex
Normal file
115
usr.bin/vi/USD.doc/edit/edit.vindex
Normal 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
|
2322
usr.bin/vi/USD.doc/edit/edittut.ms
Normal file
2322
usr.bin/vi/USD.doc/edit/edittut.ms
Normal file
File diff suppressed because it is too large
Load Diff
14
usr.bin/vi/USD.doc/exref/Makefile
Normal file
14
usr.bin/vi/USD.doc/exref/Makefile
Normal 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>
|
2230
usr.bin/vi/USD.doc/exref/ex.rm
Normal file
2230
usr.bin/vi/USD.doc/exref/ex.rm
Normal file
File diff suppressed because it is too large
Load Diff
734
usr.bin/vi/USD.doc/exref/ex.summary
Normal file
734
usr.bin/vi/USD.doc/exref/ex.summary
Normal 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
|
||||
|
14
usr.bin/vi/USD.doc/vi.man/Makefile
Normal file
14
usr.bin/vi/USD.doc/vi.man/Makefile
Normal 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}
|
1294
usr.bin/vi/USD.doc/vi.man/vi.1
Normal file
1294
usr.bin/vi/USD.doc/vi.man/vi.1
Normal file
File diff suppressed because it is too large
Load Diff
25
usr.bin/vi/USD.doc/vi.ref/Makefile
Normal file
25
usr.bin/vi/USD.doc/vi.ref/Makefile
Normal 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>
|
1776
usr.bin/vi/USD.doc/vi.ref/ex.cmd.roff
Normal file
1776
usr.bin/vi/USD.doc/vi.ref/ex.cmd.roff
Normal file
File diff suppressed because it is too large
Load Diff
16
usr.bin/vi/USD.doc/vi.ref/merge.awk
Normal file
16
usr.bin/vi/USD.doc/vi.ref/merge.awk
Normal 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"
|
||||
}
|
949
usr.bin/vi/USD.doc/vi.ref/set.opt.roff
Normal file
949
usr.bin/vi/USD.doc/vi.ref/set.opt.roff
Normal 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.
|
270
usr.bin/vi/USD.doc/vi.ref/spell.ok
Normal file
270
usr.bin/vi/USD.doc/vi.ref/spell.ok
Normal 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
|
2984
usr.bin/vi/USD.doc/vi.ref/vi.cmd.roff
Normal file
2984
usr.bin/vi/USD.doc/vi.ref/vi.cmd.roff
Normal file
File diff suppressed because it is too large
Load Diff
1270
usr.bin/vi/USD.doc/vi.ref/vi.ref
Normal file
1270
usr.bin/vi/USD.doc/vi.ref/vi.ref
Normal file
File diff suppressed because it is too large
Load Diff
5610
usr.bin/vi/USD.doc/vi.ref/vi.ref.txt
Normal file
5610
usr.bin/vi/USD.doc/vi.ref/vi.ref.txt
Normal file
File diff suppressed because it is too large
Load Diff
17
usr.bin/vi/USD.doc/vitut/Makefile
Normal file
17
usr.bin/vi/USD.doc/vitut/Makefile
Normal 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>
|
1079
usr.bin/vi/USD.doc/vitut/vi.apwh.ms
Normal file
1079
usr.bin/vi/USD.doc/vitut/vi.apwh.ms
Normal file
File diff suppressed because it is too large
Load Diff
644
usr.bin/vi/USD.doc/vitut/vi.chars
Normal file
644
usr.bin/vi/USD.doc/vitut/vi.chars
Normal 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
|
||||
\&.
|
2064
usr.bin/vi/USD.doc/vitut/vi.in
Normal file
2064
usr.bin/vi/USD.doc/vitut/vi.in
Normal file
File diff suppressed because it is too large
Load Diff
468
usr.bin/vi/USD.doc/vitut/vi.summary
Normal file
468
usr.bin/vi/USD.doc/vitut/vi.summary
Normal 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
103
usr.bin/vi/common/Makefile
Normal 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
53
usr.bin/vi/common/args.h
Normal 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
366
usr.bin/vi/common/cut.c
Normal 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
96
usr.bin/vi/common/cut.h
Normal 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
193
usr.bin/vi/common/delete.c
Normal 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
837
usr.bin/vi/common/exf.c
Normal 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
128
usr.bin/vi/common/exf.h
Normal 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
107
usr.bin/vi/common/gs.h
Normal 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
492
usr.bin/vi/common/line.c
Normal 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
698
usr.bin/vi/common/log.c
Normal 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
53
usr.bin/vi/common/log.h
Normal 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
720
usr.bin/vi/common/main.c
Normal 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
272
usr.bin/vi/common/mark.c
Normal 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
73
usr.bin/vi/common/mark.h
Normal 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
194
usr.bin/vi/common/mem.h
Normal 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
428
usr.bin/vi/common/msg.c
Normal 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
82
usr.bin/vi/common/msg.h
Normal 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 *, ...));
|
9
usr.bin/vi/common/options.awk
Normal file
9
usr.bin/vi/common/options.awk
Normal 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
890
usr.bin/vi/common/options.c
Normal 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);
|
||||
}
|
108
usr.bin/vi/common/options.h.stub
Normal file
108
usr.bin/vi/common/options.h.stub
Normal 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));
|
518
usr.bin/vi/common/options_f.c
Normal file
518
usr.bin/vi/common/options_f.c
Normal 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);
|
||||
}
|
45
usr.bin/vi/common/pathnames.h
Normal file
45
usr.bin/vi/common/pathnames.h
Normal 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
254
usr.bin/vi/common/put.c
Normal 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
869
usr.bin/vi/common/recover.c
Normal 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
311
usr.bin/vi/common/screen.c
Normal 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
342
usr.bin/vi/common/screen.h
Normal 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
833
usr.bin/vi/common/search.c
Normal 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);
|
||||
}
|
54
usr.bin/vi/common/search.h
Normal file
54
usr.bin/vi/common/search.h
Normal 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
351
usr.bin/vi/common/seq.c
Normal 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
79
usr.bin/vi/common/seq.h
Normal 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
569
usr.bin/vi/common/signal.c
Normal 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
732
usr.bin/vi/common/term.c
Normal 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
205
usr.bin/vi/common/term.h
Normal 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
84
usr.bin/vi/common/trace.c
Normal 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
213
usr.bin/vi/common/util.c
Normal 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
124
usr.bin/vi/common/vi.h
Normal 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));
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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
55
usr.bin/vi/docs/ev
Normal 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.
|
@ -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.
|
||||
|
32
usr.bin/vi/docs/internals/context
Normal file
32
usr.bin/vi/docs/internals/context
Normal 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.
|
@ -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
|
||||
|
@ -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
6
usr.bin/vi/ex/excmd.awk
Normal 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;
|
||||
}
|
46
usr.bin/vi/install/recover.script
Normal file
46
usr.bin/vi/install/recover.script
Normal 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
194
usr.bin/vi/sex/sex_window.c
Normal 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
252
usr.bin/vi/svi/svi_curses.c
Normal 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
310
usr.bin/vi/svi/svi_term.c
Normal 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));
|
||||
}
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user