mirror of
https://git.FreeBSD.org/src.git
synced 2025-01-28 16:43:09 +00:00
This commit was generated by cvs2svn to compensate for changes in r2081,
which included commits to RCS files with non-trunk default branches.
This commit is contained in:
commit
4feef85f47
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=2082
3
usr.bin/vi/Makefile
Normal file
3
usr.bin/vi/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
SUBDIR= common
|
||||
|
||||
.include <bsd.subdir.mk>
|
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}
|
798
usr.bin/vi/USD.doc/vi.man/vi.0
Normal file
798
usr.bin/vi/USD.doc/vi.man/vi.0
Normal file
@ -0,0 +1,798 @@
|
||||
EX/VI(1) BSD Reference Manual EX/VI(1)
|
||||
|
||||
NNAAMMEE
|
||||
eexx,, vvii,, vviieeww - text editors
|
||||
|
||||
SSYYNNOOPPSSIISS
|
||||
eexx [--eeFFRRrrssvv] [--cc _c_m_d] [--tt _t_a_g] [--ww _s_i_z_e] [_f_i_l_e _._._.]
|
||||
vvii [--eeFFRRrrvv] [--cc _c_m_d] [--tt _t_a_g] [--ww _s_i_z_e] [_f_i_l_e _._._.]
|
||||
vviieeww [--eeFFRRrrvv] [--cc _c_m_d] [--tt _t_a_g] [--ww _s_i_z_e] [_f_i_l_e _._._.]
|
||||
|
||||
DDEESSCCRRIIPPTTIIOONN
|
||||
VVii is a screen oriented text editor. EExx is a line-oriented text editor.
|
||||
EExx and vvii are different interfaces to the same program, and it is possi-
|
||||
ble to switch back and forth during an edit session. VViieeww is the equiva-
|
||||
lent of using the --RR (read-only) option of vvii.
|
||||
|
||||
This manual page is the one provided with the nneexx//nnvvii versions of the
|
||||
eexx//vvii text editors. NNeexx//nnvvii are intended as bug-for-bug compatible re-
|
||||
placements for the original Fourth Berkeley Software Distribution (4BSD)
|
||||
eexx and vvii programs. For the rest of this manual page, nneexx//nnvvii is used
|
||||
only when it's necessary to distinguish it from the historic implementa-
|
||||
tions of eexx//vvii.
|
||||
|
||||
This manual page is intended for users already familiar with eexx//vvii. Any-
|
||||
one else should almost certainly read a good tutorial on the editor be-
|
||||
fore this manual page. If you're in an unfamiliar environment, and you
|
||||
absolutely have to get work done immediately, read the section after the
|
||||
options description, entitled ``Fast Startup''. It's probably enough to
|
||||
get you going.
|
||||
|
||||
The following options are available:
|
||||
|
||||
--cc Execute _c_m_d immediately after starting the edit session. Partic-
|
||||
ularly useful for initial positioning in the file, however _c_m_d is
|
||||
not limited to positioning commands. This is the POSIX 1003.2
|
||||
interface for the historic ``+cmd'' syntax. NNeexx//nnvvii supports
|
||||
both the old and new syntax.
|
||||
|
||||
--ee Start editing in ex mode, as if the command name were eexx.
|
||||
|
||||
--FF Don't copy the entire file when first starting to edit. (The de-
|
||||
fault is to make a copy in case someone else modifies the file
|
||||
during your edit session.)
|
||||
|
||||
--RR Start editing in read-only mode, as if the command name was vviieeww,
|
||||
or the readonly option was set.
|
||||
|
||||
--rr Recover the specified files, or, if no files are specified, list
|
||||
the files that could be recovered. If no recoverable files by
|
||||
the specified name exist, the file is edited as if the --rr option
|
||||
had not been specified.
|
||||
|
||||
--ss Enter batch mode; applicable only to eexx edit sessions. Batch
|
||||
mode is useful when running eexx scripts. Prompts, informative
|
||||
messages and other user oriented message are turned off, and no
|
||||
startup files or environmental variables are read. This is the
|
||||
POSIX 1003.2 interface for the historic ``-'' argument. NNeexx//nnvvii
|
||||
supports both the old and new syntax.
|
||||
|
||||
--tt Start editing at the specified tag. (See ctags(1)).
|
||||
|
||||
--ww Set the initial window size to the specified number of lines.
|
||||
|
||||
|
||||
|
||||
--vv Start editing in vi mode, as if the command name was vvii or vviieeww.
|
||||
|
||||
--XX Reserved for X11 interfaces. _N_o _X_1_1 _s_u_p_p_o_r_t _i_s _c_u_r_r_e_n_t_l_y
|
||||
_i_m_p_l_e_m_e_n_t_e_d_.
|
||||
|
||||
Command input for eexx//vvii is read from the standard input. In the vvii in-
|
||||
terface, it is an error if standard input is not a terminal. In the eexx
|
||||
interface, if standard input is not a terminal, eexx will read commands
|
||||
from it regardless, however, the session will be a batch mode session,
|
||||
exactly as if the --ss option had been specified.
|
||||
|
||||
EExx//vvii exits 0 on success, and greater than 0 if an error occurs.
|
||||
|
||||
FFAASSTT SSTTAARRTTUUPP
|
||||
This section will tell you the minimum amount that you need to do simple
|
||||
editing tasks using vvii. If you've never used any screen editor before,
|
||||
you're likely to have problems even with this simple introduction. In
|
||||
that case you should find someone that already knows vvii and have them
|
||||
walk you through this section.
|
||||
|
||||
VVii is a screen editor. This means that it takes up almost the entire
|
||||
screen, displaying part of the file on each screen line, except for the
|
||||
last line of the screen. The last line of the screen is used for you to
|
||||
give commands to vvii, and for vvii to give information to you.
|
||||
|
||||
The other fact that you need to understand is that vvii is a modeful edi-
|
||||
tor, i.e. you are either entering text or you are executing commands, and
|
||||
you have to be in the right mode to do one or the other. You will be in
|
||||
command mode when you first start editing a file. There are commands
|
||||
that switch you into input mode. There is only one key that takes you
|
||||
out of input mode, and that is the <escape> key. (Key names are written
|
||||
using less-than and greater-than signs, e.g. <escape> means the
|
||||
``escape'' key, usually labeled ``esc'' on your terminal's keyboard.) If
|
||||
you're ever confused as to which mode you're in, keep entering the <es-
|
||||
cape> key until vvii beeps at you. (Generally, vvii will beep at you if you
|
||||
try and do something that's not allowed. It will also display error mes-
|
||||
sages.)
|
||||
|
||||
To start editing a file, enter the command ``vi file_name<carriage-
|
||||
return>''. The command you should enter as soon as you start editing is
|
||||
``:set verbose showmode<carriage-return>''. This will make the editor
|
||||
give you verbose error messages and display the current mode at the bot-
|
||||
tom of the screen.
|
||||
|
||||
The commands to move around the file are:
|
||||
hh Move the cursor left one character.
|
||||
jj Move the cursor down one line.
|
||||
kk Move the cursor up one line.
|
||||
ll Move the cursor right one character.
|
||||
<<ccuurrssoorr--aarrrroowwss>>
|
||||
The cursor arrow keys should work, too.
|
||||
//tteexxtt<<ccaarrrriiaaggee--rreettuurrnn>>
|
||||
Search for the string ``text'' in the file, and move the cursor to
|
||||
its first character.
|
||||
|
||||
The commands to enter new text are:
|
||||
aa Append new text, _a_f_t_e_r the cursor.
|
||||
ii Insert new text, _b_e_f_o_r_e the cursor.
|
||||
oo Open a new line below the line the cursor is on, and start entering
|
||||
text.
|
||||
OO Open a new line above the line the cursor is on, and start entering
|
||||
text.
|
||||
<<eessccaappee>>
|
||||
Once you've entered input mode using the one of the aa, ii, OO, or oo
|
||||
commands, use <<eessccaappee>> to quit entering text and return to command
|
||||
mode.
|
||||
|
||||
The commands to copy text are:
|
||||
yyyy Copy the line the cursor is on.
|
||||
pp Append the copied line after the line the cursor is on.
|
||||
|
||||
The commands to delete text are:
|
||||
dddd Delete the line the cursor is on.
|
||||
xx Delete the character the cursor is on.
|
||||
|
||||
The commands to write the file are:
|
||||
::ww<<ccaarrrriiaaggee--rreettuurrnn>>
|
||||
Write the file back to the file with the name that you originally
|
||||
used as an argument on the vvii command line.
|
||||
::ww ffiillee__nnaammee<<ccaarrrriiaaggee--rreettuurrnn>>
|
||||
Write the file back to the file with the name ``file_name''.
|
||||
|
||||
The commands to quit editing and exit the editor are:
|
||||
::qq<<ccaarrrriiaaggee--rreettuurrnn>>
|
||||
Quit editing and leave vi (if you've modified the file, but not
|
||||
saved your changes, vvii will refuse to quit).
|
||||
::qq!!<<ccaarrrriiaaggee--rreettuurrnn>>
|
||||
Quit, discarding any modifications that you may have made.
|
||||
|
||||
One final caution. Unusual characters can take up more than one column
|
||||
on the screen, and long lines can take up more than a single screen line.
|
||||
The above commands work on ``physical'' characters and lines, i.e. they
|
||||
affect the entire line no matter how many screen lines it takes up and
|
||||
the entire character no matter how many screen columns it takes up.
|
||||
|
||||
VVII CCOOMMMMAANNDDSS
|
||||
The following section describes the commands available in the command
|
||||
mode of the vvii editor. In each entry below, the tag line is a usage syn-
|
||||
opsis for the command character.
|
||||
|
||||
[[ccoouunntt]] <<ccoonnttrrooll--AA>>
|
||||
Search forward count times for the current word.
|
||||
[[ccoouunntt]] <<ccoonnttrrooll--BB>>
|
||||
Page backwards count screens.
|
||||
[[ccoouunntt]] <<ccoonnttrrooll--DD>>
|
||||
Scroll forward count lines.
|
||||
[[ccoouunntt]] <<ccoonnttrrooll--EE>>
|
||||
Scroll forward count lines, leaving the current line and column as
|
||||
is, if possible.
|
||||
[[ccoouunntt]] <<ccoonnttrrooll--FF>>
|
||||
Page forward count screens.
|
||||
<<ccoonnttrrooll--GG>>
|
||||
Display the file information.
|
||||
<<ccoonnttrrooll--HH>>
|
||||
[[ccoouunntt]] hh
|
||||
Move the cursor back count characters in the current line.
|
||||
[[ccoouunntt]] <<ccoonnttrrooll--JJ>>
|
||||
[[ccoouunntt]] <<ccoonnttrrooll--NN>>
|
||||
[[ccoouunntt]] jj
|
||||
Move the cursor down count lines without changing the current col-
|
||||
umn.
|
||||
<<ccoonnttrrooll--LL>>
|
||||
<<ccoonnttrrooll--RR>>
|
||||
Repaint the screen.
|
||||
[[ccoouunntt]] <<ccoonnttrrooll--MM>>
|
||||
[[ccoouunntt]] ++
|
||||
Move the cursor down count lines to the first nonblank character of
|
||||
that line.
|
||||
[[ccoouunntt]] <<ccoonnttrrooll--PP>>
|
||||
[[ccoouunntt]] kk
|
||||
Move the cursor up count lines, without changing the current col-
|
||||
|
||||
umn.
|
||||
<<ccoonnttrrooll--TT>>
|
||||
Return to the most recent tag context.
|
||||
<<ccoonnttrrooll--UU>>
|
||||
Scroll backwards count lines.
|
||||
<<ccoonnttrrooll--WW>>
|
||||
Switch to the next lower screen in the window, or, to the first
|
||||
screen if there are no lower screens in the window.
|
||||
<<ccoonnttrrooll--YY>>
|
||||
Scroll backwards count lines, leaving the current line and column
|
||||
as is, if possible.
|
||||
<<ccoonnttrrooll--ZZ>>
|
||||
Suspend the current editor session.
|
||||
<<eessccaappee>>
|
||||
Execute eexx commands or cancel partial commands.
|
||||
<<ccoonnttrrooll--]]>>
|
||||
Push a tag reference onto the tag stack.
|
||||
<<ccoonnttrrooll--^^>>
|
||||
Switch to the most recently edited file.
|
||||
[[ccoouunntt]] <<ssppaaccee>>
|
||||
[[ccoouunntt]] ll
|
||||
Move the cursor forward count characters without changing the cur-
|
||||
rent line.
|
||||
[[ccoouunntt]] !! mmoottiioonn sshheellll--aarrgguummeenntt((ss))
|
||||
Replace text with results from a shell command.
|
||||
[[ccoouunntt]] ## ++||--||##
|
||||
Increment or decrement the cursor number.
|
||||
[[ccoouunntt]] $$
|
||||
Move the cursor to the end of a line.
|
||||
%% Move to the matching character.
|
||||
&& Repeat the previous substitution command on the current line.
|
||||
''<<cchhaarraacctteerr>>
|
||||
``<<cchhaarraacctteerr>>
|
||||
Return to a context marked by the character <character>.
|
||||
[[ccoouunntt]] ((
|
||||
Back up count sentences.
|
||||
[[ccoouunntt]] ))
|
||||
Move forward count sentences.
|
||||
[[ccoouunntt]] ,,
|
||||
Reverse find character count times.
|
||||
[[ccoouunntt]] --
|
||||
Move to first nonblank of the previous line, count times.
|
||||
[[ccoouunntt]] ..
|
||||
Repeat the last vvii command that modified text.
|
||||
//RREE<<ccaarrrriiaaggee--rreettuurrnn>>
|
||||
//RREE// [[ooffffsseett]]<<ccaarrrriiaaggee--rreettuurrnn>>
|
||||
??RREE<<ccaarrrriiaaggee--rreettuurrnn>>
|
||||
??RREE?? [[ooffffsseett]]<<ccaarrrriiaaggee--rreettuurrnn>>
|
||||
NN
|
||||
nn Search forward or backward for a regular expression.
|
||||
00 Move to the first character in the current line.
|
||||
: Execute an ex command.
|
||||
[[ccoouunntt]] ;;
|
||||
Repeat the last character find count times.
|
||||
[[ccoouunntt]] << mmoottiioonn
|
||||
[[ccoouunntt]] >> mmoottiioonn
|
||||
Shift lines left or right.
|
||||
@@ bbuuffffeerr
|
||||
Execute a named buffer.
|
||||
[[ccoouunntt]] AA
|
||||
Enter input mode, appending the text after the end of the line.
|
||||
[[ccoouunntt]] BB
|
||||
Move backwards count bigwords.
|
||||
[[bbuuffffeerr]] [[ccoouunntt]] CC
|
||||
|
||||
|
||||
Change text from the current position to the end-of-line.
|
||||
[[bbuuffffeerr]] DD
|
||||
Delete text from the current position to the end-of-line.
|
||||
[[ccoouunntt]] EE
|
||||
Move forward count end-of-bigwords.
|
||||
[[ccoouunntt]] FF <<cchhaarraacctteerr>>
|
||||
Search count times backward through the current line for
|
||||
<character>.
|
||||
[[ccoouunntt]] GG
|
||||
Move to line count, or the last line of the file if count not spec-
|
||||
ified.
|
||||
[[ccoouunntt]] HH
|
||||
Move to the screen line count - 1 lines below the top of the
|
||||
screen.
|
||||
[[ccoouunntt]] II
|
||||
Enter input mode, inserting the text at the beginning of the line.
|
||||
[[ccoouunntt]] JJ
|
||||
Join lines.
|
||||
[[ccoouunntt]] LL
|
||||
Move to the screen line count - 1 lines above the bottom of the
|
||||
screen.
|
||||
MM Move to the screen line in the middle of the screen.
|
||||
[[ccoouunntt]] OO
|
||||
Enter input mode, appending text in a new line above the current
|
||||
line.
|
||||
[[bbuuffffeerr]] PP
|
||||
Insert text from a buffer.
|
||||
QQ Exit vvii (or visual) mode and switch to eexx mode.
|
||||
[[ccoouunntt]] RR
|
||||
Enter input mode, replacing the characters in the current line.
|
||||
[[bbuuffffeerr]] [[ccoouunntt]] SS
|
||||
Substitute count lines.
|
||||
[[ccoouunntt]] TT <<cchhaarraacctteerr>>
|
||||
Search backwards, count times, through the current line for the
|
||||
character _a_f_t_e_r the specified <character>.
|
||||
UU Restore the current line to its state before the cursor last moved
|
||||
to it.
|
||||
[[ccoouunntt]] WW
|
||||
Move forward count bigwords.
|
||||
[[bbuuffffeerr]] [[ccoouunntt]] XX
|
||||
Delete count characters before the cursor.
|
||||
[[bbuuffffeerr]] [[ccoouunntt]] YY
|
||||
Copy (or ``yank'') count lines into the specified buffer.
|
||||
ZZZZ Write the file and exit vvii.
|
||||
[[ccoouunntt]] [[[[
|
||||
Back up count section boundaries.
|
||||
[[ccoouunntt]] ]]]]
|
||||
Move forward count section boundaries.
|
||||
^^ Move to first nonblank character on the current line.
|
||||
[[ccoouunntt]] __
|
||||
Move down count - 1 lines, to the first nonblank character.
|
||||
[[ccoouunntt]] aa
|
||||
Enter input mode, appending the text after the cursor.
|
||||
[[ccoouunntt]] bb
|
||||
Move backwards count words.
|
||||
[[bbuuffffeerr]] [[ccoouunntt]] cc mmoottiioonn
|
||||
Change a region of text.
|
||||
[[bbuuffffeerr]] [[ccoouunntt]] dd mmoottiioonn
|
||||
Delete a region of text.
|
||||
[[ccoouunntt]] ee
|
||||
Move forward count end-of-words.
|
||||
[[ccoouunntt]] ff<<cchhaarraacctteerr>>
|
||||
Search forward, count times, through the rest of the current line
|
||||
for <character>.
|
||||
[[ccoouunntt]] ii
|
||||
|
||||
Enter input mode, inserting the text before the cursor.
|
||||
mm <<cchhaarraacctteerr>>
|
||||
Save the current context (line and column) as <character>.
|
||||
[[ccoouunntt]] oo
|
||||
Enter input mode, appending text in a new line under the current
|
||||
line.
|
||||
[[bbuuffffeerr]] pp
|
||||
Append text from a buffer.
|
||||
[[ccoouunntt]] rr <<cchhaarraacctteerr>>
|
||||
Replace count characters.
|
||||
[[bbuuffffeerr]] [[ccoouunntt]] ss
|
||||
Substitute count characters in the current line starting with the
|
||||
current character.
|
||||
[[ccoouunntt]] tt <<cchhaarraacctteerr>>
|
||||
Search forward, count times, through the current line for the char-
|
||||
acter immediately _b_e_f_o_r_e <character>.
|
||||
uu Undo the last change made to the file.
|
||||
[[ccoouunntt]] ww
|
||||
Move forward count words.
|
||||
[[bbuuffffeerr]] [[ccoouunntt]] xx
|
||||
Delete count characters.
|
||||
[[bbuuffffeerr]] [[ccoouunntt]] yy mmoottiioonn
|
||||
Copy (or ``yank'') a text region specified by the count and motion
|
||||
into a buffer.
|
||||
[[ccoouunntt11]] zz [[ccoouunntt22]] --||..||++||^^||<<ccaarrrriiaaggee--rreettuurrnn>>
|
||||
Redraw, optionally repositioning and resizing the screen.
|
||||
[[ccoouunntt]] {{
|
||||
Move backward count paragraphs.
|
||||
[[ccoouunntt]] ||
|
||||
Move to a specific _c_o_l_u_m_n position on the current line.
|
||||
[[ccoouunntt]] }}
|
||||
Move forward count paragraphs.
|
||||
[[ccoouunntt]] ~~
|
||||
Reverse the case of the next count character(s).
|
||||
[[ccoouunntt]] ~~ mmoottiioonn
|
||||
Reverse the case of the characters in a text region specified by
|
||||
the count and motion.
|
||||
<<iinntteerrrruupptt>>
|
||||
Interrupt the current operation.
|
||||
|
||||
VVII TTEEXXTT IINNPPUUTT CCOOMMMMAANNDDSS
|
||||
The following section describes the commands available in the text input
|
||||
mode of the vvii editor.
|
||||
|
||||
<<nnuull>>
|
||||
Replay the previous input.
|
||||
<<ccoonnttrrooll--DD>>
|
||||
Erase the previous autoindent character.
|
||||
^^<<ccoonnttrrooll--DD>>
|
||||
Erase all of the autoindent characters, and reset the autoindent
|
||||
level.
|
||||
00<<ccoonnttrrooll--DD>>
|
||||
Erase all of the autoindent characters.
|
||||
<<ccoonnttrrooll--TT>>
|
||||
Insert sufficient <tab> and <space> characters to move the cursor
|
||||
forward to a column immediately after the next column which is an
|
||||
even multiple of the sshhiiffttwwiiddtthh option.
|
||||
<<eerraassee>>
|
||||
<<ccoonnttrrooll--HH>>
|
||||
Erase the last character.
|
||||
<<lliitteerraall nneexxtt>>
|
||||
Quote the next character.
|
||||
<<eessccaappee>>
|
||||
Resolve all text input into the file, and return to command mode.
|
||||
<<lliinnee eerraassee>>
|
||||
|
||||
Erase the current line.
|
||||
<<ccoonnttrrooll--WW>>
|
||||
<<wwoorrdd eerraassee>>
|
||||
Erase the last word. The definition of word is dependent on the
|
||||
aallttwweerraassee and ttttyywweerraassee options.
|
||||
<<ccoonnttrrooll--XX>>[[00--99AA--FFaa--ff]]**
|
||||
Insert a character with the specified hexadecimal value into the
|
||||
text.
|
||||
<<iinntteerrrruupptt>>
|
||||
Interrupt text input mode, returning to command mode.
|
||||
|
||||
EEXX CCOOMMMMAANNDDSS
|
||||
The following section describes the commands available in the eexx editor.
|
||||
In each entry below, the tag line is a usage synopsis for the command.
|
||||
|
||||
<<eenndd--ooff--ffiillee>>
|
||||
Scroll the screen.
|
||||
!! aarrgguummeenntt((ss))
|
||||
[[rraannggee]]!! aarrgguummeenntt((ss))
|
||||
Execute a shell command, or filter lines through a shell command.
|
||||
"" A comment.
|
||||
[[rraannggee]] nnuu[[mmbbeerr]] [[ccoouunntt]] [[ffllaaggss]]
|
||||
[[rraannggee]] ## [[ccoouunntt]] [[ffllaaggss]]
|
||||
Display the selected lines, each preceded with its line number.
|
||||
@@ bbuuffffeerr
|
||||
** bbuuffffeerr
|
||||
Execute a buffer.
|
||||
[[rraannggee]] dd[[eelleettee]] [[bbuuffffeerr]] [[ccoouunntt]] [[ffllaaggss]]
|
||||
Delete the lines from the file.
|
||||
ddii[[ssppllaayy]] bb[[uuffffeerrss]] || ss[[ccrreeeennss]] || tt[[aaggss]]
|
||||
Display buffers, screens or tags.
|
||||
ee[[ddiitt]][[!!]] [[++ccmmdd]] [[ffiillee]]
|
||||
eexx[[!!]] [[++ccmmdd]] [[ffiillee]]
|
||||
Edit a different file.
|
||||
eexxuu[[ssaaggee]] [[ccoommmmaanndd]]
|
||||
Display usage for an eexx command.
|
||||
ff[[iillee]] [[ffiillee]]
|
||||
Display and optionally change the file name.
|
||||
ffgg [[nnaammee]]
|
||||
VVii mode only. Foreground the specified screen.
|
||||
[[rraannggee]] gg[[lloobbaall]] //ppaatttteerrnn// [[ccoommmmaannddss]]
|
||||
[[rraannggee]] vv //ppaatttteerrnn// [[ccoommmmaannddss]]
|
||||
Apply commands to lines matching (or not matching) a pattern.
|
||||
hhee[[llpp]]
|
||||
Display a help message.
|
||||
[[lliinnee]] ii[[nnsseerrtt]][[!!]]
|
||||
The input text is inserted before the specified line.
|
||||
[[rraannggee]] jj[[ooiinn]][[!!]] [[ccoouunntt]] [[ffllaaggss]]
|
||||
Join lines of text together.
|
||||
[[rraannggee]] ll[[iisstt]] [[ccoouunntt]] [[ffllaaggss]]
|
||||
Display the lines unambiguously.
|
||||
mmaapp[[!!]] [[llhhss rrhhss]]
|
||||
Define or display maps (for vvii only).
|
||||
[[lliinnee]] mmaa[[rrkk]] <<cchhaarraacctteerr>>
|
||||
[[lliinnee]] kk <<cchhaarraacctteerr>>
|
||||
Mark the line with the mark <character>.
|
||||
[[rraannggee]] mm[[oovvee]] lliinnee
|
||||
Move the specified lines after the target line.
|
||||
mmkk[[eexxrrcc]][[!!]] ffiillee
|
||||
Write the abbreviations, editor options and maps to the specified
|
||||
file.
|
||||
nn[[eexxtt]][[!!]] [[ffiillee ......]]
|
||||
Edit the next file from the argument list.
|
||||
[[lliinnee]] oo[[ppeenn]] //ppaatttteerrnn// [[ffllaaggss]]
|
||||
|
||||
|
||||
Enter open mode.
|
||||
pprree[[sseerrvvee]]
|
||||
Save the file in a form that can later be recovered using the eexx --rr
|
||||
option.
|
||||
pprreevv[[iioouuss]][[!!]]
|
||||
Edit the previous file from the argument list.
|
||||
[[rraannggee]] pp[[rriinntt]] [[ccoouunntt]] [[ffllaaggss]]
|
||||
Display the specified lines.
|
||||
[[lliinnee]] ppuu[[tt]] [[bbuuffffeerr]]
|
||||
Append buffer contents to the current line.
|
||||
qq[[uuiitt]][[!!]]
|
||||
End the editing session.
|
||||
[[lliinnee]] rr[[eeaadd]][[!!]] [[ffiillee]]
|
||||
Read a file.
|
||||
rreecc[[oovveerr]] ffiillee
|
||||
Recover file if it was previously saved.
|
||||
rreess[[iizzee]] [[++||--]]ssiizzee
|
||||
VVii mode only. Grow or shrink the current screen.
|
||||
rreeww[[iinndd]][[!!]]
|
||||
Rewind the argument list.
|
||||
ssee[[tt]] [[ooppttiioonn[[==[[vvaalluuee]]]] ......]] [[nnooooppttiioonn ......]] [[ooppttiioonn?? ......]] [[aallll]]
|
||||
Display or set editor options.
|
||||
sshh[[eellll]]
|
||||
Run a shell program.
|
||||
ssoo[[uurrccee]] ffiillee
|
||||
Read and execute eexx commands from a file.
|
||||
sspp[[lliitt]] [[ffiillee ......]]
|
||||
VVii mode only. Split the screen.
|
||||
[[rraannggee]] ss[[uubbssttiittuuttee]] [[//ppaatttteerrnn//rreeppllaaccee//]] [[ooppttiioonnss]] [[ccoouunntt]] [[ffllaaggss]]
|
||||
[[rraannggee]] && [[ooppttiioonnss]] [[ccoouunntt]] [[ffllaaggss]]
|
||||
[[rraannggee]] ~~ [[ooppttiioonnss]] [[ccoouunntt]] [[ffllaaggss]]
|
||||
Make substitutions.
|
||||
ssuu[[ssppeenndd]][[!!]]
|
||||
sstt[[oopp]][[!!]]
|
||||
<<ssuussppeenndd>>
|
||||
Suspend the edit session.
|
||||
ttaa[[gg]][[!!]] ttaaggssttrriinngg
|
||||
Edit the file containing the specified tag.
|
||||
ttaaggpp[[oopp]][[!!]] [[ffiillee || nnuummbbeerr]]
|
||||
Pop to the specified tag in the tags stack.
|
||||
uunnmm[[aapp]][[!!]] llhhss
|
||||
Unmap a mapped string.
|
||||
vvee[[rrssiioonn]]
|
||||
Display the version of the eexx//vvii editor.
|
||||
[[lliinnee]] vvii[[ssuuaall]] [[ttyyppee]] [[ccoouunntt]] [[ffllaaggss]]
|
||||
EExx mode only. Enter vvii.
|
||||
vvii[[ssuuaall]][[!!]] [[++ccmmdd]] [[ffiillee]]
|
||||
VVii mode only. Edit a new file.
|
||||
vviiuu[[ssaaggee]] [[ccoommmmaanndd]]
|
||||
Display usage for a vvii command.
|
||||
[[rraannggee]] ww[[rriittee]][[!!]] [[>>>>]] [[ffiillee]]
|
||||
[[rraannggee]] ww[[rriittee]] [[!!]] [[ffiillee]]
|
||||
[[rraannggee]] wwnn[[!!]] [[>>>>]] [[ffiillee]]
|
||||
[[rraannggee]] wwqq[[!!]] [[>>>>]] [[ffiillee]]
|
||||
Write the file.
|
||||
[[rraannggee]] xx[[iitt]][[!!]] [[ffiillee]]
|
||||
Write the file if it has been modified.
|
||||
[[rraannggee]] yyaa[[nnkk]] [[bbuuffffeerr]] [[ccoouunntt]]
|
||||
Copy the specified lines to a buffer.
|
||||
[[lliinnee]] zz [[ttyyppee]] [[ccoouunntt]] [[ffllaaggss]]
|
||||
Adjust the window.
|
||||
|
||||
SSEETT OOPPTTIIOONNSS
|
||||
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 abbre-
|
||||
viations and their default values.
|
||||
|
||||
In each entry below, the first part of the tag line is the full name of
|
||||
the option, followed by any equivalent abbreviations. 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.
|
||||
|
||||
Options apply to both eexx and vvii modes, unless otherwise specified.
|
||||
|
||||
aallttwweerraassee [[ooffff]]
|
||||
VVii only. Select an alternate word erase algorithm.
|
||||
aauuttooiinnddeenntt,, aaii [[ooffff]]
|
||||
Automatically indent new lines.
|
||||
aauuttoopprriinntt,, aapp [[ooffff]]
|
||||
EExx only. Display the current line automatically.
|
||||
aauuttoowwrriittee,, aaww [[ooffff]]
|
||||
Write modified files automatically when changing files.
|
||||
bbeeaauuttiiffyy,, bbff [[ooffff]]
|
||||
Discard control characters.
|
||||
ccddppaatthh [[eennvviirroonnmmeenntt vvaarriiaabbllee CCDDPPAATTHH,, oorr ccuurrrreenntt ddiirreeccttoorryy]]
|
||||
The directory paths used as path prefixes for the ccdd command.
|
||||
ccoolluummnnss,, ccoo [[8800]]
|
||||
Set the number of columns in the screen.
|
||||
ccoommmmeenntt [[ooffff]]
|
||||
VVii only. Skip leading comments in files.
|
||||
ddiirreeccttoorryy,, ddiirr [[eennvviirroonnmmeenntt vvaarriiaabbllee TTMMPPDDIIRR,, oorr //ttmmpp]]
|
||||
The directory where temporary files are created.
|
||||
eeddccoommppaattiibbllee,, eedd [[ooffff]]
|
||||
Remember the values of the ``c'' and ``g'' suffices to the
|
||||
ssuubbssttiittuuttee commands, instead of initializing them as unset for each
|
||||
new command.
|
||||
eerrrroorrbbeellllss,, eebb [[ooffff]]
|
||||
EExx only. Announce error messages with a bell.
|
||||
eexxrrcc,, eexx [[ooffff]]
|
||||
Never read startup files in the local directory.
|
||||
eexxtteennddeedd [[ooffff]]
|
||||
Regular expressions are extended (i.e. egrep(1) style) expres-
|
||||
sions.
|
||||
ffllaasshh [[oonn]]
|
||||
Flash the screen instead of beeping the keyboard on error.
|
||||
hhaarrddttaabbss,, hhtt [[88]]
|
||||
Set the spacing between hardware tab settings.
|
||||
iiggnnoorreeccaassee,, iicc [[ooffff]]
|
||||
Ignore case differences in regular expressions.
|
||||
kkeeyyttiimmee [[66]]
|
||||
The 10th's of a second eexx//vvii waits for a subsequent key to complete
|
||||
a key mapping.
|
||||
lleeffttrriigghhtt [[ooffff]]
|
||||
VVii only. Do left-right scrolling.
|
||||
lliinneess,, llii [[2244]]
|
||||
VVii only. Set the number of lines in the screen.
|
||||
lliisspp [[ooffff]]
|
||||
VVii only. Modify various search commands and options to work with
|
||||
Lisp.
|
||||
|
||||
_T_h_i_s _o_p_t_i_o_n _i_s _n_o_t _y_e_t _i_m_p_l_e_m_e_n_t_e_d_.
|
||||
lliisstt [[ooffff]]
|
||||
Display lines in an unambiguous fashion.
|
||||
mmaaggiicc [[oonn]]
|
||||
Treat certain characters specially in regular expressions.
|
||||
mmaattcchhttiimmee [[77]]
|
||||
VVii only. The 10th's of a second eexx//vvii pauses on the matching char-
|
||||
acter when the sshhoowwmmaattcchh option is set.
|
||||
mmeessgg [[oonn]]
|
||||
|
||||
|
||||
Permit messages from other users.
|
||||
mmooddeelliinneess,, mmooddeelliinnee [[ooffff]]
|
||||
Read the first and last few lines of each file for eexx commands.
|
||||
|
||||
_T_h_i_s _o_p_t_i_o_n _w_i_l_l _n_e_v_e_r _b_e _i_m_p_l_e_m_e_n_t_e_d_.
|
||||
nnuummbbeerr,, nnuu [[ooffff]]
|
||||
Precede each line displayed with its current line number.
|
||||
ooccttaall [[ooffff]]
|
||||
Display unknown characters as octal numbers, instead of the default
|
||||
hexadecimal.
|
||||
ooppeenn [[oonn]]
|
||||
EExx only. If this option is not set, the ooppeenn and vviissuuaall commands
|
||||
are disallowed.
|
||||
ooppttiimmiizzee,, oopptt [[oonn]]
|
||||
VVii only. Optimize text throughput to dumb terminals.
|
||||
|
||||
_T_h_i_s _o_p_t_i_o_n _i_s _n_o_t _y_e_t _i_m_p_l_e_m_e_n_t_e_d_.
|
||||
ppaarraaggrraapphhss,, ppaarraa [[IIPPLLPPPPPPQQPPPP LLIIppppllppiippbbpp]]
|
||||
VVii only. Define additional paragraph boundaries for the {{ and }}
|
||||
commands.
|
||||
pprroommpptt [[oonn]]
|
||||
EExx only. Display a command prompt.
|
||||
rreeaaddoonnllyy,, rroo [[ooffff]]
|
||||
Mark the file as read-only.
|
||||
rreeccddiirr [[//vvaarr//ttmmpp//vvii..rreeccoovveerr]]
|
||||
The directory where recovery files are stored.
|
||||
rreeddrraaww,, rree [[ooffff]]
|
||||
VVii only. Simulate an intelligent terminal on a dumb one.
|
||||
|
||||
_T_h_i_s _o_p_t_i_o_n _i_s _n_o_t _y_e_t _i_m_p_l_e_m_e_n_t_e_d_.
|
||||
rreemmaapp [[oonn]]
|
||||
Remap keys until resolved.
|
||||
rreeppoorrtt [[55]]
|
||||
Set the number of lines about which the editor reports changes or
|
||||
yanks.
|
||||
rruulleerr [[ooffff]]
|
||||
VVii only. Display a row/column ruler on the colon command line.
|
||||
ssccrroollll,, ssccrr [[wwiinnddooww // 22]]
|
||||
Set the number of lines scrolled.
|
||||
sseeccttiioonnss,, sseecctt [[NNHHSSHHHH HHUUnnhhsshh]]
|
||||
VVii only. Define additional section boundaries for the [[[[ and ]]]]
|
||||
commands.
|
||||
sshheellll,, sshh [[eennvviirroonnmmeenntt vvaarriiaabbllee SSHHEELLLL,, oorr //bbiinn//sshh]]
|
||||
Select the shell used by the editor.
|
||||
sshhiiffttwwiiddtthh,, ssww [[88]]
|
||||
Set the autoindent and shift command indentation width.
|
||||
sshhoowwddiirrttyy [[ooffff]]
|
||||
VVii only. Display an asterisk on the colon command line if the file
|
||||
has been modified.
|
||||
sshhoowwmmaattcchh,, ssmm [[ooffff]]
|
||||
VVii only. Note matching ``{'' and ``('' for ``}'' and ``)'' charac-
|
||||
ters.
|
||||
sshhoowwmmooddee [[ooffff]]
|
||||
VVii only. Display the current editor mode (command or input).
|
||||
ssiiddeessccrroollll [[1166]]
|
||||
VVii only. Set the amount a left-right scroll will shift.
|
||||
sslloowwooppeenn,, ssllooww [[ooffff]]
|
||||
Delay display updating during text input.
|
||||
|
||||
_T_h_i_s _o_p_t_i_o_n _i_s _n_o_t _y_e_t _i_m_p_l_e_m_e_n_t_e_d_.
|
||||
ssoouurrcceeaannyy [[ooffff]]
|
||||
Read startup files not owned by the current user.
|
||||
|
||||
_T_h_i_s _o_p_t_i_o_n _w_i_l_l _n_e_v_e_r _b_e _i_m_p_l_e_m_e_n_t_e_d_.
|
||||
ttaabbssttoopp,, ttss [[88]]
|
||||
|
||||
This option sets tab widths for the editor display.
|
||||
ttaagglleennggtthh,, ttll [[00]]
|
||||
Set the number of significant characters in tag names.
|
||||
ttaaggss,, ttaagg [[ttaaggss //vvaarr//ddbb//lliibbcc..ttaaggss //ssyyss//kkeerrnn//ttaaggss]]
|
||||
Set the list of tags files.
|
||||
tteerrmm,, ttttyyttyyppee,, ttttyy [[eennvviirroonnmmeenntt vvaarriiaabbllee TTEERRMM]]
|
||||
Set the terminal type.
|
||||
tteerrssee [[ooffff]]
|
||||
This option has historically made editor messages less verbose. It
|
||||
has no effect in this implementation.
|
||||
ttiillddeeoopp
|
||||
Modify the ~~ command to take an associated motion.
|
||||
ttiimmeeoouutt,, ttoo [[oonn]]
|
||||
Time out on keys which may be mapped.
|
||||
ttttyywweerraassee [[ooffff]]
|
||||
VVii only. Select an alternate erase algorithm.
|
||||
vveerrbboossee [[ooffff]]
|
||||
only. Display an error message for every error.
|
||||
ww330000 [[nnoo ddeeffaauulltt]]
|
||||
VVii only. Set the window size if the baud rate is less than 1200
|
||||
baud.
|
||||
ww11220000 [[nnoo ddeeffaauulltt]]
|
||||
VVii only. Set the window size if the baud rate is equal to 1200
|
||||
baud.
|
||||
ww99660000 [[nnoo ddeeffaauulltt]]
|
||||
VVii only. Set the window size if the baud rate is greater than 1200
|
||||
baud.
|
||||
wwaarrnn [[oonn]]
|
||||
EExx only. This option causes a warning message to the terminal if
|
||||
the file has been modified, since it was last written, before a !!
|
||||
command.
|
||||
wwiinnddooww,, ww,, wwii [[eennvviirroonnmmeenntt vvaarriiaabbllee LLIINNEESS]]
|
||||
Set the window size for the screen.
|
||||
wwrraappmmaarrggiinn,, wwmm [[00]]
|
||||
VVii only. Break lines automatically when they reach the right-hand
|
||||
margin.
|
||||
wwrraappssccaann,, wwss [[oonn]]
|
||||
Set searches to wrap around the end or beginning of the file.
|
||||
wwrriitteeaannyy,, wwaa [[ooffff]]
|
||||
Turn off file-overwriting checks.
|
||||
|
||||
EENNVVIIRROONNMMEENNTTAALL VVAARRIIAABBLLEESS
|
||||
COLUMNS The number of columns on the screen. This value overrides any
|
||||
system or terminal specific values. If the COLUMNS environ-
|
||||
mental variable is not set when eexx//vvii runs, or the ccoolluummnnss op-
|
||||
tion is explicitly reset by the user, eexx//vvii enters the value
|
||||
into the environment.
|
||||
EXINIT A list of eexx startup commands, read if the variable NEXINIT is
|
||||
not set.
|
||||
HOME The user's home directory, used as the initial directory path
|
||||
for the startup _$_H_O_M_E_/_._n_e_x_r_c and _$_H_O_M_E_/_._e_x_r_c files. This val-
|
||||
ue is also used as the default directory for the vvii ccdd com-
|
||||
mand.
|
||||
LINES The number of rows on the screen. This value overrides any
|
||||
system or terminal specific values. If the LINES environmen-
|
||||
tal variable is not set when eexx//vvii runs, or the lliinneess option
|
||||
is explicitly reset by the user, eexx//vvii enters the value into
|
||||
the environment.
|
||||
NEXINIT A list of eexx startup commands.
|
||||
SHELL The user's shell of choice (see also the sshheellll option).
|
||||
TERM The user's terminal type. The default is the type
|
||||
``unknown''. If the TERM environmental variable is not set
|
||||
when eexx//vvii runs, or the tteerrmm option is explicitly reset by the
|
||||
user, eexx//vvii enters the value into the environment.
|
||||
TMPDIR The location used to stored temporary files (see also the
|
||||
ddiirreeccttoorryy option).
|
||||
|
||||
AASSYYNNCCHHRROONNOOUUSS EEVVEENNTTSS
|
||||
SIGALRM VVii//eexx uses this signal for periodic backups of file modifica-
|
||||
tions and to display ``busy'' messages when operations are
|
||||
likely to take a long time.
|
||||
SIGHUP
|
||||
SIGTERM If the current buffer has changed since it was last written
|
||||
in its entirety, the editor attempts to save the modified
|
||||
file so it can be later recovered. See the vvii//eexx Reference
|
||||
manual section entitled ``Recovery'' for more information.
|
||||
SIGINT When an interrupt occurs, the current operation is halted,
|
||||
and the editor returns to the command level. If interrupted
|
||||
during text input, the text already input is resolved into
|
||||
the file as if the text input had been normally terminated.
|
||||
SIGWINCH The screen is resized. See the vvii//eexx Reference manual sec-
|
||||
tion entitled ``Sizing the Screen'' for more information.
|
||||
SIGCONT
|
||||
SIGQUIT
|
||||
SIGTSTP VVii//eexx ignores these signals.
|
||||
|
||||
BBUUGGSS
|
||||
See the file _n_v_i_/_d_o_c_s_/_b_u_g_s_._c_u_r_r_e_n_t for a list of the known bugs in this
|
||||
version.
|
||||
|
||||
FFIILLEESS
|
||||
/bin/sh The default user shell.
|
||||
/etc/vi.exrc System-wide vi startup file.
|
||||
/tmp Temporary file directory.
|
||||
/var/tmp/vi.recover The default recovery file directory.
|
||||
$HOME/.nexrc 1st choice for user's home directory startup file.
|
||||
$HOME/.exrc 2nd choice for user's home directory startup file.
|
||||
.nexrc 1st choice for local directory startup file.
|
||||
.exrc 2nd choice for local directory startup file.
|
||||
|
||||
SSEEEE AALLSSOO
|
||||
ctags(1), more(1), curses(3), dbopen(3)
|
||||
|
||||
The ``Vi Quick Reference'' card.
|
||||
|
||||
``An Introduction to Display Editing with Vi'', found in the ``UNIX
|
||||
User's Manual Supplementary Documents'' section of both the 4.3BSD and
|
||||
4.4BSD manual sets. This document is the closest thing available to an
|
||||
introduction to the vvii screen editor.
|
||||
|
||||
``Ex Reference Manual (Version 3.7)'', found in the ``UNIX User's Manual
|
||||
Supplementary Documents'' section of both the 4.3BSD and 4.4BSD manual
|
||||
sets. This document is the final reference for the eexx editor, as dis-
|
||||
tributed in most historic 4BSD and System V systems.
|
||||
|
||||
``Edit: A tutorial'', found in the ``UNIX User's Manual Supplementary
|
||||
Documents'' section of the 4.3BSD manual set. This document is an intro-
|
||||
duction to a simple version of the eexx screen editor.
|
||||
|
||||
``Ex/Vi Reference Manual'', found in the ``UNIX User's Manual
|
||||
Supplementary Documents'' section of the 4.4BSD manual set. This docu-
|
||||
ment is the final reference for the nneexx//nnvvii text editors, as distributed
|
||||
in 4.4BSD and 4.4BSD-Lite.
|
||||
|
||||
RRooffff source for all of these documents is distributed with nneexx//nnvvii in the
|
||||
_n_v_i_/_U_S_D_._d_o_c directory of the nneexx//nnvvii source code.
|
||||
|
||||
The files ``autowrite'', ``input'', ``quoting'', and ``structures'',
|
||||
found in the _n_v_i_/_d_o_c_s_/_i_n_t_e_r_n_a_l_s directory of the nneexx//nnvvii source code.
|
||||
|
||||
HHIISSTTOORRYY
|
||||
The nneexx//nnvvii replacements for the eexx//vvii editor first appeared in 4.4BSD.
|
||||
|
||||
SSTTAANNDDAARRDDSS
|
||||
NNeexx//nnvvii is close to IEEE Std1003.2 (``POSIX''). That document differs
|
||||
from historical eexx//vvii practice in several places; there are changes to be
|
||||
made on both sides.
|
||||
|
||||
4.4BSD July 15, 1994 13
|
1063
usr.bin/vi/USD.doc/vi.man/vi.0.ps
Normal file
1063
usr.bin/vi/USD.doc/vi.man/vi.0.ps
Normal file
File diff suppressed because it is too large
Load Diff
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"
|
||||
}
|
30924
usr.bin/vi/USD.doc/vi.ref/paper.ps
Normal file
30924
usr.bin/vi/USD.doc/vi.ref/paper.ps
Normal file
File diff suppressed because it is too large
Load Diff
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
5544
usr.bin/vi/USD.doc/vi.ref/vi.ref.txt
Normal file
5544
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
|
98
usr.bin/vi/common/Makefile
Normal file
98
usr.bin/vi/common/Makefile
Normal file
@ -0,0 +1,98 @@
|
||||
# @(#)Makefile 8.47 (Berkeley) 8/14/94
|
||||
|
||||
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 char sccsid[] = "@(#)cut.c 8.32 (Berkeley) 7/28/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 char sccsid[] = "@(#)delete.c 8.10 (Berkeley) 4/26/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);
|
||||
}
|
830
usr.bin/vi/common/exf.c
Normal file
830
usr.bin/vi/common/exf.c
Normal file
@ -0,0 +1,830 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)exf.c 8.94 (Berkeley) 8/7/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... */
|
||||
++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 char sccsid[] = "@(#)line.c 8.30 (Berkeley) 6/30/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 char sccsid[] = "@(#)log.c 8.16 (Berkeley) 5/21/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 *));
|
711
usr.bin/vi/common/main.c
Normal file
711
usr.bin/vi/common/main.c
Normal file
@ -0,0 +1,711 @@
|
||||
/*-
|
||||
* 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 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 char sccsid[] = "@(#)main.c 8.103 (Berkeley) 8/14/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.
|
||||
*/
|
||||
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) {
|
||||
CIRCLEQ_REMOVE(&sp->gp->hq, sp, q);
|
||||
CIRCLEQ_INSERT_TAIL(&sp->gp->dq, sp, q);
|
||||
} 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);
|
||||
|
||||
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 char sccsid[] = "@(#)mark.c 8.19 (Berkeley) 5/21/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));
|
178
usr.bin/vi/common/mem.h
Normal file
178
usr.bin/vi/common/mem.h
Normal file
@ -0,0 +1,178 @@
|
||||
/*-
|
||||
* 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.6 (Berkeley) 6/20/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) { \
|
||||
if ((nlen) > llen && binc(sp, &(lp), &(llen), nlen)) \
|
||||
goto binc_err; \
|
||||
}
|
||||
#define BINC_RET(sp, lp, llen, nlen) { \
|
||||
if ((nlen) > llen && binc(sp, &(lp), &(llen), nlen)) \
|
||||
return (1); \
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)))
|
||||
|
||||
int binc __P((SCR *, void *, size_t *, size_t));
|
427
usr.bin/vi/common/msg.c
Normal file
427
usr.bin/vi/common/msg.c
Normal file
@ -0,0 +1,427 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)msg.c 8.9 (Berkeley) 8/14/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 && binc(sp, &mp->mbuf, &mp->blen, len))
|
||||
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 char sccsid[] = "@(#)options.c 8.64 (Berkeley) 7/27/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 char sccsid[] = "@(#)options_f.c 8.34 (Berkeley) 8/14/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 char sccsid[] = "@(#)put.c 8.9 (Berkeley) 5/21/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 char sccsid[] = "@(#)recover.c 8.72 (Berkeley) 7/21/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);
|
||||
}
|
||||
}
|
309
usr.bin/vi/common/screen.c
Normal file
309
usr.bin/vi/common/screen.c
Normal file
@ -0,0 +1,309 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)screen.c 8.64 (Berkeley) 8/14/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. */
|
||||
CIRCLEQ_REMOVE(&sp->gp->dq, sp, q);
|
||||
|
||||
/* 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 char sccsid[] = "@(#)search.c 8.46 (Berkeley) 7/2/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 *));
|
350
usr.bin/vi/common/seq.c
Normal file
350
usr.bin/vi/common/seq.c
Normal file
@ -0,0 +1,350 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)seq.c 8.30 (Berkeley) 7/15/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 *name, *input, *output;
|
||||
size_t nlen, ilen, olen;
|
||||
enum seqtype stype;
|
||||
int flags;
|
||||
{
|
||||
SEQ *lastqp, *qp;
|
||||
CHAR_T *p;
|
||||
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. */
|
||||
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 *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 *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;
|
||||
{
|
||||
SEQ *qp;
|
||||
int cnt, len, olen;
|
||||
CHAR_T *p;
|
||||
|
||||
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;
|
||||
{
|
||||
SEQ *qp;
|
||||
size_t olen;
|
||||
int ch;
|
||||
char *p;
|
||||
|
||||
/* 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.11 (Berkeley) 7/17/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 *name; /* Sequence name (if any). */
|
||||
size_t nlen; /* Name length. */
|
||||
char *input; /* Sequence input keys. */
|
||||
size_t ilen; /* Input keys length. */
|
||||
char *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 *, size_t, enum seqtype));
|
||||
int seq_dump __P((SCR *, enum seqtype, int));
|
||||
SEQ *seq_find __P((SCR *, SEQ **, char *, 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 *, size_t,
|
||||
char *, size_t, char *, 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 char sccsid[] = "@(#)signal.c 8.32 (Berkeley) 7/23/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 char sccsid[] = "@(#)term.c 8.79 (Berkeley) 8/2/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
|
215
usr.bin/vi/common/util.c
Normal file
215
usr.bin/vi/common/util.c
Normal file
@ -0,0 +1,215 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)util.c 8.71 (Berkeley) 8/8/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.
|
||||
*/
|
||||
int
|
||||
binc(sp, argp, bsizep, min)
|
||||
SCR *sp; /* sp MAY BE NULL!!! */
|
||||
void *argp;
|
||||
size_t *bsizep, min;
|
||||
{
|
||||
size_t csize;
|
||||
void *bpp;
|
||||
|
||||
/* If already larger than the minimum, just return. */
|
||||
if (min && *bsizep >= min)
|
||||
return (0);
|
||||
|
||||
bpp = *(char **)argp;
|
||||
csize = *bsizep + MAX(min, 256);
|
||||
REALLOC(sp, bpp, void *, csize);
|
||||
|
||||
if (bpp == 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 (1);
|
||||
}
|
||||
/*
|
||||
* Memory is guaranteed to be zero-filled, various parts of
|
||||
* nvi depend on this.
|
||||
*/
|
||||
memset((char *)bpp + *bsizep, 0, csize - *bsizep);
|
||||
*(char **)argp = bpp;
|
||||
*bsizep = csize;
|
||||
return (0);
|
||||
}
|
||||
/*
|
||||
* 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));
|
200
usr.bin/vi/docs/README
Normal file
200
usr.bin/vi/docs/README
Normal file
@ -0,0 +1,200 @@
|
||||
# @(#)README 8.84 (Berkeley) 8/15/94
|
||||
|
||||
This is the README for version 1.32 of nex/nvi, a freely redistributable
|
||||
replacement for the Berkeley ex and vi text editors. The compressed or
|
||||
gzip'd archive, 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:
|
||||
|
||||
uunet!bostic
|
||||
bostic@cs.berkeley.edu
|
||||
|
||||
Keith Bostic
|
||||
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
o Redistribution:
|
||||
|
||||
This software is copyrighted by the The Regents of the University of
|
||||
California, but may be freely redistributed (or sold, or used to line
|
||||
your birdcage) under the following conditions:
|
||||
|
||||
/*-
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
o Credit where it's due:
|
||||
|
||||
This software was originally derived from software contributed
|
||||
to the University of California, Berkeley by Steve Kirkendall,
|
||||
the author of the vi clone elvis. Without his work, this work
|
||||
would have been far more difficult.
|
||||
|
||||
POSIX 1003.2 style regular expression support is courtesy of
|
||||
Henry Spencer, for which I am *very* grateful.
|
||||
|
||||
The curses library was originally done by Ken Arnold. Scrolling
|
||||
and general reworking for 4.4BSD was done by Elan Amir.
|
||||
|
||||
o From the original vi acknowledgements, by William Joy and Mark Horton:
|
||||
|
||||
Bruce Englar encouraged the early development of this display
|
||||
editor. Peter Kessler helped bring sanity to version 2's
|
||||
command layout. Bill Joy wrote versions 1 and 2.0 through 2.7,
|
||||
and created the framework that users see in the present editor.
|
||||
Mark Horton added macros and other features and made the editor
|
||||
work on a large number of terminals and Unix systems.
|
||||
|
||||
o And...
|
||||
The financial support of UUNET Communications Services is gratefully
|
||||
acknowledged.
|
||||
|
||||
=-=-=-=-=-=-=-=-=-=-=
|
||||
o Status:
|
||||
|
||||
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 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. 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 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 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. 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 Directory layout:
|
||||
|
||||
nvi/USD.doc:
|
||||
Ex/vi documentation, both historic and current.
|
||||
|
||||
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:
|
||||
Random nvi documentation:
|
||||
|
||||
README -- Nvi main README file.
|
||||
bugs.current -- Major known bugs in the current nvi.
|
||||
changelog -- Log of changes from version to version.
|
||||
features -- Todo list, suggested features list.
|
||||
internals/
|
||||
autowrite -- Vi autowrite option discussion.
|
||||
gdb.script -- GDB debugging scripts.
|
||||
input -- Vi maps, executable buffers, and input discussion.
|
||||
quoting -- Vi quoting discussion.
|
||||
structures -- Out-of-date nvi internal structure description.
|
||||
tutorial/ -- Historic vi tutorial(s), of unknown quality.
|
||||
|
||||
nvi/ex:
|
||||
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 screen support for the ex editor.
|
||||
|
||||
nvi/svi:
|
||||
The screen support for a curses based vi editor.
|
||||
|
||||
nvi/vi:
|
||||
The vi source code.
|
||||
|
||||
nvi/xaw:
|
||||
Place reserved for an X11 (Athena Widget) screen.
|
47
usr.bin/vi/docs/bugs.current
Normal file
47
usr.bin/vi/docs/bugs.current
Normal file
@ -0,0 +1,47 @@
|
||||
List of known bugs:
|
||||
|
||||
+ The number option doesn't display line numbers in ex append/insert
|
||||
mode.
|
||||
|
||||
+ The option sidescroll is completely wrong, and setting it does more
|
||||
harm than good.
|
||||
|
||||
+ We're not blocking signals when manipulating the SCR/EXF chains.
|
||||
This is necessary, since we walk them on signal receipt.
|
||||
|
||||
+ 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 options:
|
||||
|
||||
hardtabs, lisp, optimize, redraw, slowopen
|
||||
|
||||
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.
|
||||
|
||||
+ 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.
|
||||
|
||||
+ 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.
|
||||
|
||||
+ Historic vi permitted :g/xxx/vi, i.e. you could execute ex/vi as
|
||||
global commands. Need to review all of the old commands to verify
|
||||
which ones could/could not be used as global commands.
|
||||
|
||||
+ If you run out of space in the recovery directory, the recovery
|
||||
file is left in place.
|
519
usr.bin/vi/docs/changelog
Normal file
519
usr.bin/vi/docs/changelog
Normal file
@ -0,0 +1,519 @@
|
||||
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
|
||||
infinitely. If it's off, term_key() can be interrupted from the
|
||||
keyboard, which will cause the buffers to flush. I also dropped
|
||||
the default max number of remaps to 50. (Only Dave Hitz's TM
|
||||
macros and maze appear to go over that limit.)
|
||||
+ Change :mkexrc to not dump w{300,1200,9600}, lisp options.
|
||||
+ Fix backward search within a line bug.
|
||||
+ Change all the includes of "pathnames.h" to use <>'s so that the
|
||||
PORT versions can use -I. to replace it with their own versions.
|
||||
+ Make reads and writes interruptible. Rework code that enters and
|
||||
leaves ex for '!' and filter commands, rework all interrupt and
|
||||
timer code.
|
||||
+ Fix core dump when user displayed option in .exrc file.
|
||||
+ Fix bug where writing empty files didn't update the saved
|
||||
modification time.
|
||||
+ Fix bug where /pattern/ addressing was always a backward search.
|
||||
+ Fix bug triggered by autoindent of more than 32 characters, where
|
||||
nvi wasn't checking the right TEXT length.
|
||||
+ Fix bug where joining only empty lines caused a core dump.
|
||||
1.09 -> 1.10: Sat Mar 19 15:40:29 EST 1994
|
||||
+ Fix "set all" core dump.
|
||||
1.08 -> 1.09: Sat Mar 19 10:11:14 EST 1994
|
||||
+ If the tag's file path is relative, and it doesn't exist, check
|
||||
relative to the tag file location.
|
||||
+ Fix ~ command to free temporary buffer on error return.
|
||||
+ Create vi.ref, a first cut at a reference document for vi.
|
||||
The manual page and the reference document only document the
|
||||
set options, so far.
|
||||
+ Fix 1G bug not always going to the first non-blank.
|
||||
+ Upgrade PORT/regex to release alpha3.4, from Henry Spencer.
|
||||
+ Add MKS vi's "cdpath" option, supporting a cd search path.
|
||||
+ Handle if search as a motion was discarded, i.e. "d/<erase>".
|
||||
+ Change nvi to not create multiple recovery files if modifying
|
||||
a recovered file.
|
||||
+ Decide to ignore that the cursor is before the '$' when inserting
|
||||
in list mode. It's too hard to fix.
|
||||
1.07 -> 1.08: Wed Mar 16 07:37:36 EST 1994
|
||||
+ Leftright and big line scrolling fixes. This meant more changes
|
||||
to the screen display code, so there may be new problems.
|
||||
+ Don't permit search-style addresses until a file has been read.
|
||||
+ "c[Ww]" command incorrectly handled the "in whitespace" case.
|
||||
+ Fix key space allocation bug triggered by cut/paste under SunOS.
|
||||
+ Ex move command got the final cursor position wrong.
|
||||
+ Delete "optimize option not implemented" message.
|
||||
+ Make the literal-next character turn off mapping for the next
|
||||
character in text input mode.
|
||||
1.06 -> 1.07: Mon Mar 14 11:10:33 EST 1994
|
||||
+ The "wire down" change in 1.05 broke ex command parsing, there
|
||||
wasn't a corresponding change to handle multiple K_VLNEXT chars.
|
||||
+ Fix final position for vi's 't' command.
|
||||
1.05 -> 1.06: Sun Mar 13 16:12:52 EST 1994
|
||||
+ Wire down ^D, ^H, ^W, and ^V, regardless of the user's termios
|
||||
values.
|
||||
+ Add ^D as the ex scroll command.
|
||||
+ Support ^Q as a literal-next character.
|
||||
+ Rework abbreviations to be delimited by any !inword() character.
|
||||
+ Add options description to the manual page.
|
||||
+ Minor screen cache fix for svi_get.c.
|
||||
+ Rework beautify option support to match historical practice.
|
||||
+ Exit immediately if not reading from a tty and a command fails.
|
||||
+ Default the SunOS 4.* ports to the distributed curses, not SMI's.
|
||||
1.04 -> 1.05: Thu Mar 24 16:07:45 EST 1994
|
||||
+ Make cursor keys work in input mode.
|
||||
+ Rework screen column code in vi curses screen. MAJOR CHANGE --
|
||||
after this, we'll be debugging curses screen presentation from
|
||||
scratch.
|
||||
+ Explode include files in vi.h into the source files.
|
||||
1.03 -> 1.04: Sun Mar 6 14:14:16 EST 1994
|
||||
+ Make the ex move command keep the marks on the moved lines.
|
||||
+ Change resize semantics so you can set the screen size to a
|
||||
specific value. A couple of screen fixes for the resize code.
|
||||
+ Fixes for foreground/background due to SIGWINCH.
|
||||
+ Complete rework of all of vi's cursor movements. The underlying
|
||||
assumption in the old code was that the starting cursor position
|
||||
was part of the range of lines cut or deleted. The command
|
||||
"d[[" is an example where this isn't true. Change it so that all
|
||||
motion component commands set the final cursor position separately
|
||||
from the range, as it can't be done correctly later. This is a
|
||||
MAJOR CHANGE -- after this change, we'll be debugging the cursor
|
||||
positioning from scratch.
|
||||
+ Rewrite the B, b, E, e commands to use vi's getc() interface
|
||||
instead of rolling their own.
|
||||
+ Add a second MARK structure, LMARK, which is the larger mark
|
||||
needed by the logging and mark queue code. Everything else uses
|
||||
the reworked MARK structure, which is simply a line/column pair.
|
||||
+ Rework cut/delete to not expect 1-past-the-end in the range, but
|
||||
to act on text to the end of the range, inclusive.
|
||||
+ Sync on write's, to force NFS to flush.
|
||||
1.01 -> 1.03: Sun Jan 23 17:50:35 EST 1994 (PUBLICLY AVAILABLE VERSION)
|
||||
+ Tag stack fixes, was returning to the tag, not the position from
|
||||
which the user tagged.
|
||||
+ Only use from the cursor to the end of the word in cursor word
|
||||
searches and tags. (Matches historical vi behavior.)
|
||||
+ Fix delete-last-line bug when line number option set.
|
||||
+ Fix usage line for :split command.
|
||||
+ If O_NUMBER set, long input lines would eventually fail, the column
|
||||
count for the second screen of long lines wasn't set correctly.
|
||||
+ Fix for [[ reaching SOF with a column longer than the first line.
|
||||
+ Fix for multiple error messages if no screen displayed.
|
||||
+ Fix :read to set alternate file name as in historical practice.
|
||||
+ Fix cut to rotate the numeric buffers if line mode flag set.
|
||||
1.00 -> 1.01: Wed Jan 12 13:37:18 EST 1994
|
||||
+ Don't put cut items into numeric buffers if cutting less than
|
||||
parts of two lines.
|
||||
0.94 -> 1.00: Mon Jan 10 02:27:27 EST 1994
|
||||
+ Read-ahead not there; BSD tty driver problem, SunOS curses
|
||||
problem.
|
||||
+ Global command could error if it deleted the last line of
|
||||
the file.
|
||||
+ Change '.' to only apply to the 'u' if entered immediately
|
||||
after the 'u' command. "1pu.u.u. is still broken, but I
|
||||
expect that it's going to be sacrificed for multiple undo.
|
||||
+ If backward motion on a command, now move to the point; get
|
||||
yank cursor positioning correct.
|
||||
+ Rework cut buffers to match historic practice -- yank/delete
|
||||
numeric buffers redone sensibly, ignoring historic practice.
|
||||
0.92 -> 0.93: Mon Dec 20 19:52:14 EST 1993
|
||||
+ Christos Zoulas reimplemented the script windows using pty's,
|
||||
which means that they now work reasonably. The down side of
|
||||
this is that almost all ports other than 4.4BSD need to include
|
||||
two new files, login_tty.c and pty.c from the PORT/clib directory.
|
||||
I've added them to the Makefiles.
|
||||
+ All calloc/malloc/realloc functions now cast their pointers, for
|
||||
SunOS -- there should be far fewer warning messages, during the
|
||||
build. The remaining messages are where CHAR_T's meet char *'s,
|
||||
i.e. where 8-bit clean meets strcmp.
|
||||
+ The user's argument list handling has been reworked so that there
|
||||
is always a single consistent position for use by :next, :prev and
|
||||
:rewind.
|
||||
+ All of the historical options are now at least accepted, although
|
||||
not all of them are implemented. (Edcompatible, hardtabs, lisp,
|
||||
optimize, redraw, and slowopen aren't implemented.)
|
||||
+ The RE's have been reworked so that matches of length 0 are handled
|
||||
in the same way as vi used to handle them.
|
||||
+ Several more mapping fixes and ex parser addressing fixes.
|
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.
|
92
usr.bin/vi/docs/features
Normal file
92
usr.bin/vi/docs/features
Normal file
@ -0,0 +1,92 @@
|
||||
List of things that should be added at some point:
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
|
||||
+ X11 interface.
|
||||
|
||||
+ 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.
|
||||
|
||||
+ Full editing capability on the : command line.
|
||||
|
||||
+ Rob Pike's sam RE's.
|
||||
|
||||
+ 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.
|
||||
|
||||
List of suggested features:
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
+ 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
|
||||
which file is written.
|
||||
|
||||
+ Change
|
||||
:di[splay] tags -> :tags
|
||||
:di[splay] screens -> :screens
|
||||
:di[splay] buffers -> :buffers
|
||||
|
||||
+ A macro record function. Add the ability to record a sequence
|
||||
of keystrokes into a named buffer for later use. Handy when
|
||||
you're trying to build a semi-complex macro.
|
||||
|
||||
+ 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.
|
88
usr.bin/vi/docs/internals/autowrite
Normal file
88
usr.bin/vi/docs/internals/autowrite
Normal file
@ -0,0 +1,88 @@
|
||||
# @(#)autowrite 8.2 (Berkeley) 9/28/93
|
||||
|
||||
Vi autowrite behavior, the fields with *'s are "don't cares".
|
||||
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
Commands that are affected only by autowrite:
|
||||
|
||||
Command File Autowrite? Action:
|
||||
modified?
|
||||
-----------------------------------------------
|
||||
^Z Y Y Write file and suspend.
|
||||
^Z Y N Suspend.
|
||||
^Z N * Suspend.
|
||||
|
||||
# This behavior is NOT identical to :edit.
|
||||
^ Y Y Write file and jump.
|
||||
^ Y N Error.
|
||||
^ N * Jump.
|
||||
|
||||
# The new nvi command ^T (:tagpop) behaves identically to ^].
|
||||
# This behavior is identical to :tag, :tagpop, and :tagpush with
|
||||
# force always set to N.
|
||||
^] Y Y Write file and jump.
|
||||
^] Y N Error.
|
||||
^] N * Jump.
|
||||
|
||||
# There's no way to specify a force flag to the '!' command.
|
||||
:! Y Y Write file and execute.
|
||||
:! Y N Warn (if warn option) and execute.
|
||||
:! N * Execute.
|
||||
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
Commands that are affected by both autowrite and force:
|
||||
|
||||
NOTE: the "force" flag is never passed on, i.e. the write
|
||||
to the file caused by the autowrite flag is never forced.
|
||||
|
||||
Command File Autowrite? Force? Action:
|
||||
modified? (!)
|
||||
-------------------------------------------------------
|
||||
# The first rule (YYY) is historic practice, but seems wrong.
|
||||
# In nvi, :next and :prev commands behave identically to :rewind.
|
||||
:next Y Y Y Write changes and jump.
|
||||
:next Y Y N Write changes and jump.
|
||||
:next Y N Y Abandon changes and jump.
|
||||
:next Y N N Error.
|
||||
:next N * * Jump.
|
||||
|
||||
:rewind Y Y Y Abandon changes and jump.
|
||||
:rewind Y Y N Write changes and jump.
|
||||
:rewind Y N Y Abandon changes and jump.
|
||||
:rewind Y N N Error.
|
||||
:rewind N * * Jump.
|
||||
|
||||
# The new nvi commands, :tagpop and :tagtop, behave identically to :tag.
|
||||
# Note, this behavior is the same as :rewind and friends, as well.
|
||||
:tag Y Y Y Abandon changes and jump.
|
||||
:tag Y Y N Write changes and jump.
|
||||
:tag Y N Y Abandon changes and jump.
|
||||
:tag Y N N Error.
|
||||
:tag N * * Jump.
|
||||
|
||||
# The command :suspend behaves identically to :stop.
|
||||
:stop Y Y Y Suspend.
|
||||
:stop Y Y N Write changes and suspend.
|
||||
:stop Y N Y Suspend.
|
||||
:stop Y N N Suspend.
|
||||
:stop N * * Suspend.
|
||||
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
Commands that might be affected by autowrite, but aren't:
|
||||
|
||||
Command File Autowrite? Force? Action:
|
||||
modified? (!)
|
||||
-------------------------------------------------------
|
||||
#:ex, and :vi (executed while in vi mode) behave identically to :edit.
|
||||
:edit Y * Y Abandon changes and jump.
|
||||
:edit Y * N Error.
|
||||
:edit N * * Jump.
|
||||
|
||||
:quit Y * Y Quit.
|
||||
:quit Y * N Error.
|
||||
:quit N * * Quit.
|
||||
|
||||
:shell * * * Execute shell.
|
||||
|
||||
:xit Y * * Write changes and exit.
|
||||
:xit N * * Exit.
|
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.
|
68
usr.bin/vi/docs/internals/gdb.script
Normal file
68
usr.bin/vi/docs/internals/gdb.script
Normal file
@ -0,0 +1,68 @@
|
||||
# @(#)gdb.script 8.2 (Berkeley) 9/29/93
|
||||
|
||||
# display the SVI screen map
|
||||
# usage dmap(sp)
|
||||
define dmap
|
||||
set $h = ((SVI_PRIVATE *)$arg0->svi_private)->h_smap
|
||||
set $t = ((SVI_PRIVATE *)$arg0->svi_private)->t_smap
|
||||
while ($h <= $t)
|
||||
printf "lno: %d; off %d ", (int)$h->lno, (int)$h->off
|
||||
if ($h->c_ecsize == 0)
|
||||
printf "flushed\n"
|
||||
else
|
||||
printf "\n\tsboff %d; scoff %d\n", \
|
||||
(int)$h->c_sboff, (int)$h->c_scoff
|
||||
printf "\teboff %d; eclen %d; ecsize %d\n", \
|
||||
(int)$h->c_eboff, (int)$h->c_eclen, \
|
||||
(int)$h->c_ecsize
|
||||
end
|
||||
set $h = $h + 1
|
||||
end
|
||||
end
|
||||
|
||||
# display the tail of the SVI screen map
|
||||
define tmap
|
||||
set $h = ((SVI_PRIVATE *)$arg0->svi_private)->h_smap
|
||||
set $t = ((SVI_PRIVATE *)$arg0->svi_private)->t_smap
|
||||
while ($t >= $h)
|
||||
printf "lno: %d; off %d ", (int)$t->lno, (int)$t->off
|
||||
if ($t->c_ecsize == 0)
|
||||
printf "flushed\n"
|
||||
else
|
||||
printf "\n\tsboff %d; scoff %d\n", \
|
||||
(int)$t->c_sboff, (int)$t->c_scoff
|
||||
printf "\teboff %d; eclen %d; ecsize %d\n", \
|
||||
(int)$t->c_eboff, (int)$t->c_eclen, \
|
||||
(int)$t->c_ecsize
|
||||
end
|
||||
set $t = $t - 1
|
||||
end
|
||||
end
|
||||
|
||||
# display the SVI private structure
|
||||
define svp
|
||||
print *((SVI_PRIVATE *)sp->svi_private)
|
||||
end
|
||||
|
||||
# display the marks
|
||||
define markp
|
||||
set $h = sp->ep->marks.next
|
||||
set $t = &sp->ep->marks
|
||||
while ($h != 0 && $h != $t)
|
||||
printf "key %c lno: %d cno: %d flags: %x\n", \
|
||||
((MARK *)$h)->name, ((MARK *)$h)->lno, \
|
||||
((MARK *)$h)->cno, ((MARK *)$h)->flags
|
||||
set $h = ((MARK *)$h)->next
|
||||
end
|
||||
end
|
||||
|
||||
# display the tags
|
||||
define tagp
|
||||
set $h = sp->taghdr.next
|
||||
set $t = &sp->taghdr
|
||||
while ($h != 0 && $h != $t)
|
||||
printf "tag: %s lno %d cno %d\n", ((TAG *)$h)->frp->fname, \
|
||||
((TAG *)$h)->lno, ((TAG *)$h)->cno
|
||||
set $h= ((TAG *)$h)->next
|
||||
end
|
||||
end
|
350
usr.bin/vi/docs/internals/input
Normal file
350
usr.bin/vi/docs/internals/input
Normal file
@ -0,0 +1,350 @@
|
||||
# @(#)input 5.5 (Berkeley) 7/2/94
|
||||
|
||||
MAPS, EXECUTABLE BUFFERS AND INPUT IN EX/VI:
|
||||
|
||||
The basic rule is that input in ex/vi is a stack. Every time a key which
|
||||
gets expanded is encountered, it is expanded and the expansion is treated
|
||||
as if it were input from the user. So, maps and executable buffers are
|
||||
simply pushed onto the stack from which keys are returned. The exception
|
||||
is that if the "remap" option is turned off, only a single map expansion
|
||||
is done. I intend to be fully backward compatible with this.
|
||||
|
||||
Historically, if the mode of the editor changed (ex to vi or vice versa),
|
||||
any queued input was silently discarded. I don't see any reason to either
|
||||
support or not support this semantic. I intend to retain the queued input,
|
||||
mostly because it's simpler than throwing it away.
|
||||
|
||||
Historically, neither the initial command on the command line (the + flag)
|
||||
or the +cmd associated with the ex and edit commands was subject to mapping.
|
||||
Also, while the +cmd appears to be subject to "@buffer" expansion, once
|
||||
expanded it doesn't appear to work correctly. I don't see any reason to
|
||||
either support or not support these semantics, so, for consistency, I intend
|
||||
to pass both the initial command and the command associated with ex and edit
|
||||
commands through the standard mapping and @ buffer expansion.
|
||||
|
||||
One other difference between the historic ex/vi and nex/nvi is that nex
|
||||
displays the executed buffers as it executes them. This means that if
|
||||
the file is:
|
||||
|
||||
set term=xterm
|
||||
set term=yterm
|
||||
set term=yterm
|
||||
|
||||
the user will see the following during a typical edit session:
|
||||
|
||||
nex testfile
|
||||
testfile: unmodified: line 3
|
||||
:1,$yank a
|
||||
:@a
|
||||
:set term=zterm
|
||||
:set term=yterm
|
||||
:set term=xterm
|
||||
:q!
|
||||
|
||||
This seems like a feature and unlikely to break anything, so I don't
|
||||
intend to match historic practice in this area.
|
||||
|
||||
The rest of this document is a set of conclusions as to how I believe
|
||||
the historic maps and @ buffers work. The summary is as follows:
|
||||
|
||||
1: For buffers that are cut in "line mode", or buffers that are not cut
|
||||
in line mode but which contain portions of more than a single line, a
|
||||
trailing <newline> character appears in the input for each line in the
|
||||
buffer when it is executed. For buffers not cut in line mode and which
|
||||
contain portions of only a single line, no additional characters
|
||||
appear in the input.
|
||||
2: Executable buffers that execute other buffers don't load their
|
||||
contents until they execute them.
|
||||
3: Maps and executable buffers are copied when they are executed --
|
||||
they can be modified by the command but that does not change their
|
||||
actions.
|
||||
4: Historically, executable buffers are discarded if the editor
|
||||
switches between ex and vi modes.
|
||||
5: Executable buffers inside of map commands are expanded normally.
|
||||
Maps inside of executable buffers are expanded normally.
|
||||
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.
|
||||
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
1: For buffers that are cut in "line mode", or buffers that are not cut
|
||||
in line mode but which contain portions of more than a single line, a
|
||||
trailing <newline> character appears in the input for each line in the
|
||||
buffer when it is executed. For buffers not cut in line mode and which
|
||||
contain portions of only a single line, no additional characters
|
||||
appear in the input.
|
||||
|
||||
=== test file ===
|
||||
3Gw
|
||||
w
|
||||
line 1 foo bar baz
|
||||
line 2 foo bar baz
|
||||
line 3 foo bar baz
|
||||
=== end test file ===
|
||||
|
||||
If the first line is loaded into 'a' and executed:
|
||||
|
||||
1G"ayy@a
|
||||
|
||||
The cursor ends up on the '2', a result of pushing "3Gw^J" onto
|
||||
the stack.
|
||||
|
||||
If the first two lines are loaded into 'a' and executed:
|
||||
|
||||
1G2"ayy@a
|
||||
|
||||
The cursor ends up on the 'f' in "foo" in the fifth line of the
|
||||
file, a result of pushing "3Gw^Jw^J" onto the stack.
|
||||
|
||||
If the first line is loaded into 'a', but not using line mode,
|
||||
and executed:
|
||||
|
||||
1G"ay$@a
|
||||
|
||||
The cursor ends up on the '1', a result of pushing "3Gw" onto
|
||||
the stack
|
||||
|
||||
If the first two lines are loaded into 'a', but not using line mode,
|
||||
and executed:
|
||||
|
||||
1G2"ay$@a
|
||||
|
||||
The cursor ends up on the 'f' in "foo" in the fifth line of the
|
||||
file, a result of pushing "3Gw^Jw^J" onto the stack.
|
||||
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
2: Executable buffers that execute other buffers don't load their
|
||||
contents until they execute them.
|
||||
|
||||
=== test file ===
|
||||
cwLOAD B^[
|
||||
line 1 foo bar baz
|
||||
line 2 foo bar baz
|
||||
line 3 foo bar baz
|
||||
@a@b
|
||||
"byy
|
||||
=== end test file ===
|
||||
|
||||
The command is loaded into 'e', and then executed. 'e' executes
|
||||
'a', which loads 'b', then 'e' executes 'b'.
|
||||
|
||||
5G"eyy6G"ayy1G@e
|
||||
|
||||
The output should be:
|
||||
|
||||
=== output file ===
|
||||
cwLOAD B^[
|
||||
LOAD B 1 foo bar baz
|
||||
line 2 foo bar baz
|
||||
line 3 foo bar baz
|
||||
@a@b
|
||||
"byy
|
||||
=== end output file ===
|
||||
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
3: Maps and executable buffers are copied when they are executed --
|
||||
they can be modified by the command but that does not change their
|
||||
actions.
|
||||
|
||||
Executable buffers:
|
||||
|
||||
=== test file ===
|
||||
line 1 foo bar baz
|
||||
line 2 foo bar baz
|
||||
line 3 foo bar baz
|
||||
@a@b
|
||||
"eyy
|
||||
cwEXECUTE B^[
|
||||
=== end test file ===
|
||||
|
||||
4G"eyy5G"ayy6G"byy1G@eG"ep
|
||||
|
||||
The command is loaded into 'e', and then executed. 'e' executes
|
||||
'a', which loads 'e', then 'e' executes 'b' anyway.
|
||||
|
||||
The output should be:
|
||||
|
||||
=== output file ===
|
||||
line 1 foo bar baz
|
||||
EXECUTE B 2 foo bar baz
|
||||
line 3 foo bar baz
|
||||
@a@b
|
||||
"eyy
|
||||
cwEXECUTE B^[
|
||||
line 1 foo bar baz
|
||||
=== end output file ===
|
||||
|
||||
Maps:
|
||||
|
||||
=== test file ===
|
||||
Cine 1 foo bar baz
|
||||
line 2 foo bar baz
|
||||
line 3 foo bar baz
|
||||
=== end test file ===
|
||||
|
||||
Entering the command ':map = :map = rB^V^MrA^M1G==' shows that
|
||||
the first time the '=' is entered the '=' map is set and the
|
||||
character is changed to 'A', the second time the character is
|
||||
changed to 'B'.
|
||||
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
4: Historically, executable buffers are discarded if the editor
|
||||
switches between ex and vi modes.
|
||||
|
||||
=== test file ===
|
||||
line 1 foo bar baz
|
||||
line 2 foo bar baz
|
||||
line 3 foo bar baz
|
||||
cwCHANGE^[Q:set
|
||||
set|visual|1Gwww
|
||||
=== end test file ===
|
||||
|
||||
vi testfile
|
||||
4G"ayy@a
|
||||
|
||||
ex testfile
|
||||
$p
|
||||
yank a
|
||||
@a
|
||||
|
||||
In vi, the command is loaded into 'a' and then executed. The command
|
||||
subsequent to the 'Q' is (historically, silently) discarded.
|
||||
|
||||
In ex, the command is loaded into 'a' and then executed. The command
|
||||
subsequent to the 'visual' is (historically, silently) discarded. The
|
||||
first set command is output by ex, although refreshing the screen usually
|
||||
causes it not to be seen.
|
||||
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
5: Executable buffers inside of map commands are expanded normally.
|
||||
Maps inside of executable buffers are expanded normally.
|
||||
|
||||
Buffers inside of map commands:
|
||||
|
||||
=== test file ===
|
||||
line 1 foo bar baz
|
||||
line 2 foo bar baz
|
||||
line 3 foo bar baz
|
||||
cwREPLACE BY A^[
|
||||
=== end test file ===
|
||||
|
||||
4G"ay$:map x @a
|
||||
1Gx
|
||||
|
||||
The output should be:
|
||||
|
||||
=== output file ===
|
||||
REPLACE BY A 1 foo bar baz
|
||||
line 2 foo bar baz
|
||||
line 3 foo bar baz
|
||||
cwREPLACE BY A^[
|
||||
=== end output file ===
|
||||
|
||||
Maps commands inside of executable buffers:
|
||||
|
||||
=== test file ===
|
||||
line 1 foo bar baz
|
||||
line 2 foo bar baz
|
||||
line 3 foo bar baz
|
||||
X
|
||||
=== end test file ===
|
||||
|
||||
:map X cwREPLACE BY XMAP^[
|
||||
4G"ay$1G@a
|
||||
|
||||
The output should be:
|
||||
|
||||
=== output file ===
|
||||
REPLACE BY XMAP 1 foo bar baz
|
||||
line 2 foo bar baz
|
||||
line 3 foo bar baz
|
||||
X
|
||||
=== end output file ===
|
||||
|
||||
Here's a test that does both, repeatedly.
|
||||
|
||||
=== test file ===
|
||||
line 1 foo bar baz
|
||||
line 2 foo bar baz
|
||||
line 3 foo bar baz
|
||||
X
|
||||
Y
|
||||
cwREPLACED BY C^[
|
||||
blank line
|
||||
=== end test file ===
|
||||
|
||||
:map x @a
|
||||
4G"ay$
|
||||
:map X @b
|
||||
5G"by$
|
||||
:map Y @c
|
||||
6G"cy$
|
||||
1Gx
|
||||
|
||||
The output should be:
|
||||
|
||||
=== output file ===
|
||||
REPLACED BY C 1 foo bar baz
|
||||
line 2 foo bar baz
|
||||
line 3 foo bar baz
|
||||
X
|
||||
Y
|
||||
cwREPLACED BY C^[
|
||||
blank line
|
||||
=== end output file ===
|
||||
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
6: If an error is encountered while executing a mapped command or
|
||||
a buffer, the rest of the mapped command/buffer is discarded. No
|
||||
user input characters are discarded.
|
||||
|
||||
=== test file ===
|
||||
line 1 foo bar baz
|
||||
line 2 foo bar baz
|
||||
line 3 foo bar baz
|
||||
:map = 10GcwREPLACMENT^V^[^[
|
||||
=== end test file ===
|
||||
|
||||
The above mapping fails, however, if the 10G is changed to 1, 2,
|
||||
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 ===
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
219
usr.bin/vi/docs/internals/quoting
Normal file
219
usr.bin/vi/docs/internals/quoting
Normal file
@ -0,0 +1,219 @@
|
||||
# @(#)quoting 5.4 (Berkeley) 8/20/93
|
||||
|
||||
QUOTING IN EX/VI:
|
||||
|
||||
There are two escape characters in historic ex/vi, ^V (or whatever
|
||||
character the user specified as their literal next character) and
|
||||
backslashes. There are two different areas in ex/vi where escaping
|
||||
is interesting: the command and text input modes, and within the ex
|
||||
commands themselves. In the examples below, ^V is used as the
|
||||
typical literal next character.
|
||||
|
||||
1: Escaping characters in ex and vi command and text input modes.
|
||||
The set of characters that users might want to escape are as
|
||||
follows:
|
||||
|
||||
vi text input mode (a, i, o, etc.):
|
||||
|
||||
carriage return (^M)
|
||||
escape (^[)
|
||||
autoindent characters
|
||||
(^D, 0, ^, ^T)
|
||||
erase, word erase, and line erase
|
||||
(^H, ^W, ^U)
|
||||
newline (^J) (not historic practice)
|
||||
suspend (^Z) (not historic practice)
|
||||
repaint (^L) (not historic practice)
|
||||
|
||||
vi command line (:colon commands):
|
||||
|
||||
carriage return (^M)
|
||||
escape (^[)
|
||||
erase, word erase, and line erase
|
||||
(^H, ^W, ^U)
|
||||
newline (^J) (not historic practice)
|
||||
suspend (^Z) (not historic practice)
|
||||
repaint (^L) (not historic practice)
|
||||
|
||||
ex text input mode (a, i, o, etc.):
|
||||
|
||||
carriage return (^M)
|
||||
erase, word erase, and line erase
|
||||
(^H, ^W, ^U)
|
||||
newline (^J) (not historic practice)
|
||||
|
||||
ex command line:
|
||||
|
||||
carriage return (^M)
|
||||
erase, word erase, and line erase
|
||||
(^H, ^W, ^U)
|
||||
newline (^J) (not historic practice)
|
||||
suspend (^Z)
|
||||
|
||||
I intend to follow historic practice for all of these cases, which
|
||||
was that ^V was the only way to escape any of these characters, and
|
||||
that whatever character followed the ^V was taken literally, i.e.
|
||||
^V^V is a single ^V.
|
||||
|
||||
The historic ex/vi disallowed the insertion of various control
|
||||
characters (^D, ^T, whatever) during various different modes, or,
|
||||
permitted the insertion of only a single one, or lots of other random
|
||||
behaviors (you can use ^D to enter a command in ex). I have
|
||||
regularized this behavior in nvi, there are no characters that cannot
|
||||
be entered or which have special meaning other than the ones listed
|
||||
above.
|
||||
|
||||
One comment regarding the autoindent characters. In historic vi,
|
||||
if you entered "^V0^D" autoindent erasure was still triggered,
|
||||
although it wasn't if you entered "0^V^D". In nvi, if you escape
|
||||
either character, autoindent erasure is not triggered.
|
||||
|
||||
This doesn't permit whitespace in command names, but that wasn't
|
||||
historic practice and doesn't seem worth doing.
|
||||
|
||||
Fun facts to know and tell:
|
||||
The historic vi implementation for the 'r' command requires
|
||||
*three* ^V's to replace a single character with ^V.
|
||||
|
||||
2: Ex commands:
|
||||
|
||||
Ex commands are delimited by '|' or newline characters. Within
|
||||
the commands, whitespace characters delimit the arguments.
|
||||
|
||||
I intend to treat ^V, followed by any character, as that literal
|
||||
character.
|
||||
|
||||
This is historic behavior in vi, although there are special
|
||||
cases where it's impossible to escape a character, generally
|
||||
a whitespace character.
|
||||
|
||||
3: Escaping characters in file names in ex commands:
|
||||
|
||||
:cd [directory] (directory)
|
||||
:chdir [directory] (directory)
|
||||
:edit [+cmd] [file] (file)
|
||||
:ex [+cmd] [file] (file)
|
||||
:file [file] (file)
|
||||
:next [file ...] (file ...)
|
||||
:read [!cmd | file] (file)
|
||||
:source [file] (file)
|
||||
:write [!cmd | file] (file)
|
||||
:wq [file] (file)
|
||||
:xit [file] (file)
|
||||
|
||||
I intend to treat a backslash in a file name, followed by any
|
||||
character, as that literal character.
|
||||
|
||||
This is historic behavior in vi.
|
||||
|
||||
In addition, since file names are also subject to word expansion,
|
||||
the rules for escape characters in section 3 of this document also
|
||||
apply. This is NOT historic behavior in vi, making it impossible
|
||||
to insert a whitespace, newline or carriage return character into
|
||||
a file name. This change could cause a problem if there were files
|
||||
with ^V's in their names, but I think that's unlikely.
|
||||
|
||||
4: Escaping characters in non-file arguments in ex commands:
|
||||
|
||||
:abbreviate word string (word, string)
|
||||
* :edit [+cmd] [file] (+cmd)
|
||||
* :ex [+cmd] [file] (+cmd)
|
||||
:k key (key)
|
||||
:map word string (word, string)
|
||||
:mark key (key)
|
||||
* :set [option ...] (option)
|
||||
* :tag string (string)
|
||||
:unabbreviate word (word)
|
||||
:unmap word (word)
|
||||
|
||||
These commands use whitespace to delimit their arguments, and use
|
||||
^V to escape those characters. The exceptions are starred in the
|
||||
above list, and are discussed below.
|
||||
|
||||
In general, I intend to treat a ^V in any argument, followed by
|
||||
any character, as that literal character. This will permit
|
||||
editing of files name "foo|", for example, by using the string
|
||||
"foo\^V|", where the literal next character protects the pipe
|
||||
from the ex command parser and the backslash protects it from the
|
||||
shell expansion.
|
||||
|
||||
This is backward compatible with historical vi, although there
|
||||
were a number of special cases where vi wasn't consistent.
|
||||
|
||||
4.1: The edit/ex commands:
|
||||
|
||||
The edit/ex commands are a special case because | symbols may
|
||||
occur in the "+cmd" field, for example:
|
||||
|
||||
:edit +10|s/abc/ABC/ file.c
|
||||
|
||||
In addition, the edit and ex commands have historically ignored
|
||||
literal next characters in the +cmd string, so that the following
|
||||
command won't work.
|
||||
|
||||
:edit +10|s/X/^V / file.c
|
||||
|
||||
I intend to handle the literal next character in edit/ex consistently
|
||||
with how it is handled in other commands.
|
||||
|
||||
More fun facts to know and tell:
|
||||
The acid test for the ex/edit commands:
|
||||
|
||||
date > file1; date > file2
|
||||
vi
|
||||
:edit +1|s/./XXX/|w file1| e file2|1 | s/./XXX/|wq
|
||||
|
||||
No version of vi, of which I'm aware, handles it.
|
||||
|
||||
4.2: The set command:
|
||||
|
||||
The set command treats ^V's as literal characters, so the following
|
||||
command won't work. Backslashes do work in this case, though, so
|
||||
the second version of the command does work.
|
||||
|
||||
set tags=tags_file1^V tags_file2
|
||||
set tags=tags_file1\ tags_file2
|
||||
|
||||
I intend to continue permitting backslashes in set commands, but
|
||||
to also permit literal next characters to work as well. This is
|
||||
backward compatible, but will also make set consistent with the
|
||||
other commands. I think it's unlikely to break any historic
|
||||
.exrc's, given that there are probably very few files with ^V's
|
||||
in their name.
|
||||
|
||||
4.3: The tag command:
|
||||
|
||||
The tag command ignores ^V's and backslashes; there's no way to
|
||||
get a space into a tag name.
|
||||
|
||||
I think this is a don't care, and I don't intend to fix it.
|
||||
|
||||
5: Regular expressions:
|
||||
|
||||
:global /pattern/ command
|
||||
:substitute /pattern/replace/
|
||||
:vglobal /pattern/ command
|
||||
|
||||
I intend to treat a backslash in the pattern, followed by the
|
||||
delimiter character or a backslash, as that literal character.
|
||||
|
||||
This is historic behavior in vi. It would get rid of a fairly
|
||||
hard-to-explain special case if we could just use the character
|
||||
immediately following the backslash in all cases, or, if we
|
||||
changed nvi to permit using the literal next character as a
|
||||
pattern escape character, but that would probably break historic
|
||||
scripts.
|
||||
|
||||
There is an additional escaping issue for regular expressions.
|
||||
Within the pattern and replacement, the '|' character did not
|
||||
delimit ex commands. For example, the following is legal.
|
||||
|
||||
:substitute /|/PIPE/|s/P/XXX/
|
||||
|
||||
This is a special case that I will support.
|
||||
|
||||
6: Ending anything with an escape character:
|
||||
|
||||
In all of the above rules, an escape character (either ^V or a
|
||||
backslash) at the end of an argument or file name is not handled
|
||||
specially, but used as a literal character.
|
61
usr.bin/vi/docs/internals/structures
Normal file
61
usr.bin/vi/docs/internals/structures
Normal file
@ -0,0 +1,61 @@
|
||||
# @(#)structures 5.2 (Berkeley) 11/1/93
|
||||
|
||||
There are three major data structures in this package. The first is a
|
||||
single global structure (named GS) which contains information common to
|
||||
all files and screens. It's really pretty tiny, and functions more as a
|
||||
single place to hang things than anything else.
|
||||
|
||||
The second and third structures are the file structures (named EXF) and
|
||||
the screen structures (named SCR). They contain information theoretically
|
||||
unique to a screen or file, respectively. Each SCR structure has a set
|
||||
of functions which update the screen and/or return information about the
|
||||
screen from the underlying screen package.
|
||||
|
||||
The GS structure contains linked lists SCR structures. The structures
|
||||
can also be classed by persistence. The GS structure never goes away
|
||||
and the SCR structure persists over instances of files.
|
||||
|
||||
File names have different properties than files themselves, so the name
|
||||
information for a file is held in an FREF structure which is chained from
|
||||
the SCR structure.
|
||||
|
||||
In general, functions are always passed an SCR structure and often an EXF
|
||||
structure as well. The SCR structure is necessary for any routine that
|
||||
wishes to talk to the screen, the EXF structure is necessary for any
|
||||
routine that wants to modify the file. The relationship between an SCR
|
||||
structure and its underlying EXF structure is not fixed, and although you
|
||||
can translate from an SCR to the underlying EXF, it is discouraged. If
|
||||
this becomes too onerous, I suspect I'll just stop passing around the EXF
|
||||
in the future.
|
||||
|
||||
The naming of the structures is consistent across the program. (Macros
|
||||
even depend on it, so don't try and change it!) The global structure is
|
||||
"gp", the screen structure is "sp", and the file structure is "ep".
|
||||
|
||||
A few other data structures:
|
||||
|
||||
TEXT In nvi/cut.h. This structure describes a portion of a line,
|
||||
and is used by the input routines and as the "line" part of a
|
||||
cut buffer.
|
||||
|
||||
CB In nvi/cut.h. A cut buffer. A cut buffer is a place to
|
||||
hang a list of TEXT structures.
|
||||
|
||||
MARK In nvi/mark.h. A cursor position, consisting of a line number
|
||||
and a column number.
|
||||
|
||||
MSG In nvi/msg.h. A chain of messages for the user.
|
||||
|
||||
SEQ In nvi/seq.h. An abbreviation or a map entry.
|
||||
|
||||
EXCMDARG
|
||||
In nvi/ex/excmd.h.stub. The structure that gets passed around
|
||||
to the functions that implement the ex commands. (The main
|
||||
ex command loop (see nvi/ex/ex.c) builds this up and then passes
|
||||
it to the ex functions.)
|
||||
|
||||
VICMDARG
|
||||
In nvi/vi/vcmd.h. The structure that gets passed around to the
|
||||
functions that implement the vi commands. (The main vi command
|
||||
loop (see nvi/vi/vi.c) builds this up and then passes it to the
|
||||
vi functions.)
|
1458
usr.bin/vi/docs/tutorial/vi.advanced
Normal file
1458
usr.bin/vi/docs/tutorial/vi.advanced
Normal file
File diff suppressed because it is too large
Load Diff
741
usr.bin/vi/docs/tutorial/vi.beginner
Normal file
741
usr.bin/vi/docs/tutorial/vi.beginner
Normal file
@ -0,0 +1,741 @@
|
||||
Section 1: {^F} {ZZ}
|
||||
|
||||
To get out of this tutorial, type: ZZ (two capital Z's).
|
||||
|
||||
Learning a new computer system implies learning a new text editor. These
|
||||
tutorial lessons were created by Dain Samples to help you come to grips with
|
||||
UC Berkeley's screen oriented editor called vi (for VIsual). This tutorial
|
||||
uses the vi editor itself as the means of presentation.
|
||||
|
||||
For best use of this tutorial, read all of a screen before performing any of
|
||||
the indicated actions. This tutorial (or, at least, the first half of it) has
|
||||
been designed to systematically present the vi commands IF THE INSTRUCTIONS
|
||||
ARE FOLLOWED! If you are too adventuresome, you may find yourself lost. If
|
||||
you ever find yourself stuck, remember the first line of this section.
|
||||
|
||||
OK, now find the control key on your keyboard; it usually has CTL or CTRL
|
||||
written on its upper surface. Your first assignment is to hold the control
|
||||
key down while you press the 'F' key on your keyboard. Please do so now.
|
||||
|
||||
|
||||
|
||||
Section 2: {^F} {^B}
|
||||
Many of vi's commands use the control key and some other key in combination,
|
||||
as with the control and the 'F' key above. This is abbreviated CTL-F, or ^F.
|
||||
|
||||
As you have probably guessed by now, ^F (CTL-F) moves you forward a fixed
|
||||
number of lines in the file. Throughout the remainder of the tutorial when
|
||||
you are ready to advance to the next section of text, hit ^F.
|
||||
|
||||
The opposite command is ^B. Just for fun, you might want to try a ^B to see
|
||||
the previous section again. Be sure to do a ^F to return you here.
|
||||
|
||||
Determine what the cursor looks like on your screen. Whatever it is (a box,
|
||||
an underscore, blinking, flashing, inverse, etc.) it should now be positioned
|
||||
in the upper left-hand corner of your screen under or on the S of Section.
|
||||
Become familiar with your cursor: to use vi correctly it is important to
|
||||
always know where the cursor is.
|
||||
|
||||
Did you notice that when you do a ^F the cursor is left at the top of the
|
||||
screen, and a ^B leaves the cursor near the bottom of the screen? Try the two
|
||||
commands ^B^F again. And now do another ^F to see the next section.
|
||||
|
||||
Section 3: {^F} {^B}
|
||||
You now have two basic commands for examining a file, both forwards (^F) and
|
||||
backwards (^B).
|
||||
|
||||
Note that these are vi text editing commands: they are not commands for the
|
||||
tutorial. Indeed, this tutorial is nothing but a text file which you are now
|
||||
editing. Everything you do and learn in this tutorial will be applicable to
|
||||
editing text files.
|
||||
|
||||
Therefore, when you are editing a file and are ready to see more of the text,
|
||||
entering ^F will get you to the next section of the file. Entering ^B will
|
||||
show you the previous section.
|
||||
|
||||
Time for you to do another ^F.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Section 4: {^F} {^B} {^M} (return key)
|
||||
We will adopt the notation of putting commands in curly braces so we can write
|
||||
them unambiguously. For example, if you are to type the command sequence
|
||||
"control B control F" (as we asked you to do above) it would appear as {^B^F}.
|
||||
This allows clear delineation of the command strings from the text. Remember
|
||||
that the curly braces are NOT part of the command string you are to type. Do
|
||||
NOT type the curly braces.
|
||||
|
||||
Sometimes, the command string in the curly braces will be rather long, and may
|
||||
be such that the first couple of characters of the command will erase from
|
||||
the screen the string you are trying to read and type. It is suggested that
|
||||
you write down the longer commands BEFORE you type them so you won't forget
|
||||
them once they disappear.
|
||||
|
||||
Now locate the return key on your keyboard: it is usually marked 'RETURN',
|
||||
indicate hitting the return key. In fact, the control-M key sequence is
|
||||
exactly the same as if you hit the return key, and vice versa.
|
||||
|
||||
Now type {^F}.
|
||||
|
||||
|
||||
Section 5: {:q!} {ZZ} {^M} (return key)
|
||||
Recognize that this tutorial is nothing more than a text file that you
|
||||
are editing. This means that if you do something wrong, it is possible
|
||||
for you to destroy the information in this file. Don't worry. If this
|
||||
happens, type {ZZ} (two capital Z's) or {:q!^M} to leave the tutorial.
|
||||
Restart the tutorial. Once in the tutorial, you can then page forward
|
||||
with {^F} until you are back to where you want to be. (There are
|
||||
easier ways to do this, some of which will be discussed later, but this
|
||||
is the most straightforward.)
|
||||
|
||||
You may want to write these commands down in a convenient place for quick
|
||||
reference: {:q!^M} and {ZZ}
|
||||
|
||||
We will assume that you now know to do a {^F} to advance the file
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Section 6: {m} {G} {'} {z}
|
||||
Now that you know how to get around in the file via ^F and ^B let's look at
|
||||
other ways of examining a text file. Sometimes it is necessary, in the midst
|
||||
of editing a file, to examine another part of the file. You are then faced
|
||||
with the problem of remembering your place in the file, looking at the other
|
||||
text, and then getting back to your original location. Vi has a 'mark'
|
||||
command, m. Type {mp}. You have just 'marked' your current location in the
|
||||
file and given it the name 'p'. The command string below will do three
|
||||
things: position you at the beginning of the file (line 1), then return you to
|
||||
the location 'p' that you just marked with the 'm' command, and, since the
|
||||
screen will not look exactly the same as it does right now, the 'z' command
|
||||
will reposition the screen. (You may want to write the string down before
|
||||
typing it: once you type {1G} it will no longer be on the screen.)
|
||||
|
||||
So now type {1G'pz^M} - a one followed by a capital G, followed by the quote
|
||||
mark, followed by a lower case 'p', then a lower case 'z', then a return
|
||||
(which is the same as a ^M). The {1G} moves you to line 1, i.e. the beginning
|
||||
of the file. The {'p} moves you to the location you marked with {mp}. The
|
||||
{z^M} command will repaint the screen putting the cursor at the top of the
|
||||
screen. (Now {^F}.)
|
||||
|
||||
Section 7: {m} {G} {'} {z}
|
||||
Let's look at some variations on those commands. If you wanted to look at
|
||||
line 22 in the file and return to this location you could type {mp22G'p}. Do
|
||||
so now, observing that {22G} puts your cursor at the beginning of section 2 in
|
||||
the middle of the screen.
|
||||
|
||||
Also note that, without the {z^M} command, the line with 'Section 7' on it is
|
||||
now in the MIDDLE of the screen, and not at the top. Our cursor is on the
|
||||
correct line (where we did the {mp} command) but the line is not where we
|
||||
might like it to be on the screen. That is the function of the {z^M} command.
|
||||
(Remember, ^M is the same as the 'return' key on your keyboard.) Type {z^M}
|
||||
now and observe the effect.
|
||||
|
||||
As you can see, the 'Section 7' line is now at the top of the screen with the
|
||||
cursor happily under the capital S. If you would like the cursor line (i.e.
|
||||
the line which the cursor is on) in the middle of the screen again, you would
|
||||
type {z.}. If you wanted the cursor line to be at the BOTTOM of the screen,
|
||||
type {z-}. Try typing {z-z.z^M} and watch what happens.
|
||||
|
||||
{^F}
|
||||
|
||||
Section 8: {z} {m} {'}
|
||||
|
||||
Note that the z command does not change the position of our cursor in the file
|
||||
itself, it simply moves the cursor around on the screen by moving the contents
|
||||
of the file around on the screen. The cursor stays on the same line of the
|
||||
file when using the z command.
|
||||
|
||||
This brings up an important point. There are two questions that the users of
|
||||
vi continually need to know the answer to: "Where am I in the file?" and
|
||||
"Where am I on the screen?" The cursor on your terminal shows the answer to
|
||||
both questions. Some commands will move you around in the file, usually
|
||||
changing the location of the cursor on the screen as well. Other commands
|
||||
move the cursor around on the screen without changing your location in the
|
||||
file.
|
||||
|
||||
Now type {ma}. Your location in the file has been given the name 'a'. If you
|
||||
type {'p'a} you will see the previous location we marked in section 7, and
|
||||
then will be returned to the current location. (You will want to do a {z^M}
|
||||
to repaint the screen afterwards.) Try it.
|
||||
{^F}
|
||||
|
||||
Section 9: {m} {''}
|
||||
Now we can move about in our file pretty freely. By using the {m} command we
|
||||
can give the current cursor position a lower-case-character name, like 'p',
|
||||
'a', 'e', 'm', or 'b'. Using the {G} command preceded by a line number we can
|
||||
look at any line in the file we like. Using the single quote command {'}
|
||||
followed by a character used in an {m} command, we can return to any location
|
||||
in the file we have marked.
|
||||
|
||||
However, try {m3}, or {mM}. You should hear a beep, or bell. Only lower-case
|
||||
letters are acceptable to the {m} and {'} commands: numbers, upper-case
|
||||
letters, and special characters are not acceptable.
|
||||
|
||||
If you type the {'} command with a character that is lower-case alphabetic but
|
||||
that has not been used in an {m} command, or for which the 'marked' text has
|
||||
been deleted, you will also get a beep. Try {'i}. You should get a beep
|
||||
because the command {mi} has never been issued. (Unless you've been
|
||||
experimenting.)
|
||||
|
||||
The command {''} attempts to return you to the location at which you last
|
||||
modified some part of your file. However, my experience has been that it is
|
||||
difficult to predict exactly where you will end up.
|
||||
Section 10: {^M} {-}
|
||||
Now do {ma}, marking your position at the top of the screen. Now hit {^M} (or
|
||||
return) until the cursor is right ...
|
||||
* <- here, over/under the asterisk. Now
|
||||
type {mb'a'b} and watch the cursor move from the asterisk to the top of the
|
||||
screen and back again.
|
||||
|
||||
The {^M} command moves the cursor to the beginning of the next line. Now type
|
||||
{^M} until the cursor is right ...
|
||||
* <- here. The command to move the cursor to the beginning of the
|
||||
previous line is {-}. Practice moving the cursor around on the screen by using
|
||||
{^M} and {-}. BE CAREFUL to not move the cursor OFF the screen just yet. If
|
||||
you do, type {'az^M}.
|
||||
|
||||
Now we can move to any line within the screen. Practice moving around in the
|
||||
file using the {^F}, {^B}, {-}, {^M}, {z}, and {'} commands. When you are
|
||||
fairly confident that you can get to where you need to be in the file, and
|
||||
position the cursor on the screen where you want it type {'az^M^F} (which, of
|
||||
course, moves you back to the beginning of this section, repositions the
|
||||
cursor at the top of the screen, and advances you to the next section).
|
||||
|
||||
Section 11: scrolling: {^M}
|
||||
The cursor should now be on the S of 'Section 11', and this should be on the
|
||||
first line of the screen. If it is not, do {^M} or {-} as appropriate to put
|
||||
the cursor on the section line, and type {z^M}.
|
||||
|
||||
Type {mc} to mark your place.
|
||||
|
||||
Now type {^M} until the cursor is on the last line of this screen. Now do one
|
||||
more {^M} and observe the result. This is called scrolling. When you
|
||||
attempted to move to a line not displayed on the screen, the line at the top of
|
||||
the screen was 'scrolled off', and a line at the bottom of the screen was
|
||||
'scrolled on'. The top line with 'Section 11' should no longer be visible.
|
||||
|
||||
Now type {'cz^M} to reset the screen and type {^F} for the next section.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Section 12: {-} {z}
|
||||
|
||||
The {-} command moves the cursor to the previous line in the file. Now type
|
||||
{-}, which attempts to move the cursor to the previous line in this file.
|
||||
However, that line is not on the screen. The resulting action will depend on
|
||||
your terminal. (Do a {^Mz^M} to reposition the file). On intelligent
|
||||
terminals (e.g. VT100s, Z19s, Concept 100s), a top line is 'scrolled on' and
|
||||
the bottom line is 'scrolled off'. Other terminals, however, may not have
|
||||
this 'reverse scrolling' feature. They will simply repaint the screen with
|
||||
the cursor line in the middle of the screen. On such terminals it is
|
||||
necessary to type {z^M} to get the cursor line back to the top of the screen.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Section 13:
|
||||
Up until this point, the tutorial has always tried to make sure that the first
|
||||
line of each screen has on it the section number and a list of the commands
|
||||
covered in that section. This will no longer be strictly maintained. If you
|
||||
want the section line at the top of the screen, you now know enough commands to
|
||||
do it easily: do {^M} or {-} until the cursor is on the section line and
|
||||
then {z^M}. Also, from this point on, it may not be the case that a {^F} will
|
||||
put you at the beginning of the next section. Therefore, be aware of where you
|
||||
are in the file as we look at other commands. You may have to find your way
|
||||
back to a particular section without any help from the tutorial. If you do not
|
||||
feel comfortable with this, then it is suggested that you practice moving from
|
||||
section 1 to section 13, back and forth, using {^M}, {-}, {^F}, and {^B}
|
||||
commands for a while.
|
||||
|
||||
Also make liberal use of the mark command {m}: if, for example, you make a
|
||||
habit of using {mz} to mark your current location in the file, then you will
|
||||
always be able to return to that location with {'z} if the editor does
|
||||
something strange and you have no idea where you are or what happened.
|
||||
|
||||
And finally, the proscription against experimentation is hereby lifted: play
|
||||
with the editor. Feel free to try out variations on the commands and move
|
||||
around in the file. By this time you should be able to recover from any gross
|
||||
errors.
|
||||
|
||||
Section 14: {^E} {^Y} {^D} {^U}
|
||||
Let us now look at a few other commands for moving around in the file, and
|
||||
moving the file around on the screen. Note that the commands we have already
|
||||
looked at are sufficient: you really don't need any more commands for looking
|
||||
in a file. The following commands are not absolutely necessary. However,
|
||||
they can make editing more convenient, and you should take note of their
|
||||
existence. But it would be perfectly valid to decide to ignore them on this
|
||||
first pass: you can learn them later when you see a need for them, if you ever
|
||||
do.
|
||||
|
||||
First, let's clear up some potentially confusing language. In at least one
|
||||
place in the official document ('An Introduction to Display Editing with Vi'
|
||||
by William Joy, and Mark Horton, September 1980), the expression "to scroll
|
||||
down text" means that the cursor is moved down in your file. However, note
|
||||
that this may result in the text on the screen moving UP. This use of the
|
||||
word 'scroll' refers to the action of the cursor within the file. However,
|
||||
another legitimate use of the word refers to the action of the text on the
|
||||
screen. That is, if the lines on your screen move up toward the top of the
|
||||
screen, this would be 'scrolling the screen up'. If the lines move down
|
||||
toward the bottom of the screen, this would be refered to as scrolling down.
|
||||
|
||||
I have tried to maintain the following jargon: 'scrolling' refers to what the
|
||||
text does on the screen, not to what the cursor does within the file. For the
|
||||
latter I will refer to the cursor 'moving', or to 'moving the cursor'. I
|
||||
realize that this is not necessarily consistent with Joy and Horton, but they
|
||||
were wrong.
|
||||
|
||||
{^E} scrolls the whole screen up one line, keeping the cursor on the same line,
|
||||
if possible. However, if the cursor line is the first line on the screen, then
|
||||
the cursor is moved to the next line in the file. Try typing {^E}.
|
||||
|
||||
{^Y} scrolls the screen down one line, keeping the cursor on the same line, if
|
||||
possible. However, if the cursor line is the last line on the screen, then the
|
||||
cursor is moved to the previous line in the file. Try it.
|
||||
|
||||
{^D} moves the cursor down into the file, scrolling the screen up.
|
||||
|
||||
{^U} moves the cursor up into the file, also scrolling the screen if the
|
||||
terminal you are on has the reverse scroll capability. Otherwise the
|
||||
screen is repainted.
|
||||
|
||||
Note that {^E} and {^Y} move the cursor on the screen while trying to keep the
|
||||
cursor at the same place in the file (if possible: however, the cursor can
|
||||
never move off screen), while {^D} and {^U} keep the cursor at the same place
|
||||
on the screen while moving the cursor within the file.
|
||||
|
||||
Section 15: {/ .. /^M}
|
||||
|
||||
Another way to position yourself in the file is by giving the editor a string
|
||||
to search for. Type the following: {/Here 1/^M} and the cursor should end up
|
||||
right ...........................here ^. Now type {/Section 15:/^M} and the
|
||||
cursor will end up over/on .....................here ^. Now type {//^M} and
|
||||
observe that the cursor is now over the capital S five lines above this line.
|
||||
Typing {//^M} several more times will bounce the cursor back and forth between
|
||||
the two occurrences of the string. In other words, when you type a string
|
||||
between the two slashes, it is searched for. Typing the slashes with nothing
|
||||
between them acts as if you had typed the previous string again.
|
||||
|
||||
Observe that the string you type between the two slashes is entered on the
|
||||
bottom line of the screen. Now type {/Search for x /^M} except replace the 'x'
|
||||
in the string with some other character, say 'b'. The message "Pattern not
|
||||
found" should appear on the bottom of the screen. If you hadn't replaced the
|
||||
'x', then you would have found the string. Try it.
|
||||
|
||||
Section 16: {? .. ?^M} {n} (search strings: ^ $)
|
||||
|
||||
When you surround the sought-for string with slashes as in {/Search/}, the
|
||||
file is searched beginning from your current position in the file. If the
|
||||
string is not found by the end of the file, searching is restarted at the
|
||||
beginning of the file. However, if you do want the search to find the
|
||||
PREVIOUS rather than the NEXT occurrence of the string, surround the string
|
||||
with question marks instead of slash marks.
|
||||
|
||||
Below are several occurrences of the same string.
|
||||
Here 2 Here 2 Here 2
|
||||
Here 2 Here 2.
|
||||
Observe the effect of the following search commands (try them in the
|
||||
sequence shown):
|
||||
{/Here 2/^M} {//^M} {??^M}
|
||||
{/^Here 2/^M} {//^M} {??^M}
|
||||
{/Here 2$/^M} {//^M} {??^M}
|
||||
|
||||
The first command looks for the next occurrence of the string 'Here 2'.
|
||||
However the second line of commands looks for an occurrence of 'Here 2' that
|
||||
is at the beginning of the line. When the up-arrow is the first character of
|
||||
a search string it stands for the beginning of the line. When the dollar-sign
|
||||
is the last character of the search string it stands for the end of the line.
|
||||
Therefore, the third line of commands searches for the string only when it is
|
||||
at the end of the line. Since there is only one place the string begins a
|
||||
line, and only one place the string ends the line, subsequent {//^M} and
|
||||
{??^M} will find those same strings over and over.
|
||||
|
||||
The {n} command will find the next occurrence of the / or ? search
|
||||
string. Try {/Here 2/^M} followed by several {n} and observe the
|
||||
effect. Then try {??^M} followed by several {n}. The {n} command
|
||||
remembers the direction of the last search. It is just a way to save a
|
||||
few keystrokes.
|
||||
|
||||
Section 17: \ and magic-characters in search strings
|
||||
|
||||
Now type {/Here 3$/^M}. You might expect the cursor to end up
|
||||
right......^ here. However, you will get "Pattern not found" at the bottom of
|
||||
the screen. Remember that the dollar-sign stands for the end of the line.
|
||||
Somehow, you must tell vi that you do not want the end of the line, but a
|
||||
dollar-sign. In other words, you must take away the special meaning that the
|
||||
dollar-sign has for the search mechanism. You do this (for any special
|
||||
character, including the up-arrow ^) by putting a back-slash ('\', not '/') in
|
||||
front of the character.
|
||||
|
||||
Now try {/Here 3\$/^M} and you should end up nine lines above this one. Try
|
||||
{//^M} and note that it returns you to the same place, and not to the first
|
||||
line of this paragraph: the back-slash character is not part of the search
|
||||
string and will not be found. To find the string in the first line of this
|
||||
paragraph, type {/Here 3\\\$/^M}. There are three back-slashes: the first takes
|
||||
away the special meaning from the second, and the third takes away the special
|
||||
meaning from the dollar-sign.
|
||||
|
||||
Following is a list of the characters that have special meanings in search
|
||||
strings. If you wish to find a string containing one of these characters, you
|
||||
will have to be precede the character with a backslash. These characters are
|
||||
called magic characters because of the fun and games you can have with them
|
||||
and they can have with you, if you aren't aware of what they do.
|
||||
|
||||
^ - (up-arrow) beginning of a line
|
||||
$ - (dollar-sign) end of a line
|
||||
. - (period) matches any character
|
||||
\ - (backslant) the escape character itself
|
||||
[ - (square bracket) for finding patterns (see section #SEARCH)
|
||||
] - (square bracket) ditto
|
||||
* - (asterisk) ditto
|
||||
|
||||
Without trying to explain it here, note that {:set nomagic^M} turns off the
|
||||
special meanings of all but the ^ up-arrow, $ dollar-sign, and backslash
|
||||
characters.
|
||||
|
||||
Section 18: {: (colon commands)} {ZZ}
|
||||
|
||||
In this section we will discuss getting into and out of the editor in more
|
||||
detail. If you are editing a file and wish to save the results the command
|
||||
sequence {:w^M} writes the current contents of the file out to disk, using the
|
||||
file name you used when you invoked the editor. That is, if you are at the
|
||||
command level in Unix, and you invoke vi with {vi foo} where foo is the name
|
||||
of the file you wish to edit, then foo is the name of the file used by the
|
||||
{:w^M} command.
|
||||
|
||||
If you are done, the write and quit commands can be combined into a single
|
||||
command {:wq^M}. An even simpler way is the command {ZZ} (two capital Z's).
|
||||
|
||||
If, for some reason, you wish to exit without saving any changes you have made,
|
||||
{:q!^M} does the trick. If you have not made any changes, the exclamation
|
||||
point is not necessary: {:q^M}. Vi is pretty good about not letting you
|
||||
get out without warning you that you haven't saved your file.
|
||||
|
||||
We have mentioned before that you are currently in the vi editor, editing a
|
||||
file. If you wish to start the tutorial over from the very beginning, you
|
||||
could {ZZ}, and then type {vi.tut beginner} in response to the Unix prompt.
|
||||
This will create a fresh copy of this file for you, which might be necessary
|
||||
if you accidentally destroyed the copy you were working with. Just do a
|
||||
search for the last section you were in: e.g. {/Section 18:/^Mz^M}.
|
||||
|
||||
Section 19: {H} {M} {L}
|
||||
|
||||
Here are a few more commands that will move you around on the screen. Again,
|
||||
they are not absolutely necessary, but they can make screen positioning easier:
|
||||
|
||||
{H} - puts the cursor at the top of the screen (the 'home' position)
|
||||
|
||||
{M} - puts the cursor in the middle of the screen
|
||||
|
||||
{L} - puts the cursor at the bottom of the screen.
|
||||
|
||||
Try typing {HML} and watch the cursor.
|
||||
|
||||
Try typing {5HM5L} and note that 5H puts you five lines from the top of the
|
||||
screen, and 5L puts you five lines from the bottom of the screen.
|
||||
|
||||
Section 20: {w} {b} {0} {W} {B} {e} {E} {'} {`}
|
||||
|
||||
Up to this point we have concentrated on positioning in the file, and
|
||||
positioning on the screen. Now let's look at positioning in a line. Put the
|
||||
cursor at the beginning of the following line and type {z^M}:
|
||||
|
||||
This is a test line: your cursor should initially be at its beginning.
|
||||
|
||||
The test line should now be at the top of your screen. Type {w} several times.
|
||||
Note that it moves you forward to the beginning of the next word. Now type
|
||||
{b} (back to the beginning of the word) several times till you are at the
|
||||
beginning of the line. (If you accidentally type too many {b}, type {w} until
|
||||
you are on the beginning of the line again.) Type {wwwww} (five w's) and note
|
||||
that the cursor is now on the colon in the sentence. The lower-case w command
|
||||
moves you forward one word, paying attention to certain characters such as
|
||||
colon and period as delimiters and counting them as words themselves. Now
|
||||
type {0} (zero, not o 'oh'): this moves you to the beginning of the current
|
||||
line. Now type {5w} and notice that this has the effect of repeating {w} five
|
||||
times and that you are now back on the colon. Type {0} (zero) again. To
|
||||
ignore the delimiters and to move to the beginning of the next word using only
|
||||
blanks, tabs and carriage-returns (these are called white-space characters) to
|
||||
delimit the words, use the {W} command: upper-case W. {B} takes you back a
|
||||
word using white-space characters as word delimiters.
|
||||
|
||||
Note that the commands {wbWB} do not stop at the beginning or end of a line:
|
||||
they will continue to the next word on the next line in the direction specified
|
||||
(a blank line counts as a word).
|
||||
|
||||
If you are interested in the END of the word, and not the BEGINNING, then use
|
||||
the {e} and {E} commands. These commands only move forward and there are no
|
||||
corresponding 'reverse search' commands for the end of a word.
|
||||
|
||||
Also, we have been using the {'} command to move the cursor to a position that
|
||||
we have previously marked with the {m} command. However, position the cursor
|
||||
in the middle of a line (any line, just pick one) and type {mk}, marking that
|
||||
position with the letter k. Now type a few returns {^M} and type {'k}.
|
||||
Observe that the cursor is now at the beginning of the line that you marked.
|
||||
Now try {`k}: note that this is the reverse apostrophe, or back-quote, or grave
|
||||
accent, or whatever you want to call it. Also note that it moves you to the
|
||||
character that was marked, not just to the line that was marked.
|
||||
|
||||
In addition, the {``} command works just like the {''} command except that you
|
||||
are taken to the exact character, not just to the line. (I'm still not
|
||||
sure which exact character, just as I'm still not sure which line.)
|
||||
|
||||
Section 21: {l} {k} {j} {h}
|
||||
|
||||
There are several commands to move around on the screen on a character by
|
||||
character basis:
|
||||
|
||||
l - moves the cursor one character to the RIGHT
|
||||
k - moves the cursor UP one line
|
||||
j - moves the cursor DOWN one line
|
||||
h - moves the cursor one character to the LEFT
|
||||
|
||||
Section 22: {i} {a} {I} {A} {o} {O} ^[ (escape key)
|
||||
|
||||
For this and following sections you will need to use the ESCAPE key on your
|
||||
terminal. It is usually marked ESC. Since the escape key is the same as
|
||||
typing {^[} we will use ^[ for the escape key.
|
||||
|
||||
Probably the most often used command in an editor is the insert command. Below
|
||||
are two lines of text, the first correct, the second incorrect. Position your
|
||||
cursor at the beginning of Line 1 and type {z^M}.
|
||||
|
||||
Line 1: This is an example of the insert command.
|
||||
Line 2: This is an of the insert command.
|
||||
|
||||
To make line 2 look like line 1, we are going to insert the characters
|
||||
'example ' before the word 'of'. So, now move the cursor so that it is
|
||||
positioned on the 'o' of 'of'. (You can do this by typing {^M} to move
|
||||
to the beginning of line 2, followed by {6w} or {wwwwww} to position the cursor
|
||||
on the word 'of'.)
|
||||
|
||||
Now carefully type the following string and observe the effects:
|
||||
{iexample ^[} (remember: ^[ is the escape key)}
|
||||
The {i} begins the insert mode, and 'example ' is inserted into the line:
|
||||
be sure to notice the blank in 'example '. The ^[ ends insertion mode,
|
||||
and the line is updated to include the new string. Line 1 should look exactly
|
||||
like Line 2.
|
||||
|
||||
Move the cursor to the beginning of Line 3 below and type {z^M}:
|
||||
|
||||
Line 3: These lines are examples for the 'a' command.
|
||||
Line 4: These line are examples for the '
|
||||
|
||||
We will change line four to look like line three by using the append command.
|
||||
We need to append an 's' to the word 'line'. Position the cursor on the 'e'
|
||||
of 'line'. You can do this in several ways, one way is the following:
|
||||
First, type {/line /^M}. This puts us on the word 'line' in Line 4
|
||||
(the blank in the search string is important!). Next, type {e}. The 'e' puts
|
||||
us at the end of the word. Now, type {as^[ (^[ is the escape character)}.
|
||||
The 'a' puts us in insert mode, AFTER the current character. We appended the
|
||||
's', and the escape ^[ ended the insert mode.
|
||||
|
||||
The difference between {i} (insert) and {a} (append) is that {i} begins
|
||||
inserting text BEFORE the cursor, and {a} begins inserting AFTER the cursor.
|
||||
|
||||
Now type {Aa' command.^[}. The cursor is moved to the end of the line and the
|
||||
string following {A} is inserted into the text. Line 4 should now look like
|
||||
line 3.
|
||||
|
||||
Just as {A} moves you to the end of the line to begin inserting, {I} would
|
||||
begin inserting at the FRONT of the line.
|
||||
|
||||
To begin the insertion of a line after the cursor line, type {o}. To insert a
|
||||
line before the cursor line, type {O}. In other words {o123^[} is equivalent
|
||||
to {A^M123^[}, and {O123^[} is equivalent to {I123^M^[}. The text after the
|
||||
{o} or {O} is ended with an escape ^[.
|
||||
|
||||
This paragraph contains information that is terminal dependent: you will just
|
||||
have to experiment to discover what your terminal does. Once in the insert
|
||||
mode, if you make a mistake in the typing, ^H will delete the previous
|
||||
character up to the beginning of the current insertion. ^W will delete the
|
||||
previous word, and one of ^U, @, or ^X will delete the current line (up to the
|
||||
beginning of the current insertion). You will need to experiment with ^U, @,
|
||||
and ^X to determine which works for your terminal.
|
||||
|
||||
Section 23: {f} {x} {X} {w} {l} {r} {R} {s} {S} {J}
|
||||
|
||||
Position the cursor at the beginning of line 5 and {z^M}:
|
||||
|
||||
Line 5: The line as it should be.
|
||||
Line 6: The line as it shouldn't be.
|
||||
|
||||
To make Line 6 like Line 5, we have to delete the 'n', the apostrophe, and the
|
||||
't'. There are several ways to position ourselves at the 'n'. Choose
|
||||
whichever one suits your fancy:
|
||||
|
||||
{/n't/^M}
|
||||
{^M7w6l} or {^M7w6 } (note the space)
|
||||
{^M3fn} (finds the 3rd 'n' on the line)
|
||||
|
||||
Now {xxx} will delete the three characters, as will {3x}.
|
||||
|
||||
Note that {X} deletes the character just BEFORE the cursor, as opposed
|
||||
to the character AT the cursor.
|
||||
|
||||
Position the cursor at line 7 and {z^M}:
|
||||
|
||||
Line 7: The line as it would be.
|
||||
Line 8: The line as it could be.
|
||||
|
||||
To change line 8 into line 7 we need to change the 'c' in 'could' into a 'w'.
|
||||
The 'r' (replace) command was designed for this. Typing {rc} is the same as
|
||||
typing {xic^[} (i.e. delete the 'bad' character and insert the correct
|
||||
new character). Therefore, assuming that you have positioned the cursor on the
|
||||
'c' of 'could', the easiest way to change 'could' into 'would' is {rw}.
|
||||
|
||||
If you would like to now change the 'would' into 'should', use the substitute
|
||||
command, 's': {ssh^[}. The difference between 'r' and 's' is that 'r'
|
||||
(replace) replaces the current character with another character, while 's'
|
||||
(substitute) substitutes the current character with a string, ended with an
|
||||
escape.
|
||||
|
||||
The capital letter version of replace {R} replaces each character by a
|
||||
character one at a time until you type an escape, ^[. The 'S' command
|
||||
substitutes the whole line.
|
||||
|
||||
Position your cursor at the beginning of line 9 and {z^M}.
|
||||
|
||||
Line 9: Love is a many splendored thing.
|
||||
Line 10: Love is a most splendored thing.
|
||||
|
||||
To change line 10 into line 9, position the cursor at the beginning of 'most',
|
||||
and type {Rmany^[}.
|
||||
|
||||
You may have noticed that, when inserting text, a new line is formed by typing
|
||||
{^M}. When changing, replacing, or substituting text you can make a new line
|
||||
by typing {^M}. However, neither {x} nor {X} will remove ^M to make two lines
|
||||
into one line. To do this, position the cursor on the first of the two lines
|
||||
you wish to make into a single line and type {J} (uppercase J for 'Join').
|
||||
|
||||
Section 24: {u} {U}
|
||||
|
||||
Finally, before we review, let's look at the undo command. Position
|
||||
your cursor on line 11 below and {z^M}.
|
||||
|
||||
Line 11: The quick brown fox jumped over the lazy hound dog.
|
||||
Line 12: the qwick black dog dumped over the laxy poune fox.
|
||||
|
||||
Type the following set of commands, and observe carefully the effect of each
|
||||
of the commands:
|
||||
|
||||
{/^Line 12:/^M} {ft} {rT} {fw} {ru} {w} {Rbrown fox^[} {w} {rj}
|
||||
{fx} {rz} {w} {Rhound dog^[}
|
||||
|
||||
Line 12 now matches line 11. Now type {U} - capital 'U'. And line 12 now
|
||||
looks like it did before you typed in the command strings. Now type:
|
||||
|
||||
{ft} {rT} {fw} {ru} {^M} {^M}
|
||||
|
||||
and then type {u}: the cursor jumps back to the line containing the second
|
||||
change you made and 'undoes' it. That is, {U} 'undoes' all the changes on the
|
||||
line, and {u} 'undoes' only the last change. Type {u} several times and
|
||||
observe what happens: {u} can undo a previous {u}!
|
||||
|
||||
Caveat: {U} only works as long as the cursor is still on the line. Move the
|
||||
cursor off the line and {U} will have no effect, except to possibly beep at
|
||||
you. However, {u} will undo the last change, no matter where it occurred.
|
||||
|
||||
Section 25: review
|
||||
|
||||
At this point, you have all the commands you need in order to make use of vi.
|
||||
The remainder of this tutorial will discuss variations on these commands as
|
||||
well as introduce new commands that make the job of editing more efficient.
|
||||
Here is a brief review of the basic commands we have covered. They are listed
|
||||
in the order of increasing complexity and/or decreasing necessity (to say that
|
||||
a command is less necessary is not to say that it is less useful!). These
|
||||
commands allow you to comfortably edit any text file. There are other
|
||||
commands that will make life easier but will require extra time to learn,
|
||||
obviously. You may want to consider setting this tutorial aside for several
|
||||
weeks and returning to it later after gaining experience with vi and getting
|
||||
comfortable with it. The convenience of some of the more exotic commands may
|
||||
then be apparent and worth the extra investment of time and effort
|
||||
required to master them.
|
||||
|
||||
to get into the editor from Unix: {vi filename}
|
||||
to exit the editor
|
||||
saving all changes {ZZ} or {:wq^M}
|
||||
throwing away all changes {:q!^M}
|
||||
when no changes have been made {:q^M}
|
||||
save a file without exiting the editor {:w^M}
|
||||
write the file into another file {:w filename^M}
|
||||
insert text
|
||||
before the cursor {i ...text... ^[}
|
||||
at the beginning of the line {I ...text... ^[}
|
||||
after the cursor (append) {a ...text... ^[}
|
||||
at the end of the line {A ...text... ^[}
|
||||
after the current line {o ...text... ^[}
|
||||
before the current line {O ...text... ^[}
|
||||
delete the character ...
|
||||
under the cursor {x}
|
||||
to the left of the cursor {X}
|
||||
delete n characters {nx} or {nX} (for n a number)
|
||||
make two lines into one line (Join) {J}
|
||||
find a string in the file ...
|
||||
searching forward {/ ...string... /^M}
|
||||
searching backwards {? ...string... ?^M}
|
||||
repeat the last search command {n}
|
||||
repeat the last search command in the
|
||||
opposite direction {N}
|
||||
find the character c on this line ...
|
||||
searching forward {fc}
|
||||
searching backward {Fc}
|
||||
repeat the last 'find character' command {;}
|
||||
replace a character with character x {rx}
|
||||
substitute a single character with text {s ...text... ^[}
|
||||
substitute n characters with text {ns ...text... ^[}
|
||||
replace characters one-by-one with text {R ...text... ^[}
|
||||
undo all changes to the current line {U}
|
||||
undo the last single change {u}
|
||||
move forward in the file a "screenful" {^F}
|
||||
move back in the file a "screenful" {^B}
|
||||
move forward in the file one line {^M} or {+}
|
||||
move backward in the file one line {-}
|
||||
move to the beginning of the line {0}
|
||||
move to the end of the line {$}
|
||||
move forward one word {w}
|
||||
move forward one word, ignoring punctuation {W}
|
||||
move forward to the end of the next word {e}
|
||||
to the end of the word, ignoring punctuation{E}
|
||||
move backward one word {b}
|
||||
move back one word, ignoring punctuation {B}
|
||||
return to the last line modified {''}
|
||||
scroll a line onto the top of the screen {^Y}
|
||||
scroll a line onto the bottom of the screen {^E}
|
||||
move "up" in the file a half-screen {^U}
|
||||
move "down" in the file a half-screen {^D}
|
||||
move the cursor to the top screen line {H}
|
||||
move the cursor to the bottom screen line {L}
|
||||
move the cursor to the middle line {M}
|
||||
move LEFT one character position {h} or {^H}
|
||||
move RIGHT one character position {l} or { }
|
||||
move UP in the same column {k} or {^P}
|
||||
move DOWN in the same column {j} or {^N}
|
||||
mark the current position, name it x {mx}
|
||||
move to the line marked/named x {'x}
|
||||
move to the character position named x {`x}
|
||||
move to the beginning of the file {1G}
|
||||
move to the end of the file {G}
|
||||
move to line 23 in the file {23G}
|
||||
repaint the screen with the cursor line
|
||||
at the top of the screen {z^M}
|
||||
in the middle of the screen {z.}
|
||||
at the bottom of the screen {z-}
|
||||
|
||||
More information on vi can be found in the file vi.advanced, which you can
|
||||
peruse at your leisure. From UNIX, type {vi.tut advanced^M}.
|
24
usr.bin/vi/docs/tutorial/vi.tut.csh
Executable file
24
usr.bin/vi/docs/tutorial/vi.tut.csh
Executable file
@ -0,0 +1,24 @@
|
||||
#!/bin/csh -f
|
||||
#
|
||||
# This makes the user's EXINIT variable set to the 'correct' things.
|
||||
# I don't know what will happen if they also have a .exrc file!
|
||||
#
|
||||
# XXX
|
||||
# Make sure that user is using a 24 line window!!!
|
||||
#
|
||||
if ($1 != "beginner" && $1 != "advanced") then
|
||||
echo Usage: $0 beginner or $0 advanced
|
||||
exit
|
||||
endif
|
||||
|
||||
if ($?EXINIT) then
|
||||
set oexinit="$EXINIT"
|
||||
setenv EXINIT 'se ts=4 wm=8 sw=4'
|
||||
endif
|
||||
|
||||
vi vi.{$1}
|
||||
|
||||
onintr:
|
||||
if ($?oexinit) then
|
||||
setenv EXINIT "$oexinit"
|
||||
endif
|
1866
usr.bin/vi/ex/ex.c
Normal file
1866
usr.bin/vi/ex/ex.c
Normal file
File diff suppressed because it is too large
Load Diff
129
usr.bin/vi/ex/ex_abbrev.c
Normal file
129
usr.bin/vi/ex/ex_abbrev.c
Normal file
@ -0,0 +1,129 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_abbrev.c 8.12 (Berkeley) 8/14/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 <termios.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include <db.h>
|
||||
#include <regex.h>
|
||||
|
||||
#include "vi.h"
|
||||
#include "excmd.h"
|
||||
#include "../vi/vcmd.h"
|
||||
|
||||
/*
|
||||
* ex_abbr -- :abbreviate [key replacement]
|
||||
* Create an abbreviation or display abbreviations.
|
||||
*/
|
||||
int
|
||||
ex_abbr(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
CHAR_T *p;
|
||||
size_t len;
|
||||
|
||||
switch (cmdp->argc) {
|
||||
case 0:
|
||||
if (seq_dump(sp, SEQ_ABBREV, 0) == 0)
|
||||
msgq(sp, M_INFO, "No abbreviations to display");
|
||||
return (0);
|
||||
case 2:
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Check for illegal characters. */
|
||||
for (p = cmdp->argv[0]->bp, len = cmdp->argv[0]->len; len--; ++p)
|
||||
if (!inword(*p)) {
|
||||
msgq(sp, M_ERR,
|
||||
"%s may not be part of an abbreviated word",
|
||||
KEY_NAME(sp, *p));
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (seq_set(sp, NULL, 0, cmdp->argv[0]->bp, cmdp->argv[0]->len,
|
||||
cmdp->argv[1]->bp, cmdp->argv[1]->len, SEQ_ABBREV, SEQ_USERDEF))
|
||||
return (1);
|
||||
|
||||
F_SET(sp->gp, G_ABBREV);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* ex_unabbr -- :unabbreviate key
|
||||
* Delete an abbreviation.
|
||||
*/
|
||||
int
|
||||
ex_unabbr(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
ARGS *ap;
|
||||
|
||||
ap = cmdp->argv[0];
|
||||
if (!F_ISSET(sp->gp, G_ABBREV) ||
|
||||
seq_delete(sp, ap->bp, ap->len, SEQ_ABBREV)) {
|
||||
msgq(sp, M_ERR, "\"%s\" is not an abbreviation", ap->bp);
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* abbr_save --
|
||||
* Save the abbreviation sequences to a file.
|
||||
*/
|
||||
int
|
||||
abbr_save(sp, fp)
|
||||
SCR *sp;
|
||||
FILE *fp;
|
||||
{
|
||||
return (seq_save(sp, fp, "abbreviate ", SEQ_ABBREV));
|
||||
}
|
220
usr.bin/vi/ex/ex_append.c
Normal file
220
usr.bin/vi/ex/ex_append.c
Normal file
@ -0,0 +1,220 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_append.c 8.22 (Berkeley) 8/7/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 <string.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include <db.h>
|
||||
#include <regex.h>
|
||||
|
||||
#include "vi.h"
|
||||
#include "excmd.h"
|
||||
#include "../sex/sex_screen.h"
|
||||
|
||||
enum which {APPEND, CHANGE, INSERT};
|
||||
|
||||
static int aci __P((SCR *, EXF *, EXCMDARG *, enum which));
|
||||
|
||||
/*
|
||||
* ex_append -- :[line] a[ppend][!]
|
||||
* Append one or more lines of new text after the specified line,
|
||||
* or the current line if no address is specified.
|
||||
*/
|
||||
int
|
||||
ex_append(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
return (aci(sp, ep, cmdp, APPEND));
|
||||
}
|
||||
|
||||
/*
|
||||
* ex_change -- :[line[,line]] c[hange][!] [count]
|
||||
* Change one or more lines to the input text.
|
||||
*/
|
||||
int
|
||||
ex_change(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
return (aci(sp, ep, cmdp, CHANGE));
|
||||
}
|
||||
|
||||
/*
|
||||
* ex_insert -- :[line] i[nsert][!]
|
||||
* Insert one or more lines of new text before the specified line,
|
||||
* or the current line if no address is specified.
|
||||
*/
|
||||
int
|
||||
ex_insert(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
return (aci(sp, ep, cmdp, INSERT));
|
||||
}
|
||||
|
||||
static int
|
||||
aci(sp, ep, cmdp, cmd)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
enum which cmd;
|
||||
{
|
||||
MARK m;
|
||||
TEXTH *sv_tiqp, tiq;
|
||||
TEXT *tp;
|
||||
struct termios t;
|
||||
u_int flags;
|
||||
int rval;
|
||||
|
||||
rval = 0;
|
||||
|
||||
/*
|
||||
* Set input flags; the ! flag turns off autoindent for append,
|
||||
* change and insert.
|
||||
*/
|
||||
LF_INIT(TXT_DOTTERM | TXT_NLECHO);
|
||||
if (!F_ISSET(cmdp, E_FORCE) && O_ISSET(sp, O_AUTOINDENT))
|
||||
LF_SET(TXT_AUTOINDENT);
|
||||
if (O_ISSET(sp, O_BEAUTIFY))
|
||||
LF_SET(TXT_BEAUTIFY);
|
||||
|
||||
/* Input is interruptible. */
|
||||
F_SET(sp, S_INTERRUPTIBLE);
|
||||
|
||||
/*
|
||||
* If this code is called by vi, the screen TEXTH structure (sp->tiqp)
|
||||
* may already be in use, e.g. ":append|s/abc/ABC/" would fail as we're
|
||||
* only halfway through the line when the append code fires. Use the
|
||||
* local structure instead.
|
||||
*
|
||||
* If this code is called by vi, we want to reset the terminal and use
|
||||
* ex's s_get() routine. It actually works fine if we use vi's s_get()
|
||||
* routine, but it doesn't look as nice. Maybe if we had a separate
|
||||
* window or something, but getting a line at a time looks awkward.
|
||||
*/
|
||||
if (IN_VI_MODE(sp)) {
|
||||
memset(&tiq, 0, sizeof(TEXTH));
|
||||
CIRCLEQ_INIT(&tiq);
|
||||
sv_tiqp = sp->tiqp;
|
||||
sp->tiqp = &tiq;
|
||||
|
||||
if (F_ISSET(sp->gp, G_STDIN_TTY))
|
||||
SEX_RAW(t);
|
||||
(void)write(STDOUT_FILENO, "\n", 1);
|
||||
LF_SET(TXT_NLECHO);
|
||||
|
||||
}
|
||||
|
||||
/* Set the line number, so that autoindent works correctly. */
|
||||
sp->lno = cmdp->addr1.lno;
|
||||
|
||||
if (sex_get(sp, ep, sp->tiqp, 0, flags) != INP_OK)
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* If doing a change, replace lines for as long as possible. Then,
|
||||
* append more lines or delete remaining lines. Changes to an empty
|
||||
* file are just appends, and inserts are the same as appends to the
|
||||
* previous line.
|
||||
*
|
||||
* !!!
|
||||
* Adjust the current line number for the commands to match historic
|
||||
* practice if the user doesn't enter anything, and set the address
|
||||
* to which we'll append. This is safe because an address of 0 is
|
||||
* illegal for change and insert.
|
||||
*/
|
||||
m = cmdp->addr1;
|
||||
switch (cmd) {
|
||||
case INSERT:
|
||||
--m.lno;
|
||||
/* FALLTHROUGH */
|
||||
case APPEND:
|
||||
if (sp->lno == 0)
|
||||
sp->lno = 1;
|
||||
break;
|
||||
case CHANGE:
|
||||
--m.lno;
|
||||
if (sp->lno != 1)
|
||||
--sp->lno;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* !!!
|
||||
* Cut into the unnamed buffer.
|
||||
*/
|
||||
if (cmd == CHANGE &&
|
||||
(cut(sp, ep, NULL, &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE) ||
|
||||
delete(sp, ep, &cmdp->addr1, &cmdp->addr2, 1)))
|
||||
goto err;
|
||||
|
||||
for (tp = sp->tiqp->cqh_first;
|
||||
tp != (TEXT *)sp->tiqp; tp = tp->q.cqe_next) {
|
||||
if (file_aline(sp, ep, 1, m.lno, tp->lb, tp->len)) {
|
||||
err: rval = 1;
|
||||
break;
|
||||
}
|
||||
sp->lno = ++m.lno;
|
||||
}
|
||||
|
||||
if (IN_VI_MODE(sp)) {
|
||||
sp->tiqp = sv_tiqp;
|
||||
text_lfree(&tiq);
|
||||
|
||||
/* Reset the terminal state. */
|
||||
if (F_ISSET(sp->gp, G_STDIN_TTY)) {
|
||||
if (SEX_NORAW(t))
|
||||
rval = 1;
|
||||
F_SET(sp, S_REFRESH);
|
||||
}
|
||||
}
|
||||
return (rval);
|
||||
}
|
263
usr.bin/vi/ex/ex_args.c
Normal file
263
usr.bin/vi/ex/ex_args.c
Normal file
@ -0,0 +1,263 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_args.c 8.27 (Berkeley) 8/4/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"
|
||||
#include "excmd.h"
|
||||
|
||||
/*
|
||||
* ex_next -- :next [+cmd] [files]
|
||||
* Edit the next file, optionally setting the list of files.
|
||||
*
|
||||
* !!!
|
||||
* The :next command behaved differently from the :rewind command in
|
||||
* historic vi. See nvi/docs/autowrite for details, but the basic
|
||||
* idea was that it ignored the force flag if the autowrite flag was
|
||||
* set. This implementation handles them all identically.
|
||||
*/
|
||||
int
|
||||
ex_next(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
ARGS **argv, **pc;
|
||||
FREF *frp;
|
||||
int noargs;
|
||||
char **ap;
|
||||
|
||||
if (file_m1(sp, ep, F_ISSET(cmdp, E_FORCE), FS_ALL | FS_POSSIBLE))
|
||||
return (1);
|
||||
|
||||
/*
|
||||
* If the first argument is a plus sign, '+', it's an initial
|
||||
* ex command.
|
||||
*/
|
||||
argv = cmdp->argv;
|
||||
if (cmdp->argc && argv[0]->bp[0] == '+') {
|
||||
--cmdp->argc;
|
||||
pc = argv++;
|
||||
} else
|
||||
pc = NULL;
|
||||
|
||||
/* Any other arguments are a replacement file list. */
|
||||
if (cmdp->argc) {
|
||||
/* Free the current list. */
|
||||
if (!F_ISSET(sp, S_ARGNOFREE) && sp->argv != NULL) {
|
||||
for (ap = sp->argv; *ap != NULL; ++ap)
|
||||
free(*ap);
|
||||
free(sp->argv);
|
||||
}
|
||||
F_CLR(sp, S_ARGNOFREE | S_ARGRECOVER);
|
||||
sp->cargv = NULL;
|
||||
|
||||
/* Create a new list. */
|
||||
CALLOC_RET(sp,
|
||||
sp->argv, char **, cmdp->argc + 1, sizeof(char *));
|
||||
for (ap = sp->argv,
|
||||
argv = cmdp->argv; argv[0]->len != 0; ++ap, ++argv)
|
||||
if ((*ap =
|
||||
v_strdup(sp, argv[0]->bp, argv[0]->len)) == NULL)
|
||||
return (1);
|
||||
*ap = NULL;
|
||||
|
||||
/* Switch to the first one. */
|
||||
sp->cargv = sp->argv;
|
||||
if ((frp = file_add(sp, *sp->cargv)) == NULL)
|
||||
return (1);
|
||||
noargs = 0;
|
||||
} else {
|
||||
if (sp->cargv == NULL || sp->cargv[1] == NULL) {
|
||||
msgq(sp, M_ERR, "No more files to edit");
|
||||
return (1);
|
||||
}
|
||||
if ((frp = file_add(sp, sp->cargv[1])) == NULL)
|
||||
return (1);
|
||||
if (F_ISSET(sp, S_ARGRECOVER))
|
||||
F_SET(frp, FR_RECOVER);
|
||||
noargs = 1;
|
||||
}
|
||||
|
||||
if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE)))
|
||||
return (1);
|
||||
if (noargs)
|
||||
++sp->cargv;
|
||||
|
||||
/* Push the initial command onto the stack. */
|
||||
if (pc != NULL)
|
||||
if (IN_EX_MODE(sp))
|
||||
(void)term_push(sp, pc[0]->bp, pc[0]->len, 0);
|
||||
else if (IN_VI_MODE(sp)) {
|
||||
(void)term_push(sp, "\n", 1, 0);
|
||||
(void)term_push(sp, pc[0]->bp, pc[0]->len, 0);
|
||||
(void)term_push(sp, ":", 1, 0);
|
||||
(void)file_lline(sp, sp->ep, &sp->frp->lno);
|
||||
F_SET(sp->frp, FR_CURSORSET);
|
||||
}
|
||||
|
||||
F_SET(sp, S_FSWITCH);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* ex_prev -- :prev
|
||||
* Edit the previous file.
|
||||
*/
|
||||
int
|
||||
ex_prev(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
FREF *frp;
|
||||
|
||||
if (file_m1(sp, ep, F_ISSET(cmdp, E_FORCE), FS_ALL | FS_POSSIBLE))
|
||||
return (1);
|
||||
|
||||
if (sp->cargv == sp->argv) {
|
||||
msgq(sp, M_ERR, "No previous files to edit");
|
||||
return (1);
|
||||
}
|
||||
if ((frp = file_add(sp, sp->cargv[-1])) == NULL)
|
||||
return (1);
|
||||
|
||||
if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE)))
|
||||
return (1);
|
||||
|
||||
--sp->cargv;
|
||||
F_SET(sp, S_FSWITCH);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* ex_rew -- :rew
|
||||
* Re-edit the list of files.
|
||||
*/
|
||||
int
|
||||
ex_rew(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
FREF *frp;
|
||||
|
||||
/*
|
||||
* !!!
|
||||
* Historic practice -- you can rewind to the current file.
|
||||
*/
|
||||
if (sp->argv == NULL) {
|
||||
msgq(sp, M_ERR, "No previous files to rewind");
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (file_m1(sp, ep, F_ISSET(cmdp, E_FORCE), FS_ALL | FS_POSSIBLE))
|
||||
return (1);
|
||||
|
||||
/*
|
||||
* !!!
|
||||
* Historic practice, start at the beginning of the file.
|
||||
*/
|
||||
for (frp = sp->frefq.cqh_first;
|
||||
frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next)
|
||||
F_CLR(frp, FR_CURSORSET | FR_FNONBLANK);
|
||||
|
||||
/* Switch to the first one. */
|
||||
sp->cargv = sp->argv;
|
||||
if ((frp = file_add(sp, *sp->cargv)) == NULL)
|
||||
return (1);
|
||||
if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE)))
|
||||
return (1);
|
||||
|
||||
F_SET(sp, S_FSWITCH);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* ex_args -- :args
|
||||
* Display the list of files.
|
||||
*/
|
||||
int
|
||||
ex_args(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
int cnt, col, len, sep;
|
||||
char **ap;
|
||||
|
||||
if (sp->argv == NULL) {
|
||||
(void)ex_printf(EXCOOKIE, "No file list to display.\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
col = len = sep = 0;
|
||||
for (cnt = 1, ap = sp->argv; *ap != NULL; ++ap) {
|
||||
col += len = strlen(*ap) + sep + (ap == sp->cargv ? 2 : 0);
|
||||
if (col >= sp->cols - 1) {
|
||||
col = len;
|
||||
sep = 0;
|
||||
(void)ex_printf(EXCOOKIE, "\n");
|
||||
} else if (cnt != 1) {
|
||||
sep = 1;
|
||||
(void)ex_printf(EXCOOKIE, " ");
|
||||
}
|
||||
++cnt;
|
||||
|
||||
if (ap == sp->cargv)
|
||||
(void)ex_printf(EXCOOKIE, "[%s]", *ap);
|
||||
else
|
||||
(void)ex_printf(EXCOOKIE, "%s", *ap);
|
||||
}
|
||||
(void)ex_printf(EXCOOKIE, "\n");
|
||||
return (0);
|
||||
}
|
609
usr.bin/vi/ex/ex_argv.c
Normal file
609
usr.bin/vi/ex/ex_argv.c
Normal file
@ -0,0 +1,609 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_argv.c 8.36 (Berkeley) 8/4/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"
|
||||
#include "excmd.h"
|
||||
|
||||
static int argv_alloc __P((SCR *, size_t));
|
||||
static int argv_fexp __P((SCR *, EXCMDARG *,
|
||||
char *, size_t, char *, size_t *, char **, size_t *, int));
|
||||
static int argv_sexp __P((SCR *, char **, size_t *, size_t *));
|
||||
|
||||
/*
|
||||
* argv_init --
|
||||
* Build a prototype arguments list.
|
||||
*/
|
||||
int
|
||||
argv_init(sp, ep, excp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *excp;
|
||||
{
|
||||
EX_PRIVATE *exp;
|
||||
|
||||
exp = EXP(sp);
|
||||
exp->argsoff = 0;
|
||||
argv_alloc(sp, 1);
|
||||
|
||||
excp->argv = exp->args;
|
||||
excp->argc = exp->argsoff;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* argv_exp0 --
|
||||
* Append a string to the argument list.
|
||||
*/
|
||||
int
|
||||
argv_exp0(sp, ep, excp, cmd, cmdlen)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *excp;
|
||||
char *cmd;
|
||||
size_t cmdlen;
|
||||
{
|
||||
EX_PRIVATE *exp;
|
||||
|
||||
exp = EXP(sp);
|
||||
argv_alloc(sp, cmdlen);
|
||||
memmove(exp->args[exp->argsoff]->bp, cmd, cmdlen);
|
||||
exp->args[exp->argsoff]->bp[cmdlen] = '\0';
|
||||
exp->args[exp->argsoff]->len = cmdlen;
|
||||
++exp->argsoff;
|
||||
excp->argv = exp->args;
|
||||
excp->argc = exp->argsoff;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* argv_exp1 --
|
||||
* Do file name expansion on a string, and append it to the
|
||||
* argument list.
|
||||
*/
|
||||
int
|
||||
argv_exp1(sp, ep, excp, cmd, cmdlen, is_bang)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *excp;
|
||||
char *cmd;
|
||||
size_t cmdlen;
|
||||
int is_bang;
|
||||
{
|
||||
EX_PRIVATE *exp;
|
||||
size_t blen, len;
|
||||
char *bp, *p, *t;
|
||||
|
||||
GET_SPACE_RET(sp, bp, blen, 512);
|
||||
|
||||
len = 0;
|
||||
exp = EXP(sp);
|
||||
if (argv_fexp(sp, excp, cmd, cmdlen, bp, &len, &bp, &blen, is_bang)) {
|
||||
FREE_SPACE(sp, bp, blen);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* If it's empty, we're done. */
|
||||
if (len != 0) {
|
||||
for (p = bp, t = bp + len; p < t; ++p)
|
||||
if (!isblank(*p))
|
||||
break;
|
||||
if (p == t)
|
||||
goto ret;
|
||||
} else
|
||||
goto ret;
|
||||
|
||||
(void)argv_exp0(sp, ep, excp, bp, len);
|
||||
|
||||
ret: FREE_SPACE(sp, bp, blen);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* argv_exp2 --
|
||||
* Do file name and shell expansion on a string, and append it to
|
||||
* the argument list.
|
||||
*/
|
||||
int
|
||||
argv_exp2(sp, ep, excp, cmd, cmdlen, is_bang)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *excp;
|
||||
char *cmd;
|
||||
size_t cmdlen;
|
||||
int is_bang;
|
||||
{
|
||||
size_t blen, len, n;
|
||||
int rval;
|
||||
char *bp, *mp, *p;
|
||||
|
||||
GET_SPACE_RET(sp, bp, blen, 512);
|
||||
|
||||
#define SHELLECHO "echo "
|
||||
#define SHELLOFFSET (sizeof(SHELLECHO) - 1)
|
||||
memmove(bp, SHELLECHO, SHELLOFFSET);
|
||||
p = bp + SHELLOFFSET;
|
||||
len = SHELLOFFSET;
|
||||
|
||||
#if defined(DEBUG) && 0
|
||||
TRACE(sp, "file_argv: {%.*s}\n", (int)cmdlen, cmd);
|
||||
#endif
|
||||
|
||||
if (argv_fexp(sp, excp, cmd, cmdlen, p, &len, &bp, &blen, is_bang)) {
|
||||
rval = 1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
#if defined(DEBUG) && 0
|
||||
TRACE(sp, "before shell: %d: {%s}\n", len, bp);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Do shell word expansion -- it's very, very hard to figure out what
|
||||
* magic characters the user's shell expects. Historically, it was a
|
||||
* union of v7 shell and csh meta characters. We match that practice
|
||||
* by default, so ":read \%" tries to read a file named '%'. It would
|
||||
* make more sense to pass any special characters through the shell,
|
||||
* but then, if your shell was csh, the above example will behave
|
||||
* differently in nvi than in vi. If you want to get other characters
|
||||
* passed through to your shell, change the "meta" option.
|
||||
*
|
||||
* To avoid a function call per character, we do a first pass through
|
||||
* the meta characters looking for characters that aren't expected
|
||||
* to be there.
|
||||
*/
|
||||
for (p = mp = O_STR(sp, O_META); *p != '\0'; ++p)
|
||||
if (isblank(*p) || isalnum(*p))
|
||||
break;
|
||||
if (*p != '\0') {
|
||||
for (p = bp, n = len; n > 0; --n, ++p)
|
||||
if (strchr(mp, *p) != NULL)
|
||||
break;
|
||||
} else
|
||||
for (p = bp, n = len; n > 0; --n, ++p)
|
||||
if (!isblank(*p) &&
|
||||
!isalnum(*p) && strchr(mp, *p) != NULL)
|
||||
break;
|
||||
if (n > 0) {
|
||||
if (argv_sexp(sp, &bp, &blen, &len)) {
|
||||
rval = 1;
|
||||
goto err;
|
||||
}
|
||||
p = bp;
|
||||
} else {
|
||||
p = bp + SHELLOFFSET;
|
||||
len -= SHELLOFFSET;
|
||||
}
|
||||
|
||||
#if defined(DEBUG) && 0
|
||||
TRACE(sp, "after shell: %d: {%s}\n", len, bp);
|
||||
#endif
|
||||
|
||||
rval = argv_exp3(sp, ep, excp, p, len);
|
||||
|
||||
err: FREE_SPACE(sp, bp, blen);
|
||||
return (rval);
|
||||
}
|
||||
|
||||
/*
|
||||
* argv_exp3 --
|
||||
* Take a string and break it up into an argv, which is appended
|
||||
* to the argument list.
|
||||
*/
|
||||
int
|
||||
argv_exp3(sp, ep, excp, cmd, cmdlen)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *excp;
|
||||
char *cmd;
|
||||
size_t cmdlen;
|
||||
{
|
||||
EX_PRIVATE *exp;
|
||||
size_t len;
|
||||
int ch, off;
|
||||
char *ap, *p;
|
||||
|
||||
for (exp = EXP(sp); cmdlen > 0; ++exp->argsoff) {
|
||||
/* Skip any leading whitespace. */
|
||||
for (; cmdlen > 0; --cmdlen, ++cmd) {
|
||||
ch = *cmd;
|
||||
if (!isblank(ch))
|
||||
break;
|
||||
}
|
||||
if (cmdlen == 0)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Determine the length of this whitespace delimited
|
||||
* argument.
|
||||
*
|
||||
* QUOTING NOTE:
|
||||
*
|
||||
* Skip any character preceded by the user's quoting
|
||||
* character.
|
||||
*/
|
||||
for (ap = cmd, len = 0; cmdlen > 0; ++cmd, --cmdlen, ++len) {
|
||||
ch = *cmd;
|
||||
if (IS_ESCAPE(sp, ch) && cmdlen > 1) {
|
||||
++cmd;
|
||||
--cmdlen;
|
||||
} else if (isblank(ch))
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy the argument into place.
|
||||
*
|
||||
* QUOTING NOTE:
|
||||
*
|
||||
* Lose quote chars.
|
||||
*/
|
||||
argv_alloc(sp, len);
|
||||
off = exp->argsoff;
|
||||
exp->args[off]->len = len;
|
||||
for (p = exp->args[off]->bp; len > 0; --len, *p++ = *ap++)
|
||||
if (IS_ESCAPE(sp, *ap))
|
||||
++ap;
|
||||
*p = '\0';
|
||||
}
|
||||
excp->argv = exp->args;
|
||||
excp->argc = exp->argsoff;
|
||||
|
||||
#if defined(DEBUG) && 0
|
||||
for (cnt = 0; cnt < exp->argsoff; ++cnt)
|
||||
TRACE(sp, "arg %d: {%s}\n", cnt, exp->argv[cnt]);
|
||||
#endif
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* argv_fexp --
|
||||
* Do file name and bang command expansion.
|
||||
*/
|
||||
static int
|
||||
argv_fexp(sp, excp, cmd, cmdlen, p, lenp, bpp, blenp, is_bang)
|
||||
SCR *sp;
|
||||
EXCMDARG *excp;
|
||||
char *cmd, *p, **bpp;
|
||||
size_t cmdlen, *lenp, *blenp;
|
||||
int is_bang;
|
||||
{
|
||||
EX_PRIVATE *exp;
|
||||
char *bp, *t;
|
||||
size_t blen, len, tlen;
|
||||
|
||||
/* Replace file name characters. */
|
||||
for (bp = *bpp, blen = *blenp, len = *lenp; cmdlen > 0; --cmdlen, ++cmd)
|
||||
switch (*cmd) {
|
||||
case '!':
|
||||
if (!is_bang)
|
||||
goto ins_ch;
|
||||
exp = EXP(sp);
|
||||
if (exp->lastbcomm == NULL) {
|
||||
msgq(sp, M_ERR,
|
||||
"No previous command to replace \"!\"");
|
||||
return (1);
|
||||
}
|
||||
len += tlen = strlen(exp->lastbcomm);
|
||||
ADD_SPACE_RET(sp, bp, blen, len);
|
||||
memmove(p, exp->lastbcomm, tlen);
|
||||
p += tlen;
|
||||
F_SET(excp, E_MODIFY);
|
||||
break;
|
||||
case '%':
|
||||
if ((t = sp->frp->name) == NULL) {
|
||||
msgq(sp, M_ERR,
|
||||
"No filename to substitute for %%");
|
||||
return (1);
|
||||
}
|
||||
tlen = strlen(t);
|
||||
len += tlen;
|
||||
ADD_SPACE_RET(sp, bp, blen, len);
|
||||
memmove(p, t, tlen);
|
||||
p += tlen;
|
||||
F_SET(excp, E_MODIFY);
|
||||
break;
|
||||
case '#':
|
||||
if ((t = sp->alt_name) == NULL) {
|
||||
msgq(sp, M_ERR,
|
||||
"No filename to substitute for #");
|
||||
return (1);
|
||||
}
|
||||
len += tlen = strlen(t);
|
||||
ADD_SPACE_RET(sp, bp, blen, len);
|
||||
memmove(p, t, tlen);
|
||||
p += tlen;
|
||||
F_SET(excp, E_MODIFY);
|
||||
break;
|
||||
case '\\':
|
||||
/*
|
||||
* QUOTING NOTE:
|
||||
*
|
||||
* Strip any backslashes that protected the file
|
||||
* expansion characters.
|
||||
*/
|
||||
if (cmdlen > 1 && (cmd[1] == '%' || cmd[1] == '#')) {
|
||||
++cmd;
|
||||
--cmdlen;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
ins_ch: ++len;
|
||||
ADD_SPACE_RET(sp, bp, blen, len);
|
||||
*p++ = *cmd;
|
||||
}
|
||||
|
||||
/* Nul termination. */
|
||||
++len;
|
||||
ADD_SPACE_RET(sp, bp, blen, len);
|
||||
*p = '\0';
|
||||
|
||||
/* Return the new string length, buffer, buffer length. */
|
||||
*lenp = len - 1;
|
||||
*bpp = bp;
|
||||
*blenp = blen;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* argv_alloc --
|
||||
* Make more space for arguments.
|
||||
*/
|
||||
static int
|
||||
argv_alloc(sp, len)
|
||||
SCR *sp;
|
||||
size_t len;
|
||||
{
|
||||
ARGS *ap;
|
||||
EX_PRIVATE *exp;
|
||||
int cnt, off;
|
||||
|
||||
/*
|
||||
* Allocate room for another argument, always leaving
|
||||
* enough room for an ARGS structure with a length of 0.
|
||||
*/
|
||||
#define INCREMENT 20
|
||||
exp = EXP(sp);
|
||||
off = exp->argsoff;
|
||||
if (exp->argscnt == 0 || off + 2 >= exp->argscnt - 1) {
|
||||
cnt = exp->argscnt + INCREMENT;
|
||||
REALLOC(sp, exp->args, ARGS **, cnt * sizeof(ARGS *));
|
||||
if (exp->args == NULL) {
|
||||
(void)argv_free(sp);
|
||||
goto mem;
|
||||
}
|
||||
memset(&exp->args[off], 0, INCREMENT * sizeof(ARGS *));
|
||||
exp->argscnt = cnt;
|
||||
}
|
||||
|
||||
/* First argument. */
|
||||
if (exp->args[off] == NULL) {
|
||||
CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS));
|
||||
if (exp->args[off] == NULL)
|
||||
goto mem;
|
||||
}
|
||||
|
||||
/* First argument buffer. */
|
||||
ap = exp->args[off];
|
||||
ap->len = 0;
|
||||
if (ap->blen < len + 1) {
|
||||
ap->blen = len + 1;
|
||||
REALLOC(sp, ap->bp, CHAR_T *, ap->blen * sizeof(CHAR_T));
|
||||
if (ap->bp == NULL) {
|
||||
ap->bp = NULL;
|
||||
ap->blen = 0;
|
||||
F_CLR(ap, A_ALLOCATED);
|
||||
mem: msgq(sp, M_SYSERR, NULL);
|
||||
return (1);
|
||||
}
|
||||
F_SET(ap, A_ALLOCATED);
|
||||
}
|
||||
|
||||
/* Second argument. */
|
||||
if (exp->args[++off] == NULL) {
|
||||
CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS));
|
||||
if (exp->args[off] == NULL)
|
||||
goto mem;
|
||||
}
|
||||
/* 0 length serves as end-of-argument marker. */
|
||||
exp->args[off]->len = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* argv_free --
|
||||
* Free up argument structures.
|
||||
*/
|
||||
int
|
||||
argv_free(sp)
|
||||
SCR *sp;
|
||||
{
|
||||
EX_PRIVATE *exp;
|
||||
int off;
|
||||
|
||||
exp = EXP(sp);
|
||||
if (exp->args != NULL) {
|
||||
for (off = 0; off < exp->argscnt; ++off) {
|
||||
if (exp->args[off] == NULL)
|
||||
continue;
|
||||
if (F_ISSET(exp->args[off], A_ALLOCATED))
|
||||
free(exp->args[off]->bp);
|
||||
FREE(exp->args[off], sizeof(ARGS));
|
||||
}
|
||||
FREE(exp->args, exp->argscnt * sizeof(ARGS *));
|
||||
}
|
||||
exp->args = NULL;
|
||||
exp->argscnt = 0;
|
||||
exp->argsoff = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* argv_sexp --
|
||||
* Fork a shell, pipe a command through it, and read the output into
|
||||
* a buffer.
|
||||
*/
|
||||
static int
|
||||
argv_sexp(sp, bpp, blenp, lenp)
|
||||
SCR *sp;
|
||||
char **bpp;
|
||||
size_t *blenp, *lenp;
|
||||
{
|
||||
FILE *ifp;
|
||||
pid_t pid;
|
||||
size_t blen, len;
|
||||
int ch, rval, output[2];
|
||||
char *bp, *p, *sh, *sh_path;
|
||||
|
||||
bp = *bpp;
|
||||
blen = *blenp;
|
||||
|
||||
sh_path = O_STR(sp, O_SHELL);
|
||||
if ((sh = strrchr(sh_path, '/')) == NULL)
|
||||
sh = sh_path;
|
||||
else
|
||||
++sh;
|
||||
|
||||
/*
|
||||
* There are two different processes running through this code.
|
||||
* They are named the utility and the parent. The utility reads
|
||||
* from standard input and writes to the parent. The parent reads
|
||||
* from the utility and writes into the buffer. The parent reads
|
||||
* from output[0], and the utility writes to output[1].
|
||||
*/
|
||||
if (pipe(output) < 0) {
|
||||
msgq(sp, M_SYSERR, "pipe");
|
||||
return (1);
|
||||
}
|
||||
if ((ifp = fdopen(output[0], "r")) == NULL) {
|
||||
msgq(sp, M_SYSERR, "fdopen");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do the minimal amount of work possible, the shell is going
|
||||
* to run briefly and then exit. Hopefully.
|
||||
*/
|
||||
SIGBLOCK(sp->gp);
|
||||
switch (pid = vfork()) {
|
||||
case -1: /* Error. */
|
||||
SIGUNBLOCK(sp->gp);
|
||||
|
||||
msgq(sp, M_SYSERR, "vfork");
|
||||
err: (void)close(output[0]);
|
||||
(void)close(output[1]);
|
||||
return (1);
|
||||
case 0: /* Utility. */
|
||||
/* The utility has default signal behavior. */
|
||||
sig_end();
|
||||
|
||||
/* Redirect stdout/stderr to the write end of the pipe. */
|
||||
(void)dup2(output[1], STDOUT_FILENO);
|
||||
(void)dup2(output[1], STDERR_FILENO);
|
||||
|
||||
/* Close the utility's file descriptors. */
|
||||
(void)close(output[0]);
|
||||
(void)close(output[1]);
|
||||
|
||||
/* Assumes that all shells have -c. */
|
||||
execl(sh_path, sh, "-c", bp, NULL);
|
||||
msgq(sp, M_ERR,
|
||||
"Error: execl: %s: %s", sh_path, strerror(errno));
|
||||
_exit(127);
|
||||
default: /* Parent. */
|
||||
SIGUNBLOCK(sp->gp);
|
||||
|
||||
/* Close the pipe end the parent won't use. */
|
||||
(void)close(output[1]);
|
||||
break;
|
||||
}
|
||||
|
||||
rval = 0;
|
||||
|
||||
/*
|
||||
* Copy process output into a buffer.
|
||||
*
|
||||
* !!!
|
||||
* Historic vi apparently discarded leading \n and \r's from
|
||||
* the shell output stream. We don't on the grounds that any
|
||||
* shell that does that is broken.
|
||||
*/
|
||||
for (p = bp, len = 0, ch = EOF;
|
||||
(ch = getc(ifp)) != EOF; *p++ = ch, --blen, ++len)
|
||||
if (blen < 5) {
|
||||
ADD_SPACE_GOTO(sp, bp, blen, *blenp * 2);
|
||||
p = bp + len;
|
||||
blen = *blenp - len;
|
||||
}
|
||||
|
||||
/* Delete the final newline, nul terminate the string. */
|
||||
if (p > bp && (p[-1] == '\n' || p[-1] == '\r')) {
|
||||
--len;
|
||||
*--p = '\0';
|
||||
} else
|
||||
*p = '\0';
|
||||
*lenp = len;
|
||||
|
||||
if (ferror(ifp)) {
|
||||
msgq(sp, M_ERR, "I/O error: %s", sh);
|
||||
binc_err: rval = 1;
|
||||
}
|
||||
(void)fclose(ifp);
|
||||
|
||||
*bpp = bp; /* *blenp is already updated. */
|
||||
|
||||
/* Wait for the process. */
|
||||
return (proc_wait(sp, (long)pid, sh, 0) || rval);
|
||||
}
|
118
usr.bin/vi/ex/ex_at.c
Normal file
118
usr.bin/vi/ex/ex_at.c
Normal file
@ -0,0 +1,118 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_at.c 8.25 (Berkeley) 8/1/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"
|
||||
#include "excmd.h"
|
||||
|
||||
/*
|
||||
* ex_at -- :@[@ | buffer]
|
||||
* :*[* | buffer]
|
||||
*
|
||||
* Execute the contents of the buffer.
|
||||
*/
|
||||
int
|
||||
ex_at(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
CB *cbp;
|
||||
EX_PRIVATE *exp;
|
||||
TEXT *tp;
|
||||
int name;
|
||||
|
||||
exp = EXP(sp);
|
||||
|
||||
/*
|
||||
* !!!
|
||||
* Historically, [@*]<carriage-return> and [@*][@*] executed the most
|
||||
* recently executed buffer in ex mode. In vi mode, only @@ repeated
|
||||
* the last buffer. We change historic practice and make @* work from
|
||||
* vi mode as well, it's simpler and more consistent.
|
||||
*/
|
||||
name = F_ISSET(cmdp, E_BUFFER) ? cmdp->buffer : '@';
|
||||
if (name == '@' || name == '*') {
|
||||
if (!exp->at_lbuf_set) {
|
||||
msgq(sp, M_ERR, "No previous buffer to execute");
|
||||
return (1);
|
||||
}
|
||||
name = exp->at_lbuf;
|
||||
}
|
||||
|
||||
CBNAME(sp, cbp, name);
|
||||
if (cbp == NULL) {
|
||||
msgq(sp, M_ERR, "Buffer %s is empty", KEY_NAME(sp, name));
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Save for reuse. */
|
||||
exp->at_lbuf = name;
|
||||
exp->at_lbuf_set = 1;
|
||||
|
||||
/*
|
||||
* !!!
|
||||
* Historic practice is that if the buffer was cut in line mode,
|
||||
* <newlines> were appended to each line as it was pushed onto
|
||||
* the stack. If the buffer was cut in character mode, <newlines>
|
||||
* were appended to all lines but the last one.
|
||||
*/
|
||||
for (tp = cbp->textq.cqh_last;
|
||||
tp != (void *)&cbp->textq; tp = tp->q.cqe_prev)
|
||||
if ((F_ISSET(cbp, CB_LMODE) ||
|
||||
tp->q.cqe_next != (void *)&cbp->textq) &&
|
||||
term_push(sp, "\n", 1, 0) ||
|
||||
term_push(sp, tp->lb, tp->len, 0))
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
242
usr.bin/vi/ex/ex_bang.c
Normal file
242
usr.bin/vi/ex/ex_bang.c
Normal file
@ -0,0 +1,242 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_bang.c 8.33 (Berkeley) 8/14/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 "excmd.h"
|
||||
#include "../sex/sex_screen.h"
|
||||
|
||||
/*
|
||||
* ex_bang -- :[line [,line]] ! command
|
||||
*
|
||||
* Pass the rest of the line after the ! character to the program named by
|
||||
* the O_SHELL option.
|
||||
*
|
||||
* Historical vi did NOT do shell expansion on the arguments before passing
|
||||
* them, only file name expansion. This means that the O_SHELL program got
|
||||
* "$t" as an argument if that is what the user entered. Also, there's a
|
||||
* special expansion done for the bang command. Any exclamation points in
|
||||
* the user's argument are replaced by the last, expanded ! command.
|
||||
*
|
||||
* There's some fairly amazing slop in this routine to make the different
|
||||
* ways of getting here display the right things. It took a long time to
|
||||
* get it right (wrong?), so be careful.
|
||||
*/
|
||||
int
|
||||
ex_bang(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
enum filtertype ftype;
|
||||
ARGS *ap;
|
||||
EX_PRIVATE *exp;
|
||||
MARK rm;
|
||||
recno_t lno;
|
||||
size_t blen;
|
||||
int rval;
|
||||
char *bp, *msg;
|
||||
|
||||
ap = cmdp->argv[0];
|
||||
if (ap->len == 0) {
|
||||
msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Set the last bang command. */
|
||||
exp = EXP(sp);
|
||||
if (exp->lastbcomm != NULL)
|
||||
free(exp->lastbcomm);
|
||||
if ((exp->lastbcomm = strdup(ap->bp)) == NULL) {
|
||||
msgq(sp, M_SYSERR, NULL);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the command was modified by the expansion, we redisplay it.
|
||||
* Redisplaying it in vi mode is tricky, and handled separately
|
||||
* in each case below. If we're in ex mode, it's easy, so we just
|
||||
* do it here.
|
||||
*/
|
||||
bp = NULL;
|
||||
if (F_ISSET(cmdp, E_MODIFY) && !F_ISSET(sp, S_EXSILENT)) {
|
||||
if (IN_EX_MODE(sp)) {
|
||||
(void)ex_printf(EXCOOKIE, "!%s\n", ap->bp);
|
||||
(void)ex_fflush(EXCOOKIE);
|
||||
}
|
||||
/*
|
||||
* Vi: Display the command if modified. Historic vi displayed
|
||||
* the command if it was modified due to file name and/or bang
|
||||
* expansion. If piping lines, it was immediately overwritten
|
||||
* by any error or line change reporting. We don't the user to
|
||||
* have to page through the responses, so we only post it until
|
||||
* it's erased by something else. Otherwise, pass it on to the
|
||||
* ex_exec_proc routine to display after the screen has been
|
||||
* cleaned up.
|
||||
*/
|
||||
if (IN_VI_MODE(sp)) {
|
||||
GET_SPACE_RET(sp, bp, blen, ap->len + 3);
|
||||
bp[0] = '!';
|
||||
memmove(bp + 1, ap->bp, ap->len);
|
||||
bp[ap->len + 1] = '\n';
|
||||
bp[ap->len + 2] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If addresses were specified, pipe lines from the file through the
|
||||
* command.
|
||||
*
|
||||
* Historically, vi lines were replaced by both the stdout and stderr
|
||||
* lines of the command, but ex by only the stdout lines. This makes
|
||||
* no sense to me, so nvi makes it consistent for both, and matches
|
||||
* vi's historic behavior.
|
||||
*/
|
||||
if (cmdp->addrcnt != 0) {
|
||||
/* Autoprint is set historically, even if the command fails. */
|
||||
F_SET(exp, EX_AUTOPRINT);
|
||||
|
||||
/* Vi gets a busy message. */
|
||||
if (bp != NULL)
|
||||
(void)sp->s_busy(sp, bp);
|
||||
|
||||
/*
|
||||
* !!!
|
||||
* Historical vi permitted "!!" in an empty file. When it
|
||||
* happens, we get called with two addresses of 1,1 and a
|
||||
* bad attitude. The simple solution is to turn it into a
|
||||
* FILTER_READ operation, but that means that we don't put
|
||||
* an empty line into the default cut buffer as did historic
|
||||
* vi. Tough.
|
||||
*/
|
||||
ftype = FILTER;
|
||||
if (cmdp->addr1.lno == 1 && cmdp->addr2.lno == 1) {
|
||||
if (file_lline(sp, ep, &lno))
|
||||
return (1);
|
||||
if (lno == 0) {
|
||||
cmdp->addr1.lno = cmdp->addr2.lno = 0;
|
||||
ftype = FILTER_READ;
|
||||
}
|
||||
}
|
||||
rval = filtercmd(sp, ep,
|
||||
&cmdp->addr1, &cmdp->addr2, &rm, ap->bp, ftype);
|
||||
|
||||
/*
|
||||
* If in vi mode, move to the first nonblank.
|
||||
*
|
||||
* !!!
|
||||
* Historic vi wasn't consistent in this area -- if you used
|
||||
* a forward motion it moved to the first nonblank, but if you
|
||||
* did a backward motion it didn't. And, if you followed a
|
||||
* backward motion with a forward motion, it wouldn't move to
|
||||
* the nonblank for either. Going to the nonblank generally
|
||||
* seems more useful, so we do it.
|
||||
*/
|
||||
if (rval == 0) {
|
||||
sp->lno = rm.lno;
|
||||
if (IN_VI_MODE(sp)) {
|
||||
sp->cno = 0;
|
||||
(void)nonblank(sp, ep, sp->lno, &sp->cno);
|
||||
}
|
||||
}
|
||||
goto ret2;
|
||||
}
|
||||
|
||||
/*
|
||||
* If no addresses were specified, run the command. If the file
|
||||
* has been modified and autowrite is set, write the file back.
|
||||
* If the file has been modified, autowrite is not set and the
|
||||
* warn option is set, tell the user about the file.
|
||||
*/
|
||||
msg = NULL;
|
||||
if (F_ISSET(ep, F_MODIFIED))
|
||||
if (O_ISSET(sp, O_AUTOWRITE)) {
|
||||
if (file_write(sp, ep, NULL, NULL, NULL, FS_ALL)) {
|
||||
rval = 1;
|
||||
goto ret1;
|
||||
}
|
||||
} else if (O_ISSET(sp, O_WARN) && !F_ISSET(sp, S_EXSILENT))
|
||||
msg = "File modified since last write.\n";
|
||||
|
||||
/* Run the command. */
|
||||
rval = ex_exec_proc(sp, ap->bp, bp, msg);
|
||||
|
||||
/* Vi requires user permission to continue. */
|
||||
if (IN_VI_MODE(sp))
|
||||
F_SET(sp, S_CONTINUE);
|
||||
|
||||
ret2: if (IN_EX_MODE(sp)) {
|
||||
/*
|
||||
* Put ex error messages out so they aren't confused with
|
||||
* the autoprint output.
|
||||
*/
|
||||
if (rval)
|
||||
(void)sex_refresh(sp, sp->ep);
|
||||
|
||||
/* Ex terminates with a bang, even if the command fails. */
|
||||
if (!F_ISSET(sp, S_EXSILENT))
|
||||
(void)write(STDOUT_FILENO, "!\n", 2);
|
||||
}
|
||||
|
||||
/* Free the extra space. */
|
||||
ret1: if (bp != NULL)
|
||||
FREE_SPACE(sp, bp, blen);
|
||||
|
||||
/*
|
||||
* XXX
|
||||
* The ! commands never return an error, so that autoprint always
|
||||
* happens in the ex parser.
|
||||
*/
|
||||
return (0);
|
||||
}
|
223
usr.bin/vi/ex/ex_cd.c
Normal file
223
usr.bin/vi/ex/ex_cd.c
Normal file
@ -0,0 +1,223 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_cd.c 8.16 (Berkeley) 8/8/94";
|
||||
#endif /* not lint */
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <bitstring.h>
|
||||
#include <errno.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 "vi.h"
|
||||
#include "excmd.h"
|
||||
|
||||
/*
|
||||
* ex_cd -- :cd[!] [directory]
|
||||
* Change directories.
|
||||
*/
|
||||
int
|
||||
ex_cd(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
struct passwd *pw;
|
||||
ARGS *ap;
|
||||
CDPATH *cdp;
|
||||
char *dir; /* XXX END OF THE STACK, DON'T TRUST GETCWD. */
|
||||
char buf[MAXPATHLEN * 2];
|
||||
|
||||
/*
|
||||
* !!!
|
||||
* Historic practice is that the cd isn't attempted if the file has
|
||||
* been modified, unless its name begins with a leading '/' or the
|
||||
* force flag is set.
|
||||
*/
|
||||
if (F_ISSET(ep, F_MODIFIED) &&
|
||||
!F_ISSET(cmdp, E_FORCE) && sp->frp->name[0] != '/') {
|
||||
msgq(sp, M_ERR,
|
||||
"File modified since last complete write; write or use ! to override");
|
||||
return (1);
|
||||
}
|
||||
|
||||
switch (cmdp->argc) {
|
||||
case 0:
|
||||
/* If no argument, change to the user's home directory. */
|
||||
if ((dir = getenv("HOME")) == NULL) {
|
||||
if ((pw = getpwuid(getuid())) == NULL ||
|
||||
pw->pw_dir == NULL || pw->pw_dir[0] == '\0') {
|
||||
msgq(sp, M_ERR,
|
||||
"Unable to find home directory location");
|
||||
return (1);
|
||||
}
|
||||
dir = pw->pw_dir;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
dir = cmdp->argv[0]->bp;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Try the current directory first. */
|
||||
if (!chdir(dir))
|
||||
goto ret;
|
||||
|
||||
/*
|
||||
* If moving to the user's home directory, or, the path begins with
|
||||
* "/", "./" or "../", it's the only place we try.
|
||||
*/
|
||||
if (cmdp->argc == 0 ||
|
||||
(ap = cmdp->argv[0])->bp[0] == '/' ||
|
||||
ap->len == 1 && ap->bp[0] == '.' ||
|
||||
ap->len >= 2 && ap->bp[0] == '.' && ap->bp[1] == '.' &&
|
||||
(ap->bp[2] == '/' || ap->bp[2] == '\0'))
|
||||
goto err;
|
||||
|
||||
/* If the user has a CDPATH variable, try its elements. */
|
||||
for (cdp = EXP(sp)->cdq.tqh_first; cdp != NULL; cdp = cdp->q.tqe_next) {
|
||||
(void)snprintf(buf, sizeof(buf), "%s/%s", cdp->path, dir);
|
||||
if (!chdir(buf)) {
|
||||
ret: if (getcwd(buf, sizeof(buf)) != NULL)
|
||||
msgq(sp, M_INFO, "New directory: %s", buf);
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
err: msgq(sp, M_SYSERR, "%s", dir);
|
||||
return (1);
|
||||
}
|
||||
|
||||
#define FREE_CDPATH(cdp) { \
|
||||
TAILQ_REMOVE(&exp->cdq, (cdp), q); \
|
||||
free((cdp)->path); \
|
||||
FREE((cdp), sizeof(CDPATH)); \
|
||||
}
|
||||
/*
|
||||
* ex_cdalloc --
|
||||
* Create a new list of cd paths.
|
||||
*/
|
||||
int
|
||||
ex_cdalloc(sp, str)
|
||||
SCR *sp;
|
||||
char *str;
|
||||
{
|
||||
EX_PRIVATE *exp;
|
||||
CDPATH *cdp;
|
||||
size_t len;
|
||||
int founddot;
|
||||
char *p, *t;
|
||||
|
||||
/* Free current queue. */
|
||||
exp = EXP(sp);
|
||||
while ((cdp = exp->cdq.tqh_first) != NULL)
|
||||
FREE_CDPATH(cdp);
|
||||
|
||||
/*
|
||||
* Create new queue. The CDPATH environmental variable (and the
|
||||
* user's manual entry) are delimited by colon characters.
|
||||
*/
|
||||
for (p = t = str, founddot = 0;; ++p) {
|
||||
if (*p == '\0' || *p == ':') {
|
||||
/*
|
||||
* Empty strings specify ".". The only way to get an
|
||||
* empty string is a leading colon, colons in a row,
|
||||
* or a trailing colon. Or, to put it the other way,
|
||||
* if the the length is zero, then it's either ":XXX",
|
||||
* "XXX::XXXX" , "XXX:", or "", and the only failure
|
||||
* mode is the last one. Note, the string ":" gives
|
||||
* us two entries of '.', so we only include one of
|
||||
* them.
|
||||
*/
|
||||
if ((len = p - t) == 0) {
|
||||
if (p == str && *p == '\0')
|
||||
break;
|
||||
if (founddot) {
|
||||
if (*p == '\0')
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
len = 1;
|
||||
t = ".";
|
||||
founddot = 1;
|
||||
}
|
||||
MALLOC_RET(sp, cdp, CDPATH *, sizeof(CDPATH));
|
||||
MALLOC(sp, cdp->path, char *, len + 1);
|
||||
if (cdp->path == NULL) {
|
||||
free(cdp);
|
||||
return (1);
|
||||
}
|
||||
memmove(cdp->path, t, len);
|
||||
cdp->path[len] = '\0';
|
||||
TAILQ_INSERT_TAIL(&exp->cdq, cdp, q);
|
||||
t = p + 1;
|
||||
}
|
||||
if (*p == '\0')
|
||||
break;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
/* Free previous queue. */
|
||||
/*
|
||||
* ex_cdfree --
|
||||
* Free the cd path list.
|
||||
*/
|
||||
int
|
||||
ex_cdfree(sp)
|
||||
SCR *sp;
|
||||
{
|
||||
EX_PRIVATE *exp;
|
||||
CDPATH *cdp;
|
||||
|
||||
/* Free up cd path information. */
|
||||
exp = EXP(sp);
|
||||
while ((cdp = exp->cdq.tqh_first) != NULL)
|
||||
FREE_CDPATH(cdp);
|
||||
return (0);
|
||||
}
|
92
usr.bin/vi/ex/ex_delete.c
Normal file
92
usr.bin/vi/ex/ex_delete.c
Normal file
@ -0,0 +1,92 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_delete.c 8.12 (Berkeley) 8/5/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 <termios.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include <db.h>
|
||||
#include <regex.h>
|
||||
|
||||
#include "vi.h"
|
||||
#include "excmd.h"
|
||||
|
||||
/*
|
||||
* ex_delete: [line [,line]] d[elete] [buffer] [count] [flags]
|
||||
*
|
||||
* Delete lines from the file.
|
||||
*/
|
||||
int
|
||||
ex_delete(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
recno_t lno;
|
||||
|
||||
/*
|
||||
* !!!
|
||||
* Historically, lines deleted in ex were not placed in the numeric
|
||||
* buffers. We follow historic practice so that we don't overwrite
|
||||
* vi buffers accidentally.
|
||||
*/
|
||||
if (cut(sp, ep,
|
||||
F_ISSET(cmdp, E_BUFFER) ? &cmdp->buffer : NULL,
|
||||
&cmdp->addr1, &cmdp->addr2, CUT_LINEMODE))
|
||||
return (1);
|
||||
|
||||
/* Delete the lines. */
|
||||
if (delete(sp, ep, &cmdp->addr1, &cmdp->addr2, 1))
|
||||
return (1);
|
||||
|
||||
/* Set the cursor to the line after the last line deleted. */
|
||||
sp->lno = cmdp->addr1.lno;
|
||||
|
||||
/* Or the last line in the file if deleted to the end of the file. */
|
||||
if (file_lline(sp, ep, &lno))
|
||||
return (1);
|
||||
if (sp->lno > lno)
|
||||
sp->lno = lno;
|
||||
return (0);
|
||||
}
|
324
usr.bin/vi/ex/ex_digraph.c
Normal file
324
usr.bin/vi/ex/ex_digraph.c
Normal file
@ -0,0 +1,324 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_digraph.c 8.6 (Berkeley) 3/25/94";
|
||||
#endif /* not lint */
|
||||
|
||||
#ifndef NO_DIGRAPH
|
||||
#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 "compat.h"
|
||||
#include <curses.h>
|
||||
#include <db.h>
|
||||
#include <regex.h>
|
||||
|
||||
#include "vi.h"
|
||||
#include "excmd.h"
|
||||
|
||||
static void do_digraph __P((SCR *, EXF *, int, u_char *));
|
||||
|
||||
/* This stuff is used to build the default digraphs table. */
|
||||
static u_char digtable[][4] = {
|
||||
# ifdef CS_IBMPC
|
||||
"C,\200", "u\"\1", "e'\2", "a^\3",
|
||||
"a\"\4", "a`\5", "a@\6", "c,\7",
|
||||
"e^\10", "e\"\211", "e`\12", "i\"\13",
|
||||
"i^\14", "i`\15", "A\"\16", "A@\17",
|
||||
"E'\20", "ae\21", "AE\22", "o^\23",
|
||||
"o\"\24", "o`\25", "u^\26", "u`\27",
|
||||
"y\"\30", "O\"\31", "U\"\32", "a'\240",
|
||||
"i'!", "o'\"", "u'#", "n~$",
|
||||
"N~%", "a-&", "o-'", "~?(",
|
||||
"~!-", "\"<.", "\">/",
|
||||
# ifdef CS_SPECIAL
|
||||
"2/+", "4/,", "^+;", "^q<",
|
||||
"^c=", "^r>", "^t?", "pp]",
|
||||
"^^^", "oo_", "*a`", "*ba",
|
||||
"*pc", "*Sd", "*se", "*uf",
|
||||
"*tg", "*Ph", "*Ti", "*Oj",
|
||||
"*dk", "*Hl", "*hm", "*En",
|
||||
"*No", "eqp", "pmq", "ger",
|
||||
"les", "*It", "*iu", "*/v",
|
||||
"*=w", "sq{", "^n|", "^2}",
|
||||
"^3~", "^_\377",
|
||||
# endif /* CS_SPECIAL */
|
||||
# endif /* CS_IBMPC */
|
||||
# ifdef CS_LATIN1
|
||||
"~!!", "a-*", "\">+", "o-:",
|
||||
"\"<>", "~??",
|
||||
|
||||
"A`@", "A'A", "A^B", "A~C",
|
||||
"A\"D", "A@E", "AEF", "C,G",
|
||||
"E`H", "E'I", "E^J", "E\"K",
|
||||
"I`L", "I'M", "I^N", "I\"O",
|
||||
"-DP", "N~Q", "O`R", "O'S",
|
||||
"O^T", "O~U", "O\"V", "O/X",
|
||||
"U`Y", "U'Z", "U^[", "U\"\\",
|
||||
"Y'_",
|
||||
|
||||
"a``", "a'a", "a^b", "a~c",
|
||||
"a\"d", "a@e", "aef", "c,g",
|
||||
"e`h", "e'i", "e^j", "e\"k",
|
||||
"i`l", "i'm", "i^n", "i\"o",
|
||||
"-dp", "n~q", "o`r", "o's",
|
||||
"o^t", "o~u", "o\"v", "o/x",
|
||||
"u`y", "u'z", "u^{", "u\"|",
|
||||
"y'~",
|
||||
# endif /* CS_LATIN1 */
|
||||
""
|
||||
};
|
||||
|
||||
int
|
||||
digraph_init(sp)
|
||||
SCR *sp;
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; *digtable[i]; i++)
|
||||
do_digraph(sp, NULL, 0, digtable[i]);
|
||||
do_digraph(sp, NULL, 0, NULL);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
ex_digraph(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
do_digraph(sp, ep, F_ISSET(cmdp, E_FORCE), cmdp->argv[0]->bp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static struct _DIG
|
||||
{
|
||||
struct _DIG *next;
|
||||
char key1;
|
||||
char key2;
|
||||
char dig;
|
||||
char save;
|
||||
} *digs;
|
||||
|
||||
int
|
||||
digraph(sp, key1, key2)
|
||||
SCR *sp;
|
||||
char key1; /* the underlying character */
|
||||
char key2; /* the second character */
|
||||
{
|
||||
int new_key;
|
||||
register struct _DIG *dp;
|
||||
|
||||
/* if digraphs are disabled, then just return the new char */
|
||||
if (O_ISSET(sp, O_DIGRAPH))
|
||||
{
|
||||
return key2;
|
||||
}
|
||||
|
||||
/* remember the new key, so we can return it if this isn't a digraph */
|
||||
new_key = key2;
|
||||
|
||||
/* sort key1 and key2, so that their original order won't matter */
|
||||
if (key1 > key2)
|
||||
{
|
||||
key2 = key1;
|
||||
key1 = new_key;
|
||||
}
|
||||
|
||||
/* scan through the digraph chart */
|
||||
for (dp = digs;
|
||||
dp && (dp->key1 != key1 || dp->key2 != key2);
|
||||
dp = dp->next)
|
||||
{
|
||||
}
|
||||
|
||||
/* if this combination isn't in there, just use the new key */
|
||||
if (!dp)
|
||||
{
|
||||
return new_key;
|
||||
}
|
||||
|
||||
/* else use the digraph key */
|
||||
return dp->dig;
|
||||
}
|
||||
|
||||
/* this function lists or defines digraphs */
|
||||
static void
|
||||
do_digraph(sp, ep, bang, extra)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
int bang;
|
||||
u_char *extra;
|
||||
{
|
||||
int dig;
|
||||
register struct _DIG *dp;
|
||||
struct _DIG *prev;
|
||||
static int user_defined = 0; /* boolean: are all later digraphs user-defined? */
|
||||
char listbuf[8];
|
||||
|
||||
/* if "extra" is NULL, then we've reached the end of the built-ins */
|
||||
if (!extra)
|
||||
{
|
||||
user_defined = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* if no args, then display the existing digraphs */
|
||||
if (*extra < ' ')
|
||||
{
|
||||
listbuf[0] = listbuf[1] = listbuf[2] = listbuf[5] = ' ';
|
||||
listbuf[7] = '\0';
|
||||
for (dig = 0, dp = digs; dp; dp = dp->next)
|
||||
{
|
||||
if (dp->save || bang)
|
||||
{
|
||||
dig += 7;
|
||||
if (dig >= sp->cno)
|
||||
{
|
||||
addch('\n');
|
||||
refresh();
|
||||
dig = 7;
|
||||
}
|
||||
listbuf[3] = dp->key1;
|
||||
listbuf[4] = dp->key2;
|
||||
listbuf[6] = dp->dig;
|
||||
addstr(listbuf);
|
||||
}
|
||||
}
|
||||
addch('\n');
|
||||
refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
/* make sure we have at least two characters */
|
||||
if (!extra[1])
|
||||
{
|
||||
msgq(sp, M_ERR,
|
||||
"Digraphs must be composed of two characters");
|
||||
return;
|
||||
}
|
||||
|
||||
/* sort key1 and key2, so that their original order won't matter */
|
||||
if (extra[0] > extra[1])
|
||||
{
|
||||
dig = extra[0];
|
||||
extra[0] = extra[1];
|
||||
extra[1] = dig;
|
||||
}
|
||||
|
||||
/* locate the new digraph character */
|
||||
for (dig = 2; extra[dig] == ' ' || extra[dig] == '\t'; dig++)
|
||||
{
|
||||
}
|
||||
dig = extra[dig];
|
||||
if (!bang && dig)
|
||||
{
|
||||
dig |= 0x80;
|
||||
}
|
||||
|
||||
/* search for the digraph */
|
||||
for (prev = (struct _DIG *)0, dp = digs;
|
||||
dp && (dp->key1 != extra[0] || dp->key2 != extra[1]);
|
||||
prev = dp, dp = dp->next)
|
||||
{
|
||||
}
|
||||
|
||||
/* deleting the digraph? */
|
||||
if (!dig)
|
||||
{
|
||||
if (!dp)
|
||||
{
|
||||
#ifndef CRUNCH
|
||||
msgq(sp, M_ERR,
|
||||
"%c%c not a digraph", extra[0], extra[1]);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if (prev)
|
||||
prev->next = dp->next;
|
||||
else
|
||||
digs = dp->next;
|
||||
free(dp);
|
||||
return;
|
||||
}
|
||||
|
||||
/* if necessary, create a new digraph struct for the new digraph */
|
||||
if (dig && !dp)
|
||||
{
|
||||
MALLOC(sp, dp, struct _DIG *, sizeof(struct _DIG));
|
||||
if (dp == NULL)
|
||||
return;
|
||||
if (prev)
|
||||
prev->next = dp;
|
||||
else
|
||||
digs = dp;
|
||||
dp->next = (struct _DIG *)0;
|
||||
}
|
||||
|
||||
/* assign it the new digraph value */
|
||||
dp->key1 = extra[0];
|
||||
dp->key2 = extra[1];
|
||||
dp->dig = dig;
|
||||
dp->save = user_defined;
|
||||
}
|
||||
|
||||
void
|
||||
digraph_save(sp, fd)
|
||||
SCR *sp;
|
||||
int fd;
|
||||
{
|
||||
static char buf[] = "digraph! XX Y\n";
|
||||
register struct _DIG *dp;
|
||||
|
||||
for (dp = digs; dp; dp = dp->next)
|
||||
{
|
||||
if (dp->save)
|
||||
{
|
||||
buf[9] = dp->key1;
|
||||
buf[10] = dp->key2;
|
||||
buf[12] = dp->dig;
|
||||
write(fd, buf, (unsigned)14);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
169
usr.bin/vi/ex/ex_display.c
Normal file
169
usr.bin/vi/ex/ex_display.c
Normal file
@ -0,0 +1,169 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_display.c 8.21 (Berkeley) 8/3/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 <string.h>
|
||||
#include <termios.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include <db.h>
|
||||
#include <regex.h>
|
||||
|
||||
#include "vi.h"
|
||||
#include "tag.h"
|
||||
#include "excmd.h"
|
||||
|
||||
static int bdisplay __P((SCR *, EXF *));
|
||||
static void db __P((SCR *, CB *, CHAR_T *));
|
||||
|
||||
/*
|
||||
* ex_display -- :display b[uffers] | s[creens] | t[ags]
|
||||
*
|
||||
* Display buffers, tags or screens.
|
||||
*/
|
||||
int
|
||||
ex_display(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
switch (cmdp->argv[0]->bp[0]) {
|
||||
case 'b':
|
||||
#undef ARG
|
||||
#define ARG "buffers"
|
||||
if (cmdp->argv[0]->len >= sizeof(ARG) ||
|
||||
memcmp(cmdp->argv[0]->bp, ARG, cmdp->argv[0]->len))
|
||||
break;
|
||||
return (bdisplay(sp, ep));
|
||||
case 's':
|
||||
#undef ARG
|
||||
#define ARG "screens"
|
||||
if (cmdp->argv[0]->len >= sizeof(ARG) ||
|
||||
memcmp(cmdp->argv[0]->bp, ARG, cmdp->argv[0]->len))
|
||||
break;
|
||||
return (ex_sdisplay(sp, ep));
|
||||
case 't':
|
||||
#undef ARG
|
||||
#define ARG "tags"
|
||||
if (cmdp->argv[0]->len >= sizeof(ARG) ||
|
||||
memcmp(cmdp->argv[0]->bp, ARG, cmdp->argv[0]->len))
|
||||
break;
|
||||
return (ex_tagdisplay(sp, ep));
|
||||
}
|
||||
msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* bdisplay --
|
||||
*
|
||||
* Display buffers.
|
||||
*/
|
||||
static int
|
||||
bdisplay(sp, ep)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
{
|
||||
CB *cbp;
|
||||
|
||||
if (sp->gp->cutq.lh_first == NULL && sp->gp->dcbp == NULL) {
|
||||
(void)ex_printf(EXCOOKIE, "No cut buffers to display.\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Buffers can be infinitely long, make it interruptible. */
|
||||
F_SET(sp, S_INTERRUPTIBLE);
|
||||
|
||||
/* Display regular cut buffers. */
|
||||
for (cbp = sp->gp->cutq.lh_first; cbp != NULL; cbp = cbp->q.le_next) {
|
||||
if (isdigit(cbp->name))
|
||||
continue;
|
||||
if (cbp->textq.cqh_first != (void *)&cbp->textq)
|
||||
db(sp, cbp, NULL);
|
||||
if (INTERRUPTED(sp))
|
||||
return (0);
|
||||
}
|
||||
/* Display numbered buffers. */
|
||||
for (cbp = sp->gp->cutq.lh_first; cbp != NULL; cbp = cbp->q.le_next) {
|
||||
if (!isdigit(cbp->name))
|
||||
continue;
|
||||
if (cbp->textq.cqh_first != (void *)&cbp->textq)
|
||||
db(sp, cbp, NULL);
|
||||
if (INTERRUPTED(sp))
|
||||
return (0);
|
||||
}
|
||||
/* Display default buffer. */
|
||||
if ((cbp = sp->gp->dcbp) != NULL)
|
||||
db(sp, cbp, "default buffer");
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* db --
|
||||
* Display a buffer.
|
||||
*/
|
||||
static void
|
||||
db(sp, cbp, name)
|
||||
SCR *sp;
|
||||
CB *cbp;
|
||||
CHAR_T *name;
|
||||
{
|
||||
CHAR_T *p;
|
||||
TEXT *tp;
|
||||
size_t len;
|
||||
|
||||
(void)ex_printf(EXCOOKIE, "********** %s%s\n",
|
||||
name == NULL ? KEY_NAME(sp, cbp->name) : name,
|
||||
F_ISSET(cbp, CB_LMODE) ? " (line mode)" : " (character mode)");
|
||||
for (tp = cbp->textq.cqh_first;
|
||||
tp != (void *)&cbp->textq; tp = tp->q.cqe_next) {
|
||||
for (len = tp->len, p = tp->lb; len--; ++p) {
|
||||
(void)ex_printf(EXCOOKIE, "%s", KEY_NAME(sp, *p));
|
||||
if (INTERRUPTED(sp))
|
||||
return;
|
||||
}
|
||||
(void)ex_printf(EXCOOKIE, "\n");
|
||||
}
|
||||
}
|
122
usr.bin/vi/ex/ex_edit.c
Normal file
122
usr.bin/vi/ex/ex_edit.c
Normal file
@ -0,0 +1,122 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_edit.c 8.18 (Berkeley) 8/4/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"
|
||||
#include "excmd.h"
|
||||
|
||||
/*
|
||||
* ex_edit -- :e[dit][!] [+cmd] [file]
|
||||
* :vi[sual][!] [+cmd] [file]
|
||||
*
|
||||
* Edit a file; if none specified, re-edit the current file. The second
|
||||
* form of the command can only be executed while in vi mode. See the
|
||||
* hack in ex.c:ex_cmd().
|
||||
*
|
||||
* !!!
|
||||
* Historic vi didn't permit the '+' command form without specifying
|
||||
* a file name as well.
|
||||
*/
|
||||
int
|
||||
ex_edit(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
ARGS *ap;
|
||||
FREF *frp;
|
||||
|
||||
frp = sp->frp;
|
||||
switch (cmdp->argc) {
|
||||
case 0:
|
||||
/*
|
||||
* If the name has been changed, we edit that file, not the
|
||||
* original name. If the user was editing a temporary file,
|
||||
* create another one. The reason for this is that we do
|
||||
* special exit processing of temporary files, and reusing
|
||||
* them is tricky.
|
||||
*/
|
||||
if (F_ISSET(frp, FR_TMPFILE)) {
|
||||
if ((frp = file_add(sp, NULL)) == NULL)
|
||||
return (1);
|
||||
} else {
|
||||
if ((frp = file_add(sp, frp->name)) == NULL)
|
||||
return (1);
|
||||
set_alt_name(sp, sp->frp->name);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
ap = cmdp->argv[0];
|
||||
if ((frp = file_add(sp, ap->bp)) == NULL)
|
||||
return (1);
|
||||
set_alt_name(sp, ap->bp);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for modifications.
|
||||
*
|
||||
* !!!
|
||||
* Contrary to POSIX 1003.2-1992, autowrite did not affect :edit.
|
||||
*/
|
||||
if (file_m2(sp, ep, F_ISSET(cmdp, E_FORCE)))
|
||||
return (1);
|
||||
|
||||
/* Switch files. */
|
||||
if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE)))
|
||||
return (1);
|
||||
F_SET(sp, S_FSWITCH);
|
||||
return (0);
|
||||
}
|
86
usr.bin/vi/ex/ex_equal.c
Normal file
86
usr.bin/vi/ex/ex_equal.c
Normal file
@ -0,0 +1,86 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_equal.c 8.6 (Berkeley) 4/26/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 <termios.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include <db.h>
|
||||
#include <regex.h>
|
||||
|
||||
#include "vi.h"
|
||||
#include "excmd.h"
|
||||
|
||||
/*
|
||||
* ex_equal -- :address =
|
||||
*/
|
||||
int
|
||||
ex_equal(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
recno_t lno;
|
||||
|
||||
/*
|
||||
* Print out the line number matching the specified address,
|
||||
* or the number of the last line in the file if no address
|
||||
* specified.
|
||||
*
|
||||
* !!!
|
||||
* Historically, ":0=" displayed 0, and ":=" or ":1=" in an
|
||||
* empty file displayed 1. Until somebody complains loudly,
|
||||
* we're going to do it right. The tables in excmd.c permit
|
||||
* lno to get away with any address from 0 to the end of the
|
||||
* file, which, in an empty file, is 0.
|
||||
*/
|
||||
if (F_ISSET(cmdp, E_ADDRDEF)) {
|
||||
if (file_lline(sp, ep, &lno))
|
||||
return (1);
|
||||
} else
|
||||
lno = cmdp->addr1.lno;
|
||||
|
||||
(void)ex_printf(EXCOOKIE, "%ld\n", lno);
|
||||
return (0);
|
||||
}
|
79
usr.bin/vi/ex/ex_exit.c
Normal file
79
usr.bin/vi/ex/ex_exit.c
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.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)ex_exit.c 8.13 (Berkeley) 8/4/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 <termios.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include <db.h>
|
||||
#include <regex.h>
|
||||
|
||||
#include "vi.h"
|
||||
#include "excmd.h"
|
||||
|
||||
/*
|
||||
* ex_quit -- :quit[!]
|
||||
* Quit.
|
||||
*/
|
||||
int
|
||||
ex_quit(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
int force;
|
||||
|
||||
force = F_ISSET(cmdp, E_FORCE);
|
||||
|
||||
/* Check for modifications. */
|
||||
if (file_m2(sp, ep, force))
|
||||
return (1);
|
||||
|
||||
/* Check for more files to edit. */
|
||||
if (ex_ncheck(sp, force))
|
||||
return (1);
|
||||
|
||||
F_SET(sp, force ? S_EXIT_FORCE : S_EXIT);
|
||||
return (0);
|
||||
}
|
103
usr.bin/vi/ex/ex_file.c
Normal file
103
usr.bin/vi/ex/ex_file.c
Normal file
@ -0,0 +1,103 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_file.c 8.10 (Berkeley) 8/8/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"
|
||||
#include "excmd.h"
|
||||
|
||||
/*
|
||||
* ex_file -- :f[ile] [name]
|
||||
* Change the file's name and display the status line.
|
||||
*/
|
||||
int
|
||||
ex_file(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
CHAR_T *p;
|
||||
FREF *frp;
|
||||
|
||||
switch (cmdp->argc) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
frp = sp->frp;
|
||||
|
||||
/* Make sure can allocate enough space. */
|
||||
if ((p = v_strdup(sp,
|
||||
cmdp->argv[0]->bp, cmdp->argv[0]->len)) == NULL)
|
||||
return (1);
|
||||
|
||||
/* If already have a file name, it becomes the alternate. */
|
||||
if (!F_ISSET(frp, FR_TMPFILE))
|
||||
set_alt_name(sp, frp->name);
|
||||
|
||||
/* Free the previous name. */
|
||||
free(frp->name);
|
||||
frp->name = p;
|
||||
|
||||
/*
|
||||
* The read-only bit follows the file name; clear it.
|
||||
* The file has a real name, it's no longer a temporary.
|
||||
*/
|
||||
F_CLR(frp, FR_RDONLY | FR_TMPFILE);
|
||||
|
||||
/* Have to force a write if the file exists, next time. */
|
||||
F_SET(frp, FR_NAMECHANGE);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
return (msg_status(sp, ep, sp->lno, 1));
|
||||
}
|
400
usr.bin/vi/ex/ex_global.c
Normal file
400
usr.bin/vi/ex/ex_global.c
Normal file
@ -0,0 +1,400 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_global.c 8.41 (Berkeley) 8/9/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"
|
||||
#include "excmd.h"
|
||||
|
||||
enum which {GLOBAL, VGLOBAL};
|
||||
|
||||
static int global __P((SCR *, EXF *, EXCMDARG *, enum which));
|
||||
|
||||
/*
|
||||
* ex_global -- [line [,line]] g[lobal][!] /pattern/ [commands]
|
||||
* Exec on lines matching a pattern.
|
||||
*/
|
||||
int
|
||||
ex_global(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
return (global(sp, ep,
|
||||
cmdp, F_ISSET(cmdp, E_FORCE) ? VGLOBAL : GLOBAL));
|
||||
}
|
||||
|
||||
/*
|
||||
* ex_vglobal -- [line [,line]] v[global] /pattern/ [commands]
|
||||
* Exec on lines not matching a pattern.
|
||||
*/
|
||||
int
|
||||
ex_vglobal(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
return (global(sp, ep, cmdp, VGLOBAL));
|
||||
}
|
||||
|
||||
static int
|
||||
global(sp, ep, cmdp, cmd)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
enum which cmd;
|
||||
{
|
||||
MARK abs;
|
||||
RANGE *rp;
|
||||
EX_PRIVATE *exp;
|
||||
recno_t elno, lno;
|
||||
regmatch_t match[1];
|
||||
regex_t *re, lre;
|
||||
size_t clen, len;
|
||||
int delim, eval, reflags, replaced, rval;
|
||||
char *cb, *ptrn, *p, *t;
|
||||
|
||||
/*
|
||||
* Skip leading white space. Historic vi allowed any non-
|
||||
* alphanumeric to serve as the global command delimiter.
|
||||
*/
|
||||
for (p = cmdp->argv[0]->bp; isblank(*p); ++p);
|
||||
if (*p == '\0' || isalnum(*p)) {
|
||||
msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage);
|
||||
return (1);
|
||||
}
|
||||
delim = *p++;
|
||||
|
||||
/*
|
||||
* Get the pattern string, toss escaped characters.
|
||||
*
|
||||
* QUOTING NOTE:
|
||||
* Only toss an escaped character if it escapes a delimiter.
|
||||
*/
|
||||
for (ptrn = t = p;;) {
|
||||
if (p[0] == '\0' || p[0] == delim) {
|
||||
if (p[0] == delim)
|
||||
++p;
|
||||
/*
|
||||
* !!!
|
||||
* Nul terminate the pattern string -- it's passed
|
||||
* to regcomp which doesn't understand anything else.
|
||||
*/
|
||||
*t = '\0';
|
||||
break;
|
||||
}
|
||||
if (p[0] == '\\' && p[1] == delim)
|
||||
++p;
|
||||
*t++ = *p++;
|
||||
}
|
||||
|
||||
/* If the pattern string is empty, use the last one. */
|
||||
if (*ptrn == '\0') {
|
||||
if (!F_ISSET(sp, S_SRE_SET)) {
|
||||
msgq(sp, M_ERR, "No previous regular expression");
|
||||
return (1);
|
||||
}
|
||||
re = &sp->sre;
|
||||
} else {
|
||||
/* Set RE flags. */
|
||||
reflags = 0;
|
||||
if (O_ISSET(sp, O_EXTENDED))
|
||||
reflags |= REG_EXTENDED;
|
||||
if (O_ISSET(sp, O_IGNORECASE))
|
||||
reflags |= REG_ICASE;
|
||||
|
||||
/* Convert vi-style RE's to POSIX 1003.2 RE's. */
|
||||
if (re_conv(sp, &ptrn, &replaced))
|
||||
return (1);
|
||||
|
||||
/* Compile the RE. */
|
||||
re = &lre;
|
||||
eval = regcomp(re, ptrn, reflags);
|
||||
|
||||
/* Free up any allocated memory. */
|
||||
if (replaced)
|
||||
FREE_SPACE(sp, ptrn, 0);
|
||||
|
||||
if (eval) {
|
||||
re_error(sp, eval, re);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set saved RE. Historic practice is that
|
||||
* globals set direction as well as the RE.
|
||||
*/
|
||||
sp->sre = lre;
|
||||
sp->searchdir = FORWARD;
|
||||
F_SET(sp, S_SRE_SET);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a copy of the command string; the default command is print.
|
||||
* Don't worry about a set of <blank>s with no command, that will
|
||||
* default to print in the ex parser.
|
||||
*/
|
||||
if ((clen = strlen(p)) == 0) {
|
||||
p = "p";
|
||||
clen = 1;
|
||||
}
|
||||
MALLOC_RET(sp, cb, char *, clen);
|
||||
memmove(cb, p, clen);
|
||||
|
||||
/*
|
||||
* The global commands sets the substitute RE as well as
|
||||
* the everything-else RE.
|
||||
*/
|
||||
sp->subre = sp->sre;
|
||||
F_SET(sp, S_SUBRE_SET);
|
||||
|
||||
/* Set the global flag. */
|
||||
F_SET(sp, S_GLOBAL);
|
||||
|
||||
/* The global commands always set the previous context mark. */
|
||||
abs.lno = sp->lno;
|
||||
abs.cno = sp->cno;
|
||||
if (mark_set(sp, ep, ABSMARK1, &abs, 1))
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* For each line... The semantics of global matching are that we first
|
||||
* have to decide which lines are going to get passed to the command,
|
||||
* and then pass them to the command, ignoring other changes. There's
|
||||
* really no way to do this in a single pass, since arbitrary line
|
||||
* creation, deletion and movement can be done in the ex command. For
|
||||
* example, a good vi clone test is ":g/X/mo.-3", or "g/X/.,.+1d".
|
||||
* What we do is create linked list of lines that are tracked through
|
||||
* each ex command. There's a callback routine which the DB interface
|
||||
* routines call when a line is created or deleted. This doesn't help
|
||||
* the layering much.
|
||||
*/
|
||||
exp = EXP(sp);
|
||||
for (rval = 0, lno = cmdp->addr1.lno,
|
||||
elno = cmdp->addr2.lno; lno <= elno; ++lno) {
|
||||
/* Someone's unhappy, time to stop. */
|
||||
if (INTERRUPTED(sp))
|
||||
goto interrupted;
|
||||
|
||||
/* Get the line and search for a match. */
|
||||
if ((t = file_gline(sp, ep, lno, &len)) == NULL) {
|
||||
GETLINE_ERR(sp, lno);
|
||||
goto err;
|
||||
}
|
||||
match[0].rm_so = 0;
|
||||
match[0].rm_eo = len;
|
||||
switch(eval = regexec(re, t, 1, match, REG_STARTEND)) {
|
||||
case 0:
|
||||
if (cmd == VGLOBAL)
|
||||
continue;
|
||||
break;
|
||||
case REG_NOMATCH:
|
||||
if (cmd == GLOBAL)
|
||||
continue;
|
||||
break;
|
||||
default:
|
||||
re_error(sp, eval, re);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* If follows the last entry, extend the last entry's range. */
|
||||
if ((rp = exp->rangeq.cqh_last) != (void *)&exp->rangeq &&
|
||||
rp->stop == lno - 1) {
|
||||
++rp->stop;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Allocate a new range, and append it to the list. */
|
||||
CALLOC(sp, rp, RANGE *, 1, sizeof(RANGE));
|
||||
if (rp == NULL)
|
||||
goto err;
|
||||
rp->start = rp->stop = lno;
|
||||
CIRCLEQ_INSERT_TAIL(&exp->rangeq, rp, q);
|
||||
}
|
||||
|
||||
exp = EXP(sp);
|
||||
exp->range_lno = OOBLNO;
|
||||
for (;;) {
|
||||
/*
|
||||
* Start at the beginning of the range each time, it may have
|
||||
* been changed (or exhausted) if lines were inserted/deleted.
|
||||
*/
|
||||
if ((rp = exp->rangeq.cqh_first) == (void *)&exp->rangeq)
|
||||
break;
|
||||
if (rp->start > rp->stop) {
|
||||
CIRCLEQ_REMOVE(&exp->rangeq, exp->rangeq.cqh_first, q);
|
||||
free(rp);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute the command, setting the cursor to the line so that
|
||||
* relative addressing works. This means that the cursor moves
|
||||
* to the last line sent to the command, by default, even if
|
||||
* the command fails.
|
||||
*/
|
||||
exp->range_lno = sp->lno = rp->start++;
|
||||
if (ex_cmd(sp, ep, cb, clen, 0))
|
||||
goto err;
|
||||
|
||||
/* Someone's unhappy, time to stop. */
|
||||
if (INTERRUPTED(sp)) {
|
||||
interrupted: msgq(sp, M_INFO, "Interrupted");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the cursor to the new value, making sure it exists. */
|
||||
if (exp->range_lno != OOBLNO) {
|
||||
if (file_lline(sp, ep, &lno))
|
||||
return (1);
|
||||
sp->lno =
|
||||
lno < exp->range_lno ? (lno ? lno : 1) : exp->range_lno;
|
||||
}
|
||||
if (0) {
|
||||
err: rval = 1;
|
||||
}
|
||||
|
||||
/* Command we ran may have set the autoprint flag, clear it. */
|
||||
F_CLR(exp, EX_AUTOPRINT);
|
||||
|
||||
/* Clear the global flag. */
|
||||
F_CLR(sp, S_GLOBAL);
|
||||
|
||||
/* Free any remaining ranges and the command buffer. */
|
||||
while ((rp = exp->rangeq.cqh_first) != (void *)&exp->rangeq) {
|
||||
CIRCLEQ_REMOVE(&exp->rangeq, exp->rangeq.cqh_first, q);
|
||||
free(rp);
|
||||
}
|
||||
free(cb);
|
||||
return (rval);
|
||||
}
|
||||
|
||||
/*
|
||||
* global_insdel --
|
||||
* Update the ranges based on an insertion or deletion.
|
||||
*/
|
||||
void
|
||||
global_insdel(sp, ep, op, lno)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
enum operation op;
|
||||
recno_t lno;
|
||||
{
|
||||
EX_PRIVATE *exp;
|
||||
RANGE *nrp, *rp;
|
||||
|
||||
exp = EXP(sp);
|
||||
|
||||
switch (op) {
|
||||
case LINE_APPEND:
|
||||
return;
|
||||
case LINE_DELETE:
|
||||
for (rp = exp->rangeq.cqh_first;
|
||||
rp != (void *)&exp->rangeq; rp = nrp) {
|
||||
nrp = rp->q.cqe_next;
|
||||
/* If range less than the line, ignore it. */
|
||||
if (rp->stop < lno)
|
||||
continue;
|
||||
/* If range greater than the line, decrement range. */
|
||||
if (rp->start > lno) {
|
||||
--rp->start;
|
||||
--rp->stop;
|
||||
continue;
|
||||
}
|
||||
/* Lno is inside the range, decrement the end point. */
|
||||
if (rp->start > --rp->stop) {
|
||||
CIRCLEQ_REMOVE(&exp->rangeq, rp, q);
|
||||
free(rp);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LINE_INSERT:
|
||||
for (rp = exp->rangeq.cqh_first;
|
||||
rp != (void *)&exp->rangeq; rp = rp->q.cqe_next) {
|
||||
/* If range less than the line, ignore it. */
|
||||
if (rp->stop < lno)
|
||||
continue;
|
||||
/* If range greater than the line, increment range. */
|
||||
if (rp->start >= lno) {
|
||||
++rp->start;
|
||||
++rp->stop;
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* Lno is inside the range, so the range must be split.
|
||||
* Since we're inserting a new element, neither range
|
||||
* can be exhausted.
|
||||
*/
|
||||
CALLOC(sp, nrp, RANGE *, 1, sizeof(RANGE));
|
||||
if (nrp == NULL) {
|
||||
F_SET(sp, S_INTERRUPTED);
|
||||
return;
|
||||
}
|
||||
nrp->start = lno + 1;
|
||||
nrp->stop = rp->stop + 1;
|
||||
rp->stop = lno - 1;
|
||||
CIRCLEQ_INSERT_AFTER(&exp->rangeq, rp, nrp, q);
|
||||
rp = nrp;
|
||||
}
|
||||
break;
|
||||
case LINE_RESET:
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* If the command deleted/inserted lines, the cursor moves to
|
||||
* the line after the deleted/inserted line.
|
||||
*/
|
||||
exp->range_lno = lno;
|
||||
}
|
202
usr.bin/vi/ex/ex_init.c
Normal file
202
usr.bin/vi/ex/ex_init.c
Normal file
@ -0,0 +1,202 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_init.c 8.16 (Berkeley) 8/8/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"
|
||||
#include "excmd.h"
|
||||
#include "tag.h"
|
||||
|
||||
/*
|
||||
* ex_screen_copy --
|
||||
* Copy ex screen.
|
||||
*/
|
||||
int
|
||||
ex_screen_copy(orig, sp)
|
||||
SCR *orig, *sp;
|
||||
{
|
||||
EX_PRIVATE *oexp, *nexp;
|
||||
|
||||
/* Create the private ex structure. */
|
||||
CALLOC_RET(orig, nexp, EX_PRIVATE *, 1, sizeof(EX_PRIVATE));
|
||||
sp->ex_private = nexp;
|
||||
|
||||
/* Initialize queues. */
|
||||
TAILQ_INIT(&nexp->tagq);
|
||||
TAILQ_INIT(&nexp->tagfq);
|
||||
TAILQ_INIT(&nexp->cdq);
|
||||
CIRCLEQ_INIT(&nexp->rangeq);
|
||||
|
||||
if (orig == NULL) {
|
||||
nexp->at_lbuf_set = 0;
|
||||
} else {
|
||||
oexp = EXP(orig);
|
||||
|
||||
nexp->at_lbuf = oexp->at_lbuf;
|
||||
nexp->at_lbuf_set = oexp->at_lbuf_set;
|
||||
|
||||
if (oexp->lastbcomm != NULL &&
|
||||
(nexp->lastbcomm = strdup(oexp->lastbcomm)) == NULL) {
|
||||
msgq(sp, M_SYSERR, NULL);
|
||||
return(1);
|
||||
}
|
||||
|
||||
if (ex_tagcopy(orig, sp))
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* ex_screen_end --
|
||||
* End a vi screen.
|
||||
*/
|
||||
int
|
||||
ex_screen_end(sp)
|
||||
SCR *sp;
|
||||
{
|
||||
EX_PRIVATE *exp;
|
||||
int rval;
|
||||
|
||||
rval = 0;
|
||||
exp = EXP(sp);
|
||||
|
||||
if (argv_free(sp))
|
||||
rval = 1;
|
||||
|
||||
if (exp->ibp != NULL)
|
||||
FREE(exp->ibp, exp->ibp_len);
|
||||
|
||||
if (exp->lastbcomm != NULL)
|
||||
FREE(exp->lastbcomm, strlen(exp->lastbcomm) + 1);
|
||||
|
||||
if (ex_tagfree(sp))
|
||||
rval = 1;
|
||||
|
||||
if (ex_cdfree(sp))
|
||||
rval = 1;
|
||||
|
||||
/* Free private memory. */
|
||||
FREE(exp, sizeof(EX_PRIVATE));
|
||||
sp->ex_private = NULL;
|
||||
|
||||
return (rval);
|
||||
}
|
||||
|
||||
/*
|
||||
* ex_init --
|
||||
* Initialize ex.
|
||||
*/
|
||||
int
|
||||
ex_init(sp, ep)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
{
|
||||
size_t len;
|
||||
|
||||
/*
|
||||
* The default address is the last line of the file. If the address
|
||||
* set bit is on for this file, load the address, ensuring that it
|
||||
* exists.
|
||||
*/
|
||||
if (F_ISSET(sp->frp, FR_CURSORSET)) {
|
||||
sp->lno = sp->frp->lno;
|
||||
sp->cno = sp->frp->cno;
|
||||
|
||||
if (file_gline(sp, ep, sp->lno, &len) == NULL) {
|
||||
if (file_lline(sp, ep, &sp->lno))
|
||||
return (1);
|
||||
if (sp->lno == 0)
|
||||
sp->lno = 1;
|
||||
sp->cno = 0;
|
||||
} else if (sp->cno >= len)
|
||||
sp->cno = 0;
|
||||
} else {
|
||||
if (file_lline(sp, ep, &sp->lno))
|
||||
return (1);
|
||||
if (sp->lno == 0)
|
||||
sp->lno = 1;
|
||||
sp->cno = 0;
|
||||
}
|
||||
|
||||
/* Display the status line. */
|
||||
return (msg_status(sp, ep, sp->lno, 0));
|
||||
}
|
||||
|
||||
/*
|
||||
* ex_end --
|
||||
* End ex session.
|
||||
*/
|
||||
int
|
||||
ex_end(sp)
|
||||
SCR *sp;
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* ex_optchange --
|
||||
* Handle change of options for vi.
|
||||
*/
|
||||
int
|
||||
ex_optchange(sp, opt)
|
||||
SCR *sp;
|
||||
int opt;
|
||||
{
|
||||
switch (opt) {
|
||||
case O_CDPATH:
|
||||
return (ex_cdalloc(sp, O_STR(sp, O_CDPATH)));
|
||||
case O_TAGS:
|
||||
return (ex_tagalloc(sp, O_STR(sp, O_TAGS)));
|
||||
}
|
||||
return (0);
|
||||
}
|
200
usr.bin/vi/ex/ex_join.c
Normal file
200
usr.bin/vi/ex/ex_join.c
Normal file
@ -0,0 +1,200 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_join.c 8.12 (Berkeley) 5/21/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"
|
||||
#include "excmd.h"
|
||||
|
||||
/*
|
||||
* ex_join -- :[line [,line]] j[oin][!] [count] [flags]
|
||||
* Join lines.
|
||||
*/
|
||||
int
|
||||
ex_join(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
recno_t from, to;
|
||||
size_t blen, clen, len, tlen;
|
||||
int echar, extra, first;
|
||||
char *bp, *p, *tbp;
|
||||
|
||||
from = cmdp->addr1.lno;
|
||||
to = cmdp->addr2.lno;
|
||||
|
||||
/* Check for no lines to join. */
|
||||
if ((p = file_gline(sp, ep, from + 1, &len)) == NULL) {
|
||||
msgq(sp, M_ERR, "No following lines to join");
|
||||
return (1);
|
||||
}
|
||||
|
||||
GET_SPACE_RET(sp, bp, blen, 256);
|
||||
|
||||
/*
|
||||
* The count for the join command was off-by-one,
|
||||
* historically, to other counts for other commands.
|
||||
*/
|
||||
if (F_ISSET(cmdp, E_COUNT))
|
||||
++cmdp->addr2.lno;
|
||||
|
||||
/*
|
||||
* If only a single address specified, or, the same address
|
||||
* specified twice, the from/two addresses will be the same.
|
||||
*/
|
||||
if (cmdp->addr1.lno == cmdp->addr2.lno)
|
||||
++cmdp->addr2.lno;
|
||||
|
||||
clen = tlen = 0;
|
||||
for (first = 1,
|
||||
from = cmdp->addr1.lno, to = cmdp->addr2.lno; from <= to; ++from) {
|
||||
/*
|
||||
* Get next line. Historic versions of vi allowed "10J" while
|
||||
* less than 10 lines from the end-of-file, so we do too.
|
||||
*/
|
||||
if ((p = file_gline(sp, ep, from, &len)) == NULL) {
|
||||
cmdp->addr2.lno = from - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Empty lines just go away. */
|
||||
if (len == 0)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Get more space if necessary. Note, tlen isn't the length
|
||||
* of the new line, it's roughly the amount of space needed.
|
||||
* tbp - bp is the length of the new line.
|
||||
*/
|
||||
tlen += len + 2;
|
||||
ADD_SPACE_RET(sp, bp, blen, tlen);
|
||||
tbp = bp + clen;
|
||||
|
||||
/*
|
||||
* Historic practice:
|
||||
*
|
||||
* If force specified, join without modification.
|
||||
* If the current line ends with whitespace, strip leading
|
||||
* whitespace from the joined line.
|
||||
* If the next line starts with a ), do nothing.
|
||||
* If the current line ends with ., ? or !, insert two spaces.
|
||||
* Else, insert one space.
|
||||
*
|
||||
* Echar is the last character in the last line joined.
|
||||
*/
|
||||
extra = 0;
|
||||
if (!first && !F_ISSET(cmdp, E_FORCE)) {
|
||||
if (isblank(echar))
|
||||
for (; len && isblank(*p); --len, ++p);
|
||||
else if (p[0] != ')') {
|
||||
if (strchr(".?!", echar)) {
|
||||
*tbp++ = ' ';
|
||||
++clen;
|
||||
extra = 1;
|
||||
}
|
||||
*tbp++ = ' ';
|
||||
++clen;
|
||||
for (; len && isblank(*p); --len, ++p);
|
||||
}
|
||||
}
|
||||
|
||||
if (len != 0) {
|
||||
memmove(tbp, p, len);
|
||||
tbp += len;
|
||||
clen += len;
|
||||
echar = p[len - 1];
|
||||
} else
|
||||
echar = ' ';
|
||||
|
||||
/*
|
||||
* Historic practice for vi was to put the cursor at the first
|
||||
* inserted whitespace character, if there was one, or the
|
||||
* first character of the joined line, if there wasn't, or the
|
||||
* last character of the line if joined to an empty line. If
|
||||
* a count was specified, the cursor was moved as described
|
||||
* for the first line joined, ignoring subsequent lines. If
|
||||
* the join was a ':' command, the cursor was placed at the
|
||||
* first non-blank character of the line unless the cursor was
|
||||
* "attracted" to the end of line when the command was executed
|
||||
* in which case it moved to the new end of line. There are
|
||||
* probably several more special cases, but frankly, my dear,
|
||||
* I don't give a damn. This implementation puts the cursor
|
||||
* on the first inserted whitespace character, the first
|
||||
* character of the joined line, or the last character of the
|
||||
* line regardless. Note, if the cursor isn't on the joined
|
||||
* line (possible with : commands), it is reset to the starting
|
||||
* line.
|
||||
*/
|
||||
if (first) {
|
||||
sp->cno = (tbp - bp) - (1 + extra);
|
||||
first = 0;
|
||||
} else
|
||||
sp->cno = (tbp - bp) - len - (1 + extra);
|
||||
}
|
||||
sp->lno = cmdp->addr1.lno;
|
||||
|
||||
/* Delete the joined lines. */
|
||||
for (from = cmdp->addr1.lno, to = cmdp->addr2.lno; to > from; --to)
|
||||
if (file_dline(sp, ep, to))
|
||||
goto err;
|
||||
|
||||
/* If the original line changed, reset it. */
|
||||
if (!first && file_sline(sp, ep, from, bp, tbp - bp)) {
|
||||
err: FREE_SPACE(sp, bp, blen);
|
||||
return (1);
|
||||
}
|
||||
FREE_SPACE(sp, bp, blen);
|
||||
|
||||
sp->rptlines[L_JOINED] += (cmdp->addr2.lno - cmdp->addr1.lno) + 1;
|
||||
return (0);
|
||||
}
|
160
usr.bin/vi/ex/ex_map.c
Normal file
160
usr.bin/vi/ex/ex_map.c
Normal file
@ -0,0 +1,160 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_map.c 8.17 (Berkeley) 7/16/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 <curses.h>
|
||||
#include <db.h>
|
||||
#include <regex.h>
|
||||
|
||||
#include "vi.h"
|
||||
#include "excmd.h"
|
||||
|
||||
/*
|
||||
* ex_map -- :map[!] [input] [replacement]
|
||||
* Map a key/string or display mapped keys.
|
||||
*
|
||||
* Historical note:
|
||||
* Historic vi maps were fairly bizarre, and likely to differ in
|
||||
* very subtle and strange ways from this implementation. Two
|
||||
* things worth noting are that vi would often hang or drop core
|
||||
* if the map was strange enough (ex: map X "xy$@x^V), or, simply
|
||||
* not work. One trick worth remembering is that if you put a
|
||||
* mark at the start of the map, e.g. map X mx"xy ...), or if you
|
||||
* put the map in a .exrc file, things would often work much better.
|
||||
* No clue why.
|
||||
*/
|
||||
int
|
||||
ex_map(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
enum seqtype stype;
|
||||
CHAR_T *input, *p;
|
||||
|
||||
stype = F_ISSET(cmdp, E_FORCE) ? SEQ_INPUT : SEQ_COMMAND;
|
||||
|
||||
switch (cmdp->argc) {
|
||||
case 0:
|
||||
if (seq_dump(sp, stype, 1) == 0)
|
||||
msgq(sp, M_INFO, "No %s map entries",
|
||||
stype == SEQ_INPUT ? "input" : "command");
|
||||
return (0);
|
||||
case 2:
|
||||
input = cmdp->argv[0]->bp;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
/*
|
||||
* If the mapped string is #[0-9]* (and wasn't quoted) then store
|
||||
* the function key mapping, and call the screen specific routine.
|
||||
* Note, if the screen specific routine is able to create the
|
||||
* mapping, the SEQ_FUNCMAP type stays around, maybe the next screen
|
||||
* type can get it right.
|
||||
*/
|
||||
if (input[0] == '#') {
|
||||
for (p = input + 1; isdigit(*p); ++p);
|
||||
if (p[0] != '\0')
|
||||
goto nofunc;
|
||||
|
||||
if (seq_set(sp, NULL, 0, input, cmdp->argv[0]->len,
|
||||
cmdp->argv[1]->bp, cmdp->argv[1]->len, stype, SEQ_FUNCMAP))
|
||||
return (1);
|
||||
return (sp->s_fmap(sp, stype, input, cmdp->argv[0]->len,
|
||||
cmdp->argv[1]->bp, cmdp->argv[1]->len));
|
||||
}
|
||||
|
||||
/* Some single keys may not be remapped in command mode. */
|
||||
nofunc: if (stype == SEQ_COMMAND && input[1] == '\0')
|
||||
switch (KEY_VAL(sp, input[0])) {
|
||||
case K_COLON:
|
||||
case K_ESCAPE:
|
||||
case K_NL:
|
||||
msgq(sp, M_ERR, "The %s character may not be remapped",
|
||||
KEY_NAME(sp, input[0]));
|
||||
return (1);
|
||||
}
|
||||
return (seq_set(sp, NULL, 0, input, cmdp->argv[0]->len,
|
||||
cmdp->argv[1]->bp, cmdp->argv[1]->len, stype, SEQ_USERDEF));
|
||||
}
|
||||
|
||||
/*
|
||||
* ex_unmap -- (:unmap[!] key)
|
||||
* Unmap a key.
|
||||
*/
|
||||
int
|
||||
ex_unmap(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
if (seq_delete(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len,
|
||||
F_ISSET(cmdp, E_FORCE) ? SEQ_INPUT : SEQ_COMMAND)) {
|
||||
msgq(sp, M_INFO, "\"%s\" isn't mapped", cmdp->argv[0]->bp);
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* map_save --
|
||||
* Save the mapped sequences to a file.
|
||||
*/
|
||||
int
|
||||
map_save(sp, fp)
|
||||
SCR *sp;
|
||||
FILE *fp;
|
||||
{
|
||||
if (seq_save(sp, fp, "map ", SEQ_COMMAND))
|
||||
return (1);
|
||||
return (seq_save(sp, fp, "map! ", SEQ_INPUT));
|
||||
}
|
66
usr.bin/vi/ex/ex_mark.c
Normal file
66
usr.bin/vi/ex/ex_mark.c
Normal file
@ -0,0 +1,66 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_mark.c 8.6 (Berkeley) 5/21/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 <termios.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include <db.h>
|
||||
#include <regex.h>
|
||||
|
||||
#include "vi.h"
|
||||
#include "excmd.h"
|
||||
|
||||
int
|
||||
ex_mark(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
if (cmdp->argv[0]->len != 1) {
|
||||
msgq(sp, M_ERR, "Mark names must be a single character");
|
||||
return (1);
|
||||
}
|
||||
return (mark_set(sp, ep, cmdp->argv[0]->bp[0], &cmdp->addr1, 1));
|
||||
}
|
130
usr.bin/vi/ex/ex_mkexrc.c
Normal file
130
usr.bin/vi/ex/ex_mkexrc.c
Normal file
@ -0,0 +1,130 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_mkexrc.c 8.12 (Berkeley) 7/15/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 <unistd.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include <db.h>
|
||||
#include <regex.h>
|
||||
#include <pathnames.h>
|
||||
|
||||
#include "vi.h"
|
||||
#include "excmd.h"
|
||||
|
||||
/*
|
||||
* ex_mkexrc -- :mkexrc[!] [file]
|
||||
*
|
||||
* Create (or overwrite) a .exrc file with the current info.
|
||||
*/
|
||||
int
|
||||
ex_mkexrc(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
struct stat sb;
|
||||
FILE *fp;
|
||||
int fd, sverrno;
|
||||
char *fname;
|
||||
|
||||
switch (cmdp->argc) {
|
||||
case 0:
|
||||
fname = _PATH_EXRC;
|
||||
break;
|
||||
case 1:
|
||||
fname = cmdp->argv[0]->bp;
|
||||
set_alt_name(sp, fname);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
if (!F_ISSET(cmdp, E_FORCE) && !stat(fname, &sb)) {
|
||||
msgq(sp, M_ERR,
|
||||
"%s exists, not written; use ! to override", fname);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Create with max permissions of rw-r--r--. */
|
||||
if ((fd = open(fname, O_CREAT | O_TRUNC | O_WRONLY,
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) {
|
||||
msgq(sp, M_SYSERR, fname);
|
||||
return (1);
|
||||
}
|
||||
|
||||
if ((fp = fdopen(fd, "w")) == NULL) {
|
||||
sverrno = errno;
|
||||
(void)close(fd);
|
||||
errno = sverrno;
|
||||
goto e2;
|
||||
}
|
||||
|
||||
if (abbr_save(sp, fp) || ferror(fp))
|
||||
goto e1;
|
||||
if (map_save(sp, fp) || ferror(fp))
|
||||
goto e1;
|
||||
if (opts_save(sp, fp) || ferror(fp))
|
||||
goto e1;
|
||||
#ifndef NO_DIGRAPH
|
||||
digraph_save(sp, fd);
|
||||
#endif
|
||||
if (fclose(fp))
|
||||
goto e2;
|
||||
|
||||
msgq(sp, M_INFO, "New .exrc file: %s. ", fname);
|
||||
return (0);
|
||||
|
||||
e1: sverrno = errno;
|
||||
(void)fclose(fp);
|
||||
errno = sverrno;
|
||||
e2: msgq(sp, M_ERR, "%s: incomplete: %s", fname, strerror(errno));
|
||||
return (1);
|
||||
}
|
222
usr.bin/vi/ex/ex_move.c
Normal file
222
usr.bin/vi/ex/ex_move.c
Normal file
@ -0,0 +1,222 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_move.c 8.17 (Berkeley) 8/8/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 "compat.h"
|
||||
#include <db.h>
|
||||
#include <regex.h>
|
||||
|
||||
#include "vi.h"
|
||||
#include "excmd.h"
|
||||
|
||||
/*
|
||||
* ex_copy -- :[line [,line]] co[py] line [flags]
|
||||
* Copy selected lines.
|
||||
*/
|
||||
int
|
||||
ex_copy(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
CB cb;
|
||||
MARK fm1, fm2, m, tm;
|
||||
recno_t cnt;
|
||||
int rval;
|
||||
|
||||
rval = 0;
|
||||
|
||||
/*
|
||||
* It's possible to copy things into the area that's being
|
||||
* copied, e.g. "2,5copy3" is legitimate. Save the text to
|
||||
* a cut buffer.
|
||||
*/
|
||||
fm1 = cmdp->addr1;
|
||||
fm2 = cmdp->addr2;
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
CIRCLEQ_INIT(&cb.textq);
|
||||
for (cnt = fm1.lno; cnt <= fm2.lno; ++cnt)
|
||||
if (cut_line(sp, ep, cnt, 0, 0, &cb)) {
|
||||
rval = 1;
|
||||
goto err;
|
||||
}
|
||||
cb.flags |= CB_LMODE;
|
||||
|
||||
/* Put the text into place. */
|
||||
tm.lno = cmdp->lineno;
|
||||
tm.cno = 0;
|
||||
if (put(sp, ep, &cb, NULL, &tm, &m, 1))
|
||||
rval = 1;
|
||||
else {
|
||||
/*
|
||||
* Copy puts the cursor on the last line copied. The cursor
|
||||
* returned by the put routine is the first line put, not the
|
||||
* last, because that's the historic semantic of vi.
|
||||
*/
|
||||
cnt = (fm2.lno - fm1.lno) + 1;
|
||||
sp->lno = m.lno + (cnt - 1);
|
||||
sp->cno = 0;
|
||||
}
|
||||
err: text_lfree(&cb.textq);
|
||||
return (rval);
|
||||
}
|
||||
|
||||
/*
|
||||
* ex_move -- :[line [,line]] mo[ve] line
|
||||
* Move selected lines.
|
||||
*/
|
||||
int
|
||||
ex_move(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
LMARK *lmp;
|
||||
MARK fm1, fm2;
|
||||
recno_t cnt, diff, fl, tl, mfl, mtl;
|
||||
size_t blen, len;
|
||||
int mark_reset;
|
||||
char *bp, *p;
|
||||
|
||||
/*
|
||||
* It's not possible to move things into the area that's being
|
||||
* moved.
|
||||
*/
|
||||
fm1 = cmdp->addr1;
|
||||
fm2 = cmdp->addr2;
|
||||
if (cmdp->lineno >= fm1.lno && cmdp->lineno <= fm2.lno) {
|
||||
msgq(sp, M_ERR, "Destination line is inside move range");
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Log the positions of any marks in the to-be-deleted lines. This
|
||||
* has to work with the logging code. What happens is that we log
|
||||
* the old mark positions, make the changes, then log the new mark
|
||||
* positions. Then the marks end up in the right positions no matter
|
||||
* which way the log is traversed.
|
||||
*
|
||||
* XXX
|
||||
* Reset the MARK_USERSET flag so that the log can undo the mark.
|
||||
* This isn't very clean, and should probably be fixed.
|
||||
*/
|
||||
fl = fm1.lno;
|
||||
tl = cmdp->lineno;
|
||||
|
||||
/* Log the old positions of the marks. */
|
||||
mark_reset = 0;
|
||||
for (lmp = ep->marks.lh_first; lmp != NULL; lmp = lmp->q.le_next)
|
||||
if (lmp->name != ABSMARK1 &&
|
||||
lmp->lno >= fl && lmp->lno <= tl) {
|
||||
mark_reset = 1;
|
||||
F_CLR(lmp, MARK_USERSET);
|
||||
(void)log_mark(sp, ep, lmp);
|
||||
}
|
||||
|
||||
/* Get memory for the copy. */
|
||||
GET_SPACE_RET(sp, bp, blen, 256);
|
||||
|
||||
/* Move the lines. */
|
||||
diff = (fm2.lno - fm1.lno) + 1;
|
||||
if (tl > fl) { /* Destination > source. */
|
||||
mfl = tl - diff;
|
||||
mtl = tl;
|
||||
for (cnt = diff; cnt--;) {
|
||||
if ((p = file_gline(sp, ep, fl, &len)) == NULL)
|
||||
return (1);
|
||||
BINC_RET(sp, bp, blen, len);
|
||||
memmove(bp, p, len);
|
||||
if (file_aline(sp, ep, 1, tl, bp, len))
|
||||
return (1);
|
||||
if (mark_reset)
|
||||
for (lmp = ep->marks.lh_first;
|
||||
lmp != NULL; lmp = lmp->q.le_next)
|
||||
if (lmp->name != ABSMARK1 &&
|
||||
lmp->lno == fl)
|
||||
lmp->lno = tl + 1;
|
||||
if (file_dline(sp, ep, fl))
|
||||
return (1);
|
||||
}
|
||||
} else { /* Destination < source. */
|
||||
mfl = tl;
|
||||
mtl = tl + diff;
|
||||
for (cnt = diff; cnt--;) {
|
||||
if ((p = file_gline(sp, ep, fl, &len)) == NULL)
|
||||
return (1);
|
||||
BINC_RET(sp, bp, blen, len);
|
||||
memmove(bp, p, len);
|
||||
if (file_aline(sp, ep, 1, tl++, bp, len))
|
||||
return (1);
|
||||
if (mark_reset)
|
||||
for (lmp = ep->marks.lh_first;
|
||||
lmp != NULL; lmp = lmp->q.le_next)
|
||||
if (lmp->name != ABSMARK1 &&
|
||||
lmp->lno == fl)
|
||||
lmp->lno = tl;
|
||||
++fl;
|
||||
if (file_dline(sp, ep, fl))
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
FREE_SPACE(sp, bp, blen);
|
||||
|
||||
sp->lno = tl; /* Last line moved. */
|
||||
sp->cno = 0;
|
||||
|
||||
/* Log the new positions of the marks. */
|
||||
if (mark_reset)
|
||||
for (lmp = ep->marks.lh_first;
|
||||
lmp != NULL; lmp = lmp->q.le_next)
|
||||
if (lmp->name != ABSMARK1 &&
|
||||
lmp->lno >= mfl && lmp->lno <= mtl)
|
||||
(void)log_mark(sp, ep, lmp);
|
||||
|
||||
|
||||
sp->rptlines[L_MOVED] += diff;
|
||||
return (0);
|
||||
}
|
75
usr.bin/vi/ex/ex_open.c
Normal file
75
usr.bin/vi/ex/ex_open.c
Normal file
@ -0,0 +1,75 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_open.c 8.4 (Berkeley) 5/21/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 <termios.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include <db.h>
|
||||
#include <regex.h>
|
||||
|
||||
#include "vi.h"
|
||||
#include "excmd.h"
|
||||
|
||||
/*
|
||||
* ex_open -- :[line] o[pen] [/pattern/] [flags]
|
||||
*
|
||||
* Switch to single line "open" mode.
|
||||
*/
|
||||
int
|
||||
ex_open(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
/* If open option off, disallow open command. */
|
||||
if (!O_ISSET(sp, O_OPEN)) {
|
||||
msgq(sp, M_ERR,
|
||||
"The open command requires that the open option be set");
|
||||
return (1);
|
||||
}
|
||||
|
||||
msgq(sp, M_ERR, "The open command is not yet implemented");
|
||||
return (1);
|
||||
}
|
128
usr.bin/vi/ex/ex_preserve.c
Normal file
128
usr.bin/vi/ex/ex_preserve.c
Normal file
@ -0,0 +1,128 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_preserve.c 8.12 (Berkeley) 8/4/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"
|
||||
|
||||
/*
|
||||
* ex_preserve -- :pre[serve]
|
||||
* Push the file to recovery.
|
||||
*/
|
||||
int
|
||||
ex_preserve(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
recno_t lno;
|
||||
|
||||
if (!F_ISSET(ep, F_RCV_ON)) {
|
||||
msgq(sp, M_ERR, "Preservation of this file not possible");
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* If recovery not initialized, do so. */
|
||||
if (F_ISSET(ep, F_FIRSTMODIFY) && rcv_init(sp, ep))
|
||||
return (1);
|
||||
|
||||
/* Force the file to be read in, in case it hasn't yet. */
|
||||
if (file_lline(sp, ep, &lno))
|
||||
return (1);
|
||||
|
||||
/* Sync to disk. */
|
||||
if (rcv_sync(sp, ep, RCV_SNAPSHOT))
|
||||
return (1);
|
||||
|
||||
msgq(sp, M_INFO, "File preserved");
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* ex_recover -- :rec[over][!] file
|
||||
*
|
||||
* Recover the file.
|
||||
*/
|
||||
int
|
||||
ex_recover(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
ARGS *ap;
|
||||
FREF *frp;
|
||||
|
||||
ap = cmdp->argv[0];
|
||||
|
||||
/* Set the alternate file name. */
|
||||
set_alt_name(sp, ap->bp);
|
||||
|
||||
/*
|
||||
* Check for modifications. Autowrite did not historically
|
||||
* affect :recover.
|
||||
*/
|
||||
if (file_m2(sp, ep, F_ISSET(cmdp, E_FORCE)))
|
||||
return (1);
|
||||
|
||||
/* Get a file structure for the file. */
|
||||
if ((frp = file_add(sp, ap->bp)) == NULL)
|
||||
return (1);
|
||||
|
||||
/* Set the recover bit. */
|
||||
F_SET(frp, FR_RECOVER);
|
||||
|
||||
/* Switch files. */
|
||||
if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE)))
|
||||
return (1);
|
||||
F_SET(sp, S_FSWITCH);
|
||||
return (0);
|
||||
}
|
212
usr.bin/vi/ex/ex_print.c
Normal file
212
usr.bin/vi/ex/ex_print.c
Normal file
@ -0,0 +1,212 @@
|
||||
/*-
|
||||
* 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 char sccsid[] = "@(#)ex_print.c 8.14 (Berkeley) 8/7/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 <string.h>
|
||||
#include <termios.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include <db.h>
|
||||
#include <regex.h>
|
||||
|
||||
#include "vi.h"
|
||||
#include "excmd.h"
|
||||
|
||||
/*
|
||||
* ex_list -- :[line [,line]] l[ist] [count] [flags]
|
||||
*
|
||||
* Display the addressed lines such that the output is unambiguous.
|
||||
*/
|
||||
int
|
||||
ex_list(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
if (ex_print(sp, ep,
|
||||
&cmdp->addr1, &cmdp->addr2, cmdp->flags | E_F_LIST))
|
||||
return (1);
|
||||
sp->lno = cmdp->addr2.lno;
|
||||
sp->cno = cmdp->addr2.cno;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* ex_number -- :[line [,line]] nu[mber] [count] [flags]
|
||||
*
|
||||
* Display the addressed lines with a leading line number.
|
||||
*/
|
||||
int
|
||||
ex_number(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
if (ex_print(sp, ep,
|
||||
&cmdp->addr1, &cmdp->addr2, cmdp->flags | E_F_HASH))
|
||||
return (1);
|
||||
sp->lno = cmdp->addr2.lno;
|
||||
sp->cno = cmdp->addr2.cno;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* ex_pr -- :[line [,line]] p[rint] [count] [flags]
|
||||
*
|
||||
* Display the addressed lines.
|
||||
*/
|
||||
int
|
||||
ex_pr(sp, ep, cmdp)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
EXCMDARG *cmdp;
|
||||
{
|
||||
if (ex_print(sp, ep, &cmdp->addr1, &cmdp->addr2, cmdp->flags))
|
||||
return (1);
|
||||
sp->lno = cmdp->addr2.lno;
|
||||
sp->cno = cmdp->addr2.cno;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* ex_print --
|
||||
* Print the selected lines.
|
||||
*/
|
||||
int
|
||||
ex_print(sp, ep, fp, tp, flags)
|
||||
SCR *sp;
|
||||
EXF *ep;
|
||||
MARK *fp, *tp;
|
||||
register int flags;
|
||||
{
|
||||
recno_t from, to;
|
||||
size_t col, len;
|
||||
char *p;
|
||||
|
||||
F_SET(sp, S_INTERRUPTIBLE);
|
||||
for (from = fp->lno, to = tp->lno; from <= to; ++from) {
|
||||
/*
|
||||
* Display the line number. The %6 format is specified
|
||||
* by POSIX 1003.2, and is almost certainly large enough.
|
||||
* Check, though, just in case.
|
||||
*/
|
||||
if (LF_ISSET(E_F_HASH))
|
||||
if (from <= 999999)
|
||||
col = ex_printf(EXCOOKIE, "%6ld ", from);
|
||||
else
|
||||
col = ex_printf(EXCOOKIE, "TOOBIG ");
|
||||
else
|
||||
col = 0;
|
||||
|
||||
/*
|
||||
* Display the line. The format for E_F_PRINT isn't very good,
|
||||
* especially in handling end-of-line tabs, but they're almost
|
||||
* backward compatible.
|
||||
*/
|
||||
if ((p = file_gline(sp, ep, from, &len)) == NULL) {
|
||||
GETLINE_ERR(sp, from);
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (len == 0 && !LF_ISSET(E_F_LIST))
|
||||
(void)ex_printf(EXCOOKIE, "\n");
|
||||
else if (ex_ldisplay(sp, p, len, col, flags))
|
||||
return (1);
|
||||
|
||||
if (INTERRUPTED(sp))
|
||||
break;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* ex_ldisplay --
|
||||
* Display a line.
|
||||
*/
|
||||
int
|
||||
ex_ldisplay(sp, lp, len, col, flags)
|
||||
SCR *sp;
|
||||
CHAR_T *lp;
|
||||
size_t len, col;
|
||||
u_int flags;
|
||||
{
|
||||
CHAR_T ch, *kp;
|
||||
u_long ts;
|
||||
size_t tlen;
|
||||
|
||||
ts = O_VAL(sp, O_TABSTOP);
|
||||
for (;; --len) {
|
||||
if (len > 0)
|
||||
ch = *lp++;
|
||||
else if (LF_ISSET(E_F_LIST))
|
||||
ch = '$';
|
||||
else
|
||||
break;
|
||||
if (ch == '\t' && !LF_ISSET(E_F_LIST))
|
||||
for (tlen = ts - col % ts;
|
||||
col < sp->cols && tlen--; ++col)
|
||||
(void)ex_printf(EXCOOKIE, " ");
|
||||
else {
|
||||
kp = KEY_NAME(sp, ch);
|
||||
tlen = KEY_LEN(sp, ch);
|
||||
if (col + tlen < sp->cols) {
|
||||
(void)ex_printf(EXCOOKIE, "%s", kp);
|
||||
col += tlen;
|
||||
} else
|
||||
for (; tlen--; ++kp, ++col) {
|
||||
if (col == sp->cols) {
|
||||
col = 0;
|
||||
(void)ex_printf(EXCOOKIE, "\n");
|
||||
}
|
||||
(void)ex_printf(EXCOOKIE, "%c", *kp);
|
||||
}
|
||||
}
|
||||
if (len == 0)
|
||||
break;
|
||||
}
|
||||
(void)ex_printf(EXCOOKIE, "\n");
|
||||
return (0);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user