Termcap update, replacing elvis by nvi.

Removing elvis, importing nvi, ctags, updating libedit.

Change-Id: I881eb04d2dc64cf112facd992de1114e1a59107f
This commit is contained in:
2013-01-22 12:03:53 +01:00
committed by Lionel Sambuc
parent f9f6c9251a
commit 3e1db26a5a
491 changed files with 192709 additions and 28905 deletions

34
dist/nvi/Changes vendored Normal file
View File

@@ -0,0 +1,34 @@
Changes since 1.81.5
* various compilation fixes
* support for newer DBs
* tcl support compiles again
Changes since 1.81.4
* add Changes document
* the preserve command overwrote the edited file; the problem
is really in DB; a bandaid was added
* configure changes
- -rpath now on by default
- check for iconv
* handle incomplete/invalid input
* upgrade libtool and automake
* Motif front-end was getting out of sync; fixed
* ^T works in presence of wide chars
* fix use of OPT_GLOBAL (YAMAMOTO Takashi <takashi.yamamoto@bigfoot.com>)
* missing fallback function added
* fix use of both leftright and number
(problem reported by Dima Dorfman <dima@unixfreak.org>)
* install everything 1.79 installed
* fix count argument for commands
(problem reported by Arfst Ludwig <Arfst.Ludwig@LHSystems.COM>)
* perl changes
- map no longer assumes its argument is the name of a perl function
- EXISTS for marks
* documentation changes
- vi.ref converted to texinfo
- vsplit documented
Changes since 1.81.3
* support for newer ncurses
* various other wide character related changes
* eliminate spurious cursor movements on split screens
* use of gtk-1.2 allowed again
* small fix to Motif front-end

39
dist/nvi/LICENSE vendored Normal file
View File

@@ -0,0 +1,39 @@
/*-
* Id: LICENSE,v 8.17 2000/08/18 14:58:41 bostic Exp (Sleepycat) Date: 2000/08/18 14:58:41
*/
The following are the copyrights and redistribution conditions that apply
to this copy of the Vi software.
/*
* Copyright (c) 1991, 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000
* Keith Bostic. All rights reserved.
* Copyright (c) 1999, 2000
* Sven Verdoolaege. 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. 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.
*/

104
dist/nvi/README vendored Normal file
View File

@@ -0,0 +1,104 @@
# Id: README,v 8.153 2001/04/30 09:31:12 skimo Exp (Berkeley) Date: 2001/04/30 09:31:12
This is version 1.81.6 (2007-11-18) of nex/nvi, a reimplementation of the ex/vi
text editors originally distributed as part of the Fourth Berkeley
Software Distribution (4BSD), by the University of California, Berkeley.
The directory layout is as follows:
LICENSE ....... Copyright, use and redistribution information.
README ........ This file.
build.unix .... UNIX build directory.
catalog ....... Message catalogs; see catalog/README.
cl ............ Vi interface to the curses(3) library.
clib .......... C library replacement source code.
common ........ Code shared by ex and vi.
db ............ A stripped-down, replacement db(3) library.
dist .......... Various files used to build the vi distribution.
docs .......... Ex/vi documentation, both current and historic.
docs/README ... Documentation overview.
docs/edit ..... Edit: A tutorial.
docs/exref .... Ex Reference Manual -- Version 3.7.
docs/vi.man ... UNIX manual page for nex/nvi.
docs/vi.ref ... Nex/nvi reference manual.
docs/vitut .... An Introduction to Display Editing with Vi.
ex ............ Ex source code.
gtk ........... Vi gtk application.
include ....... Replacement include files.
ip ............ Library interface to vi: vi side.
ipc ........... Library interface to vi: application side.
motif ......... Vi motif application.
motif_l ....... Motif library interface to vi.
perl_api ...... Perl scripting language support.
perl_scripts .. Perl scripts.
regex ......... POSIX 1003.2 regular expression library.
tcl_api ....... Tcl scripting language support.
tcl_scripts ... Tcl scripts.
vi ............ Vi source code.
To build DB for a UNIX platform:
cd build.unix
../dist/configure
make
To build multiple UNIX versions of DB in the same source tree, create
a new directory then configure and build.
mkdir build.bsdos3.0
cd build.bsdos3.0
../dist/configure
make
For additional information about building DB for UNIX platforms, the
description of possible configuration options and other information
on DB configuration and build issues, see the file build.unix/README.
Bug fixes and updated versions of this software will periodically be made
available. For more information, as well as a list of Frequently Asked
Questions, see:
http://www.bostic.com/vi
To ask questions about vi, report vi problems, request notification of
future releases and/or bug fixes, or to contact the authors for any
reason, please send email to:
bostic@bostic.com
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
o This software is several years old and is the product of many folks' work.
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.
IEEE POSIX 1003.2 style regular expression support is courtesy of
Henry Spencer, for which I am *very* grateful.
Elan Amir did the original 4BSD curses work that made it possible
to support a full-screen editor using curses.
George Neville-Neil added the Tcl interpreter, and the initial
interpreter design was his.
Sven Verdoolaege added the Perl interpreter.
Rob Mayoff provided the original Cscope support.
o Many, many people suggested enhancements, and provided bug reports and
testing, far too many to individually thank.
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.

17
dist/nvi/README.1st vendored Normal file
View File

@@ -0,0 +1,17 @@
This is a development version of nvi.
Use at your own risk.
Please do not contact the original authors about bugs you
find in this version. Contact skimo-vi@kotnet.org instead.
There is no guarantee that anything in this version will be
available in upcoming stable releases.
New versions will be made available on
http://www.kotnet.org/~skimo/nvi
As always this software comes with absolutely NO WARRANTY.
Now read the original README file.
Sven Verdoolaege

22
dist/nvi/README.DB3 vendored Normal file
View File

@@ -0,0 +1,22 @@
This version of vi requires DB3.1 or better, which can be found
on http://www.sleepycat.com/ .
Note that there is a small problem with DB 3.2 in that it will
not let nvi read in a final line that doesn't end in a newline.
This should be fixed in DB 3.3
If your system library (such as glibc prior to version 2.2) uses a
previous version of db (e.g. DB2) internally, you must configure with
--enable-dynamic-loading to avoid symbols in the internally used db from
being resolved against the newer db.
If, on top of that, the vi binary is explicitly linked against that
previous version of db (such as might happen if you enable the perl
embedding), you should compile the 3.x version with all symbols internally
resolved. In case you use the Gnu linker (ld), this can be accomplished
by passing it the -Bsymbolic option. You can do this by setting CC
to e.g. "gcc -Wl,-Bsymbolic" prior to configuring db.
See docs/ref/build_unix/flags.html in the db distribution for more
information.
skimo@kotnet.org

123
dist/nvi/TODO vendored Normal file
View File

@@ -0,0 +1,123 @@
# Id: TODO,v 8.2 1997/04/12 15:53:31 bostic Exp (Berkeley) Date: 1997/04/12 15:53:31
========================================================================
GENERAL
========================================================================
2.0: Open mode is not yet implemented.
========================================================================
DB
========================================================================
1.N When nvi edits files that don't have trailing newlines, it appends
one, regardless. This is required, by default, from POSIX.2.
1.N: If you run out of space in the recovery directory, the recovery
file is left in place.
2.0: It's known that it's possible to sync the backing files in the
wrong manner, leaving backup files that aren't recoverable. This
is going to be left alone until we have a logging version of DB,
which will hopefully fix this (or at least make it possible to
easily do so).
========================================================================
VI
========================================================================
1.N: Make the search pattern and command history (what the '.' command
executes) remembered between windows, or configurable so that it
is.
1.N: Change the screen scrolling to not eat user characters... i.e.
g/pattern/foo should not eat already entered chars.
1.N: The vi main command loop should use the general-purpose overflow
and underflow routines. In addition, the vi command loop uses
unsigned longs -- should probably be fixed as a 32-bit unsigned
type, and then check to make sure it's never used as as variable
type again.
1.N: Should "view" set a lock on the file?
1.N: Should "view" copy the file (or even open a recovery file)?
1.N: The strings found by searches should be highlighted until the next
character is entered.
1.N: Display a split vi screen for the :help command.
1.N: When getting a key for a continue screen, we should always read from
the terminal, not from a mapped key.
1.N: The sentence, paragraph and section movement commands don't match
historic practice in some boundary cases. This should be left
alone until POSIX 1003.2 makes up its mind.
1.N: The vs_sm_fill routine should scroll if possible, not always redraw.
1.N: Think about setting a dirty/inuse bits on the lines of the SMAP
structure. That way the message routines could steal lines and
refresh would continue to work, because it would know not to touch
the lines that were in use.
========================================================================
EX
========================================================================
2.0: ^C isn't passed to the shell in the script windows as an interrupt
character.
2.0: It would be nice to inverse video the replaced text during
interactive substitute.
2.0: The :args command should put the current file name out in reverse
video. This isn't going to be easy, currently only full lines can
be in reverse video, not just parts.
========================================================================
CURSES
========================================================================
1.N In single-line screens, have to press 'q' twice when quitting out
of a ":set all" display.
========================================================================
MOTIF/IPC
========================================================================
1.N: We currently permit the user to change the lines, columns and term
edit options. Shouldn't that be illegal in a window interface?
========================================================================
REDESIGN
========================================================================
2.0: There's a serious problem with error returns -- we need to separate
command failure from fatal error, consistently, over the entire source
tree. We need to rework all of vi to have three return values:
0: success
1: vi error, continue
2: fatal error, die
Right now we don't recognize fatal errors for what they are.
2.0: The complete list of POSIX.1 calls that can return EINTR are:
wait, waitpid, sleep, dup2, close, read, write,
fcntl(SETLCKW) tcsetattr, tcdrain
The problem is that technically, any system/library call can
return EINTR, so, while nvi blocks (most of?) the obvious ones,
someone may have to do a complete pass and block signals
everywhere.
2.0: The options hardtabs, lisp, optimize, redraw, and slowopen
are recognized, but not implemented.
2.0: Field editing shouldn't be hard to add to nvi:
Field editing file template:
version #
field # row/column start row/column stop
label field # Label string
re field # Matching re string.
field # row/column start row/column stop
label field # Label string
re field # Matching re string.
<tab> moves to the next field
<bs> in column 0 moves to the previous field

362
dist/nvi/build.unix/README vendored Normal file
View File

@@ -0,0 +1,362 @@
# Id: README,v 8.29 2001/05/13 20:52:36 skimo Exp (Berkeley) Date: 2001/05/13 20:52:36
Nvi uses the GNU autoconf program for configuration and compilation. You
should enter:
../dist/configure
make
and nvi will configure the system and build one or two binaries: nvi and
tknvi. You can use any path to the configure script, e.g., to build for
an x86 architecture, I suggest that you do:
mkdir build.x86
cd build.x86
../dist/configure
make
There are options that you can specify to the configure command. See
the next section for a description of these options.
If you want to rebuild or reconfigure nvi, for example, because you change
your mind as to the curses library that you want to use, create a new
directory and reconfigure it using "configure" and whatever options you
choose, don't try to selectively edit the files.
By default, nvi is installed as "vi", with hard links to "ex" and "view".
To install them using different names, use the configure program options.
For example, to install them as "nvi", "nex" and "nview", use:
configure --program-prefix=n
See the section below on installation for details.
Note, if you're building nvi on a LynxOS system, you should read the
README.LynxOS file in this directory for additional build instructions
that are specific to that operating system.
If you have trouble with this procedure, send email to the addresses
listed in ../README. In that email, please provide a complete script
of the output for all of the above commands that you entered.
=-=-=-=-=-=-=
NVI'S OPTIONS TO THE CONFIGURE PROGRAM
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
There are many options that you can enter to the configuration program.
To see a complete list of the options, enter "configure --help". Only
a few of them are nvi specific. These options are as follows:
--disable-re DON'T use the nvi-provided RE routines.
--enable-debug Build a debugging version.
--enable-perlinterp Include a Perl interpreter in vi.
--enable-tclinterp Include a Tk/Tcl interpreter in vi.
--enable-gtk Build a gtk front-end.
--enable-motif Build a motif front-end.
--enable-threads Turn on thread support.
--enable-widechar Build a wide character aware vi (experimental).
--with-curses=DIR Path to curses installation.
--with-db3=db3prefix Path to db3 installation.
--enable-dynamic-loading Load DB 3 dynamically.
disable-re:
By default, nvi loads its own versions of the POSIX 1003.2 Regular
Expression routines (which are Henry Spencer's implementation).
If your C library contains an implementation of the POSIX 1003.2
RE routines (note, this is NOT the same as the historic UNIX RE
routines), and you want to use them instead, enter:
--disable-re
as an argument to configure, and the RE routines will be taken
from whatever libraries you load. Please ensure that your RE
routines implement Henry Spencer's extensions for doing vi-style
"word" searches.
enable-debug:
If you want to build nvi with no optimization (i.e. without -O
as a compiler flag), with -g as a compiler flag, and with DEBUG
defined during compilation, enter:
--enable-debug
as an argument to configure.
enable-perlinterp:
If you have the Perl 5 libraries and you want to compile in the
Perl interpreter, enter:
--enable-perlinterp
as an argument to configure. (Note: this is NOT possible with
Perl 4, or even with Perl 5 versions earlier than 5.002.)
enable-tclinterp:
If you have the Tk/Tcl libraries and you want to compile in the
Tcl/Tk interpreter, enter:
--enable-tclinterp
as an argument to configure. If your Tk/Tcl include files and
libraries aren't in the standard library and include locations,
see the next section of this README file for more information.
enable-gtk:
If you have the Gtk libraries and you want to build the Gtk
nvi front-end, enter:
--enable-gtk
as an argument to configure. If your Gtk include files and
libraries aren't in the standard library and include locations,
see the next section of this README file for more information.
See also the enable-threads option.
enable-motif:
If you have the Motif libraries and you want to build the Motif
nvi front-end, enter:
--enable-motif
as an argument to configure. If your Motif include files and
libraries aren't in the standard library and include locations,
see the next section of this README file for more information.
enable-threads:
If you want to be able to use multiple windows in the Gtk
front-end, you should specify this option.
with-curses:
Specifies the path where curses is installed.
with-db3:
Specifies the path where DB3 is installed.
See README.DB3 for more information about DB3.
enable-dynamic-loading:
Dynamically load DB3 library.
See README.DB3 for more information about DB3.
enable-widechar:
Enables support for wide characters.
Note that this is still rather experimental.
If you try this out on Solaris, you will want to point nvi
to the curses in /usr/xpg4/ which is CSI compliant.
=-=-=-=-=-=-=
ADDING OR CHANGING COMPILERS, OR COMPILE OR LOAD LINE FLAGS
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
If you want to use a specific compiler, specify the CC environment
variable before running configure. For example:
env CC=gcc configure
Using anything other than the native compiler will almost certainly
mean that you'll want to check the compile and load line flags, too.
If you want to specify additional load line flags, specify the ADDLDFLAGS
environment variable before running configure. For example:
env ADDLDFLAGS="-Q" configure
would specify the -Q flag in the load line when the nvi programs are
loaded.
If you don't want configure to use the default load line flags for the
system, specify the LDFLAGS environment variable before running configure.
For example:
env LDFLAGS="-32" configure
will cause configure to set the load line flags to "-32", and not set
them based on the current system.
If you want to specify additional compile line flags, specify the
ADDCPPFLAGS environment variable before running configure. For example:
env ADDCPPFLAGS="-I../foo" configure
would cause the compiler to be passed the -I../foo flag when compiling
test programs during configuration as well as when building nvi object
files.
If you don't want configure to use the default compile line flags for the
system, specify the CPPFLAGS environment variable before running configure.
For example:
env CPPFLAGS="-I.." configure
will cause configure to use "-I.." as the compile line flags instead of
the default values.
=-=-=-=-=-=-=
ADDING LIBRARIES AND INCLUDE FILES
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
If the Tk/Tcl or any other include files or libraries are in non-standard
places on your system, you will need to specify the directory path where
they can be found.
If you want to specify additional library paths, set the ADDLIBS environment
variable before running configure. For example:
env ADDLIBS="-L/a/b -L/e/f -ldb" configure
would specify two additional directories to search for libraries, /a/b
and /e/f, and one additional library to load, "db".
If you want to specify additional include paths, specify the ADDCPPFLAGS
environment variable before running configure. For example:
env ADDCPPFLAGS="-I/usr/local/include" LIBS="-ldb" configure
would search /usr/local/include for include files, as well as load the db
library as described above.
As a final example, let's say that you've downloaded ncurses from the net
and you've built it in a directory named ncurses which is at the same
level in the filesystem hierarchy as nvi. You would enter something like:
env ADDCPPFLAGS="-I../../ncurses/include" \
ADDLIBS="-L../../ncurses/libraries" configure
to cause nvi to look for the curses include files and the curses library
in the ncurses environment.
Notes:
Make sure that you prepend -L to any library directory names, and
that you prepend -I to any include file directory names! Also,
make sure that you quote the paths as shown above, i.e. with
single or double quotes around the values you're specifying for
ADDCPPFLAGS and ADDLIBS.
=-=-=-=-=-=
You should NOT need to add any libraries or include files to load
the Perl5 interpreter. The configure script will obtain that
information directly from the Perl5 program. This means that the
configure script must be able to find perl in its path. It looks
for "perl5" first, and then "perl". If you're building a Perl
interpreter and neither is found, it's a fatal error.
=-=-=-=-=-=
You do not need to specify additional libraries to load Tk/Tcl,
Perl or curses, as the nvi configuration script adds the
appropriate libraries to the load line whenever you specify
--enable-tknvi or other Perl or Tk/Tcl related option, or build
the Tk/Tcl or curses version of nvi. The library names that are
automatically loaded are as follows:
for Perl: -lperl
for Tk/Tcl: -ltk -ltcl -lm
for curses: -lcurses
In addition, the configure script loads:
... the X libraries when loading the Tk/Tcl libraries,
if they exist.
... the -ltermcap or -ltermlib libraries when loading
any curses library, if they exist.
=-=-=-=-=-=
The env command is available on most systems, and simply sets one
or more environment variables before running a command. If the
env command is not available to you, you can set the environment
variables in your shell before running configure. For example,
in sh or ksh, you could do:
ADDLIBS="-L/a/b -L/e/f -ldb" configure
and in csh or tcsh, you could do:
setenv ADDLIBS "-L/a/b -L/e/f -ldb"
configure
See your shell manual page for further information.
=-=-=-=-=-=-=
INSTALLING NVI
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Nvi installs the following files into the following locations, with
the following default values:
Variables: Default value:
prefix /usr/local
exec_prefix $(prefix)
bindir $(prefix)/bin
datadir $(prefix)/share
mandir $(prefix)/man
File(s): Default location
----------------------------------------
vi $(bindir)/vi
vi.1 $(mandir)/man1/vi.1
vi.0 $(mandir)/cat1/vi.0
Perl scripts $(datadir)/vi/perl/
Tcl scripts $(datadir)/vi/tcl/
Message Catalogs $(datadir)/vi/catalog/
Notes:
There are two hard links to the vi program, named ex and view.
Similarly, there are two hard links to the unformatted vi manual
page, named ex.1 and view.1, and two hard links to the formatted
manual page, named ex.0 and view.0. These links are created when
the program and man pages are installed.
If you want to install vi, ex, view and the man pages as nvi, nex,
nview, use the configure option --program-prefix=n. Other, more
complex transformations are possible -- use configure --help to
see more options.
To move the entire installation tree somewhere besides /usr/local,
change the value of both "exec_prefix" and "prefix". To move the
binaries to a different place, change the value of "bindir".
Similarly, to put the datafiles (the message catalogs, Perl and
Tcl scripts) or the man pages in a different place, change the
value of "datadir" or "mandir". These values can be changed as
part of configuration:
configure --exec_prefix=/usr/contrib --prefix=/usr/share
or when doing the install itself:
make exec_prefix=/usr/contrib prefix=/usr/contrib install
The datafile directory (e.g., /usr/local/share/vi by default) is
completely removed and then recreated as part of the installation
process.
=-=-=-=-=-=-=
NVI AND THE CURSES LIBRARY
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
The major portability problem for nvi is selecting a curses library.
Unfortunately, it is common to find broken versions of curses -- the
original System V curses was broken, resulting in all vendors whose
implementations are derived from System V having broken implementations
in turn.
If you use the vendor's or other curses library, and you see any of the
following symptoms:
+ Core dumps in curses routines.
+ Missing routines when compiling.
+ Repainting the wrong characters on the screen.
+ Displaying inverse video in the wrong places.
+ Failure to reset your terminal to the correct modes on exit.
you have a broken curses implementation, and you should reconfigure nvi
to use another curses library.
An alternative for your vendor's curses is ncurses, available from
ftp://ftp.gnu.org/pub/gnu/ncurses/
One final note. If you see the following symptoms:
+ Line-by-line screen repainting instead of scrolling.
it usually means that your termcap or terminfo information is insufficient
for the terminal.

320
dist/nvi/build.unix/README.LynxOS vendored Normal file
View File

@@ -0,0 +1,320 @@
README.LynxOS
=============
Written by Ronald F. Guilmette <rfg@monkeys.com>
Last modified Wed Aug 14 23:10:07 PDT 1996
------------------------------------------
0. Introduction
---------------
This file describes how to build and install the Berkeley nvi editor for
the LynxOS 2.4.0 operating system.
LynxOS 2.4.0 is available for a variety of different hardware platforms, in
particular, x86, m680x0, Sparc, and PowerPC. I have successfully built nvi
on all four of these flavors of LynxOS by following the procedures given in
this file.
Note that these procedures may not work on versions of LynxOS prior to 2.4.0.
(As I understand it, a good deal of work went into making the 2.4.0 release
more POSIX-compliant, and I have no idea what build glitches, if any, you
might encounter if you try to build nvi on a pre-2.4.0 version of LynxOS.)
There are basically four steps to configuring, building, and installing nvi
on LynxOS, namely:
1. Get setup to use the proper C compiler.
2. Replace your installed `tr' program.
3. Fix your system include files.
4. Do a normal configure, build, and install of nvi.
These steps are described in separate sections below.
1. Get Setup to Use the Proper C Compiler
------------------------------------------
The first step when building nvi on LynxOS is to set your $PATH environment
variable properly so that the gcc 2.x compiler appears first on your path,
prior to the older (and less robust) gcc 1.xx compiler (typically installed
as /bin/gcc) and/or the old Lynx proprietary C compiler (typically installed
as /bin/cc), both of which may also be present on your system.
Note that for most operating systems, the configure script for nvi tries
to use whatever compiler you have installed (and in your $PATH) as "cc",
however in the special case of LynxOS, the configure script will auto-
matically try to find a "gcc" program on your $PATH in preference to a
compiler called "cc". If the nvi configure script only find a compiler
called "cc", that's OK. It will still try to see if that is really just
the GNU C compiler installed under the name "cc".
Regardless of the name however (be it "gcc" or "cc") the first C compiler
in your $PATH should be some _recent_ (i.e. 2.0 or later) version of the
GNU C compiler... and the nvi configure script now checks that this is the
case, and fails if it isn't.
Oddly enough, LynxOS 2.4.0 (and some prior versions) shipped with as many
as three different C compilers installed, so it is important to set your
$PATH environment variable carfully in order to get the proper C compiler
to appear first in your $PATH. You want to avoid having either the /bin/gcc
compiler or the /bin/cc compiler be the first C compiler in your $PATH.
To make sure that the GNU C version 2.x compiler which was shipped with your
LynxOS system appears first on your path, you will need to either set your
$PATH variable (for sh/bash/ksh users) or your $path variable (for csh/tcsh
users). You can, of course, just do this at the shell command prompt, but
it is probably better to actually edit this change into your .profile file
(for sh/bash/ksh users) or into your .cshrc file (for csh/tcsh users).
The pathname of the directory that contains the GNU C version 2.x compiler
is (unfortunately) dependent upon the exact type of LynxOS system you have.
For LynxOS 2.4.0 on x86 systems, gcc 2.x is located in:
/cygnus/94q4-lynxos-x86/bin
For LynxOS 2.4.0 on m680x0 systems, gcc 2.x is located in:
/cygnus/94q4-lynxos-68k/bin
For LynxOS 2.4.0 on Sparc systems, gcc 2.x is located in:
/cygnus/94q4-lynxos-usparc/bin
For LynxOS 2.4.0 on PowerPC systems, gcc 2.x is located in:
/cygnus/95q2-lynxos-ppc/bin
(Note also that these locations may change in LynxOS 2.5.x and beyond.)
Anyway, it is imperative that you setup your $PATH environment variable
(*before* you do the configure step for nvi) so that the GNU C version 2.x
compiler appears in your $PATH before either the /bin/cc or /bin/gcc
compilers (if present). If you fail to do this, the configure step for
nvi will fail, because the compiler script actually checks (now) that the
compiler you are using (if your are on a LynxOS system) is gcc 2.0 or
later.
To make absolutely sure that you will be configuring and building nvi with
the proper C compiler (i.e. the GNU C version 2.x compiler on your system)
you should add the directory name listed above for your specific system type
to your $PATH setting in your $HOME/.profile file. (For csh/tcsh users, you
will instead want to add the relevant directory name to the setting of your
$path variable in your ~/.cshrc file.) Once you have added the proper direc-
tory name (from the list given above) to your $HOME/.profile file (or to your
~/.cshrc file, if you are using csh or tcsh) you should log out completely
and then log back into the system just to make sure your new $PATH/$path
setting takes effect properly.
When you finish making this adjustment to your $PATH (or $path), the most
up-to-date version of gcc on your system should be available to you as the
first `gcc' program on your $PATH. You should verify that this is indeed the
case simply by typing `gcc -v' and then checking the version number reported
by the compiler. It should say either "2.6-94q4" or (on PowerPC systems) it
should say "2.6-95q2". If you don't get these results, try again to set your
$PATH (or $path) until you do. You won't be able to build nvi until you are
properly setup to use gcc version 2.0 or later.
Performing the steps shown above will insure that your subsequent configura-
tion and build steps for nvi will make use of the most up-to-date version of
gcc that was shipped with your Lynx operating system. (Note that the versions
of gcc which are currently shipping with LynxOS 2.4.0 are also somewhat out-
of-date themselves, but they are still quite a bit newer and more bug-free
and ANSI conformant that those other two C compilers, /bin/cc and /bin/gcc,
which also ship with LynxOS 2.4.0.)
(Note: At present, LynxOS version 2.4.0 is the latest officially released
version of LynxOS, and all of the above information is accurate and correct
for LynxOS 2.4.0 as of the time of this writing. However it is rumored that
future releases of LynxOS may provide a still newer version of gcc, and that
it may be located in the /usr/bin directory. Thus, if you are building nvi
for some LynxOS version later than 2.4.0, you may wish to check and see if
your system has a program called /usr/bin/gcc, and use that version of gcc,
if available, rather than the one suggested above.)
2. Replace Your Installed `tr' Program
---------------------------------------
The `tr' program which comes bundled with LynxOS 2.4.0 (as /bin/tr) has a
somewhat obscure bug which just happens to be tickled by almost all GNU
`autoconf' generated `configure' scripts (including the one that nowadays
comes bundled with nvi). Using the stock /bin/tr program on LynxOS when
executing such `configure' scripts _will_ cause these scripts to malfunction
in various ways. It is therefore imperative that you replace your LynxOS
/bin/tr program with a properly working version of the `tr' command _before_
you even try to configure nvi. (You can tell if your `tr' program has the
bug by executng the command "echo ab- | tr ab- ABC". If this yields the
string "Ab-" then you have the bug. If it yields "ABC" then you don't.)
You can obtain sources for a working version of the `tr' command as part of
the GNU `textutils' package (the latest version of which, at the time of this
writing, is 1.19). The GNU textutils package is available for downloading
from prep.ai.mit.edu in the pub/gnu directory. Look for the file named
textutils-1.19.tar.gz, or an even more recent version of textutils, if one
is available. Fetch it, gunzip it, untar it, and follow the directions in
the INSTALL file included in the tar file to build and install the entire
textutils set of utility programs (which includes a working `tr' program).
Then just make sure that the GNU version of `tr' appears on your $PATH
_before_ the LynxOS version of `tr' (i.e. /bin/tr). Be sure to do this
step _before_ you start to configure nvi.
When building the textutils set of programs, I suggest that you use the most
up-to-date C compiler available on your system (as described above). Also,
note that it will be important for you to AVOID using the -O (optimize)
compiler option when building the GNU textutils package, even if you are
using the most up-to-date version of gcc which shipped with your system.
If you try to use -O when building the textutils package on an x86 with
the Cygnus 94q4 C compiler, you will end up with a `tr' program which will
malfunction even worse than the one you are trying to replace! If you use
-O when building the textutils package on LynxOS on the PowerPC (using the
Cygnus 95q2 C compiler) you will just get yourself a compiler crash. So
just don't use -O when building textutils. You can avoid using -O by in-
voking make in the textutils directory as follows:
make CFLAGS="-g"
(Note: At present, LynxOS version 2.4.0 is the latest officially released
version of LynxOS, and all of the above information is accurate and correct
for LynxOS 2.4.0 as of the time of this writing. However it is rumored that
the bug in the /bin/tr program will be fixed in future releases of LynxOS,
so if you have a version of LynxOS later than 2.4.0, you may wish to check
and see if your /bin/tr program even has the problematic bug before bothering
with all of this.)
3. Fix Your System Include Files
---------------------------------
If you are building nvi on a PowerPC system, it is also important that you
apply the patches given at the end of this file to your /usr/include files.
(Note that you will have to be root in order to do this.) Two of the patches
included below fix a pair of serious bugs in the /usr/include/stdarg.h file
on the PowerPC, and you really _do_ want to have these bugs fixed anyway,
because without these fixes, anything that you compile which uses <stdarg.h>
will very likely malfunction at run-time.
Regardless of which LynxOS platform you are using (i.e. x86, PowerPC, Sparc,
or m680x0) you may want to apply all of the system include files patches that
are included below anyway. Doing so will clean up a few minor problems with
the relevant system include files (i.e. <stdarg.h>, <ioctl.h>, and <wait.h>)
and this step will also prevent a few warnings which you would otherwise get
during the build of nvi.
You can apply all of the patches given at the end of this file simply by
doing the following:
su root
cd /usr/include
/bin/patch < this-file
Where `this-file' is the actual full pathname of the file you are now reading,
wherever it may reside on your own system.
(Note: At present, LynxOS version 2.4.0 is the latest officially released
version of LynxOS, and all of the above information is accurate and correct
for LynxOS 2.4.0 as of the time of this writing. However it is rumored that
future releases of LynxOS may incorporate some or all of the important system
include file fixes provided below. Thus, if you are building nvi for some
LynxOS version later than 2.4.0, you should probably go ahead and try to
apply the patches given below to your system include files, and then just
don't worry about it if these patches seem to have already been applied.)
4. A Brief Note about Sendmail
-------------------------------
I should mention also that LynxOS does not normally ship with the `sendmail'
mail transfer program installed, either under /usr/lib/ or anywhere else for
that matter. This isn't really a big problem, but nvi normally wants and
expects to have a sendmail program available so that it can send users notifi-
cations (by mail) whenever a partially edited file is preserved by the editor
in response to a sudden system crash, a sudden system shutdown, or an unexpect-
ed serial-line hangup. You can configure and build nvi without any sendmail
program installed on your system, but you will get warnings about its absence
when you are doing the initial configure step prior to actually building nvi.
If you want to have a fully-functional nvi which does send out notification
messages (by mail) whenever partially edited files are preserved during a
serial line hangup or system crash, then you should get the BSD sendmail
sources (via ftp from ftp.cs.berkeley.edu), build and install sendmail, and
then reconfigure, rebuild, and reinstall nvi.
Please contact me at the E-mail address below if you experience any problems in
building or using nvi on LynxOS. I make no guarrantees, but I may be willing
to try to help.
Ron Guilmette
Roseville, California
<rfg@monkeys.com>
August 14, 1996
cut here for LynxOS 2.4.0 system include files patches
-----------------------------------------------------------------------------
*** wait.h Fri Apr 26 10:02:45 1996
--- wait.h Sun May 19 05:36:50 1996
***************
*** 94,104 ****
/* Function prototypes */
#ifndef __LYNXOS
- #ifdef _POSIX_SOURCE
extern pid_t wait _AP((int *));
extern pid_t waitpid _AP((pid_t, int *, int));
! #else
! extern int wait _AP((union wait *));
! extern int waitpid _AP((int, union wait *, int));
! extern int wait3 _AP((union wait *, int, struct rusage *));
#endif
#endif /* !__LYNXOS */
--- 94,101 ----
/* Function prototypes */
#ifndef __LYNXOS
extern pid_t wait _AP((int *));
extern pid_t waitpid _AP((pid_t, int *, int));
! #ifndef _POSIX_SOURCE
! extern int wait3 _AP((int *, int, struct rusage *));
#endif
#endif /* !__LYNXOS */
*** ioctl.h Fri Apr 26 16:50:51 1996
--- ioctl.h Sat May 18 17:55:16 1996
***************
*** 572,576 ****
#ifndef __LYNXOS
! extern int ioctl _AP((int, int, char *));
#endif
--- 572,576 ----
#ifndef __LYNXOS
! extern int ioctl _AP((int, int, ...));
#endif
*** stdarg.h Fri Apr 26 16:51:02 1996
--- stdarg.h Sat May 18 19:34:13 1996
***************
*** 88,92 ****
(((sizeof(TYPE) + sizeof(int) - 1) / sizeof(int)) * sizeof(int))
! #define va_start(AP, LASTARG) (AP = ((char *) __builtin_next_arg ()))
void va_end(va_list); /* Defined in libgcc.a */
--- 88,92 ----
(((sizeof(TYPE) + sizeof(int) - 1) / sizeof(int)) * sizeof(int))
! #define va_start(AP, LASTARG) (AP = ((char *) __builtin_next_arg (LASTARG)))
void va_end(va_list); /* Defined in libgcc.a */
***************
*** 162,166 ****
(((sizeof(TYPE) + sizeof(int) - 1) / sizeof(int)) * sizeof(int))
! #define va_start(AP, LASTARG) (AP = ((char *) __builtin_next_arg ()))
void va_end(va_list); /* Defined in libgcc.a */
--- 162,166 ----
(((sizeof(TYPE) + sizeof(int) - 1) / sizeof(int)) * sizeof(int))
! #define va_start(AP, LASTARG) (AP = ((char *) __builtin_next_arg (LASTARG)))
void va_end(va_list); /* Defined in libgcc.a */

10
dist/nvi/build.unix/README.Solaris vendored Normal file
View File

@@ -0,0 +1,10 @@
There are some known problems with Solaris curses.
Please give ncurses a try when you encounter a screen output problem.
Apparently, the more recent Solaris compilers invoke the incremental linker,
`ild', when the "-g" option is used and one or more "xxx.o" files are
specified on the command line during the link phase. Unfortunately, the
executable produced is up to 3-4 times as big as an executable generated
by the standard linker `ld'.
You can bypass this by adding "-xildoff" to LDFLAGS or by
setting CFLAGS (to something that does not contain "-g").

84
dist/nvi/catalog/Makefile vendored Normal file
View File

@@ -0,0 +1,84 @@
# Id: Makefile,v 8.30 1996/11/27 11:59:09 bostic Exp (Berkeley) Date: 1996/11/27 11:59:09
CAT= dutch english french german ru_RU.KOI8-R spanish swedish
FILES= ../cl/*.c ../common/*.c ../ex/*.c ../vi/*.c
all: dump ${CAT}
${CAT}: english.base
@echo "... $@"; \
rm -f $@; \
sort -u $@.base | \
awk '{ \
if ($$1 == 1) { \
print "\nMESSAGE NUMBER 1 IS NOT LEGAL"; \
exit 1; \
} \
if (++nline > $$1) { \
print "DUPLICATE MESSAGE NUMBER " $$1; \
exit 1; \
} \
for (; nline < $$1; ++nline) \
print ""; \
print $0; \
}' | \
sed -e '1s/^/VI_MESSAGE_CATALOG/' \
-e '/"/s/^[^"]*"//' \
-e '1!s/"$$/X/' > $@; \
chmod 444 $@; \
if grep DUPLICATE $@ > /dev/null; then \
grep DUPLICATE $@; \
fi; \
if grep 'NOT LEGAL' $@ > /dev/null; then \
grep 'NOT LEGAL' $@; \
fi
CHK= dutch.check english.check french.check german.check \
ru_RU.KOI8-R.check spanish.check swedish.check
check: ${CHK}
${CHK}: ${CAT}
@echo "... $@"; \
f=`basename $@ .check`; \
(echo "Unused message id's (this is okay):"; \
awk '{ \
while (++nline < $$1) \
printf "%03d\n", nline; \
}' < $$f.base; \
echo =========================; \
echo "MISSING ERROR MESSAGES (Please add!):"; \
awk '{print $$1}' < $$f.base | sort -u > __ck1; \
awk '{print $$1}' < english.base | sort -u > __ck2; \
comm -13 __ck1 __ck2; \
echo =========================; \
echo "Extra error messages (just delete them):"; \
comm -23 __ck1 __ck2; \
echo =========================; \
echo "MESSAGES WITH THE SAME MESSAGE ID's (FIX!):"; \
for j in \
`sed '/^$$/d' < $$f.base | sort -u | \
awk '{print $$1}' | uniq -d`; do \
egrep $$j $$f.base; \
done; \
echo =========================; \
echo "Duplicate messages, both id and message (this is okay):"; \
sed '/^$$/d' < $$f.base | sort | uniq -c | \
awk '$$1 != 1 { print $$0 }' | sort -n; \
echo =========================; \
echo "Duplicate messages, just message (this is okay):"; \
sed '/^$$/d' < $$f | sort | uniq -c | \
awk '$$1 != 1 { print $$0 }' | sort -n; \
echo =========================) > $@
english.base: dump ${FILES} #Makefile
./dump ${FILES} |\
sed -e '/|/!d' \
-e 's/|/ "/' \
-e 's/^"//' \
-e 's/\\"/"/g' |\
sort -n > $@
dump: dump.c
${CC} -O -o dump dump.c
clean:
rm -f dump dump.o ${CAT} english.base *.check __ck1 __ck2

166
dist/nvi/catalog/README vendored Normal file
View File

@@ -0,0 +1,166 @@
# Id: README,v 8.4 1994/11/22 09:52:04 bostic Exp (Berkeley) Date: 1994/11/22 09:52:04
Generally, all non-system error and informational messages in nvi are
catalog messages, i.e. they can be tailored to a specific langauge.
Command strings, usage strings, system errors and other "known text"
are not. It would certainly be possible to internationalize all the
text strings in nvi, but it's unclear that it's the right thing to do.
First, there's no portable way to do message catalogs. The System V
scheme is a reasonable choice, but none of the 4BSD derived systems
support it. So, catalogs are completely implemented within nvi, and
don't require any library support.
Message catalogs in nvi are fairly simple. Every catalog message
consists of two parts -- an initial number followed by a pipe (`|')
character, followed by the English text for the message. For example:
msgq(sp, M_ERR, "001|This is an error message");
would be a typical message.
When the msgq() routine is called, if the user has specified a message
catalog and the format string (the third argument) has a leading number,
then it is converted to a record number, and that record is retrieved
from the message catalog and used as a replacement format string. If
the record can't be retrieved for any reason, the English text is displayed
instead.
Each message format string MUST map into the English format string, i.e.
it can't display more or different arguments than the English one.
For example:
msgq(sp, M_ERR, "002|Error: %d %x", arg1, arg2);
is a format string that displays two arguments. It is possible, however,
to reorder the arguments or to not display all of them. The convention
nvi uses is the System V printf(3) convention, i.e. "%[0-9]*$" is the name
of a specific, numbered argument. For example:
msgq(sp, M_ERR, "002|Error: %2$d %1$x", arg1, arg2);
displays the arguments in reverse order.
If the system supports this convention in its library printf routines
(as specified by the test #define NL_ARGMAX), nvi uses those routines.
Otherwise, there is some serious magic going on in common/msg.c to make
this all work.
Arguments to the msgq function are required to contain ONLY printable
characters. No further translation is done by the msgq routine before
displaying the message on the screen. For example, in the msgq call:
msgq(sp, M_ERR, "003|File: %s", file_name);
"file_name" must contain only printable characters. The routine
msg_print() returns a printable version of a string in allocated
memory. For example:
char *p;
p = msg_print(sp, file_name);
msgq(sp, M_ERR, M("003", "File: %s"), p);
FREE_SPACE(sp, p, 0);
makes sure that "file_name" is printable before calling the msgq
routine.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
The message catalogs themselves are maintained in two files. The first
is the "base file" which contains two fields, a record number and the
message itself. All base files are named using the convention
"vi_<language>.base", e.g. the English one is "vi_english.base". For
example:
002 "Unable to create temporary file"
003 "Warning: %s is not a regular file"
004 "%s already locked, session is read-only"
005 "%s: remove"
006 "%s: close"
007 "%s: remove"
008 "%s: remove"
009 "Read-only file, not written; use ! to override"
010 "Read-only file, not written"
are the first few lines of the current vi_english.base file. Note that
message #1 is missing -- the first message of each catalog is a special
one, so that nvi can recognize message catalog files. It's added by the
Makefile script that creates the second version of the message catalog.
The second file is the file used by nvi to access messages, and is a list
of the messages, one per line:
VI_MESSAGE_CATALOG
Unable to create temporary fileX
Warning: %s is not a regular fileX
%s already locked, session is read-onlyX
%s: removeX
%s: closeX
%s: removeX
%s: removeX
Read-only file, not written; use ! to overrideX
Read-only file, not writtenX
Note that all messages have had a trailing 'X' character appended. This
is to provide nvi a place to store a trailing nul for the message so that
C library routines that expect one won't be disappointed.
These files are named for their language, e.g. "vi_english". The second
files are automatically created from the first files.
To create a new catalog for nvi:
Copy the file vi_english.base to a file that you can modify , e.g. "cp
vi_english.base vi_german.base". For each of the messages in the file,
replace the message with the string that you want to use. To find out
what the arguments to a message are, I'm afraid you'll have to search
the source code for the message number. You can find them fairly quickly
by doing:
cd ..; egrep '123\|' */*.[chys]
I'm sorry that there's not an easier way, but I couldn't think of
anything that wasn't a lot of work.
If, for some reason, you don't have the file vi_english.base, or you
have new sources for which you want to create a new base catalog, you
can create it by running the command "make english" in the catalog
directory.
Once you've translated all of the strings, then add your catalog to the
"CAT=" line of the Makefile, and run the command "make catalog". This
will create the second (and corresponding) file for each file named
<language>.base.
Don't worry about missing line numbers, i.e. base files that look like:
005 Message number 5.
007 Message number 7.
This simply means that a message was deleted during the course of nvi's
development. It will be taken care of automatically when you create
the second form of the file.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
If you add new messages to the nvi sources, you can check your work by
doing "make english; make check". The "make check" target lists unused
message numbers, duplicate message numbers, and duplicate messages.
Unused message numbers are only useful if you are condensing messages.
Duplicate message numbers are a serious problem and have to be fixed.
Duplicate messages are only interesting if a message appears often enough
that it's worth creating a routine so that the string is only need in
a single place.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
To select a catalog when running nvi, set the "msgcat" option. If the
value of this option ends with a '/', it is treated as the name of a
directory that contains a message catalog "vi_XXXX", where XXXX is the
value of the LANG environmental variable, if it's set, or the value of
the LC_MESSAGES environmental variable if it's not. If neither of those
environmental variables are set, or if the option doesn't end in a '/',
the option is treated as the full path name of the message catalog to use.
If any messages are missing from the catalog, the backup text (English)
is used instead.

91
dist/nvi/catalog/dump.c vendored Normal file
View File

@@ -0,0 +1,91 @@
/* $NetBSD: dump.c,v 1.2 2008/06/11 21:30:52 aymeric Exp $ */
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* %sccs.include.redist.c%
*/
#ifndef lint
static char copyright[] =
"%Z% 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[] = "Id: dump.c,v 8.1 1994/08/31 13:27:37 bostic Exp (Berkeley) Date: 1994/08/31 13:27:37";
#endif /* not lint */
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
static void
parse(fp)
FILE *fp;
{
int ch, s1, s2, s3;
#define TESTD(s) { \
if ((s = getc(fp)) == EOF) \
return; \
if (!isdigit(s)) \
continue; \
}
#define TESTP { \
if ((ch = getc(fp)) == EOF) \
return; \
if (ch != '|') \
continue; \
}
#define MOVEC(t) { \
do { \
if ((ch = getc(fp)) == EOF) \
return; \
} while (ch != (t)); \
}
for (;;) {
MOVEC('"');
TESTD(s1);
TESTD(s2);
TESTD(s3);
TESTP;
putchar('"');
putchar(s1);
putchar(s2);
putchar(s3);
putchar('|');
for (;;) { /* dump to end quote. */
if ((ch = getc(fp)) == EOF)
return;
putchar(ch);
if (ch == '"')
break;
if (ch == '\\') {
if ((ch = getc(fp)) == EOF)
return;
putchar(ch);
}
}
putchar('\n');
}
}
int
main(argc, argv)
int argc;
char *argv[];
{
FILE *fp;
for (; *argv != NULL; ++argv) {
if ((fp = fopen(*argv, "r")) == NULL) {
perror(*argv);
exit (1);
}
parse(fp);
(void)fclose(fp);
}
exit (0);
}

306
dist/nvi/catalog/dutch.base vendored Normal file
View File

@@ -0,0 +1,306 @@
002 "regel te lang"
003 "kan regel %lu niet verwijderen"
004 "kan niet toevoegen aan regel %lu"
005 "kan niet invoegen vooraan regel %lu"
006 "kan regel %lu niet opslaan"
007 "kan laatste regel niet lezen"
008 "Fout: kan regel %lu niet vinden"
009 "log bestand"
010 "Er vindt geen logging plaats, kan wijzigingen niet ongedaan maken"
011 "geen wijzigingen om ongedaan te maken"
012 "Er vindt geen logging plaats, kan wijzigingen niet ongedaan maken"
013 "Er vindt geen logging plaats, herhaling niet mogelijk"
014 "geen wijzigingen om te herhalen"
015 "%s/%d: schrijven naar log mislukt"
016 "Vi's standaard invoer en uitvoer moeten aan een terminal gekoppeld zijn"
017 "Merk %s: niet gezet"
018 "Merk %s: de regel is verwijderd"
019 "Merk %s: de cursor positie bestaat niet meer"
020 "Fout: "
021 "nieuw bestand"
022 "naam veranderd"
023 "gewijzigd"
024 "ongewijzigd"
025 "NIET BEVEILIGD"
026 "niet schrijfbaar"
027 "regel %lu uit %lu [%ld%%]"
028 "leeg bestand"
029 "regel %lu"
030 "Het bestand %s is geen message catalog"
031 "Niet in staat om de standaard %s optie in te stellen"
032 "Gebruik: %s"
033 "set: optie %s onbekend: 'set all' laat alle opties zien"
034 "set: [no]%s optie kan geen waarde hebben"
035 "set: %s optie moet een waarde hebben"
036 "set: %s optie: %s"
037 "set: %s optie: %s: getal is te groot"
038 "set: %s optie: %s is een ongeldige waarde"
039 "set: %s optie moet een waarde hebben"
040 "Te weinig kolommen op het scherm, minder dan %d"
041 "Aantal kolommen te groot, meer dan %d"
042 "Te weinig regels op het scherm, minder dan %d"
043 "Aantal regels te groot, meer dan %d"
044 "De lisp optie is niet ondersteund"
045 "messages niet uitgeschakeld: %s"
046 "messages niet geactiveerd: %s"
047 "set: de %s optie moet karakter paren bevatten"
053 "De standaard buffer is leeg"
054 "Buffer %s is leeg"
055 "Bestanden met newlines in de naam kunnen niet hersteld worden"
056 "Wijzigingen kunnen niet ongedaan gemaakt worden als deze sessie mislukt"
057 "Bestand wordt gecopieerd voor herstel..."
058 "Herstel mechanisme werkt niet: %s"
059 "Wijzigingen kunnen niet ongedaan gemaakt worden als deze sessie mislukt"
060 "Kon bestand niet veilig stellen: %s"
061 "Bestand wordt gecopieerd voor herstel..."
062 "Informatie met betrekking tot gebruiker nummer %u niet gevonden"
063 "Kan herstel bestand niet beveiligen"
064 "herstel buffer overgelopen"
065 "herstel bestand"
066 "%s: verminkt herstel bestand"
067 "%s: verminkt herstel bestand"
068 "U heeft geen bestand genaamd %s te herstellen"
069 "U kan eerdere versies van dit bestand herstellen"
070 "U kan nog meer bestanden herstellen"
071 "kan geen email versturen: %s"
072 "Bestand leeg; niets om te doorzoeken"
073 "Einde van het bestand bereikt zonder dat het patroon gevonden is"
074 "Geen vorig zoek patroon"
075 "Patroon niet gevonden"
076 "Begin van het bestand bereikt zonder dat het patroon gevonden is"
077 "Zoek-operatie omgeslagen"
078 "Bezig met zoeken..."
079 "Geen niet-printbaar karakter gevonden"
080 "Onbekend commando"
082 "Commando niet beschikbaar in ex mode"
083 "Aantal mag niet nul zijn"
084 "%s: ongeldige regel aanduiding"
085 "Interne fout in syntax tabel (%s: %s)"
086 "Gebruik: %s"
087 "%s: tijdelijke buffer niet vrijgegeven"
088 "Vlag offset voor regel 1"
089 "Vlag offset voorbij bestands einde"
090 "bestand/scherm veranderd tijdens uitvoeren van @ in een blok"
091 "bestand/scherm veranderd tijdens uitvoeren van globaal/v commando"
092 "Ex commando mislukt: rest van commando(s) genegeerd"
093 "Ex commando mislukt: gemappede toetsen genegeerd"
094 "Het tweede adres is kleiner dan het eerste"
095 "Geen merk naam opgegeven"
096 "\\ niet gevolgd door / of ?"
097 "Referentie aan een regel nummer kleiner dan 0"
098 "Het %s commando is onbekend"
099 "Adres waarde te groot"
100 "Adres waarde te klein"
101 "Ongeldige adres combinatie"
102 "Ongeldig adres: slechts %lu regels in het bestand aanwezig"
103 "Ongeldig adres: het bestand is leeg"
104 "Het %s commando staat het adres 0 niet toe"
105 "Geen afkortingen om weer te geven"
106 "Afkortingen moeten eindigen met een "woord" letter"
107 "Afkortingen mogen geen tabulaties of spaties bevatten"
108 "Afkortingen mogen geen woord/niet-woord karakters mengen, behalve aan het einde"
109 ""%s" is geen afkorting"
110 "Vi commando mislukt: gemappede toetsen genegeerd"
111 "Dit is het laatste bestand"
112 "Dit is het eerste bestand"
113 "Dit is het eerste bestand"
114 "lijst met bestanden is leeg"
115 "Geen voorgaand commando om "!" te vervangen"
116 "Geen bestandsnaam voor %%"
117 "Geen bestandsnaam voor #"
118 "Fout: execl: %s"
119 "I/O fout: %s"
120 "Bestand gewijzigd sinds laatste schrijfactie; schrijf het weg of gebruik ! om het te forceren"
121 "Kan uw home directory niet vinden"
122 "Nieuwe huidige directory: %s"
123 "Geen cut buffers aanwezig"
124 "Het %s commando kan niet gebruikt worden in een globaal of v commando"
125 "%s/%s: niet gelezen: noch U noch root is de eigenaar"
126 "%s/%s: niet gelezen: U bent niet de eigenaar"
127 "%s/%s: niet gelezen: kan gewijzigd worden door andere gebruikers"
128 "%s: niet gelezen: noch U noch root is de eigenaar""
129 "%s: niet gelezen: U bent niet de eigenaar"
130 "%s: niet gelezen: kan gewijzigd worden door andere gebruikers"
131 "Geen volgende regel om samen te voegen"
132 "Geen input map entries"
133 "Geen command map entries"
134 "Het %s karakter kan niet ge-remapped worden"
135 ""%s" is niet gemapped"
136 "Merk naam moet een enkel karakter zijn"
137 "%s bestaat al, niet weggeschreven; gebruik ! om het te forceren"
138 "Nieuw .exrc bestand: %s. "
139 "doel regel ligt in het blok"
140 "Het open commando vereist dat de open optie actief is"
141 "Het open commando is nog niet ondersteund"
142 "Kan dit bestand niet veilig stellen"
143 "Bestand veilig gesteld"
144 "%s resulteert in te veel bestandsnamen"
145 "Alleen echte bestanden en named pipes kunnen gelezen worden"
146 "%s: lees beveiliging niet beschikbaar"
147 "Bezig met lezen..."
148 "%s: %lu regels, %lu karakters"
149 "Geen achtergrond schermen aanwezig"
150 "Het script commando is alleen beschikbaar in vi mode"
151 "Geen comando om uit te voeren"
152 "shiftwidth optie op 0 gezet"
153 "Count te groot"
154 "Count te klein"
155 "Reguliere expressie opgegeven; r vlag heeft geen betekenis"
156 "De #, l en p vlaggen kunnen niet gecombineerd worden met de c vlag in vi mode"
157 "Geen match gevonden"
158 "Geen voorafgaande tag aanwezig"
159 "Minder dan %s elementen op de tags stapel; gebruik :display t[ags]"
160 "Geen bestand genaamd %s op de tags stapel; gebruik :display t[ags]"
161 "Kies Enter om door te gaan: "
162 "%s: tag niet gevonden"
163 "%s: verminkte tag in %s"
164 "%s: Het regel nummer van deze tag is voorbij het einde van het bestand"
165 "De tags stapel is leeg"
166 "%s: zoek patroon niet gevonden"
167 "%d andere bestanden te wijzigen"
168 "Buffer %s is leeg"
169 "Bevestig wijziging? [n]"
170 "Onderbroken"
171 "Geen voorafgaande buffer om uit te voeren"
172 "Geen vorige reguliere expressie"
173 "Het %s commando vereist dat er een bestand geladen is"
174 "Gebruik: %s"
175 "Het visual commando vereist dat de open optie actief is"
177 "Leeg bestand"
178 "Geen voorafgaand F, f, T of t zoek commando"
179 "%s niet gevonden"
180 "Geen voorafgaand bestand te bewerken"
181 "Cursor niet op een getal"
182 "Getal wordt te groot"
183 "Getal wordt te klein"
184 "Geen overeenkomstig karakter op deze regel"
185 "Overeenkomstig karakter niet gevonden"
186 "Geen karakters te vervangen"
187 "Geen ander scherm aanwezig"
188 "Karakters achter het zoek patroon, de regel offset, en/of het z commando"
189 "Geen voorafgaand zoek patroon"
190 "Zoekopdracht na omslag teruggekeerd op originele positie"
191 "Afkorting overschrijdt expansie limiet: karakters genegeerd"
192 "Ongeldig karakter; quote to enter"
193 "Reeds aan het begin van de invoer"
194 "Niet meer karakters te verwijderen"
195 "Verplaatsing voorbij het einde van het bestand"
196 "Verplaatsing voorbij het einde van de regel"
197 "Cursor niet verplaatst"
198 "Reeds aan het begin van het bestand"
199 "Verplaatsing voorbij het begin van het bestand"
200 "Reeds in de eerste kolom"
201 "Buffers moeten voor het commando opgegeven worden"
202 "Reeds bij het einde van het bestand"
203 "Reeds bij het einde van de regel"
204 "%s is geen vi commando"
205 "Gebruik: %s"
206 "Geen karakters te verwijderen"
207 "Het Q commando vereist de ex terminal interface"
208 "Geen commando om te herhalen"
209 "Het bestand is leeg"
210 "%s mag niet gebruikt worden als een verplaatsings commando"
211 "Al in commando mode"
212 "Cursor niet in een woord"
214 "Windows optie waarde is te groot, maximum is %u"
215 "Toevoegen"
216 "Veranderen"
217 "Commando"
218 "Invoegen"
219 "Vervangen"
220 "Verplaatsing voorbij het eind van het scherm"
221 "Verplaatsing voorbij het begin van het scherm"
222 "Scherm moet meer dan %d regels hebben om het te kunnen splitsen"
223 "Er zijn geen achtergrond schermen"
224 "Er is geen achtergrond scherm waarin U bestand %s aan het bewerken bent"
225 "U kan uw enige scherm niet in de achtergrond zetten"
226 "Het scherm kan slechts verkleind worden tot %d regels"
227 "Het scherm kan niet kleiner"
228 "Het scherm kan niet groter"
230 "Dit scherm kan niet gesuspend worden"
231 "Onderbroken: gemappede toetsen genegeerd"
232 "vi: tijdelijke buffer niet vrijgegeven"
233 "Deze terminal heeft geen %s toets"
234 "Er kan slechts een buffer opgegeven worden"
235 "Getal groter dan %lu"
236 "Onderbroken"
237 "Aanmaken van tijdelijk bestand is mislukt"
238 "Waarschuwing: %s is geen regulier bestand"
239 "%s is al geopend, bestand is in deze sessie niet schrijfbaar"
240 "%s: verwijdering mislukt"
241 "%s: sluiting mislukt"
242 "%s: verwijdering mislukt"
243 "%s: verwijdering mislukt"
244 "Bestand niet schrijfbaar, niet weggeschreven; gebruik ! om het te forceren"
245 "Bestand niet schrijfbaar, niet weggeschreven"
246 "%s bestaat al, niet weggeschreven; gebruik ! om het te forceren"
247 "%s bestaat al, niet weggeschreven"
248 "Gebruik ! om een incompleet bestand weg te schrijven"
249 "Bestand incompleet, niet weggeschreven"
250 "%s: bestand op disk nieuwer dan deze versie; gebruik ! om het te forceren"
251 "%s: bestand op disk nieuwer dan deze versie"
252 "%s: schrijf beveiliging niet beschikbaar"
253 "Bezig met schrijven..."
254 "%s: WAARSCHUWING: BESTAND INCOMPLEET"
255 "Reeds op de eerste tag van deze groep"
256 "%s: nieuw bestand: %lu regels, %lu karakters"
257 "%s: %lu regels, %lu karakters"
258 "%s resulteert in te veel bestandsnamen"
259 "%s: geen normaal bestand"
260 "%s: U bent niet de eigenaar"
261 "%s: kan gewijzigd worden door andere gebruikers"
262 "Bestand gewijzigd sinds laatste schrijfactie; schrijf het weg of gebruik ! om het te forceren"
263 "Bestand gewijzigd sinds laatste schrijfactie; schrijf het weg of gebruik :edit! om het te forceren"
264 "Bestand gewijzigd sinds laatste schrijfactie; schrijf het weg of gebruik ! om het te forceren"
265 "Tijdelijk bestand; exit negeert wijzigingen"
266 "Bestand niet schrijfbaar, wijzigingen niet automatisch weggeschreven"
267 "log opnieuw gestart"
268 "Bevestig? [ynq]"
269 "Druk op een toets om door te gaan: "
270 "Druk op een toets om door te gaan [: voor meer ex commandos]: "
271 "Druk op een toets om door te gaan [q om te stoppen]: "
272 "Deze vorm van %s vereist de ex terminal interface"
273 "Entering ex input mode."
274 "Commando mislukt, nog geen bestand geladen."
275 " doorgaan?"
276 "Onverwacht character event"
277 "Onverwacht end-of-file event"
278 "Geen match gevonden voor dit patroon"
279 "Onverwacht interrupt event"
280 "Onverwacht quit event"
281 "Onverwacht repaint event"
282 "Reeds op de laatste tag van deze groep"
283 "Het %s command vereist de ex terminal interface"
284 "Deze vorm van %s is niet ondersteund als de secure edit optie actief is"
285 "Onverwacht string event"
286 "Onverwacht timeout event"
287 "Onverwacht write event"
289 "Shell expansies zijn niet ondersteund als de secure edit optie actief is"
290 "Het %s commando is niet ondersteund als de secure edit optie actief is"
291 "set: %s mag niet uitgezet worden"
292 "Scherm te klein."
293 "toegevoegd"
294 "gewijzigd"
295 "verwijderd"
296 "samengevoegd"
297 "verplaatst"
298 "verschoven"
299 "gebufferd"
300 "regel"
301 "regels"
302 "Vi was niet geladen met een Tcl interpreter"
303 "Bestand gewijzigd sinds het de laatste keer weg is geschreven."
304 "Shell expansie mislukt"
305 "Geen %s edit optie opgegeven"
306 "Vi was niet geladen met een Perl interpreter"
307 "Geen ex commando om uit te voeren"
308 "Kies <CR> om commando uit te voeren, :q om te stoppen"
309 "Gebruik "cscope help" voor uitleg"
310 "Nog geen cscope connectie aanwezig"
311 "%s: onbekend zoek type: gebruik een van %s"
312 "%d: onbekende cscope sessie"
313 "set: de %s optie mag nooit aangezet worden"
314 "set: de %s optie mag nooit op 0 gezet worden"
315 "%s: toegevoegd: %lu regels, %lu karakters"
316 "Onverwacht resize event"
317 "%d bestanden te wijzigen"

1
dist/nvi/catalog/dutch.owner vendored Normal file
View File

@@ -0,0 +1 @@
J.G. Vons <Gert-Jan.Vons@ocegr.fr>

1
dist/nvi/catalog/english.owner vendored Normal file
View File

@@ -0,0 +1 @@
Keith Bostic <bostic@cs.berkeley.edu>

308
dist/nvi/catalog/french.base vendored Normal file
View File

@@ -0,0 +1,308 @@
002 "Dépassement de longueur de ligne"
003 "impossible de supprimer la ligne %lu"
004 "impossible d'ajouter à la ligne %lu"
005 "impossible d'insérer devant la ligne %lu"
006 "impossible de stocker la ligne %lu"
007 "impossible d'obtenir la dernière ligne"
008 "Erreur : impossible de récupérer la ligne %lu"
009 "Fichier journal"
010 "Aucune journalisation n'étant effectuée, impossible d'annuler"
011 "Aucune action à annuler"
012 "Aucune journalisation n'étant effectuée, impossible d'annuler"
013 "Aucune journalisation n'étant effectuée, reprise actualisée impossible"
014 "Aucune action à refaire"
015 "%s/%d : Erreur d'écriture de journal"
016 "L'entrée et la sortie Vi standards doivent être un terminal"
017 "Marque %s : non définie"
018 "Marque %s : la ligne a été supprimée"
019 "Marque %s : la position du curseur n'existe plus"
020 "Erreur : "
021 "nouveau fichier"
022 "le nom a changé"
023 "modifié"
024 "non modifié"
025 "DÉVERROUILLÉ"
026 "lecture seule"
027 "ligne %lu de %lu [%ld%%]"
028 "fichier vide"
029 "ligne %lu"
030 "Ce fichier %s n'est pas un catalogue de messages"
031 "Impossible de configurer option %s par défaut"
032 "Utilisation : %s"
033 "Définition : pas d'option %s : 'set all' donne toutes les valeurs optionnelles"
034 "Définition : option [no]%s ne prend pas de valeur"
035 "Définition : l'option %s n'est pas booléenne"
036 "Définition : option %s : %s"
037 "Définition : option %s : %s : Dépassement de valeur"
038 "Définition : option %s : %s n'est pas un nombre valide"
039 "Définition : l'option %s n'est pas booléenne"
040 "Les colonnes de l'écran sont trop petites, inférieures à %d"
041 "Les colonnes de l'écran sont trop grandes, supérieures à %d"
042 "Les lignes de l'écran sont trop courtes, inférieures à %d"
043 "Les lignes de l'écran sont trop longues, supérieures à %d"
044 "L'option lisp n'est pas implémentée"
045 "Les messages ne sont pas désactivés : %s"
046 "Les messages ne sont pas activés : %s"
047 "Dfinition : l'option de %s doit ˆtre en groupe de deux caractŠres"
053 "Le tampon par défaut est vide"
054 "Le tampon %s est vide"
055 "Les fichiers dont le nom contient des caractères de saut de ligne sont irrécupérables"
056 "Impossible de récupérer les modifications si la session échoue"
057 "Copie en cours du fichier pour récupération..."
058 "La préservation a échoué : %s"
059 "Impossible de récupérer les modifications si la session échoue"
060 "La sauvegarde du fichier a échoué : %s"
061 "Copie en cours du fichier pour récupération..."
062 "Les renseignements sur l'identité %u de l'utilisateur sont introuvables"
063 "Impossible de verrouiller le fichier de récupération"
064 "Débordement de tampon du fichier de récupération"
065 "Fichier de récupération"
066 "%s : Fichier de récupération malformé"
067 "%s : Fichier de récupération malformé"
068 "Aucun fichier nommé %s à récupérer, que vous puissiez lire"
069 "Il existe des versions récupérables antérieures à ce fichier"
070 "Vous avez d'autres fichiers à récupérer"
071 "pas d'envoi d'email : %s"
072 "Fichier vide, rien à rechercher"
073 "Fin de fichier atteinte sans trouver le motif"
074 "Pas de motif de recherche précédent"
075 "Motif introuvable"
076 "Début du fichier atteint sans trouver le motif"
077 "La recherche est revenue à son point de départ"
078 "Recherche en cours..."
079 "Caractère non-imprimable introuvable"
080 "Nom de commande inconnu"
082 "%s : Commande non disponible en ex mode"
083 "Le compteur ne peut être zéro"
084 "%s : mauvaise spécification de ligne"
085 "Erreur de tableau de syntaxe interne (%s: %s)"
086 "Utilisation : %s"
087 "%s : tampon temporaire non libéré"
088 "Décalage de drapeau hors de la ligne 1"
089 "Décalage de drapeau hors de la fin du fichier"
090 "@ avec plage, en cours d'exécution quand le fichier/l'écran a changé"
091 "Commande Global/v en cours d'exécution quand le fichier/l'écran a changé"
092 "La commande ex a échoué : commandes en attente abandonnées"
093 "La commande ex a échoué : les touches affectées sont abandonnées"
094 "La deuxième adresse est plus petite que la première"
095 "Aucun nom de marque fourni"
096 "\\ non suivi par / ou ?"
097 "Référence à un numéro de ligne inférieure à 0"
098 "La commande %s est inconnue"
099 "Dépassement de la valeur adresse"
100 "Dépassement négatif de la valeur adresse"
101 "Combinaison d'adresse non valide"
102 "Adresse non valide : il n'y a que %lu lignes dans ce fichier"
103 "Adresse non valide : le fichier est vide"
104 "La commande %s ne permet pas une adresse de 0"
105 "Pas d'abréviations à afficher"
106 "Les abréviations doivent finir par un caractère"mot"
107 "Les abréviations ne peuvent pas contenir de tabulations ni d'espaces"
108 "Les abréviations ne peuvent pas contenir un mélange de caractères mot/non-mot, sauf à la fin"
109 ""%s" n'est pas une abréviation"
110 "La commande Vi a échoué : Les touches affectées ont été abandonnées"
111 "Plus de fichiers à éditer"
112 "Pas de fichiers précédents à éditer"
113 "Pas de fichiers précédents à rembobiner"
114 "Pas de liste de fichiers à afficher"
115 "Pas de commande précédente à remplacer"!""
116 "Pas de nom de fichier à substituer à %%"
117 "Pas de nom de fichier à substituer à #"
118 "Erreur : execl : %s"
119 "Erreur E/S : %s"
120 "Fichier modifié depuis la dernière écriture complète ; écrire ou utiliser ! pour outrepasser"
121 "Impossible de trouver l'emplacement du répertoire d'origine"
122 "Nouveau répertoire en cours : %s"
123 "Pas de tampon de coupure à afficher"
124 "La commande %s ne peut pas être utilisée à l'intérieur d'une commande globale ou commande v"
125 "%s/%s : échec de source : ni vous ni le super-utilisateur n'êtes les propriétaires "
126 "%s/%s : échec de source : vous n'êtes pas le propriétaire"
127 "%s/%s : échec de source : peut être écrit par un utilisateur autre que le propriétaire"
128 "%s : échec de source : ni vous ni le super-utilisateur n'êtes les propriétaires"
129 "%s : échec de source : vous n'êtes pas le propriétaire"
130 "%s : échec de source : peut être écrit par un utilisateur autre que le propriétaire"
131 "Pas de lignes suivantes à joindre"
132 "Pas d'entrées de mappage d'entrée"
133 "Pas d'entrées de mappage de commandes"
134 "Le caractère %s ne peut pas être remappé"
135 ""%s" n'est pas actuellement mappé"
136 "Les noms de marque ne doivent avoir qu'un caractère"
137 "%s existe, non enregistré; utiliser ! pour outrepasser"
138 "Nouveau fichier exrc : %s"
139 "La ligne de destination est à l'intérieur de la plage à déplacer"
140 "La commande ouverte nécessite que l'option ouverte soit définie"
141 "La commande ouverte n'est pas encore implémentée"
142 "La préservation de ce fichier est impossible"
143 "Fichier préservé"
144 "%s: étendu dans trop de noms de fichiers"
145 "Vous ne pouvez lire que les fichiers standards et les canaux de transmission nommés"
146 "%s: Interdiction de lecture non disponible"
147 "Lecture en cours..."
148 "%s: %lu lignes, %lu caractères"
149 "Pas d'écrans d'arrière-plan à afficher"
150 "La commande script n'est disponible qu'en mode vi"
151 "Pas de commande à exécuter"
152 "Option de largeur de décalage définie sur 0"
153 "Compter dépassement"
154 "Compter dépassement négatif"
155 "Expression standard spécifiée; drapeau r superflu"
156 "Vous ne pouvez pas en mode vi, combiner les drapeaux #, l et p avec le drapeau c"
157 "Aucune correspondance trouvée"
158 "Aucune marque précédente entrée"
159 "Moins de %s entrées dans la pile de marques ; utilisez t[ags]"
160 "Pas de fichier %s vers lequel retourner dans la pile de marques ; utiliser : affichage t[ags]"
161 "Appuyez sur Entrée pour continuer :"
162 "%s : marque introuvable"
163 "%s : marque corrompue en %s"
164 "%s : le numéro de ligne de la marque dépasse la fin du fichier"
165 "La pile de marques est vide"
166 "%s : motif de recherche introuvable"
167 "%d fichiers supplémentaires à éditer"
168 "Le tampon %s est vide
169 "Confirmer les changements ? [n]"
170 "Interrompu"
171 "Pas de tampon précédent à exécuter"
172 "Pas d'expression standard précédente"
173 "La commande %s nécessite qu'un fichier ait déjà été lu en mémoire"
174 "Utilisation : %s"
175 "La commande visual nécessite que l'option open soit définie"
177 "Fichier vide"
178 "Pas de recherche précédente F, f, T ou t"
179 "%s introuvable"
180 "Pas de fichier précédent à éditer"
181 "Le curseur n'est pas dans un nombre"
182 "Le nombre obtenu est trop grand"
183 "Le nombre obtenu est trop petit"
184 "Pas de correspondance de caractère sur cette ligne"
185 "Caractère correspondant introuvable"
186 "Pas de caractères à remplacer"
187 "Pas d'autre écran vers lequel basculer"
188 "Caractères après la chaîne de recherche, décalage de ligne et/ou commande z"
189 "Pas de motif de recherche précédent"
190 "La recherche est revenue à son point de départ"
191 "L'abréviation a dépassé la limite de l'expansion : caractères abandonnés"
192 "Caractère non valide ; guillemet pour saisir"
193 "Déjà au début de l'insertion"
194 "Plus de caractères à effacer"
195 "Déplacement hors de fin de fichier"
196 "Déplacement hors de fin de ligne"
197 "Aucun mouvement de curseur n'a été effectué"
198 "Déjà au début du fichier"
199 "Déplacement hors du début du fichier"
200 "Déjà dans la première colonne"
201 "Les tampons doivent être spécifiés avant la commande"
202 "Déjà à la fin du fichier"
203 "Déjà à la fin de la ligne"
204 "%s n'est pas une commande vi"
205 "Utilisation : %s"
206 "Pas de caractères à supprimer"
207 "La commande Q nécessite une interface terminal ex"
208 "Pas de commande à répéter"
209 "Le fichier est vide"
209 "Le fichier est vide"
210 "Vous ne pouvez pas utiliser %s comme commande de déplacement"
211 "Déjà en mode commande"
212 "Le curseur n'est pas dans un mot"
214 "Valeur optionnelle de fenêtre trop grande, maximum est %u"
215 "Ajouter"
216 "Changer"
217 "Commande"
218 "Insérer"
219 "Remplacer"
220 "Déplacement hors de la fin d'écran"
221 "Déplacement hors du début d'écran"
222 "L'écran doit être supérieur à %d lignes pour se fractionner"
223 "Il n'y a pas d'écran d'arrière-plan"
224 "Il n'y a pas d'écran d'arrière-plan qui édite un fichier nommé %s"
225 "Vous ne pouvez pas mettre à l'arrière-plan votre seul écran affiché"
226 "L'écran ne peut être réduit qu'à %d rangs"
227 "L'écran n'est pas auto-réductible"
228 "L'écran n'est pas auto-extensible"
230 "Vous ne pouvez pas mettre cet écran en attente"
231 "Interrompu : les touches affectées ont été abandonnées"
232 "vi : le tampon temporaire n' a pas été libéré"
233 "Ce terminal n'a pas de touche %s"
234 "Vous ne pouvez spécifier qu'un seul tampon"
235 "Nombre supérieur à %lu"
236 "Interrompu"
237 "Impossible de créer un fichier temporaire"
238 "Avertissement : %s n'est pas un fichier standard"
239 "%s déjà verrouillé, session en lecture seule"
240 "%s: supprimer"
241 "%s: fermer"
242 "%s: supprimer"
243 "%s: supprimer"
244 "Fichier en lecture seule, non écrit, utiliser ! pour outrepasser"
245 "Fichier en lecture seule, non écrit"
246 "%s existe, non écrit; utiliser ! pour outrepasser"
247 "%s existe, non écrit"
248 "Fichier partiel, non écrit; utiliser ! pour outrepasser"
249 "Fichier partiel, non écrit"
250 "%s: fichier modifié plus récemment que cet exemplaire; utiliser ! pour outrepasser"
251 "%s: fichier modifié plus récemment que cet exemplaire"
252 "%s: interdiction d'écriture non disponible"
253 "Ecriture en cours..."
254 "%s: AVERTISSEMENT : FICHIER TRONQUÉ"
255 "Première marque de ce groupe déjà atteinte"
256 "%s: nouveau fichier : %lu lignes, %lu caractères"
257 "%s: %lu lignes, %lu caractères"
258 "%s étendue à trop de noms de fichiers"
259 "%s: pas un fichier standard"
260 "%s: ne vous appartient pas"
261 "%s: accessible par un utilisateur autre que son propriétaire"
262 "Fichier modifé depuis la dernière écriture complète ; écrire ou utiliser ! pour outrepasser "
263 "Fichier modifé depuis la dernière écriture complète ; écrire ou utiliser :edit! pour outrepasser"
264 "Fichier modifé depuis la dernière écriture complète ; écrire ou utiliser ! pour outrepasser"
265 "Fichier temporaire ; quitter annulera les modifications"
266 "Fichier en lecture seule ; les modifications ne sont pas écrites automatiquement"
267 "Journal redémarré"
268 "confirmer ? [ynq]"
269 "Appuyez sur n'importe quelle touche pour continuer : "
270 "Appuyez sur n'importe quelle touche pour continuer [: pour entrer plus de commandes ex] : "
271 "Appuyez sur n'importe quelle touche pour continuer [q pour Quitter]: "
272 "Cette forme de %s nécessite l'interface de terminal ex"
273 "Entrée de mode entrée ex."
274 "La commande a échoué, aucun fichier n'a encore été lu."
275 "cont?"
276 "Evénement imprévu de caractère"
277 "Evénement imprévu de fin-de-fichier"
278 "Pas de correspondances pour cette requête"
279 "Evénement imprévu d'interruption"
280 "Evénement quitter imprévu"
281 "Evénement imprévu de rafraîchissement"
282 "La dernière marque de ce groupe a déjà été atteinte"
283 "La commande %s nécessite l'interface de terminal ex"
284 "Cette forme de %s n'est pas reconnue quand l'option d'édition protégée est activée"
285 "Evénement imprévu de chaîne"
286 "Evénement imprévu de délai imparti"
287 "Evénement d'écriture imprévu"
289 "Les expansions du shell ne sont pas reconnues quand l'option d'édition protégée est activée"
290 "La commande %s n'est pas reconnue quand l'option d'édition protégée est activée"
291 "Définition : l'option %s ne peut pas être désactivée"
292 "Affichage trop petit."
293 "ajouté"
294 "changé"
295 "supprimé"
296 "joint"
297 "déplacé"
298 "décalé"
299 "coupé"
300 "ligne"
301 "lignes"
302 "Vi n'a pas été chargé avec un interprétateur Tcl"
303 "Ficher modifié depuis le dernier enregistrement."
304 "L'expansion du shell a échoué"
304 "L'expansion du shell a échoué"
305 "Pas d'option d'édition %s spécifiée"
306 "Vi n'a pas été chargé avec un interprétateur Perl"
307 "Pas de commande ex à exécuter"
308 "Entrez <CR> pour exécuter une commande, :q pour quitter"
309 "Utiliser "cscope help" pour obtenir de l'aide"
310 "Aucune connexion cscope n'est lancée"
311 "%s : type de recherche inconnu : utiliser un des %s"
312 "%d : Il n'existe pas de telle session cscope"
313 "Définition : l'option %s ne peut jamais être activée"
314 "Définition : l'option %s ne peut jamais être définie sur 0"
315 "%s: joints : %lu lignes, %lu caractères"
316 "événement imprévu de redimensionnement"
317 "%d fichiers à éditer"

306
dist/nvi/catalog/german.base vendored Normal file
View File

@@ -0,0 +1,306 @@
002 "Zeilenlängen-Überlauf"
003 "kann Zeile %lu nicht löschen"
004 "kann an Zeile %lu nicht anfügen"
005 "kann in Zeile %lu nicht einfügen"
006 "kann Zeile %lu nicht speichern"
007 "kann letzte Zeile nicht lesen"
008 "Fehler: kann Zeile %lu nicht wiederherstellen"
009 "Protokolldatei"
010 "Keine Protokollierung aktiv, Rückgängigmachen nicht möglich"
011 "Keine Änderungen rückgängig zu machen"
012 "Keine Protokollierung aktiv, Rückgängigmachen nicht möglich"
013 "Keine Protokollierung aktiv, Wiederholung von Änderungen nicht möglich"
014 "Keine Änderungen zu wiederholen"
015 "%s/%d: Protokollschreibfehler"
016 "Vi's Standardein- und -ausgabe muß ein Terminal sein"
017 "Marke %s: nicht gesetzt"
018 "Marke %s: die Zeile wurde gelöscht"
019 "Marke %s: Cursorposition existiert nicht mehr"
020 "Fehler: "
021 "neue Datei"
022 "Name geändert"
023 "geändert"
024 "nicht geändert"
025 "NICHT GELOCKT"
026 "nur zum Lesen"
027 "Zeile %lu von %lu [%ld%%]"
028 "leere Datei"
029 "Zeile %lu"
030 "Die Datei %s ist kein Meldungskatalog"
031 "Setzen der Voreinstellung für %s Option nicht möglich"
032 "Benutzung: %s"
033 "set: keine %s Option: 'set all' zeigt alle Optionen mit Werten an"
034 "set: der [no]%s Option kann kein Wert zugewiesen werden"
035 "set: %s ist keine Boolsche Option"
036 "set: %s Option: %s"
037 "set: %s Option: %s: Werte-Überlauf"
038 "set: %s Option: %s ist eine ungültige Zahl"
039 "set: %s ist keine Boolsche Option"
040 "Bildschirm hat zu wenig Spalten, weniger als %d"
041 "Bildschirm hat zu viele Spalten, mehr als %d"
042 "Bildschirm hat zu wenig Zeilen, weniger als %d"
043 "Bildschirm hat zu viele Zeilen, mehr als %d"
044 "Die lisp-Option ist nicht implementiert"
045 "Meldungen nicht abgeschaltet: %s"
046 "Meldungen nicht eingeschaltet: %s"
047 "set: Die %s Option muss Gruppen zu zwei Zeichen enthalten"
053 "Der Standardpuffer ist leer"
054 "Puffer %s ist leer"
055 "Dateien mit newlines im Namen sind nicht wiederherstellbar"
056 "Änderungen nicht wiederherstellbar, falls die Editorsitzung schiefgeht"
057 "kopiere Datei für Wiederherstellung ..."
058 "Rettungsmechanismus funktioniert nicht: %s"
059 "Änderungen nicht wiederherstellbar, falls die Editorsitzung schiefgeht"
060 "Sicherung der Datei gescheitert: %s"
061 "kopiere Datei für Wiederherstellung ..."
062 "Informationen über den Benutzer mit id %u nicht gefunden"
063 "Wiederherstellungsdatei kann nicht gelockt werden"
064 "Puffer der Wiederherstellungsdatei übergelaufen"
065 "Wiederherstellungsdatei"
066 "%s: Wiederherstellungsdatei hat falsches Format"
067 "%s: Wiederherstellungsdatei hat falsches Format"
068 "Keine von Ihnen lesbaren Dateien mit Namen %s zum Wiederherstellen"
069 "Es gibt ältere Versionen dieser Datei von Ihnen zum Wiederherstellen"
070 "Sie haben noch andere Dateien zum Wiederherstellen"
071 "schicke keine email: %s"
072 "Datei leer; nichts zu suchen"
073 "Dateiende erreicht, ohne das Suchmuster zu finden"
074 "Kein altes Suchmuster"
075 "Suchmuster nicht gefunden"
076 "Dateianfang erreicht, ohne das Suchmuster zu finden"
077 "Suche beginnt von vorn"
078 "suche ..."
079 "Keine nichtdruckbaren Zeichen gefunden"
080 "Unbekannter Kommandoname"
082 "%s: Kommando im ex-Modus nicht verfügbar"
083 "Anzahl darf nicht Null sein"
084 "%s: falsche Zeilenangabe"
085 "Interner Syntaxtabellenfehler (%s: %s)"
086 "Benutzung: %s"
087 "%s: temporärer Puffer nicht freigegeben"
088 "Flagoffset vor Zeile 1"
089 "Flagoffset hinter dem Dateiende"
090 "@ mit Bereich lief, als Datei/Anzeige geändert wurde"
091 "globales oder v-Kommando lief, als Datei/Anzeige geändert wurde"
092 "Ex-Kommando mißlungen: restliche Kommandoeingabe ignoriert"
093 "Ex-Kommando mißlungen: umdefinierte Tasten ungültig"
094 "Die zweite Adresse ist kleiner als die erste"
095 "Kein Markenname angegeben"
096 "\\ ohne folgenden / oder ?"
097 "Referenz auf eine negative Zeilennummer"
098 "Das Kommando %s ist unbekannt"
099 "Adreßwert zu groß"
100 "Adreßwert zu klein"
101 "Ungültige Adreßkombination"
102 "Ungültige Adresse: nur %lu Zeilen in der Datei"
103 "Ungültige Adresse: die Datei ist leer"
104 "Das Kommando %s erlaubt keine Adresse 0"
105 "Keine Abkürzungen zum Anzeigen"
106 "Abkürzungen müssen mit einem "Wort"-Zeichen enden"
107 "Abkürzungen dürfen keine Tabulator- oder Leerzeichen enthalten"
108 "In Abkürzungen dürfen außer am Ende Wort- und Nicht-Wort-Zeichen nicht gemischt werden"
109 ""%s" ist keine Abkürzung"
110 "Vi Kommando mißlungen: umdefinierte Tasten ungültig"
111 "Keine weiteren Dateien zu editieren"
112 "Keine vorhergehenden Dateien zu editieren"
113 "Keine vorhergehenden Dateien für rewind"
114 "Keine Dateiliste zum Anzeigen"
115 "Kein vorhergehendes Kommando, um "!" zu ersetzen"
116 "Kein Dateiname für %%"
117 "Kein Dateiname für #"
118 "Fehler: execl: %s"
119 "E/A-Fehler: %s"
120 "Datei wurde seit dem letzten vollständigen Schreiben geändert; schreibe oder verwende ! zum ignorieren"
121 "Kann Homedirectory nicht bestimmen"
122 "Neues aktuelles Directory: %s"
123 "Keine Puffer anzuzeigen"
124 "Das Kommando %s kann nicht als Teil eines global oder v Kommandos verwendet werden"
125 "%s/%s: nicht gelesen: gehört weder Ihnen noch root"
126 "%s/%s: nicht gelesen: gehört nicht Ihnen"
127 "%s/%s: nicht gelesen: anderer Benutzer als Eigentümer hat Schreibrecht"
128 "%s: nicht gelesen: gehört weder Ihnen noch root"
129 "%s: nicht gelesen: gehört nicht Ihnen"
130 "%s: nicht gelesen: anderer Benutzer als Eigentümer hat Schreibrecht"
131 "Keine folgenden Zeilen zum Verbinden"
132 "Kein input-Map Eintrag"
133 "Kein command-Map Eintrag"
134 "Das %s Zeichen kann nicht umgemappt werden"
135 ""%s" ist momentan nicht gemappt"
136 "Markennamen müssen einzelne Buchstaben sein"
137 "%s existiert, nicht geschrieben; verwende ! zum Überschreiben"
138 "Neue .exrc Datei: %s. "
139 "Zielzeile ist innerhalb des Verschiebebereichs"
140 "Das open Kommando verlangt, daß die open Option gesetzt ist"
141 "Das open Kommando ist nocht nicht implementiert"
142 "Rettung dieser Datei nicht möglich"
143 "Datei gerettet"
144 "%s wurde in zu viele Dateinamen expandiert"
145 "Nur reguläre Dateien und named pipes können gelesen werden"
146 "%s: Locken zum Lesen war nicht möglich"
147 "lese ..."
148 "%s: %lu Zeilen, %lu Zeichen"
149 "Keine Hintergrundanzeigen vorhanden"
150 "Das script Kommando ist nur im vi Modus verfügbar"
151 "Kein Kommando auszuführen"
152 "shiftwidth Option auf 0 gesetzt"
153 "Anzahl-Überlauf"
154 "Anzahl-Unterlauf"
155 "Regulärer Ausdruck angegeben; r Flag bedeutungslos"
156 "Die #, l und p Flags können im vi Modus nicht mit dem c Flag kombiniert werden"
157 "Keine Übereinstimmung gefunden"
158 "Kein vorhergehender 'tag' angegeben"
159 "Weniger als %s Einträge auf dem 'tag'-Stack; verwende :display t[ags]"
160 "Keine Datei namens %s auf dem 'tag'-Stack; verwende :display t[ags]"
161 "Drücke Enter um fortzufahren: "
162 "%s: 'tag' nicht gefunden"
163 "%s: kaputter 'tag' in %s"
164 "%s: die Zeilennummer des 'tag' ist hinter dem Dateiende"
165 "Der 'tag' Stack ist leer"
166 "%s: Suchmuster nicht gefunden"
167 "%d weitere Dateien zu editieren"
168 "Puffer %s ist leer"
169 "Bestätige Änderung [n]"
170 "Unterbrochen."
171 "Kein vorhergehender Puffer zum Ausführen"
172 "Kein vorhergehender regulärer Ausdruck"
173 "Das Kommando %s verlangt, daß bereits eine Datei eingelesen wurde"
174 "Benutzung: %s"
175 "Das visual Kommando verlangt daß die open Option gesetzt ist"
177 "Leere Datei"
178 "Keine vorhergehende F, f, T oder t Suche"
179 "%s nicht gefunden"
180 "Keine vorhergehende Datei zu editieren"
181 "Cursor nicht auf einer Zahl"
182 "Sich ergebende Zahl ist zu groß"
183 "Sich ergebende Zahl ist zu klein"
184 "Kein korrespondierendes Zeichen in dieser Zeile"
185 "Korrespondierendes Zeichen nicht gefunden"
186 "Keine Zeichen zu ersetzen"
187 "Keine andere Anzeige zum Hinschalten"
188 "Zeichen nach Suchmuster, Zeilenoffset und/oder z Kommando"
189 "Kein altes Suchmuster"
190 "Suche zur ursprünglichen Position zurückgekehrt"
191 "Abkürzung überschreitet Expansionsgrenze: Zeichen weggelassen"
192 "Nicht erlaubtes Zeichen; maskiere zum Eingeben"
193 "Bereits am Anfang der Eingabe"
194 "Keine weiteren Zeichen zu löschen"
195 "Bewegung hinter das Dateiende"
196 "Bewegung hinter das Zeilenende"
197 "Keine Cursorbewegung gemacht"
198 "Bereits am Dateianfang"
199 "Bewegung vor den Dateianfang"
200 "Bereits in der ersten Spalte"
201 "Puffer sollen vor dem Kommando angegeben werden"
202 "Bereits am Dateiende"
203 "Bereits am Zeilenende"
204 "%s ist kein vi Kommando"
205 "Benutzung: %s"
206 "Keine Zeichen zu löschen"
207 "Das Q Kommando benötigt das ex Terminal Interface"
208 "Kein Kommando zu wiederholen"
209 "Die Datei ist leer"
210 "%s kann nicht als Bewegungskommando verwendet werden"
211 "Bereits im Kommando-Modus"
212 " Cursor nicht auf einem Wort"
214 "Wert der Window Option ist zu groß, max ist %u"
215 "Anhängen"
216 "Ändern"
217 "Kommando"
218 "Einfügen"
219 "Ersetzen"
220 "Bewegung hinter das Anzeigenende"
221 "Bewegung vor den Anzeigenanfang"
222 "Anzeige muß für Bildschirmteilung größer als %d sein"
223 "Keine Hintergrundanzeigen vorhanden"
224 "Es gibt keine Hintergrundanzeige, die eine Datei namens %s editiert"
225 "Die einzige dargestellte Anzeige kann nicht in den Hintergrund gebracht werden"
226 "Die Anzeige kann nur auf %d Zeilen verkleinert werden"
227 "Die Anzeige kann nicht verkleinert werden"
228 "Die Anzeige kann nicht vergrößert werden"
230 "Diese Anzeige kann nicht gestopt werden"
231 "Unterbrochen: umdefinierte Tasten ungültig"
232 "vi: temporärer Puffer nicht freigegeben"
233 "Dieses Terminal hat keine %s Taste"
234 "es kann nur ein Puffer angegeben werden"
235 "Zahl größer als %lu"
236 "Unterbrochen"
237 "Nicht möglich, temporäre Datei anzulegen"
238 "Warnung: %s ist keine reguläre Datei"
239 "%s ist bereits gelockt, Editorsitzung schreibgeschützt"
240 "%s: löschen"
241 "%s: schließen"
242 "%s: löschen"
243 "%s: löschen"
244 "Datei nicht schreibbar, nicht geschrieben; verwende ! zum Überschreiben"
245 "Datei nicht schreibbar, nicht geschrieben"
246 "%s existiert, nicht geschrieben; verwende ! zum Überschreiben"
247 "%s existiert, nicht geschrieben"
248 "Teil der Datei, nicht geschrieben; verwende ! zum Überschreiben"
249 "Teil der Datei, nicht geschrieben"
250 "%s: Datei wurde später als diese Kopie verändert; verwende ! zum Überschreiben"
251 "%s: Datei wurde später als diese Kopie verändert"
252 "%s: Locken zum Schreiben war nicht möglich"
253 "schreibe ..."
254 "%s: WARNUNG: DATEI ABGESCHNITTEN"
255 "Bereits am ersten 'tag' dieser Gruppe"
256 "%s: neue Datei: %lu Zeilen, %lu Zeichen"
257 "%s: %lu Zeilen, %lu Zeichen"
258 "%s wurde in zu viele Dateinamen expandiert"
259 "%s: keine reguläre Datei"
260 "%s: gehört nicht Ihnen"
261 "%s: anderer Benutzer als Eigentümer hat Zugriff"
262 "Datei wurde seit dem letzten vollständigen Schreiben geändert; schreibe oder verwende ! zum ignorieren"
263 "Datei wurde seit dem letzten vollständigen Schreiben geändert; schreibe oder verwende :edit! zum ignorieren"
264 "Datei wurde seit dem letzten vollständigen Schreiben geändert; schreibe oder verwende ! zum ignorieren"
265 "Datei ist temporär; beim Verlassen gehen die Änderungen verloren"
266 "Datei ist schreibgeschützt, Änderungen werden nicht automatisch geschrieben"
267 "Portokollierung neu begonnen"
268 "bestätigen [ynq]"
269 "Drücke beliebige Taste um fortzufahren"
270 "Drücke beliebige Taste um fortzufahren [: um weitere Kommandos einzugeben]: "
271 "Drücke beliebige Taste um fortzufahren [q zum Verlassen]: "
272 "Diese Form von %s benötigt das ex Terminal-Interface"
273 "Gehe in ex Eingabe-Modus.\n"
274 "Kommando schiefgelaufen, noch keine Datei eingelesen"
275 " weiter?"
276 "unerwartetes Zeichen - Ereignis"
277 "unerwartetes Dateiende - Ereignis"
278 "Keine Position zum Anspringen gefunden"
279 "unerwartetes Unterbrechungs - Ereignis"
280 "unerwartetes Verlassen - Ereignis"
281 "unerwartetes Wiederherstellungs - Ereignis"
282 "Bereits am letzten 'tag' dieser Gruppe"
283 "Das %s Kommando benötigt das ex Terminal-Interface"
284 "Diese Form von %s wird nicht unterstützt wenn die 'secure edit' - Option gesetzt ist"
285 "unerwartetes Zeichenketten - Ereignis"
286 "unerwartetes timeout - Ereignis"
287 "unerwartetes Schreibe - Ereignis"
289 "Shell Expandierungen nicht unterstützt wenn die 'secure edit' - Option gesetzt ist"
290 "Das %s Kommando wird nicht unterstützt wenn die 'secure edit' - Option gesetzt ist"
291 "set: %s kann nicht ausgeschaltet werden"
292 "Anzeige zu klein."
293 "angefügt"
294 "geändert"
295 "gelöscht"
296 "verbunden"
297 "verschoben"
298 "geschoben"
299 "in Puffer geschrieben"
300 "Zeile"
301 "Zeilen"
302 "Vi wurde nicht mit dem Tcl Interpreter gelinkt"
303 "Datei wurde seit dem letzten Schreiben verändert."
304 "Shell Expansion hat nicht geklappt"
305 "Es ist keine %s Editieroption angegeben"
306 "Vi wurde nicht mit einem Perl Interpreter geladen"
307 "Kein ex Kommando auszuführen"
308 "Drücke <CR> um ein Kommando auszuführen, :q zum verlassen"
309 "Verwende "cscope help" für Hilestellung"
310 "Keine cscope Verbindung aktiv"
311 "%s: unbekannter Suchtyp: verwende einen aus %s"
312 "%d: keine solche cscope Verbindung"
313 "set: die %s Option kann nicht eingeschaltet werden"
314 "set: die %s Option kann nicht auf 0 gesetzt werden"
315 "%s: angefügt: %lu Zeilen, %lu Zeichen"
316 "unerwartetes Größenveränderungs - Ereignis"
317 "%d Dateien zu edieren"

2
dist/nvi/catalog/german.owner vendored Normal file
View File

@@ -0,0 +1,2 @@
Bernhard Daeubler <daeb@physik.uni-ulm.de>
Joerg Wunsch <joerg_wunsch@uriah.heep.sax.de>

214
dist/nvi/catalog/ru_RU.KOI8-R.base vendored Normal file
View File

@@ -0,0 +1,214 @@
002 "Переполнение значения длины строки"
003 "невозможно удалить строку %lu"
004 "невозможно добавить к строке %lu"
005 "невозможно вставить в строку %lu"
006 "невозможно сохранить строку %lu"
007 "невозможно достать последнюю строку"
009 "Файл записей"
010 "Записи не велись, невозможно отменить последнюю команду"
011 "Нет изменений для отмены"
012 "Записи не велись, невозможно отменить последнюю команду"
013 "Записи не велись, невозможно просмотреть вперед"
014 "Нет изменений для переделки"
015 "%s/%d: ошибка при записи протокола"
016 "Стандартный ввод/вывод для VI должен быть терминал"
017 "Отметка %s: не установлена"
018 "Отметка %s: строка была удалена"
019 "Отметка %s: позиции курсора больше не существует"
020 "Ошибка: "
030 "Файл %s не является каталогом сообщений"
031 "Невозможно установить опцию %s по умолчанию"
032 "Использование: %s"
033 "Опции %s нет: 'set all' показывает все возможные опции"
034 "set: [no]%s не принимает такого значения"
035 "set: %s опция не является логической"
038 "set: неправильное значение %s"
039 "set: %s опция не является логической"
040 "Количество колонок экрана слишком мало, меньше чем %d"
041 "Количество колонок экрана слишком велико, больше чем %d"
042 "Количество строк экрана слишком мало, меньше чем %d"
043 "Количество строк экрана слишком велико, больше чем %d"
044 "Опция lisp отсутствует"
045 "Сообщения не выключены: %s"
046 "Сообщения не включены: %s"
048 "Опция paragraph должна состоять из групп с двумя символами"
049 "Опция section должна состоять из групп с двумя символами"
053 "Стартовый буфер пуст"
054 "Буфер %s пуст"
055 "Файлы с символами перевода строки в имени не могут быть восстановлены"
056 "Изменения не сохранены при крахе сессии"
058 "Сохранение не удалось: %s"
059 "Изменения не сохраняются при обрыве сессии"
060 "Сохранение копии файла не удалось: %s"
062 "Информации на пользователя %u не найдено"
063 "Невозможно защитить спасенный файл"
064 "Буфер восстановленного файла переполнен"
065 "Восстановленный файл"
066 "%s: не до конца восстановленный файл"
067 "%s: не до конца восстановленный файл"
068 "Файлов с именем %s, которые Вы можете читать, не существует"
069 "Есть старые версии файла, которые можно восстановить"
070 "Существуют другие файлы, которые можно восстановить"
071 "е-mail не послан: %s"
072 "Файл пуст - искать нечего"
073 "Достигнут конец файла без нахождения образца поиска"
074 "Не задан образец поиска"
075 "Образец поиска не найден"
076 "Доступно начало файла без нахождения образца поиска"
077 "Поиск зациклен"
079 "Непечатных символов не найдено"
080 "Неизвестная команда"
082 "%s: команда не доступна в режиме ex"
083 "Счетчик не может быть нулем"
084 "%s: неправильное указание строки"
085 "Внутренняя ошибка в синтаксисе (%s: %s)"
086 "Использование: %s"
087 "%s: временный буфер не использован"
088 "Метка поставлена перед строкой 1"
089 "Метка поставлена после конца файла"
092 "Команда ex не удалась: последующие команды забыты"
094 "Второй адрес меньше чем первый"
095 "Не указано название отметки"
096 "\\ не завершается / или ?"
097 "Ссылка к строке с номером меньше 0"
098 "Команда %s неизвестна"
099 "Переполнение значения адреса"
100 "Недобор значения адреса"
101 "Недопустимая комбинация в адресе"
102 "Неправильный адрес: всего %lu строк в файле"
103 "Неправильный адрес: файл пуст"
104 "Комманда %s не может использовать адрес 0"
105 "Аббревиатуры отсутствуют"
106 "Аббревиатуры должны заканчиваться символом "word""
107 "Аббревиатуры не могут содержать символоы табляции или пробелы"
108 "Аббревиатуры не могут сочетаться с символами слов/не-слов, за исключением конца строки"
109 ""%s" не является аббревиатурой"
111 "Файлов для редактирования больше нет"
112 "Отсутствие предыдущего файла для редактирования"
113 "Отсутствие предыдущего файла для просмотра назад"
114 "Нет файлов"
115 "Отсутствие предыдущей команды для замены "!""
116 "Отсутствие замены для %%"
117 "Отсутствие замены для #"
118 "Ошибка: execl: %s"
119 "Ошибка ввода/вывода: %s"
120 "Файл изменен с момента последней полной записи: используйте ! для обхода"
121 "Невозможно найти домашний каталог"
122 "Новый каталог: %s"
123 "Нет вырезаных буферов"
124 "Команда %s не может быть использована внутри глобальной или v команды"
125 "%s/%s: не открыт: не принадлежит Вам или root-у"
126 "%s/%s: не открыт: не принадлежит Вам"
127 "%s/%s: не открыт: возможность записи у пользователя, не являющегося владельцем"
128 "%s: не считан: не принадлежит Вам или root-у"
129 "%s: не считан: не принадлежит Вам"
130 "%s: не считан: возможность записи у пользователя, не являющегося владельцем"
131 "Последующие строки отсутствуют"
132 "Отсутствие параметров ввода"
133 "Отсутствие параметров команды"
134 "Символ %s не может быть переназначен"
135 ""%s" на данный момент не назначен"
136 "Имя метки должно быть одним символом"
137 "%s существует, не записан; используйте ! для обхода"
138 "Новый файл exrc: %s"
139 "Строка переноса находится внутри параметров переноса"
140 "Команда open подразумевает установку опции open"
141 "Команда open не реализована"
142 "Защита файла невозможна"
143 "Файл защищен"
144 "%s расширился в слишком большое количество имен файлов"
146 "%s: защита на чтение была недоступна"
148 "%s: %lu строк, %lu символов"
149 "Нет теневых окон"
150 "Команда script используется только в режиме vi"
151 "Нет команды для исполнения"
152 "Опция shiftwidth установлена на 0"
153 "Переполнение счетчика"
154 "Цикл выполнен не до конца"
155 "Указано регулярное выражение: флаг r не нужен"
156 "Флаги #, l и p не могут быть объединены с флагом c в режиме vi"
157 "Совпадений нет"
158 "Метка отсутствует"
159 "В стеке меток записей меньше, чем %s, используйте :display t[ags]"
160 "Файла с именем %s в стеке меток нет; используйте :display t[ags]"
162 "%s: метка не найдена"
163 "%s: плохая метка в %s"
165 "Стек меток пуст"
166 "%s: образец поиска не найден"
168 "Буфер %s пуст"
170 "Прервано"
171 "Отсутствие буфера для использования"
172 "Нет предидущего регулярного выражения"
173 "Команда %s подразумевает наличие прочтенного файла"
174 "Использование: %s"
175 "Команда visual подразумевает обязательную установку опции open"
177 "Пустой файл"
178 "Нет предыдущего поиска F, f, T, или t"
179 "%s не найдено"
180 "Нет предыдущего файла для редактирования"
181 "Курсор стоит не на цифре"
182 "Полученное число слишком велико"
183 "Полученное число слишком мало"
184 "Подходящего символа нет на этой строке"
185 "Подходящий символ не найден"
186 "Нет символов для удаления"
187 "Другого экрана не существует"
188 "Символы после строки для поиска, смещения строки и/или команды z"
189 "Прошлый образец поиска отсутствует"
190 "Поиск завершился на начальной позиции"
192 "Символ неправилен; квотируйте для ввода"
193 "Уже на начале вставки"
194 "Нет символов для удаления"
195 "Передвижение за конец файла"
196 "Передвижение за конец строки"
197 "Движение строки не сделано"
198 "Уже на начале файла"
199 "Движение курсора за начало файла"
200 "Уже в первой колонке"
201 "Буферы должны быть указаны до выполнения команды"
202 "Уже на конце файла"
203 "Уже на конце строки"
204 "%s не является командой VI"
205 "Использование: %s"
206 "Нет символов для удаления"
208 "Нет команды для повтора"
210 "Команда %s не может быть использована как команда перемещения"
211 "Уже в командном режиме"
214 "Значение опции windows слишком велико, максимальное значение = %u"
220 "Движение курсора за конец экрана"
221 "Движение курсора за начало экрана"
223 "Теневых окон нет"
224 "Не существует теневого окна с редактированием файла %s"
225 "Вы не можете сделать единственное окно теневым"
226 "Экран может быть сжат"
227 "Экран не может быть сжат только до %d строк"
228 "Экран не может быть расширен"
233 "Данный тип терминала не имеет клавиши %s"
237 "Невозможно создать временный файл"
238 "Внимание: %s специальный файл"
239 "%s уже заблокирован, доступен только на чтение"
240 "%s: удален"
241 "%s: закрыт"
242 "%s: удален"
243 "%s: удален"
244 "Файл только для чтения, не записан: Используйте ! для обхода"
245 "Файл только для чтения, не записан"
246 "%s существует, не записан; используйте ! для обхода"
247 "%s существует, не записан"
248 "Используйте ! для частичной записи файла"
249 "Часть файла, файл не записан"
250 "%s: Файл изменялся позднее, чем данная копия: используйте ! для обхода"
251 "%s: Файл изменялся позднее, чем данная копия"
252 "%s: защита на запись была недоступна"
254 "%s: ВНИМАНИЕ: ФАЙЛ УСЕЧЕН"
256 "%s: новый файл: %lu строк, %lu символов"
257 "%s: %lu строк, %lu символов"
258 "%s расширился в слишком большое количество имен файлов"
259 "%s: специальный файл"
260 "%s: не принадлежит Вам"
261 "%s: доступен не только Вам"
262 "Файл изменен со времени последней записи: сохраните или используйте ! для обхода"
263 "Файл изменен со времени последней записи: сохраните или используйте :edit для обхода"
264 "Файл изменен со времени последней записи: сохраните или используйте ! для обхода"
265 "Файл временный: выход сотрет любые изменения"
267 "Записи начаты заново"

2
dist/nvi/catalog/ru_RU.KOI8-R.owner vendored Normal file
View File

@@ -0,0 +1,2 @@
Andrey A. Chernov <ache@nagual.ru>
Dima Ruban <dima@rdy.com>

308
dist/nvi/catalog/spanish.base vendored Normal file
View File

@@ -0,0 +1,308 @@
002 "Desbordamiento de longitud de l¡nea"
003 "no se puede borrar la l¡nea %lu"
004 "no se puede a¤adir la l¡nea %lu"
005 "no se puede insertar en la l¡nea %lu"
006 "no se puede guardar la l¡nea %lu"
007 "no se puede obtener la £ltima l¡nea"
008 "Error: no se puede recuperar la l¡nea %lu"
009 "Archivo de log"
010 "No se realiza log, no se puede deshacer"
011 "No hay cambios para deshacer"
012 "No se realiza log, no se puede deshacer"
013 "No se realiza log, no se puede remontar hacia adelante"
014 "No hay cambios para rehacer"
015 "%s/%d: error de log"
016 "La entrada y salida est ndar de Vi debe ser una terminal"
017 "Marcar %s: no determinado"
018 "Marcar %s: se borr¢ la l¡nea"
019 "Marcar %s: la posici¢n del cursor ya no existe"
020 "Error: "
021 "nuevo archivo"
022 "nombre cambiado"
023 "modificado"
024 "no modificado"
025 "DESTRABADO"
026 "lectura solamente"
027 "l¡nea %lu de %lu [%ld%%]"
028 "archivo vac¡o"
029 "l¡nea %lu"
030 "El archivo %s no es un cat logo de mensajes"
031 "No se puede determinar la opci¢n por omisi¢n %s"
032 "Uso: %s"
033 "determinar: no hay opci¢n %s: 'determinar todo' establece todos los valores de opci¢n"
034 "determinar: [no] hay opci¢n %s no tiene valor"
035 "determinar: opci¢n %s no es booleano"
036 "determinar: opci¢n %s: %s"
037 "determinar: opci¢n %s: %s: desbordamiento de valores"
038 "determinar: opci¢n %s: %s es un n£mero ilegal"
039 "determinar: opci¢n %s no es booleano"
040 "Las columnas en pantalla son demasiado peque¤as, menos de %d"
041 "Las columnas en pantalla son demasiado grandes, m s de %d"
042 "Las l¡neas en pantalla son demasiado peque¤as, menos de %d"
043 "Las l¡neas en pantalla son demasiado grandes, m s de %d"
044 "La opci¢n lisp no est  implementada"
045 "mensajes no desconectados: %s"
046 "mensajes no conectados: %s"
047 "determinar: La opci¢n de %s debe estar en dos grupos de caracteres"
053 "El buffer por omisi¢n est  vac¡o"
054 "El buffer %s est  vac¡o"
055 "Los archivos con nuevas l¡neas en el nombre son irrecuperables"
056 "Las modificaciones no pueden recuperarse si la sesi¢n falla"
057 "Copiando archivo para recuperaci¢n..."
058 "Preservaci¢n fracasada: %s"
059 "Las modificaciones no pueden recuperarse si la sesi¢n falla"
060 "Archivo de respaldo fracasado: %s"
061 "Copiando archivo para recuperaci¢n..."
062 "Informaci¢n sobre identificaci¢n del usuario %u no encontrada"
063 "No se puede trabar archivo de recuperaci¢n"
064 "Desbordamiento de buffer de archivo de recuperaci¢n"
065 "Archivo de recuperaci¢n"
066 "%s: archivo de recuperaci¢n defectuoso"
067 "%s: archivo de recuperaci¢n defectuoso"
068 "No hay archivos denominados %s, que usted pueda leer, para recuperar"
069 "Existen versiones m s antiguas de este archivo que usted puede recuperar"
070 "Existen otros archivos que usted puede recuperar"
071 "no env¡a email: %s"
072 "Archivo vac¡o; no hay nada para buscar"
073 "Se alcanz¢ el final del archivo sin encontrar el patr¢n"
074 "No hay patr¢n anterior de b£squeda"
075 "No se encontr¢ el patr¢n"
076 " Se alcanz¢ el principio del archivo sin encontrar el patr¢n"
077 "B£squeda reiniciada"
078 "Buscando..."
079 "No se encontr¢ ning£n car cter no imprimible"
080 "Nombre de comando desconocido"
082 "%s: comando no disponible en modalidad ex"
083 "La cuenta no puede ser cero"
084 "%s: mala especificaci¢n de l¡nea"
085 "Error interno de tabla de sintaxis (%s: %s)"
086 "Uso: %s"
087 "%s: buffer temporario no liberado"
088 "Desplazamiento de marcador a antes de la l¡nea 1"
089 "Desplazamiento de marcador m s all  del final del archivo"
090 "@ con rango que corre cuando se cambia el archivo/la pantalla"
091 "Comando global/v que corre cuando se cambia el archivo/la pantalla"
092 "Comando Ex fracasado: comandos pendientes descartados"
093 "Comando Ex fracasado: teclas mapeadas descartadas"
094 "La segunda direcci¢n es m s peque¤a que la primera"
095 "No se suministra nombre de marca"
096 "\\ no es seguido por / o ?"
097 "Referencia a un n£mero de l¡nea menor que 0"
098 "El comando %s es desconocido"
099 "Desbordamiento de valor de direcci¢n"
100 "Subdesbordamiento de valor de direcci¢n"
101 "Combinaci¢n de direcci¢n ilegal"
102 "Direcci¢n ilegal: s¢lo %lu l¡neas en el archivo"
103 "Direcci¢n ilegal: el archivo est  vac¡o"
104 "El comando %s no permite una direcci¢n de 0"
105 "No hay abreviaturas para visualizar"
106 "Las abreviaturas deben terminar con un car cter de \"palabra\" "
107 "Las abreviaturas no pueden contener tabs o espacios"
108 "Las abreviaturas no pueden mezclar caracteres palabra/no-palabra, salvo al final"
109 "\"%s\" no es una abreviatura"
110 "Comando Vi fracasado: teclas mapeadas descartadas"
111 "No hay m s archivos para editar"
112 "No hay archivos anteriores para editar"
113 "No hay archivos anteriores para rebobinar"
114 "No hay lista de archivos para visualizar"
115 "No hay un comando anterior para reemplazar a \"!\""
116 "No hay nombre de archivo para sustituir por %%"
117 "No hay nombre de archivo para sustituir por #"
118 "Error: execl: %s"
119 "Error de E/S: %s"
120 "Archivo modificado desde la £ltima escritura completa; escribir o usar ! para alterar"
121 "No se puede encontrar la ubicaci¢n del directorio inicial"
122 "Nuevo directorio actual: %s"
123 "No hay buffers sueltos para visualizar"
124 "El comando %s no puede usarse como parte de un comando global o v"
125 "%s/%s: sin fuente: no le pertenece a usted o a ra¡z"
126 "%s/%s: sin fuente: no le pertenece a usted"
127 "%s/%s: sin fuente: puede ser escrito por un usuario que no sea el propietario"
128 "%s: sin fuente: no le pertenece a usted o a ra¡z"
129 "%s: sin fuente: no le pertenece a usted"
130 "%s: sin fuente: puede ser escrito por un usuario que no sea el propietario"
131 "No hay l¡neas siguientes para unir"
132 "No hay anotaciones de mapa de entrada"
133 "No hay anotaciones de mapa de comando"
134 "El car cter %s no puede remapearse"
135 "\"%s\" no est  mapeado actualmente"
136 "Marca de nombres debe ser un s¢lo car cter"
137 "%s existe, no est  escrito; usar ! para alterar"
138 "Nuevo archivo exrc: %s"
139 "La l¡nea de destino se encuentra dentro del rango de movimiento"
140 "El comando abierto requiere que se determine la opci¢n abierta"
141 "El comando abierto no se ha implementado a£n"
142 "No es posible preservar este archivo"
143 "Archivo preservado"
144 "%s: expandido a demasiados nombres de archivo"
145 "S¢lo pueden leerse los archivos regulares y los conductos nombrados"
146 "%s: traba de lectura no disponible"
147 "Leyendo..."
148 "%s: %lu l¡neas, %lu caracteres"
149 "No hay pantallas de fondo para mostrar"
150 "El comando de script s¢lo est  disponible en modalidad vi"
151 "No hay comando para ejecutar"
152 "opci¢n de ancho de desplazamiento en 0"
153 "Desbordamiento de cuenta"
154 "Subdesbordamiento de cuenta"
155 "Expresi¢n regular especificada; marcador r no tiene significado"
156 "Los marcadores #, l y p no pueden combinarse con el marcador c en la modalidad vi"
157 "No se encontr¢ coincidencia"
158 "No se ingres¢ un identificador anterior"
159 "Se encontraron menos de %s anotaciones en la pila de identificadores; usar :visualizar i[dentificadores]"
160 "No hay archivo %s en la pila de identificadores al que se pueda volver; usar :visualizar i[dentificadores]"
161 "Presionar Intro para continuar: "
162 "%s: no se encontr¢ el identificador"
163 "%s: identificador corrompido en %s"
164 "%s: el n£mero de l¡nea del identificador es posterior al final del archivo"
165 "La pila de identificadores est  vac¡a"
166 "%s: patr¢n de b£squeda no encontrado"
167 "%d archivos m s para editar"
168 "El buffer %s est  vac¡o"
169 "¨Confirmar cambio? [n]"
170 "Interrumpido"
171 "No hay buffer anterior para ejecutar"
172 "No hay expresi¢n regular anterior"
173 "El comando %s requiere que se haya le¡do un archivo"
174 "Uso: %s"
175 "El comando visual requiere que se determine la opci¢n abierta"
177 "Archivo vac¡o"
178 "No hay b£squeda F, f, T o t anterior"
179 "%s no se encontr¢"
180 "No hay archivo anterior para editar"
181 "El cursor no est  en un n£mero"
182 "El n£mero resultante es demasiado grande"
183 " El n£mero resultante es demasiado peque¤o"
184 "No hay car cter coincidente en esta l¡nea"
185 "No se encontr¢ un car cter coincidente"
186 "No hay caracteres para reemplazar"
187 "No hay otra pantalla a la que se pueda pasar"
188 "Caracteres despus de cadena de b£squeda, desplazamiento de l¡nea y/o comando z"
189 "No hay patr¢n anterior de b£squeda"
190 "B£squeda vuelve a la posici¢n inicial"
191 "Se super¢ el l¡mite de expansi¢n de abreviatura: se descartaron caracteres"
192 "Car cter ilegal; mencionar para entrar"
193 "Ya se encuentra al principio de la inserci¢n"
194 "No hay m s caracteres para borrar"
195 "Movimiento m s all  del final del archivo"
196 "Movimiento m s all  del final de la l¡nea"
197 "No hay movimiento del cursor"
198 "Ya se encuentra al principio del archivo"
199 "Movimiento m s all  del principio del archivo"
200 "Ya se encuentra en la primera columna"
201 "Los buffers deben especificarse antes del comando"
202 "Ya se encuentra al final del archivo"
203 "Ya se encuentra al final de la l¡nea"
204 "%s no es un comando vi"
205 "Uso: %s"
206 "No hay caracteres para borrar"
207 "El comando Q requiere la interfase de terminal ex"
208 "No hay comando para repetir"
209 "El archivo est  vac¡o"
209 "El archivo est  vac¡o"
210 "%s no puede usarse como comando de movimiento"
211 "Ya se encuentra en modalidad de comando"
212 "El cursor no se encuentra en una palabra"
214 "El valor de opci¢n de Windows es demasiado grande, el m x. es %u"
215 "A¤adir"
216 "Cambiar"
217 "Comando"
218 "Insertar"
219 "Reemplazar"
220 "El movimiento va m s all  del final de la pantalla"
221 "El movimiento va m s all  del principio de la pantalla"
222 "La pantalla debe tener m s de %d l¡neas para dividirse"
223 "No hay pantallas de fondo"
224 "No hay pantalla de fondo editando un archivo denominado %s"
225 "No se puede poner fondo a la £nica pantalla que se visualiza"
226 "La pantalla s¢lo puede reducirse a %d hileras"
227 "La pantalla no puede reducirse"
228 "La pantalla no puede aumentarse"
230 "Esta pantalla no puede suspenderse"
231 "Interrumpido: teclas mapeadas descartadas"
232 "vi: buffer temporario no liberado"
233 "Esta terminal no tiene tecla %s"
234 "S¢lo un buffer puede especificarse"
235 "N£mero mayor que %lu"
236 "Interrumpido"
237 "No se puede crear archivo temporario"
238 "Advertencia: %s no es un archivo regular"
239 "%s ya se encuentra trabado, la sesi¢n es de lectura solamente"
240 "%s: eliminar"
241 "%s: cerrar"
242 "%s: eliminar"
243 "%s: eliminar"
244 "Archivo de lectura solamente, no escrito; usar ! para alterar"
245 " Archivo de lectura solamente, no escrito"
246 "%s existe, no escrito; usar ! para alterar"
247 "%s existe, no escrito"
248 "Archivo parcial, no escrito; usar ! para alterar"
249 "Archivo parcial, no escrito"
250 "%s: archivo modificado m s recientemente que esta copia; usar ! para alterar"
251 "%s: archivo modificado m s recientemente que esta copia"
252 "%s: la traba de escritura no estaba disponible"
253 "Escribiendo..."
254 "%s: ADVERTENCIA: ARCHIVO TRUNCADO"
255 "Ya se encuentra en el primer identificador de este grupo"
256 "%s: nuevo archivo: %lu l¡neas, %lu caracteres"
257 "%s: %lu l¡neas, %lu caracteres"
258 "%s expandido a demasiados nombres de archivos"
259 "%s: no es un archivo regular"
260 "%s: no le pertenece"
261 "%s: accesible por un usuario que no sea el propietario"
262 "Archivo modificado desde la £ltima escritura completa; escribir o usar ! para alterar"
263 "Archivo modificado desde la £ltima escritura completa; escribir o usar :editar! para alterar"
264 "Archivo modificado desde la £ltima escritura completa; escribir o usar ! para alterar"
265 "El archivo es temporario; al salir se descartar n las modificaciones"
266 "Archivo de lectura solamente, las modificaciones no se autoescriben"
267 "Se reinici¢ el log"
268 "confirmar? [snq]"
269 "Presionar cualquier tecla para continuar: "
270 "Presionar cualquier tecla para continuar [: para ingresar m s comandos ex]: "
271 "Presionar cualquier tecla para continuar [q para salir]: "
272 "Esta forma de %s requiere la interfase terminal ex"
273 "Ingresando en la modalidad de entrada ex."
274 "Comando fracasado, no hay archivo le¡do aun."
275 " cont?"
276 "Evento inesperado de car cter"
277 "Evento inesperado de final de archivo"
278 "No hay coincidencias para consulta"
279 "Evento inesperado de interrupci¢n"
280 "Evento inesperado de salida"
281 "Evento inesperado de repintura"
282 "Ya se encuentra en el £ltimo identificador de este grupo"
283 "El comando %s requiere la interfase terminal ex"
284 "Esta forma de %s no se encuentra soportada cuando se determina la opci¢n de edici¢n segura"
285 "Evento inesperado de cadena"
286 "Evento inesperado de tiempo excedido"
287 "Evento inesperado de escritura"
289 "Las expansiones de shell no se encuentran soportadas cuando se determina la opci¢n de edici¢n segura"
290 "El comando %s no se encuentra soportado cuando se determina la opci¢n de edici¢n segura"
291 "determinar: la opci¢n %s puede no estar desconectada"
292 "El monitor es demasiado peque¤o."
293 "agregado"
294 "cambiado"
295 "borrado"
296 "unido"
297 "movido"
298 "desplazado"
299 "arrancado"
300 "l¡nea"
301 "l¡neas"
302 "Vi no se carg¢ con un intrprete Tcl"
303 "Archivo modificado desde la £ltima escritura."
304 "Expansi¢n de shell fracasada"
304 "Expansi¢n de shell fracasada"
305 "No hay opci¢n de edici¢n %s especificada"
306 "Vi no se carg¢ con un intrprete Perl"
307 "No hay comando ex para ejecutar"
308 "Ingresar <CR> para ejecutar un comando, :q para salir"
309 "Usar \"cscope ayuda\" para obtener ayuda"
310 "No hay conexiones cscope corriendo"
311 "%s: tipo de b£squeda desconocido: usar uno de %s"
312 "%d: no existe esta sesi¢n cscope"
313 "determinar: la opci¢n %s no puede conectarse nunca"
314 "determinar: la opci¢n %s no puede determinarse nunca en 0"
315 "%s: a¤adido: %lu l¡neas, %lu caracteres"
316 "Evento inesperado de modificaci¢n de tama¤o"
317 "%d archivos para editar"

19
dist/nvi/catalog/spell.ok vendored Normal file
View File

@@ -0,0 +1,19 @@
ARGMAX
LC
NL
XXXX
arg1
arg2
chys
english
english.base
german.base
langauge
msg
msg.c
msgcat
msgq
nvi
nvi's
pathname
sp

306
dist/nvi/catalog/swedish.base vendored Normal file
View File

@@ -0,0 +1,306 @@
002 "För långa rader"
003 "kan inte ta bort rad %lu"
004 "kan inte lägga till på rad %lu"
005 "kan inte sätta in på rad %lu"
006 "kan inte lagra rad %lu"
007 "kan inte hämta sista raden"
008 "Fel: kan inte hämta rad %lu"
009 "Loggningsfil"
010 "Loggning utförs inte, ångra är inte möjligt"
011 "Inga ändringar att ångra"
012 "Loggning utförs inte, ångra är inte möjligt"
013 "Loggning utförs inte, ångra ångra är inte möjligt"
014 "Inga ändringar att återgöra"
015 "%s/%d: fel vid loggning"
016 "Vi:s standard in och ut måste gå till en terminal"
017 "Markering %s: inte satt"
018 "Markering %s: raden har tagits bort"
019 "Markering %s: markörpositionen finns inte längre"
020 "Fel: "
021 "ny fil"
022 "namnet ändrades"
023 "ändrad"
024 "oförändrad"
025 "OLÅST"
026 "inte skrivbar"
027 "rad %lu av %lu [%ld%%]"
028 "tom fil"
029 "rad %lu"
030 "Filen %s är ingen meddelandekatalog"
031 "Kan inte sätta standardvärde för %s flaggan"
032 "Användning: %s"
033 "set: %s är en okänd flagga: "set all" visar alla flaggor"
034 "set: [no]%s flaggan kan inte ges ett värde"
035 "set: %s flaggan är inte boleansk"
036 "set: %s flaggan: %s"
037 "set: %s flaggan: %s: för stort värde"
038 "set: %s flaggan: %s är ett otillåtet tal"
039 "set: %s flaggan är inte boleansk"
040 "Fönsterkolumnerna är för få, mindre än %d"
041 "Fönsterkolumnerna är för många, fler än %d"
042 "Fönsterraderna är för få, mindre än %d"
043 "Fönsterraderna är för många, fler än %d"
044 "Lisp flaggan är inte implementerad"
045 "meddelanden är inte avslagna: %s"
046 "meddelanden är inte påslagna: %s"
047 "set: %s flaggan måste ges i teckengrupper om två"
053 "Standardbufferten är tom"
054 "Buffer %s är tom"
055 "Filer med radmatning i namnet kan inte återskapas"
056 "Ändringar kan inte återskapas om programmet kraschar"
057 "Kopierar filen för återskapning..."
058 "Säkerhetskopiering misslyckades: %s"
059 "Ändringar kan inte återskapas om programmet kraschar"
060 "Misslyckades att säkerhetskopiera filen: %s"
061 "Kopierar filen för återskapning..."
062 "Kan inte hitta information om användaridentitet %u"
063 "Kan inte låsa återskapningsfilen"
064 "Återskapningsfilens buffer överskriven"
065 "Återskapningsfil"
066 "%s: Återskapningsfilen är korrupt"
067 "%s: Återskapningsfilen är korrupt"
068 "Det finns ingen fil %s, läsbar av dig, att återskapa"
069 "Det finns äldre versioner av denna fil som du kan återskapa"
070 "Det finns andra filer du kan återskapa"
071 "skickar inte email: %s"
072 "Filen är tom; inget att söka i"
073 "Kom till slutet på filen utan att hitta söksträngen"
074 "Ingen tidigare söksträng"
075 "Hittar inte söksträngen"
076 "Kom till början av filen utan att hitta söksträngen"
077 "Sökningen slog runt"
078 "Söker..."
079 "Inga icke skrivbara tecken funna"
080 "Okänt kommandonamn"
082 "%s: kommandot är inte tillgängligt i "ex" läge"
083 "Talet får inte vara noll"
084 "%s: Ogiltig radspecifikation"
085 "Fel i intern syntaxtabell (%s: %s)"
086 "Användning: %s"
087 "%s: temporärbuffert inte frisläppt"
088 "Offset är före rad 1"
089 "Offset är efter slutet på filen"
090 "@ med intervall exekverades när filen/fönstret ändrades"
091 "Global/v kommando exekverades när filen/fönstret ändrades"
092 "Ex kommando misslyckades: efterföljande kommandon ignorerade"
093 "Ex kommando misslyckades: omdefinierade tangenter ignorerade"
094 "Den andra adressen är mindre än den första"
095 "Inget namn på markering givet"
096 "\\ följs inte av / eller ?"
097 "Referens till ett radnummer mindre än 0"
098 "%s kommandot är inte känt"
099 "Värdet på adressen är för stort"
100 "Värdet på adressen är för litet"
101 "Otillåten adresskombination"
102 "Otillåten adress: bara %lu rader finns i filen"
103 "Otillåten adress: filen är tom"
104 "%s kommandot tillåter inte en adress som är 0"
105 "Inga förkortningar att visa"
106 "Förkortningar måste sluta med ett "ord" tecken"
107 "Förkortningar kan inte innehålla mellanslag eller tab"
108 "Förkortningar kan inte blanda "ord"/"icke ord" tecken, utom i slutet"
109 ""%s" är ingen förkortning"
110 "Vi kommando misslyckades: omdefinierade tangenter ignorerade"
111 "Inga fler filer att editera"
112 "Inga tidigare filer att editera"
113 "Inga tidigare filer att spela tillbaka"
114 "Ingen fillista att visa"
115 "Inget tidigare kommando att ersätta "!" med"
116 "Inget filnamn att ersätta %% med"
117 "Inget filnamn att ersätta # med"
118 "Fel: execl: %s"
119 "I/O fel: %s"
120 "Filen ändrad efter sista skrivning; spara eller använd !"
121 "Kan inte hitta hemkatalog"
122 "Ny nuvarande katalog: %s"
123 "Inga "cut buffers" att visa"
124 "%s kommandot kan inte används som del i ett "global" eller v kommando"
125 "%s/%s: inte läst: varken du eller root är ägare"
126 "%s/%s: inte läst: du är inte ägare"
127 "%s/%s: inte läst: skrivbar av annan än ägaren"
128 "%s: inte läst: varken du eller root är ägare"
129 "%s: inte läst: du är inte ägare"
130 "%s: inte läst: skrivbar av annan än ägaren"
131 "Ingen nästa rad att sätta ihop med"
132 "Det finns inget i inmatningsmappningen"
133 "Det finns inget i kommandomappningen"
134 "%s tecknet kan inte mappas om"
135 ""%s" är inte ommappat just nu"
136 "Namn på markeringar måste vara ett tecken långa"
137 "%s finns, inget sparat; använd ! för att spara"
138 "Ny exrc fil: %s"
139 "Målraden ligger inne i området som ska flyttas"
140 "Open kommandot kräver att open flaggan är satt"
141 "Open kommandot är inte implementerat ännu"
142 "Säkerhetskopiering av filen är inte möjligt"
143 "Filen säkerhetskopierad"
144 "%s expanderade till för många filnamn"
145 "Endast vanliga filer och namngivna rör kan läsas"
146 "%s: läslåset är otillgängligt"
147 "Läser..."
148 "%s: %lu rader, %lu tecken"
149 "Inga bakgrundsfönster att visa"
150 "Script kommandot finns bara i "vi" läge"
151 "Inget kommando att exekvera"
152 "shiftwidth flaggan satt till 0"
153 "Talet har för stort värde"
154 "Talet har för litet värde"
155 "Reguljärt uttryck är givet; r flaggan är meningslös"
156 "#, l och p flaggorna kan inte kombineras med c flaggan i "vi" läge"
157 "Ingen matchande text funnen"
158 "Inget tidigare märke har givits"
159 "Det är färre än %s märken i stacken; använd :display t[ags]"
160 "Det finns ingen fil %s i märkesstacken; använd :display t[ags]"
161 "Tryck Enter för att fortsätta: "
162 "%s: märke inte funnet"
163 "%s: korrupt märke i %s"
164 "%s: märkets radnummer är bortom filslutet"
165 "Märkesstacken är tom"
166 "%s: söksträngen inte funnen"
167 "%d filer till att editera"
168 "Buffert %s är tom"
169 "Bekräfta ändring? [n]"
170 "Avbruten"
171 "Ingen tidigare buffert att exekvera"
172 "Inget tidigare reguljärt uttryck"
173 "%s kommandot kräver att en fil redan lästs in"
174 "Användning: %s"
175 "Visual kommandot kräver att open flaggan är satt"
177 "Tom fil"
178 "Ingen tidigare F, f, T eller t sökning"
179 "%s inte funnen"
180 "Ingen tidigare fil att editera"
181 "Markören är inte i ett tal"
182 "Det resulterande talet är för stort"
183 "Det resulterande talet är för litet"
184 "Inget matchande tecken på denna rad"
185 "Matchande tecken inte funnet"
186 "Det finns inga tecken att ersätta"
187 "Det finns inget fönster att byta till"
188 "Tecken efter söksträng, radoffset och/eller z kommandot"
189 "Ingen tidigare söksträng"
190 "Sökningen slog runt till ursprungliga positionen"
191 "Förkortning överskred expanderingsgränsen: tecken har tagits bort"
192 "Ogiltigt tecken; använd "quote" för att sätta in"
193 "Redan i början på insättningen"
194 "Inga fler tecken att ta bort"
195 "Försök att gå bortom slutet på filen"
196 "Försök att gå bortom slutet på raden"
197 "Ingen förflyttning gjord"
198 "Redan i början på filen"
199 "Försök att gå före början på filen"
200 "Redan i första kolumnen"
201 "Buffertar måste anges före kommandot"
202 "Redan i slutet av filen"
203 "Redan på slutet av raden"
204 "%s är inte ett "vi" kommando"
205 "Användning: %s"
206 "Inga tecken att ta bort"
207 "Q kommandot kräver "ex" i terminalläge"
208 "Inget kommando att repetera"
209 "Filen är tom"
210 "%s kan inte användas som ett förflyttningskommando"
211 "Redan i kommando läge"
212 "Markören är inte i ett ord"
214 "Windows flaggans värde är för stor, största värde är %u"
215 "Lägg till"
216 "Ändra"
217 "Kommando"
218 "Sätt in"
219 "Ersätt"
220 "Förflyttning bortom fönsterslut"
221 "Förflyttning till före fönstrets början"
222 "Fönstret måste vara större än %d rader för delning"
223 "Det finns inga fönster i bakgrunden"
224 "Det finns inget fönster i bakgrunden som editerar filen %s"
225 "Du får inte sätta ditt enda synliga fönster i bakgrunden"
226 "Fönstret kan bara krympa till %d rader"
227 "Fönstret kan inte krympa"
228 "Fönstret kan inte växa"
230 "Detta fönster kan inte pausas"
231 "Avbrutet: omdefinierade tangenter ignorerade"
232 "vi: temporärbuffertar inte frisläppta"
233 "Denna terminal har ingen %s tangent"
234 "Endast en buffert kan anges"
235 "Talet är större än %lu"
236 "Avbrutet"
237 "Kan inte skapa temporär fil"
238 "Warning: %s är inte en normal fil"
239 "%s är redan låst, detta blir en icke skrivbar session"
240 "%s: ta bort"
241 "%s: stäng"
242 "%s: ta bort"
243 "%s: ta bort"
244 "Ej skrivbar fil, filen inte sparad; använd ! för att skriva över"
245 "Ej skrivbar fil, filen inte sparad"
246 "%s finns, ej sparad; använd ! för att utföra operationen"
247 "%s finns, filen inte sparad"
248 "Ofullständig fil, filen inte sparad, använd ! för att skriva över"
249 "Ofullständig fil, filen inte sparad"
250 "%s: filen ändrad efter denna kopia togs; använd ! för att utföra operationen"
251 "%s: filen ändrad efter denna kopia togs"
252 "%s: skrivlåset är otillgängligt"
253 "Skriver..."
254 "%s: VARNING: FILEN TRUNKERAD"
255 "Redan vid första märket i denna grupp"
256 "%s: ny fil: %lu rader, %lu tecken"
257 "%s: %lu rader, %lu tecken"
258 "%s expanderade till för många filnamn"
259 "%s är inte en normal fil"
260 "%s ägs inte av dig"
261 "%s är åtkomstbar av andra än ägaren"
262 "Filen har ändrats efter den sparats; spara eller använd !"
263 "Filen har ändrats efter den sparats; spara eller använd :edit!"
264 "Filen har ändrats efter den sparats; spara eller använd !"
265 "Filen är temporär; exit kastar bort ändringarna"
266 "Ej skrivbar fil, ändringar har inte automatsparats"
267 "Loggningen startar om"
268 "bekräfta? [ynq]"
269 "Tryck på en tangent för att fortsätta: "
270 "Tryck på en tangent för att fortsätta [: för att ge fler kommandon]: "
271 "Tryck på en tangent för att fortsätta [q för att avsluta]: "
272 "Den formen av %s kräver "ex" i terminalläge"
273 "Går till "ex" inmatningsläge."
274 "Kommandot misslyckades, ingen fil inläst ännu."
275 " forts?"
276 "Oväntad teckenhändelse"
277 "Oväntad filslutshändelse"
278 "Sökningen hittade ingenting"
279 "Oväntad avbrottshändelse"
280 "Oväntad avslutningshändelse"
281 "Oväntad omritningshändelse"
282 "Redan vid sista märket i denna grupp"
283 "%s kommandot kräver "ex" i terminalläge"
284 "Den formen av %s är inte tillgänglig när secure edit flaggan är satt"
285 "Oväntad stränghändelse"
286 "Oväntad tidshändelse"
287 "Oväntad skrivhändelse"
289 "Skalexpansion är inte tillgänglig när secure edit flaggan är satt"
290 "%s kommandot är inte tillgänglig när secure edit flaggan är satt"
291 "set: %s kan inte slås av"
292 "Fönstret för litet."
293 "tillagda"
294 "ändrade"
295 "borttagna"
296 "ihopsatta"
297 "flyttade"
298 "flyttade"
299 "inklistrade"
300 "rad"
301 "rader"
302 "Vi har inte länkats med en Tcl tolk"
303 "Filen har ändrats efter den sparats."
304 "Skalexpansion misslyckades"
305 "Ingen %s edit flagga given"
306 "Vi har inte länkats med en Perl tolk"
307 "Inga "ex" kommandon att exekvera"
308 "Tryck <CR> för att exekvera kommando, :q för att avsluta"
309 "Gör "cscope help" för hjälp"
310 "Inga cscope kopplingar körs"
311 "%s: okänd söktyp: använd en av %s"
312 "%d: ingen sådan cscope session"
313 "set: %s flaggan får aldrig slås på"
314 "set: %s flaggan får aldrig sättas till 0"
315 "%s: tillagt: %lu rader, %lu tecken"
316 "Oväntad storleksändring"
317 "%d filer att editera"

1
dist/nvi/catalog/swedish.owner vendored Normal file
View File

@@ -0,0 +1 @@
Jan Djarv <jan.djarv@mbox200.swipnet.se>

174
dist/nvi/cl/README.signal vendored Normal file
View File

@@ -0,0 +1,174 @@
# Id: README.signal,v 10.1 1995/06/23 10:28:17 bostic Exp (Berkeley) Date: 1995/06/23 10:28:17
There are six (normally) asynchronous actions about which vi cares:
SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGTSTP and SIGWINCH.
The assumptions:
1: The DB routines are not reentrant.
2: The curses routines may not be reentrant.
3: Neither DB nor curses will restart system calls.
XXX
Note, most C library functions don't restart system calls. So, we should
*probably* start blocking around any imported function that we don't know
doesn't make a system call. This is going to be a genuine annoyance...
SIGHUP, SIGTERM
Used for file recovery. The DB routines can't be reentered, nor
can they handle interrupted system calls, so the vi routines that
call DB block signals. This means that DB routines could be
called at interrupt time, if necessary.
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, especially things like
making it a pure text widget. 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 cl_init.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.)

120
dist/nvi/cl/cl.h vendored Normal file
View File

@@ -0,0 +1,120 @@
/* $NetBSD: cl.h,v 1.2 2010/02/03 15:34:37 roy Exp $ */
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*
* Id: cl.h,v 10.32 2001/08/28 11:33:40 skimo Exp (Berkeley) Date: 2001/08/28 11:33:40
*/
/* Avoid clash on OSF1 */
#undef DB
#ifdef USE_SLANG_CURSES
#include <slcurses.h>
#else
#ifdef HAVE_NCURSESW_NCURSES_H /* { */
#include <ncursesw/ncurses.h>
#else /* } { */
#ifdef HAVE_NCURSES_H /* { */
#include <ncurses.h>
#else /* } { */
#include <curses.h>
#include <term.h>
#include <termcap.h>
#endif /* } */
#endif
#endif
typedef struct _cl_private {
char ibuf[256]; /* Input keys. */
size_t skip; /* Remaining keys. */
CONVWIN cw; /* Conversion buffer. */
int eof_count; /* EOF count. */
struct termios orig; /* Original terminal values. */
struct termios ex_enter;/* Terminal values to enter ex. */
struct termios vi_enter;/* Terminal values to enter vi. */
char *el; /* Clear to EOL terminal string. */
char *cup; /* Cursor movement terminal string. */
char *cuu1; /* Cursor up terminal string. */
char *rmso, *smso; /* Inverse video terminal strings. */
char *smcup, *rmcup; /* Terminal start/stop strings. */
char *oname; /* Original screen window name. */
SCR *focus; /* Screen that has the "focus". */
int killersig; /* Killer signal. */
#define INDX_HUP 0
#define INDX_INT 1
#define INDX_TERM 2
#define INDX_WINCH 3
#define INDX_MAX 4 /* Original signal information. */
struct sigaction oact[INDX_MAX];
enum { /* Tty group write mode. */
TGW_UNKNOWN=0, TGW_SET, TGW_UNSET } tgw;
enum { /* Terminal initialization strings. */
TE_SENT=0, TI_SENT } ti_te;
#define CL_IN_EX 0x0001 /* Currently running ex. */
#define CL_LAYOUT 0x0002 /* Screen layout changed. */
#define CL_RENAME 0x0004 /* X11 xterm icon/window renamed. */
#define CL_RENAME_OK 0x0008 /* User wants the windows renamed. */
#define CL_SCR_EX_INIT 0x0010 /* Ex screen initialized. */
#define CL_SCR_VI_INIT 0x0020 /* Vi screen initialized. */
#define CL_SIGHUP 0x0040 /* SIGHUP arrived. */
#define CL_SIGINT 0x0080 /* SIGINT arrived. */
#define CL_SIGTERM 0x0100 /* SIGTERM arrived. */
#define CL_SIGWINCH 0x0200 /* SIGWINCH arrived. */
#define CL_STDIN_TTY 0x0400 /* Talking to a terminal. */
u_int32_t flags;
} CL_PRIVATE;
#define CLP(sp) ((CL_PRIVATE *)((sp)->gp->cl_private))
#define GCLP(gp) ((CL_PRIVATE *)gp->cl_private)
#define CLSP(sp) ((WINDOW *)((sp)->cl_private))
/* Return possibilities from the keyboard read routine. */
typedef enum { INP_OK=0, INP_EOF, INP_ERR, INP_INTR, INP_TIMEOUT } input_t;
/* The screen position relative to a specific window. */
/*
#define RCNO(sp, cno) (sp)->coff + (cno)
#define RLNO(sp, lno) (sp)->roff + (lno)
*/
#define RCNO(sp, cno) (cno)
#define RLNO(sp, lno) (lno)
/*
* XXX
* Some implementations of curses.h don't define these for us. Used for
* compatibility only.
*/
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#include "extern.h"
#ifdef USE_PERL_SETENV
#include "../perl_api/extern.h"
#define cl_setenv(sp,name,val) perl_setenv(sp,name,val)
#define cl_unsetenv(sp,name) perl_setenv(sp,name,NULL)
#else
#define cl_setenv(sp,name,val) setenv(name,val,1)
#define cl_unsetenv(sp,name) unsetenv(name)
#endif

350
dist/nvi/cl/cl_bsd.c vendored Normal file
View File

@@ -0,0 +1,350 @@
/* $NetBSD: cl_bsd.c,v 1.4 2010/02/03 15:34:37 roy Exp $ */
/*-
* Copyright (c) 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: cl_bsd.c,v 8.32 2000/12/01 13:56:17 skimo Exp (Berkeley) Date: 2000/12/01 13:56:17";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <ctype.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "../common/common.h"
#include "../vi/vi.h"
#include "cl.h"
#ifndef HAVE_CURSES_SETUPTERM
static char *ke; /* Keypad on. */
static char *ks; /* Keypad off. */
static char *vb; /* Visible bell string. */
#endif
/*
* HP's support the entire System V curses package except for the tigetstr
* and tigetnum functions. Ultrix supports the BSD curses package except
* for the idlok function. Cthulu only knows why. Break things up into a
* minimal set of functions.
*/
#ifndef HAVE_CURSES_WADDNSTR
/*
* waddnstr --
*
* PUBLIC: #ifndef HAVE_CURSES_WADDNSTR
* PUBLIC: int waddnstr __P((WINDOW*, char *, int));
* PUBLIC: #endif
*/
int
waddnstr(w, s, n)
WINDOW *w;
char *s;
int n;
{
int ch;
while (n-- && (ch = *s++))
waddch(w, ch);
return (OK);
}
#endif
#ifndef HAVE_CURSES_BEEP
/*
* beep --
*
* PUBLIC: #ifndef HAVE_CURSES_BEEP
* PUBLIC: void beep __P((void));
* PUBLIC: #endif
*/
void
beep()
{
(void)write(1, "\007", 1); /* '\a' */
}
#endif /* !HAVE_CURSES_BEEP */
#ifndef HAVE_CURSES_FLASH
/*
* flash --
* Flash the screen.
*
* PUBLIC: #ifndef HAVE_CURSES_FLASH
* PUBLIC: void flash __P((void));
* PUBLIC: #endif
*/
void
flash()
{
if (vb != NULL) {
(void)tputs(vb, 1, cl_putchar);
(void)fflush(stdout);
} else
beep();
}
#endif /* !HAVE_CURSES_FLASH */
#ifndef HAVE_CURSES_IDLOK
/*
* idlok --
* Turn on/off hardware line insert/delete.
*
* PUBLIC: #ifndef HAVE_CURSES_IDLOK
* PUBLIC: void idlok __P((WINDOW *, int));
* PUBLIC: #endif
*/
void
idlok(win, bf)
WINDOW *win;
int bf;
{
return;
}
#endif /* !HAVE_CURSES_IDLOK */
#ifndef HAVE_CURSES_KEYPAD
/*
* keypad --
* Put the keypad/cursor arrows into or out of application mode.
*
* PUBLIC: #ifndef HAVE_CURSES_KEYPAD
* PUBLIC: int keypad __P((void *, int));
* PUBLIC: #endif
*/
int
keypad(a, on)
void *a;
int on;
{
char *p;
if ((p = tigetstr(on ? "smkx" : "rmkx")) != (char *)-1) {
(void)tputs(p, 0, cl_putchar);
(void)fflush(stdout);
}
return (0);
}
#endif /* !HAVE_CURSES_KEYPAD */
#ifndef HAVE_CURSES_NEWTERM
/*
* newterm --
* Create a new curses screen.
*
* PUBLIC: #ifndef HAVE_CURSES_NEWTERM
* PUBLIC: void *newterm __P((const char *, FILE *, FILE *));
* PUBLIC: #endif
*/
void *
newterm(a, b, c)
const char *a;
FILE *b, *c;
{
return (initscr());
}
#endif /* !HAVE_CURSES_NEWTERM */
#ifndef HAVE_CURSES_SETUPTERM
/*
* setupterm --
* Set up terminal.
*
* PUBLIC: #ifndef HAVE_CURSES_SETUPTERM
* PUBLIC: void setupterm __P((char *, int, int *));
* PUBLIC: #endif
*/
void
setupterm(ttype, fno, errp)
char *ttype;
int fno, *errp;
{
static char buf[2048];
char *p;
if ((*errp = tgetent(buf, ttype)) > 0) {
if (ke != NULL)
free(ke);
ke = ((p = tigetstr("rmkx")) == (char *)-1) ?
NULL : strdup(p);
if (ks != NULL)
free(ks);
ks = ((p = tigetstr("smkx")) == (char *)-1) ?
NULL : strdup(p);
if (vb != NULL)
free(vb);
vb = ((p = tigetstr("flash")) == (char *)-1) ?
NULL : strdup(p);
}
}
#endif /* !HAVE_CURSES_SETUPTERM */
#ifndef HAVE_CURSES_TIGETSTR
/* Terminfo-to-termcap translation table. */
typedef struct _tl {
const char *terminfo; /* Terminfo name. */
const char *termcap; /* Termcap name. */
} TL;
static const TL list[] = {
{ "cols", "co", }, /* Terminal columns. */
{ "cup", "cm", }, /* Cursor up. */
{ "cuu1", "up", }, /* Cursor up. */
{ "el", "ce", }, /* Clear to end-of-line. */
{ "flash", "vb", }, /* Visible bell. */
{ "kcub1", "kl", }, /* Cursor left. */
{ "kcud1", "kd", }, /* Cursor down. */
{ "kcuf1", "kr", }, /* Cursor right. */
{ "kcuu1", "ku", }, /* Cursor up. */
{ "kdch1", "kD", }, /* Delete character. */
{ "kdl1", "kL", }, /* Delete line. */
{ "ked", "kS", }, /* Delete to end of screen. */
{ "kel", "kE", }, /* Delete to eol. */
{ "kend", "@7", }, /* Go to eol. */
{ "khome", "kh", }, /* Go to sol. */
{ "kich1", "kI", }, /* Insert at cursor. */
{ "kil1", "kA", }, /* Insert line. */
{ "kind", "kF", }, /* Scroll down. */
{ "kll", "kH", }, /* Go to eol. */
{ "knp", "kN", }, /* Page down. */
{ "kpp", "kP", }, /* Page up. */
{ "kri", "kR", }, /* Scroll up. */
{ "lines", "li", }, /* Terminal lines. */
{ "rmcup", "te", }, /* Terminal end string. */
{ "rmkx", "ke", }, /* Exit "keypad-transmit" mode. */
{ "rmso", "se", }, /* Standout end. */
{ "smcup", "ti", }, /* Terminal initialization string. */
{ "smkx", "ks", }, /* Enter "keypad-transmit" mode. */
{ "smso", "so", }, /* Standout begin. */
};
#ifdef _AIX
/*
* AIX's implementation for function keys greater than 10 is different and
* only goes as far as 36.
*/
static const char codes[] = {
/* 0-10 */ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ';',
/* 11-20 */ '<', '>', '!', '@', '#', '$', '%', '^', '&', '*',
/* 21-30 */ '(', ')', '-', '_', '+', ',', ':', '?', '[', ']',
/* 31-36 */ '{', '}', '|', '~', '/', '='
};
#else
/*
* !!!
* Historically, the 4BSD termcap code didn't support functions keys greater
* than 9. This was silently enforced -- asking for key k12 would return the
* value for k1. We try and get around this by using the tables specified in
* the terminfo(TI_ENV) man page from the 3rd Edition SVID. This assumes the
* implementors of any System V compatibility code or an extended termcap used
* those codes.
*/
static const char codes[] = {
/* 0-10 */ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ';',
/* 11-19 */ '1', '2', '3', '4', '5', '6', '7', '8', '9',
/* 20-63 */ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
};
#endif /* _AIX */
/*
* lcmp --
* list comparison routine for bsearch.
*/
static int
lcmp(const void *a, const void *b)
{
return (strcmp(a, ((const TL *)b)->terminfo));
}
/*
* tigetstr --
*
* Vendors put the prototype for tigetstr into random include files, including
* <term.h>, which we can't include because it makes other systems unhappy.
* Try and work around the problem, since we only care about the return value.
*
* PUBLIC: #ifdef HAVE_CURSES_TIGETSTR
* PUBLIC: char *tigetstr();
* PUBLIC: #else
* PUBLIC: char *tigetstr __P((char *));
* PUBLIC: #endif
*/
char *
tigetstr(name)
const char *name;
{
static char sbuf[256];
TL *tlp;
int n;
char *p, mykeyname[3];
if ((tlp = bsearch(name,
list, sizeof(list) / sizeof(TL), sizeof(TL), lcmp)) == NULL) {
#ifdef _AIX
if (name[0] == 'k' &&
name[1] == 'f' && (n = atoi(name + 2)) <= 36) {
mykeyname[0] = 'k';
mykeyname[1] = codes[n];
mykeyname[2] = '\0';
#else
if (name[0] == 'k' &&
name[1] == 'f' && (n = atoi(name + 2)) <= 63) {
mykeyname[0] = n <= 10 ? 'k' : 'F';
mykeyname[1] = codes[n];
mykeyname[2] = '\0';
#endif
name = mykeyname;
}
} else
name = tlp->termcap;
p = sbuf;
#ifdef _AIX
return ((p = tgetstr(name, &p)) == NULL ? (char *)-1 : strcpy(sbuf, p));
#else
return (tgetstr(name, &p) == NULL ? (char *)-1 : sbuf);
#endif
}
/*
* tigetnum --
*
* PUBLIC: #ifndef HAVE_CURSES_TIGETSTR
* PUBLIC: int tigetnum __P((char *));
* PUBLIC: #endif
*/
int
tigetnum(name)
const char *name;
{
TL *tlp;
int val;
if ((tlp = bsearch(name,
list, sizeof(list) / sizeof(TL), sizeof(TL), lcmp)) != NULL) {
name = tlp->termcap;
}
return ((val = tgetnum(name)) == -1 ? -2 : val);
}
#endif /* !HAVE_CURSES_TIGETSTR */

894
dist/nvi/cl/cl_funcs.c vendored Normal file
View File

@@ -0,0 +1,894 @@
/* $NetBSD: cl_funcs.c,v 1.4 2009/11/15 18:43:28 dsl Exp $ */
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: cl_funcs.c,v 10.72 2002/03/02 23:18:33 skimo Exp (Berkeley) Date: 2002/03/02 23:18:33";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <ctype.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "../common/common.h"
#include "../vi/vi.h"
#include "cl.h"
static void cl_rdiv __P((SCR *));
static int
addstr4(SCR *sp, const void *str, size_t len, int wide)
{
CL_PRIVATE *clp;
WINDOW *win;
size_t y, x;
int iv;
clp = CLP(sp);
win = CLSP(sp) ? CLSP(sp) : stdscr;
/*
* If ex isn't in control, it's the last line of the screen and
* it's a split screen, use inverse video.
*/
iv = 0;
getyx(win, y, x);
if (!F_ISSET(sp, SC_SCR_EXWROTE) &&
y == RLNO(sp, LASTLINE(sp)) && IS_SPLIT(sp)) {
iv = 1;
(void)wstandout(win);
}
#ifdef USE_WIDECHAR
if (wide) {
if (waddnwstr(win, str, len) == ERR)
return (1);
} else
#endif
if (waddnstr(win, str, len) == ERR)
return (1);
if (iv)
(void)wstandend(win);
return (0);
}
/*
* cl_waddstr --
* Add len bytes from the string at the cursor, advancing the cursor.
*
* PUBLIC: int cl_waddstr __P((SCR *, const CHAR_T *, size_t));
*/
int
cl_waddstr(SCR *sp, const CHAR_T *str, size_t len)
{
return addstr4(sp, (const void *)str, len, 1);
}
/*
* cl_addstr --
* Add len bytes from the string at the cursor, advancing the cursor.
*
* PUBLIC: int cl_addstr __P((SCR *, const char *, size_t));
*/
int
cl_addstr(SCR *sp, const char *str, size_t len)
{
return addstr4(sp, (const void *)str, len, 0);
}
/*
* cl_attr --
* Toggle a screen attribute on/off.
*
* PUBLIC: int cl_attr __P((SCR *, scr_attr_t, int));
*/
int
cl_attr(SCR *sp, scr_attr_t attribute, int on)
{
CL_PRIVATE *clp;
WINDOW *win;
clp = CLP(sp);
win = CLSP(sp) ? CLSP(sp) : stdscr;
switch (attribute) {
case SA_ALTERNATE:
/*
* !!!
* There's a major layering violation here. The problem is that the
* X11 xterm screen has what's known as an "alternate" screen. Some
* xterm termcap/terminfo entries include sequences to switch to/from
* that alternate screen as part of the ti/te (smcup/rmcup) strings.
* Vi runs in the alternate screen, so that you are returned to the
* same screen contents on exit from vi that you had when you entered
* vi. Further, when you run :shell, or :!date or similar ex commands,
* you also see the original screen contents. This wasn't deliberate
* on vi's part, it's just that it historically sent terminal init/end
* sequences at those times, and the addition of the alternate screen
* sequences to the strings changed the behavior of vi. The problem
* caused by this is that we don't want to switch back to the alternate
* screen while getting a new command from the user, when the user is
* continuing to enter ex commands, e.g.:
*
* :!date <<< switch to original screen
* [Hit return to continue] <<< prompt user to continue
* :command <<< get command from user
*
* Note that the :command input is a true vi input mode, e.g., input
* maps and abbreviations are being done. So, we need to be able to
* switch back into the vi screen mode, without flashing the screen.
*
* To make matters worse, the curses initscr() and endwin() calls will
* do this automatically -- so, this attribute isn't as controlled by
* the higher level screen as closely as one might like.
*/
if (on) {
if (clp->ti_te != TI_SENT) {
clp->ti_te = TI_SENT;
if (clp->smcup == NULL)
(void)cl_getcap(sp, "smcup", &clp->smcup);
if (clp->smcup != NULL)
(void)tputs(clp->smcup, 1, cl_putchar);
}
} else
if (clp->ti_te != TE_SENT) {
clp->ti_te = TE_SENT;
if (clp->rmcup == NULL)
(void)cl_getcap(sp, "rmcup", &clp->rmcup);
if (clp->rmcup != NULL)
(void)tputs(clp->rmcup, 1, cl_putchar);
(void)fflush(stdout);
}
(void)fflush(stdout);
break;
case SA_INVERSE:
if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) {
if (clp->smso == NULL)
return (1);
if (on)
(void)tputs(clp->smso, 1, cl_putchar);
else
(void)tputs(clp->rmso, 1, cl_putchar);
(void)fflush(stdout);
} else {
if (on)
(void)wstandout(win);
else
(void)wstandend(win);
}
break;
default:
abort();
}
return (0);
}
/*
* cl_baud --
* Return the baud rate.
*
* PUBLIC: int cl_baud __P((SCR *, u_long *));
*/
int
cl_baud(SCR *sp, u_long *ratep)
{
CL_PRIVATE *clp;
/*
* 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.
*/
clp = CLP(sp);
switch (cfgetospeed(&clp->orig)) {
case B50:
case B75:
case B110:
case B134:
case B150:
case B200:
case B300:
case B600:
*ratep = 600;
break;
case B1200:
*ratep = 1200;
break;
default:
*ratep = 9600;
break;
}
return (0);
}
/*
* cl_bell --
* Ring the bell/flash the screen.
*
* PUBLIC: int cl_bell __P((SCR *));
*/
int
cl_bell(SCR *sp)
{
if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE | SC_SCR_EX))
(void)write(STDOUT_FILENO, "\07", 1); /* \a */
else {
/*
* Vi has an edit option which determines if the terminal
* should be beeped or the screen flashed.
*/
if (O_ISSET(sp, O_FLASH))
(void)flash();
else
(void)beep();
}
return (0);
}
/*
* cl_clrtoeol --
* Clear from the current cursor to the end of the line.
*
* PUBLIC: int cl_clrtoeol __P((SCR *));
*/
int
cl_clrtoeol(SCR *sp)
{
WINDOW *win;
#if 0
size_t spcnt, y, x;
#endif
win = CLSP(sp) ? CLSP(sp) : stdscr;
#if 0
if (IS_VSPLIT(sp)) {
/* The cursor must be returned to its original position. */
getyx(win, y, x);
for (spcnt = (sp->coff + sp->cols) - x; spcnt > 0; --spcnt)
(void)waddch(win, ' ');
(void)wmove(win, y, x);
return (0);
} else
#endif
return (wclrtoeol(win) == ERR);
}
/*
* cl_cursor --
* Return the current cursor position.
*
* PUBLIC: int cl_cursor __P((SCR *, size_t *, size_t *));
*/
int
cl_cursor(SCR *sp, size_t *yp, size_t *xp)
{
WINDOW *win;
win = CLSP(sp) ? CLSP(sp) : stdscr;
/*
* The curses screen support splits a single underlying curses screen
* into multiple screens to support split screen semantics. For this
* reason the returned value must be adjusted to be relative to the
* current screen, and not absolute. Screens that implement the split
* using physically distinct screens won't need this hack.
*/
getyx(win, *yp, *xp);
/*
*yp -= sp->roff;
*xp -= sp->coff;
*/
return (0);
}
/*
* cl_deleteln --
* Delete the current line, scrolling all lines below it.
*
* PUBLIC: int cl_deleteln __P((SCR *));
*/
int
cl_deleteln(SCR *sp)
{
CHAR_T ch;
CL_PRIVATE *clp;
WINDOW *win;
size_t col, lno, spcnt, y, x;
clp = CLP(sp);
win = CLSP(sp) ? CLSP(sp) : stdscr;
/*
* This clause is required because the curses screen uses reverse
* video to delimit split screens. If the screen does not do this,
* this code won't be necessary.
*
* If the bottom line was in reverse video, rewrite it in normal
* video before it's scrolled.
*
* Check for the existence of a chgat function; XSI requires it, but
* historic implementations of System V curses don't. If it's not
* a #define, we'll fall back to doing it by hand, which is slow but
* acceptable.
*
* By hand means walking through the line, retrieving and rewriting
* each character. Curses has no EOL marker, so track strings of
* spaces, and copy the trailing spaces only if there's a non-space
* character following.
*/
if (!F_ISSET(sp, SC_SCR_EXWROTE) && IS_SPLIT(sp)) {
getyx(win, y, x);
#ifdef mvchgat
mvwchgat(win, RLNO(sp, LASTLINE(sp)), 0, -1, A_NORMAL, 0, NULL);
#else
for (lno = RLNO(sp, LASTLINE(sp)), col = spcnt = 0;;) {
(void)wmove(win, lno, col);
ch = winch(win);
if (isblank(ch))
++spcnt;
else {
(void)wmove(win, lno, col - spcnt);
for (; spcnt > 0; --spcnt)
(void)waddch(win, ' ');
(void)waddch(win, ch);
}
if (++col >= sp->cols)
break;
}
#endif
(void)wmove(win, y, x);
}
/*
* The bottom line is expected to be blank after this operation,
* and other screens must support that semantic.
*/
return (wdeleteln(win) == ERR);
}
/*
* cl_discard --
* Discard a screen.
*
* PUBLIC: int cl_discard __P((SCR *, SCR **));
*/
int
cl_discard(SCR *discardp, SCR **acquirep)
{
CL_PRIVATE *clp;
SCR* tsp;
if (discardp) {
clp = CLP(discardp);
F_SET(clp, CL_LAYOUT);
if (CLSP(discardp)) {
delwin(CLSP(discardp));
discardp->cl_private = NULL;
}
}
/* no screens got a piece; we're done */
if (!acquirep)
return 0;
for (; (tsp = *acquirep) != NULL; ++acquirep) {
clp = CLP(tsp);
F_SET(clp, CL_LAYOUT);
if (CLSP(tsp))
delwin(CLSP(tsp));
tsp->cl_private = subwin(stdscr, tsp->rows, tsp->cols,
tsp->roff, tsp->coff);
}
/* discardp is going away, acquirep is taking up its space. */
return (0);
}
/*
* cl_ex_adjust --
* Adjust the screen for ex. This routine is purely for standalone
* ex programs. All special purpose, all special case.
*
* PUBLIC: int cl_ex_adjust __P((SCR *, exadj_t));
*/
int
cl_ex_adjust(SCR *sp, exadj_t action)
{
CL_PRIVATE *clp;
int cnt;
clp = CLP(sp);
switch (action) {
case EX_TERM_SCROLL:
/* Move the cursor up one line if that's possible. */
if (clp->cuu1 != NULL)
(void)tputs(clp->cuu1, 1, cl_putchar);
else if (clp->cup != NULL)
(void)tputs(tgoto(clp->cup,
0, LINES - 2), 1, cl_putchar);
else
return (0);
/* FALLTHROUGH */
case EX_TERM_CE:
/* Clear the line. */
if (clp->el != NULL) {
(void)putchar('\r');
(void)tputs(clp->el, 1, cl_putchar);
} else {
/*
* Historically, ex didn't erase the line, so, if the
* displayed line was only a single glyph, and <eof>
* was more than one glyph, the output would not fully
* overwrite the user's input. To fix this, output
* the maxiumum character number of spaces. Note,
* this won't help if the user entered extra prompt
* or <blank> characters before the command character.
* We'd have to do a lot of work to make that work, and
* it's almost certainly not worth the effort.
*/
for (cnt = 0; cnt < MAX_CHARACTER_COLUMNS; ++cnt)
(void)putchar('\b');
for (cnt = 0; cnt < MAX_CHARACTER_COLUMNS; ++cnt)
(void)putchar(' ');
(void)putchar('\r');
(void)fflush(stdout);
}
break;
default:
abort();
}
return (0);
}
/*
* cl_insertln --
* Push down the current line, discarding the bottom line.
*
* PUBLIC: int cl_insertln __P((SCR *));
*/
int
cl_insertln(SCR *sp)
{
WINDOW *win;
win = CLSP(sp) ? CLSP(sp) : stdscr;
/*
* The current line is expected to be blank after this operation,
* and the screen must support that semantic.
*/
return (winsertln(win) == ERR);
}
/*
* cl_keyval --
* Return the value for a special key.
*
* PUBLIC: int cl_keyval __P((SCR *, scr_keyval_t, CHAR_T *, int *));
*/
int
cl_keyval(SCR *sp, scr_keyval_t val, CHAR_T *chp, int *dnep)
{
CL_PRIVATE *clp;
/*
* VEOF, VERASE and VKILL are required by POSIX 1003.1-1990,
* VWERASE is a 4BSD extension.
*/
clp = CLP(sp);
switch (val) {
case KEY_VEOF:
*dnep = (*chp = clp->orig.c_cc[VEOF]) == _POSIX_VDISABLE;
break;
case KEY_VERASE:
*dnep = (*chp = clp->orig.c_cc[VERASE]) == _POSIX_VDISABLE;
break;
case KEY_VKILL:
*dnep = (*chp = clp->orig.c_cc[VKILL]) == _POSIX_VDISABLE;
break;
#ifdef VWERASE
case KEY_VWERASE:
*dnep = (*chp = clp->orig.c_cc[VWERASE]) == _POSIX_VDISABLE;
break;
#endif
default:
*dnep = 1;
break;
}
return (0);
}
/*
* cl_move --
* Move the cursor.
*
* PUBLIC: int cl_move __P((SCR *, size_t, size_t));
*/
int
cl_move(SCR *sp, size_t lno, size_t cno)
{
WINDOW *win;
win = CLSP(sp) ? CLSP(sp) : stdscr;
/* See the comment in cl_cursor. */
if (wmove(win, RLNO(sp, lno), RCNO(sp, cno)) == ERR) {
msgq(sp, M_ERR, "Error: move: l(%zu + %zu) c(%zu + %zu)",
lno, sp->roff, cno, sp->coff);
return (1);
}
return (0);
}
/*
* cl_refresh --
* Refresh the screen.
*
* PUBLIC: int cl_refresh __P((SCR *, int));
*/
int
cl_refresh(SCR *sp, int repaint)
{
GS *gp;
CL_PRIVATE *clp;
WINDOW *win;
SCR *psp, *tsp;
size_t y, x;
gp = sp->gp;
clp = CLP(sp);
win = CLSP(sp) ? CLSP(sp) : stdscr;
/*
* If we received a killer signal, we're done, there's no point
* in refreshing the screen.
*/
if (clp->killersig)
return (0);
/*
* If repaint is set, the editor is telling us that we don't know
* what's on the screen, so we have to repaint from scratch.
*
* If repaint set or the screen layout changed, we need to redraw
* any lines separating vertically split screens. If the horizontal
* offsets are the same, then the split was vertical, and need to
* draw a dividing line.
*/
if (repaint || F_ISSET(clp, CL_LAYOUT)) {
getyx(stdscr, y, x);
for (psp = sp;
psp != (void *)&sp->wp->scrq; psp = psp->q.cqe_next)
for (tsp = psp->q.cqe_next;
tsp != (void *)&sp->wp->scrq;
tsp = tsp->q.cqe_next)
if (psp->roff == tsp->roff) {
if (psp->coff + psp->cols + 1 == tsp->coff)
cl_rdiv(psp);
else
if (tsp->coff + tsp->cols + 1 == psp->coff)
cl_rdiv(tsp);
}
(void)wmove(stdscr, y, x);
F_CLR(clp, CL_LAYOUT);
}
/*
* In the curses library, doing wrefresh(curscr) is okay, but the
* screen flashes when we then apply the refresh() to bring it up
* to date. So, use clearok().
*/
if (repaint)
clearok(curscr, 1);
/*
* Only do an actual refresh, when this is the focus window,
* i.e. the one holding the cursor. This assumes that refresh
* is called for that window after refreshing the others.
* This prevents the cursor being drawn in the other windows.
*/
return (wnoutrefresh(stdscr) == ERR ||
wnoutrefresh(win) == ERR ||
(sp == clp->focus && doupdate() == ERR));
}
/*
* cl_rdiv --
* Draw a dividing line between two vertically split screens.
*/
static void
cl_rdiv(SCR *sp)
{
size_t cnt;
for (cnt = 0; cnt < sp->rows - 1; ++cnt) {
wmove(stdscr, sp->roff + cnt, sp->cols + sp->coff);
waddch(stdscr, '|');
}
}
/*
* cl_rename --
* Rename the file.
*
* PUBLIC: int cl_rename __P((SCR *, char *, int));
*/
int
cl_rename(SCR *sp, char *name, int on)
{
CL_PRIVATE *clp;
FILE *pfp;
GS *gp;
char buf[256], *p;
gp = sp->gp;
clp = CLP(sp);
if (on) {
clp->focus = sp;
if (!F_ISSET(clp, CL_RENAME_OK))
return (0);
/*
* XXX
* We can only rename windows for xterm.
*/
if (strncmp(OG_STR(gp, GO_TERM), "xterm", sizeof("xterm") - 1))
return (0);
/*
* XXX
* Try and figure out the current name of this window. There
* are two forms of the xwininfo output I've seen:
*
* Window id: 0x400000d "name"
* Window id: 0x140000d (name)
*/
#define COMMAND \
"expr \"`xwininfo -id $WINDOWID | grep id:`\" : '.* [\"(]\\(.*\\)[\")]'"
if (clp->oname == NULL &&
(pfp = popen(COMMAND, "r")) != NULL) {
if (fgets(buf, sizeof(buf), pfp) != NULL &&
(p = strchr(buf, '\n')) != NULL) {
*p = '\0';
clp->oname = strdup(buf);
}
(void)fclose(pfp);
}
cl_setname(gp, name);
F_SET(clp, CL_RENAME);
} else
if (F_ISSET(clp, CL_RENAME)) {
cl_setname(gp, clp->oname);
F_CLR(clp, CL_RENAME);
}
return (0);
}
/*
* cl_setname --
* Set a X11 icon/window name.
*
* PUBLIC: void cl_setname __P((GS *, char *));
*/
void
cl_setname(GS *gp, char *name)
{
/* X11 xterm escape sequence to rename the icon/window. */
#define XTERM_RENAME "\033]0;%s\007"
(void)printf(XTERM_RENAME, name == NULL ? OG_STR(gp, GO_TERM) : name);
(void)fflush(stdout);
}
/*
* cl_split --
* Split a screen.
*
* PUBLIC: int cl_split __P((SCR *, SCR *));
*/
int
cl_split(SCR *origp, SCR *newp)
{
CL_PRIVATE *clp;
clp = CLP(origp);
F_SET(clp, CL_LAYOUT);
if (CLSP(origp))
delwin(CLSP(origp));
origp->cl_private = subwin(stdscr, origp->rows, origp->cols,
origp->roff, origp->coff);
newp->cl_private = subwin(stdscr, newp->rows, newp->cols,
newp->roff, newp->coff);
/* origp is the original screen, giving up space to newp. */
return (0);
}
/*
* cl_suspend --
* Suspend a screen.
*
* PUBLIC: int cl_suspend __P((SCR *, int *));
*/
int
cl_suspend(SCR *sp, int *allowedp)
{
struct termios t;
CL_PRIVATE *clp;
WINDOW *win;
GS *gp;
size_t y, x;
int changed;
gp = sp->gp;
clp = CLP(sp);
win = CLSP(sp) ? CLSP(sp) : stdscr;
*allowedp = 1;
/*
* The ex implementation of this function isn't needed by screens not
* supporting ex commands that require full terminal canonical mode
* (e.g. :suspend).
*
* The vi implementation of this function isn't needed by screens not
* supporting vi process suspension, i.e. any screen that isn't backed
* by a UNIX shell.
*
* Setting allowedp to 0 will cause the editor to reject the command.
*/
if (F_ISSET(sp, SC_EX)) {
/* Save the terminal settings, and restore the original ones. */
if (F_ISSET(clp, CL_STDIN_TTY)) {
(void)tcgetattr(STDIN_FILENO, &t);
(void)tcsetattr(STDIN_FILENO,
TCSASOFT | TCSADRAIN, &clp->orig);
}
/* Stop the process group. */
(void)kill(0, SIGTSTP);
/* Time passes ... */
/* Restore terminal settings. */
if (F_ISSET(clp, CL_STDIN_TTY))
(void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t);
return (0);
}
/*
* Move to the lower left-hand corner of the screen.
*
* XXX
* Not sure this is necessary in System V implementations, but it
* shouldn't hurt.
*/
getyx(win, y, x);
(void)wmove(win, LINES - 1, 0);
(void)wrefresh(win);
/*
* Temporarily end the screen. System V introduced a semantic where
* endwin() could be restarted. We use it because restarting curses
* from scratch often fails in System V. 4BSD curses didn't support
* restarting after endwin(), so we have to do what clean up we can
* without calling it.
*/
/* Save the terminal settings. */
(void)tcgetattr(STDIN_FILENO, &t);
/* Restore the cursor keys to normal mode. */
(void)keypad(stdscr, FALSE);
/* Restore the window name. */
(void)cl_rename(sp, NULL, 0);
#ifdef HAVE_BSD_CURSES
(void)cl_attr(sp, SA_ALTERNATE, 0);
#else
(void)endwin();
#endif
/*
* XXX
* Restore the original terminal settings. This is bad -- the
* reset can cause character loss from the tty queue. However,
* we can't call endwin() in BSD curses implementations, and too
* many System V curses implementations don't get it right.
*/
(void)tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &clp->orig);
/* Stop the process group. */
(void)kill(0, SIGTSTP);
/* Time passes ... */
/*
* If we received a killer signal, we're done. Leave everything
* unchanged. In addition, the terminal has already been reset
* correctly, so leave it alone.
*/
if (clp->killersig) {
F_CLR(clp, CL_SCR_EX_INIT | CL_SCR_VI_INIT);
return (0);
}
/* Restore terminal settings. */
wrefresh(win); /* Needed on SunOs/Solaris ? */
if (F_ISSET(clp, CL_STDIN_TTY))
(void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t);
#ifdef HAVE_BSD_CURSES
(void)cl_attr(sp, SA_ALTERNATE, 1);
#endif
/* Set the window name. */
(void)cl_rename(sp, sp->frp->name, 1);
/* Put the cursor keys into application mode. */
(void)keypad(stdscr, TRUE);
/* Refresh and repaint the screen. */
(void)wmove(win, y, x);
(void)cl_refresh(sp, 1);
/* If the screen changed size, set the SIGWINCH bit. */
if (cl_ssize(sp, 1, NULL, NULL, &changed))
return (1);
if (changed)
F_SET(CLP(sp), CL_SIGWINCH);
return (0);
}
/*
* cl_usage --
* Print out the curses usage messages.
*
* PUBLIC: void cl_usage __P((void));
*/
void
cl_usage(void)
{
#define USAGE "\
usage: ex [-eFRrSsv] [-c command] [-t tag] [-w size] [file ...]\n\
usage: vi [-eFlRrSv] [-c command] [-t tag] [-w size] [file ...]\n"
(void)fprintf(stderr, "%s", USAGE);
#undef USAGE
}
#ifdef DEBUG
/*
* gdbrefresh --
* Stub routine so can flush out curses screen changes using gdb.
*/
int
gdbrefresh(void)
{
refresh();
return (0); /* XXX Convince gdb to run it. */
}
#endif

440
dist/nvi/cl/cl_main.c vendored Normal file
View File

@@ -0,0 +1,440 @@
/* $NetBSD: cl_main.c,v 1.4 2011/09/16 16:13:41 joerg Exp $ */
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: cl_main.c,v 10.54 2001/07/29 19:07:27 skimo Exp (Berkeley) Date: 2001/07/29 19:07:27";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <bitstring.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "../common/common.h"
#include "../ip/extern.h"
#include "cl.h"
#include "pathnames.h"
GS *__global_list; /* GLOBAL: List of screens. */
sigset_t __sigblockset; /* GLOBAL: Blocked signals. */
static void cl_func_std __P((WIN *));
#ifdef notused
static void cl_end __P((CL_PRIVATE *));
#endif
static CL_PRIVATE *cl_init __P((WIN *));
__dead static void perr __P((const char *, const char *));
static int setsig __P((int, struct sigaction *, void (*)(int)));
static void sig_end __P((GS *));
static void term_init __P((const char *, const char *));
/*
* main --
* This is the main loop for the standalone curses editor.
*/
int
main(int argc, char **argv)
{
static int reenter;
CL_PRIVATE *clp;
GS *gp;
WIN *wp;
size_t rows, cols;
int rval;
char **p_av, **t_av;
const char *ttype;
/* If loaded at 0 and jumping through a NULL pointer, stop. */
if (reenter++)
abort();
/* Create and initialize the global structure. */
__global_list = gp = gs_init(argv[0]);
/*
* Strip out any arguments that vi isn't going to understand. There's
* no way to portably call getopt twice, so arguments parsed here must
* be removed from the argument list.
*/
for (p_av = t_av = argv;;) {
if (*t_av == NULL) {
*p_av = NULL;
break;
}
if (!strcmp(*t_av, "--")) {
while ((*p_av++ = *t_av++) != NULL);
break;
}
*p_av++ = *t_av++;
}
/* Create new window */
wp = gs_new_win(gp);
/* Create and initialize the CL_PRIVATE structure. */
clp = cl_init(wp);
/*
* Initialize the terminal information.
*
* We have to know what terminal it is from the start, since we may
* have to use termcap/terminfo to find out how big the screen is.
*/
if ((ttype = getenv("TERM")) == NULL) {
if (isatty(STDIN_FILENO))
fprintf(stderr, "%s: warning: TERM is not set\n",
gp->progname);
ttype = "unknown";
}
term_init(gp->progname, ttype);
/* Add the terminal type to the global structure. */
if ((OG_D_STR(gp, GO_TERM) =
OG_STR(gp, GO_TERM) = strdup(ttype)) == NULL)
perr(gp->progname, NULL);
/* Figure out how big the screen is. */
if (cl_ssize(NULL, 0, &rows, &cols, NULL))
exit (1);
/* Add the rows and columns to the global structure. */
OG_VAL(gp, GO_LINES) = OG_D_VAL(gp, GO_LINES) = rows;
OG_VAL(gp, GO_COLUMNS) = OG_D_VAL(gp, GO_COLUMNS) = cols;
/* Ex wants stdout to be buffered. */
(void)setvbuf(stdout, NULL, _IOFBF, 0);
/* Start catching signals. */
if (sig_init(gp, NULL))
exit (1);
/* Run ex/vi. */
rval = editor(wp, argc, argv);
/* Clean out the global structure. */
gs_end(gp);
/* Clean up signals. */
sig_end(gp);
/* Clean up the terminal. */
(void)cl_quit(gp);
/*
* XXX
* Reset the O_MESG option.
*/
if (clp->tgw != TGW_UNKNOWN)
(void)cl_omesg(NULL, clp, clp->tgw == TGW_SET);
/*
* XXX
* Reset the X11 xterm icon/window name.
*/
if (F_ISSET(clp, CL_RENAME))
cl_setname(gp, clp->oname);
/* If a killer signal arrived, pretend we just got it. */
if (clp->killersig) {
(void)signal(clp->killersig, SIG_DFL);
(void)kill(getpid(), clp->killersig);
/* NOTREACHED */
}
/* Free the global and CL private areas. */
#if defined(DEBUG) || defined(PURIFY) || defined(LIBRARY)
cl_end(clp);
free(gp);
#endif
exit (rval);
}
/*
* cl_init --
* Create and partially initialize the CL structure.
*/
static CL_PRIVATE *
cl_init(WIN *wp)
{
CL_PRIVATE *clp;
int fd;
GS *gp;
gp = wp->gp;
/* Allocate the CL private structure. */
CALLOC_NOMSG(NULL, clp, CL_PRIVATE *, 1, sizeof(CL_PRIVATE));
if (clp == NULL)
perr(gp->progname, NULL);
gp->cl_private = clp;
/*
* Set the CL_STDIN_TTY flag. It's purpose is to avoid setting
* and resetting the tty if the input isn't from there. We also
* use the same test to determine if we're running a script or
* not.
*/
if (isatty(STDIN_FILENO))
F_SET(clp, CL_STDIN_TTY);
else
F_SET(gp, G_SCRIPTED);
/*
* We expect that if we've lost our controlling terminal that the
* open() (but not the tcgetattr()) will fail.
*/
if (F_ISSET(clp, CL_STDIN_TTY)) {
if (tcgetattr(STDIN_FILENO, &clp->orig) == -1)
goto tcfail;
} else if ((fd = open(_PATH_TTY, O_RDONLY, 0)) != -1) {
if (tcgetattr(fd, &clp->orig) == -1) {
tcfail: perr(gp->progname, "tcgetattr");
exit (1);
}
(void)close(fd);
}
/* Initialize the list of curses functions. */
cl_func_std(wp);
return (clp);
}
#ifdef notused
/*
* cl_end --
* Discard the CL structure.
*/
static void
cl_end(CL_PRIVATE *clp)
{
if (clp->oname != NULL)
free(clp->oname);
free(clp);
}
#endif
/*
* term_init --
* Initialize terminal information.
*/
static void
term_init(const char *name, const char *ttype)
{
int err;
/* Set up the terminal database information. */
setupterm(__UNCONST(ttype), STDOUT_FILENO, &err);
switch (err) {
case -1:
(void)fprintf(stderr,
"%s: No terminal database found\n", name);
exit (1);
case 0:
(void)fprintf(stderr,
"%s: %s: unknown terminal type\n", name, ttype);
exit (1);
}
}
#define GLOBAL_CLP \
CL_PRIVATE *clp = GCLP(__global_list);
static void
h_hup(int signo)
{
GLOBAL_CLP;
F_SET(clp, CL_SIGHUP);
clp->killersig = SIGHUP;
}
static void
h_int(int signo)
{
GLOBAL_CLP;
F_SET(clp, CL_SIGINT);
}
static void
h_term(int signo)
{
GLOBAL_CLP;
F_SET(clp, CL_SIGTERM);
clp->killersig = SIGTERM;
}
static void
h_winch(int signo)
{
GLOBAL_CLP;
F_SET(clp, CL_SIGWINCH);
}
#undef GLOBAL_CLP
/*
* sig_init --
* Initialize signals.
*
* PUBLIC: int sig_init __P((GS *, SCR *));
*/
int
sig_init(GS *gp, SCR *sp)
{
CL_PRIVATE *clp;
clp = GCLP(gp);
if (sp == NULL) {
(void)sigemptyset(&__sigblockset);
if (sigaddset(&__sigblockset, SIGHUP) ||
setsig(SIGHUP, &clp->oact[INDX_HUP], h_hup) ||
sigaddset(&__sigblockset, SIGINT) ||
setsig(SIGINT, &clp->oact[INDX_INT], h_int) ||
sigaddset(&__sigblockset, SIGTERM) ||
setsig(SIGTERM, &clp->oact[INDX_TERM], h_term)
#ifdef SIGWINCH
||
sigaddset(&__sigblockset, SIGWINCH) ||
setsig(SIGWINCH, &clp->oact[INDX_WINCH], h_winch)
#endif
) {
perr(gp->progname, NULL);
return (1);
}
} else
if (setsig(SIGHUP, NULL, h_hup) ||
setsig(SIGINT, NULL, h_int) ||
setsig(SIGTERM, NULL, h_term)
#ifdef SIGWINCH
||
setsig(SIGWINCH, NULL, h_winch)
#endif
) {
msgq(sp, M_SYSERR, "signal-reset");
}
return (0);
}
/*
* setsig --
* Set a signal handler.
*/
static int
setsig(int signo, struct sigaction *oactp, void (*handler) (int))
{
struct sigaction act;
/*
* 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. Besides, you can't portably
* restart system calls (thanks, POSIX!). On the other hand, you
* can't portably NOT restart system calls (thanks, Sun!). SunOS
* used SA_INTERRUPT as their extension to NOT restart read calls.
* We sure hope nobody else used it for anything else. Mom told me
* there'd be days like this. She just never told me that there'd
* be so many.
*/
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
#ifdef SA_INTERRUPT
act.sa_flags = SA_INTERRUPT;
#else
act.sa_flags = 0;
#endif
return (sigaction(signo, &act, oactp));
}
/*
* sig_end --
* End signal setup.
*/
static void
sig_end(GS *gp)
{
CL_PRIVATE *clp;
clp = GCLP(gp);
(void)sigaction(SIGHUP, NULL, &clp->oact[INDX_HUP]);
(void)sigaction(SIGINT, NULL, &clp->oact[INDX_INT]);
(void)sigaction(SIGTERM, NULL, &clp->oact[INDX_TERM]);
#ifdef SIGWINCH
(void)sigaction(SIGWINCH, NULL, &clp->oact[INDX_WINCH]);
#endif
}
/*
* cl_func_std --
* Initialize the standard curses functions.
*/
static void
cl_func_std(WIN *wp)
{
GS *gp;
gp = wp->gp;
gp->scr_addstr = cl_addstr;
gp->scr_waddstr = cl_waddstr;
gp->scr_attr = cl_attr;
gp->scr_baud = cl_baud;
gp->scr_bell = cl_bell;
gp->scr_busy = NULL;
gp->scr_child = NULL;
gp->scr_clrtoeol = cl_clrtoeol;
gp->scr_cursor = cl_cursor;
gp->scr_deleteln = cl_deleteln;
gp->scr_reply = NULL;
gp->scr_discard = cl_discard;
gp->scr_event = cl_event;
gp->scr_ex_adjust = cl_ex_adjust;
gp->scr_fmap = cl_fmap;
gp->scr_insertln = cl_insertln;
gp->scr_keyval = cl_keyval;
gp->scr_move = cl_move;
wp->scr_msg = NULL;
gp->scr_optchange = cl_optchange;
gp->scr_refresh = cl_refresh;
gp->scr_rename = cl_rename;
gp->scr_screen = cl_screen;
gp->scr_split = cl_split;
gp->scr_suspend = cl_suspend;
gp->scr_usage = cl_usage;
}
/*
* perr --
* Print system error.
*/
static void
perr(const char *name, const char *msg)
{
(void)fprintf(stderr, "%s:", name);
if (msg != NULL)
(void)fprintf(stderr, "%s:", msg);
(void)fprintf(stderr, "%s\n", strerror(errno));
exit(1);
}

312
dist/nvi/cl/cl_read.c vendored Normal file
View File

@@ -0,0 +1,312 @@
/* $NetBSD: cl_read.c,v 1.2 2008/12/05 22:51:42 christos Exp $ */
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: cl_read.c,v 10.29 2001/08/18 21:51:59 skimo Exp (Berkeley) Date: 2001/08/18 21:51:59";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include <sys/time.h>
#include <bitstring.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "../common/common.h"
#include "../ex/script.h"
#include "cl.h"
/* Pollution by Solaris curses. */
#undef columns
#undef lines
static input_t cl_read __P((SCR *,
u_int32_t, char *, size_t, int *, struct timeval *));
static int cl_resize __P((SCR *, size_t, size_t));
/*
* cl_event --
* Return a single event.
*
* PUBLIC: int cl_event __P((SCR *, EVENT *, u_int32_t, int));
*/
int
cl_event(SCR *sp, EVENT *evp, u_int32_t flags, int ms)
{
struct timeval t, *tp;
CL_PRIVATE *clp;
size_t lines, columns;
int changed, nr = 0;
const CHAR_T *wp;
size_t wlen;
int rc;
/*
* Queue signal based events. We never clear SIGHUP or SIGTERM events,
* so that we just keep returning them until the editor dies.
*/
clp = CLP(sp);
retest: if (LF_ISSET(EC_INTERRUPT) || F_ISSET(clp, CL_SIGINT)) {
if (F_ISSET(clp, CL_SIGINT)) {
F_CLR(clp, CL_SIGINT);
evp->e_event = E_INTERRUPT;
} else
evp->e_event = E_TIMEOUT;
return (0);
}
if (F_ISSET(clp, CL_SIGHUP | CL_SIGTERM | CL_SIGWINCH)) {
if (F_ISSET(clp, CL_SIGHUP)) {
evp->e_event = E_SIGHUP;
return (0);
}
if (F_ISSET(clp, CL_SIGTERM)) {
evp->e_event = E_SIGTERM;
return (0);
}
if (F_ISSET(clp, CL_SIGWINCH)) {
F_CLR(clp, CL_SIGWINCH);
if (cl_ssize(sp, 1, &lines, &columns, &changed))
return (1);
if (changed) {
(void)cl_resize(sp, lines, columns);
evp->e_event = E_WRESIZE;
return (0);
}
/* No real change, ignore the signal. */
}
}
/* Set timer. */
if (ms == 0)
tp = NULL;
else {
t.tv_sec = ms / 1000;
t.tv_usec = (ms % 1000) * 1000;
tp = &t;
}
/* Read input characters. */
read:
switch (cl_read(sp, LF_ISSET(EC_QUOTED | EC_RAW),
clp->ibuf + clp->skip, SIZE(clp->ibuf) - clp->skip, &nr, tp)) {
case INP_OK:
rc = INPUT2INT5(sp, clp->cw, clp->ibuf, nr + clp->skip,
wp, wlen);
evp->e_csp = __UNCONST(wp);
evp->e_len = wlen;
evp->e_event = E_STRING;
if (rc < 0) {
int n = -rc;
memmove(clp->ibuf, clp->ibuf + nr + clp->skip - n, n);
clp->skip = n;
if (wlen == 0)
goto read;
} else if (rc == 0)
clp->skip = 0;
else
msgq(sp, M_ERR, "323|Invalid input. Truncated.");
break;
case INP_ERR:
evp->e_event = E_ERR;
break;
case INP_EOF:
evp->e_event = E_EOF;
break;
case INP_INTR:
goto retest;
case INP_TIMEOUT:
evp->e_event = E_TIMEOUT;
break;
default:
abort();
}
return (0);
}
/*
* cl_read --
* Read characters from the input.
*/
static input_t
cl_read(SCR *sp, u_int32_t flags, char *bp, size_t blen, int *nrp, struct timeval *tp)
{
struct termios term1, term2;
struct timeval poll;
CL_PRIVATE *clp;
GS *gp;
fd_set rdfd;
input_t rval;
int maxfd, nr, term_reset;
gp = sp->gp;
clp = CLP(sp);
term_reset = 0;
/*
* 1: A read from a file or a pipe. In this case, the reads
* never timeout regardless. This means that we can hang
* when trying to complete a map, but we're going to hang
* on the next read anyway.
*/
if (!F_ISSET(clp, CL_STDIN_TTY)) {
switch (nr = read(STDIN_FILENO, bp, blen)) {
case 0:
return (INP_EOF);
case -1:
goto err;
default:
*nrp = nr;
return (INP_OK);
}
/* NOTREACHED */
}
/*
* 2: A read with an associated timeout, e.g., trying to complete
* a map sequence. If input exists, we fall into #3.
*/
FD_ZERO(&rdfd);
poll.tv_sec = 0;
poll.tv_usec = 0;
if (tp != NULL) {
FD_SET(STDIN_FILENO, &rdfd);
switch (select(STDIN_FILENO + 1,
&rdfd, NULL, NULL, tp == NULL ? &poll : tp)) {
case 0:
return (INP_TIMEOUT);
case -1:
goto err;
default:
break;
}
}
/*
* The user can enter a key in the editor to quote a character. If we
* get here and the next key is supposed to be quoted, do what we can.
* Reset the tty so that the user can enter a ^C, ^Q, ^S. There's an
* obvious race here, when the key has already been entered, but there's
* nothing that we can do to fix that problem.
*
* The editor can ask for the next literal character even thought it's
* generally running in line-at-a-time mode. Do what we can.
*/
if (LF_ISSET(EC_QUOTED | EC_RAW) && !tcgetattr(STDIN_FILENO, &term1)) {
term_reset = 1;
if (LF_ISSET(EC_QUOTED)) {
term2 = term1;
term2.c_lflag &= ~ISIG;
term2.c_iflag &= ~(IXON | IXOFF);
(void)tcsetattr(STDIN_FILENO,
TCSASOFT | TCSADRAIN, &term2);
} else
(void)tcsetattr(STDIN_FILENO,
TCSASOFT | TCSADRAIN, &clp->vi_enter);
}
/*
* 3: Wait for input.
*
* Select on the command input and scripting window file descriptors.
* It's ugly that we wait on scripting file descriptors here, but it's
* the only way to keep from locking out scripting windows.
*/
if (F_ISSET(gp, G_SCRWIN)) {
FD_ZERO(&rdfd);
FD_SET(STDIN_FILENO, &rdfd);
maxfd = STDIN_FILENO;
if (sscr_check_input(sp, &rdfd, maxfd))
goto err;
}
/*
* 4: Read the input.
*
* !!!
* What's going on here is some scary stuff. Ex runs the terminal in
* canonical mode. So, the <newline> character terminating a line of
* input is returned in the buffer, but a trailing <EOF> character is
* not similarly included. As ex uses 0<EOF> and ^<EOF> as autoindent
* commands, it has to see the trailing <EOF> characters to determine
* the difference between the user entering "0ab" and "0<EOF>ab". We
* leave an extra slot in the buffer, so that we can add a trailing
* <EOF> character if the buffer isn't terminated by a <newline>. We
* lose if the buffer is too small for the line and exactly N characters
* are entered followed by an <EOF> character.
*/
#define ONE_FOR_EOF 1
switch (nr = read(STDIN_FILENO, bp, blen - ONE_FOR_EOF)) {
case 0: /* EOF. */
/*
* ^D in canonical mode returns a read of 0, i.e. EOF. EOF is
* a valid command, but we don't want to loop forever because
* the terminal driver is returning EOF because the user has
* disconnected. The editor will almost certainly try to write
* something before this fires, which should kill us, but You
* Never Know.
*/
if (++clp->eof_count < 50) {
bp[0] = clp->orig.c_cc[VEOF];
*nrp = 1;
rval = INP_OK;
} else
rval = INP_EOF;
break;
case -1: /* Error or interrupt. */
err: if (errno == EINTR)
rval = INP_INTR;
else {
rval = INP_ERR;
msgq(sp, M_SYSERR, "input");
}
break;
default: /* Input characters. */
if (F_ISSET(sp, SC_EX) && bp[nr - 1] != '\n')
bp[nr++] = clp->orig.c_cc[VEOF];
*nrp = nr;
clp->eof_count = 0;
rval = INP_OK;
break;
}
/* Restore the terminal state if it was modified. */
if (term_reset)
(void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &term1);
return (rval);
}
/*
* cl_resize --
* Reset the options for a resize event.
*/
static int
cl_resize(SCR *sp, size_t lines, size_t columns)
{
int rval;
rval = api_opts_set(sp, L("lines"), NULL, lines, 0);
if (api_opts_set(sp, L("columns"), NULL, columns, 0))
rval = 1;
return (rval);
}

580
dist/nvi/cl/cl_screen.c vendored Normal file
View File

@@ -0,0 +1,580 @@
/* $NetBSD: cl_screen.c,v 1.2 2008/12/05 22:51:42 christos Exp $ */
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: cl_screen.c,v 10.56 2002/05/03 19:59:44 skimo Exp (Berkeley) Date: 2002/05/03 19:59:44";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <bitstring.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "../common/common.h"
#include "cl.h"
static int cl_ex_end __P((GS *));
static int cl_ex_init __P((SCR *));
static void cl_freecap __P((CL_PRIVATE *));
static int cl_vi_end __P((GS *));
static int cl_vi_init __P((SCR *));
static int cl_putenv __P((SCR *, const char *, const char *, u_long));
/*
* cl_screen --
* Switch screen types.
*
* PUBLIC: int cl_screen __P((SCR *, u_int32_t));
*/
int
cl_screen(SCR *sp, u_int32_t flags)
{
CL_PRIVATE *clp;
WINDOW *win;
GS *gp;
gp = sp->gp;
clp = CLP(sp);
win = CLSP(sp) ? CLSP(sp) : stdscr;
/* See if the current information is incorrect. */
if (F_ISSET(gp, G_SRESTART)) {
if (CLSP(sp)) {
delwin(CLSP(sp));
sp->cl_private = NULL;
}
if (cl_quit(gp))
return (1);
F_CLR(gp, G_SRESTART);
}
/* See if we're already in the right mode. */
if ((LF_ISSET(SC_EX) && F_ISSET(sp, SC_SCR_EX)) ||
(LF_ISSET(SC_VI) && F_ISSET(sp, SC_SCR_VI)))
return (0);
/*
* Fake leaving ex mode.
*
* We don't actually exit ex or vi mode unless forced (e.g. by a window
* size change). This is because many curses implementations can't be
* called twice in a single program. Plus, it's faster. If the editor
* "leaves" vi to enter ex, when it exits ex we'll just fall back into
* vi.
*/
if (F_ISSET(sp, SC_SCR_EX))
F_CLR(sp, SC_SCR_EX);
/*
* Fake leaving vi mode.
*
* Clear out the rest of the screen if we're in the middle of a split
* screen. Move to the last line in the current screen -- this makes
* terminal scrolling happen naturally. Note: *don't* move past the
* end of the screen, as there are ex commands (e.g., :read ! cat file)
* that don't want to. Don't clear the info line, its contents may be
* valid, e.g. :file|append.
*/
if (F_ISSET(sp, SC_SCR_VI)) {
F_CLR(sp, SC_SCR_VI);
if (sp->q.cqe_next != (void *)&sp->wp->scrq) {
(void)wmove(win, RLNO(sp, sp->rows), 0);
wclrtobot(win);
}
(void)wmove(win, RLNO(sp, sp->rows) - 1, 0);
wrefresh(win);
}
/* Enter the requested mode. */
if (LF_ISSET(SC_EX)) {
if (cl_ex_init(sp))
return (1);
F_SET(clp, CL_IN_EX | CL_SCR_EX_INIT);
/*
* If doing an ex screen for ex mode, move to the last line
* on the screen.
*/
if (F_ISSET(sp, SC_EX) && clp->cup != NULL)
tputs(tgoto(clp->cup,
0, O_VAL(sp, O_LINES) - 1), 1, cl_putchar);
} else {
if (cl_vi_init(sp))
return (1);
F_CLR(clp, CL_IN_EX);
F_SET(clp, CL_SCR_VI_INIT);
}
return (0);
}
/*
* cl_quit --
* Shutdown the screens.
*
* PUBLIC: int cl_quit __P((GS *));
*/
int
cl_quit(GS *gp)
{
CL_PRIVATE *clp;
int rval;
rval = 0;
clp = GCLP(gp);
/*
* If we weren't really running, ignore it. This happens if the
* screen changes size before we've called curses.
*/
if (!F_ISSET(clp, CL_SCR_EX_INIT | CL_SCR_VI_INIT))
return (0);
/* Clean up the terminal mappings. */
if (cl_term_end(gp))
rval = 1;
/* Really leave vi mode. */
if (F_ISSET(clp, CL_STDIN_TTY) &&
F_ISSET(clp, CL_SCR_VI_INIT) && cl_vi_end(gp))
rval = 1;
/* Really leave ex mode. */
if (F_ISSET(clp, CL_STDIN_TTY) &&
F_ISSET(clp, CL_SCR_EX_INIT) && cl_ex_end(gp))
rval = 1;
/*
* If we were running ex when we quit, or we're using an implementation
* of curses where endwin() doesn't get this right, restore the original
* terminal modes.
*
* XXX
* We always do this because it's too hard to figure out what curses
* implementations get it wrong. It may discard type-ahead characters
* from the tty queue.
*/
(void)tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &clp->orig);
F_CLR(clp, CL_SCR_EX_INIT | CL_SCR_VI_INIT);
return (rval);
}
/*
* cl_vi_init --
* Initialize the curses vi screen.
*/
static int
cl_vi_init(SCR *sp)
{
CL_PRIVATE *clp;
GS *gp;
char *o_cols, *o_lines, *o_term;
const char *ttype;
gp = sp->gp;
clp = CLP(sp);
/* If already initialized, just set the terminal modes. */
if (F_ISSET(clp, CL_SCR_VI_INIT))
goto fast;
/* Curses vi always reads from (and writes to) a terminal. */
if (!F_ISSET(clp, CL_STDIN_TTY) || !isatty(STDOUT_FILENO)) {
msgq(sp, M_ERR,
"016|Vi's standard input and output must be a terminal");
return (1);
}
/* We'll need a terminal type. */
if (opts_empty(sp, O_TERM, 0))
return (1);
ttype = O_STR(sp, O_TERM);
/*
* XXX
* Changing the row/column and terminal values is done by putting them
* into the environment, which is then read by curses. What this loses
* in ugliness, it makes up for in stupidity. We can't simply put the
* values into the environment ourselves, because in the presence of a
* kernel mechanism for returning the window size, entering values into
* the environment will screw up future screen resizing events, e.g. if
* the user enters a :shell command and then resizes their window. So,
* if they weren't already in the environment, we make sure to delete
* them immediately after setting them.
*
* XXX
* Putting the TERM variable into the environment is necessary, even
* though we're using newterm() here. We may be using initscr() as
* the underlying function.
*/
o_term = getenv("TERM");
cl_putenv(sp, "TERM", ttype, 0);
o_lines = getenv("LINES");
cl_putenv(sp, "LINES", NULL, (u_long)O_VAL(sp, O_LINES));
o_cols = getenv("COLUMNS");
cl_putenv(sp, "COLUMNS", NULL, (u_long)O_VAL(sp, O_COLUMNS));
/*
* We don't care about the SCREEN reference returned by newterm, we
* never have more than one SCREEN at a time.
*
* XXX
* The SunOS initscr() can't be called twice. Don't even think about
* using it. It fails in subtle ways (e.g. select(2) on fileno(stdin)
* stops working). (The SVID notes that applications should only call
* initscr() once.)
*
* XXX
* The HP/UX newterm doesn't support the NULL first argument, so we
* have to specify the terminal type.
*/
errno = 0;
if (newterm(__UNCONST(ttype), stdout, stdin) == NULL) {
if (errno)
msgq(sp, M_SYSERR, "%s", ttype);
else
msgq(sp, M_ERR, "%s: unknown terminal type", ttype);
return (1);
}
if (o_term == NULL)
cl_unsetenv(sp, "TERM");
if (o_lines == NULL)
cl_unsetenv(sp, "LINES");
if (o_cols == NULL)
cl_unsetenv(sp, "COLUMNS");
/*
* XXX
* Someone got let out alone without adult supervision -- the SunOS
* newterm resets the signal handlers. There's a race, but it's not
* worth closing.
*/
(void)sig_init(sp->gp, sp);
/*
* We use raw mode. What we want is 8-bit clean, however, signals
* and flow control should continue to work. Admittedly, it sounds
* like cbreak, but it isn't. Using cbreak() can get you additional
* things like IEXTEN, which turns on flags like DISCARD and LNEXT.
*
* !!!
* If raw isn't turning off echo and newlines, something's wrong.
* However, it shouldn't hurt.
*/
noecho(); /* No character echo. */
nonl(); /* No CR/NL translation. */
raw(); /* 8-bit clean. */
idlok(stdscr, 1); /* Use hardware insert/delete line. */
/* Put the cursor keys into application mode. */
(void)keypad(stdscr, TRUE);
/*
* XXX
* The screen TI sequence just got sent. See the comment in
* cl_funcs.c:cl_attr().
*/
clp->ti_te = TI_SENT;
/*
* XXX
* Historic implementations of curses handled SIGTSTP signals
* in one of three ways. They either:
*
* 1: Set their own handler, regardless.
* 2: Did not set a handler if a handler was already installed.
* 3: Set their own handler, but then called any previously set
* handler after completing their own cleanup.
*
* We don't try and figure out which behavior is in place, we force
* it to SIG_DFL after initializing the curses interface, which means
* that curses isn't going to take the signal. Since curses isn't
* reentrant (i.e., the whole curses SIGTSTP interface is a fantasy),
* we're doing The Right Thing.
*/
(void)signal(SIGTSTP, SIG_DFL);
/*
* If flow control was on, turn it back on. Turn signals on. ISIG
* turns on VINTR, VQUIT, VDSUSP and VSUSP. The main curses code
* already installed a handler for VINTR. We're going to disable the
* other three.
*
* XXX
* We want to use ^Y as a vi scrolling command. If the user has the
* DSUSP character set to ^Y (common practice) clean it up. As it's
* equally possible that the user has VDSUSP set to 'a', we disable
* it regardless. It doesn't make much sense to suspend vi at read,
* so I don't think anyone will care. Alternatively, we could look
* it up in the table of legal command characters and turn it off if
* it matches one. VDSUSP wasn't in POSIX 1003.1-1990, so we test for
* it.
*
* XXX
* We don't check to see if the user had signals enabled originally.
* If they didn't, it's unclear what we're supposed to do here, but
* it's also pretty unlikely.
*/
if (tcgetattr(STDIN_FILENO, &clp->vi_enter)) {
msgq(sp, M_SYSERR, "tcgetattr");
goto err;
}
if (clp->orig.c_iflag & IXON)
clp->vi_enter.c_iflag |= IXON;
if (clp->orig.c_iflag & IXOFF)
clp->vi_enter.c_iflag |= IXOFF;
clp->vi_enter.c_lflag |= ISIG;
#ifdef VDSUSP
clp->vi_enter.c_cc[VDSUSP] = _POSIX_VDISABLE;
#endif
clp->vi_enter.c_cc[VQUIT] = _POSIX_VDISABLE;
clp->vi_enter.c_cc[VSUSP] = _POSIX_VDISABLE;
/*
* XXX
* OSF/1 doesn't turn off the <discard>, <literal-next> or <status>
* characters when curses switches into raw mode. It should be OK
* to do it explicitly for everyone.
*/
#ifdef VDISCARD
clp->vi_enter.c_cc[VDISCARD] = _POSIX_VDISABLE;
#endif
#ifdef VLNEXT
clp->vi_enter.c_cc[VLNEXT] = _POSIX_VDISABLE;
#endif
#ifdef VSTATUS
clp->vi_enter.c_cc[VSTATUS] = _POSIX_VDISABLE;
#endif
/* Initialize terminal based information. */
if (cl_term_init(sp))
goto err;
fast: /* Set the terminal modes. */
if (tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &clp->vi_enter)) {
if (errno == EINTR)
goto fast;
msgq(sp, M_SYSERR, "tcsetattr");
err: (void)cl_vi_end(sp->gp);
return (1);
}
return (0);
}
/*
* cl_vi_end --
* Shutdown the vi screen.
*/
static int
cl_vi_end(GS *gp)
{
CL_PRIVATE *clp;
clp = GCLP(gp);
/* Restore the cursor keys to normal mode. */
(void)keypad(stdscr, FALSE);
/*
* If we were running vi when we quit, scroll the screen up a single
* line so we don't lose any information.
*
* Move to the bottom of the window (some endwin implementations don't
* do this for you).
*/
if (!F_ISSET(clp, CL_IN_EX)) {
(void)move(0, 0);
(void)deleteln();
(void)move(LINES - 1, 0);
(void)refresh();
}
cl_freecap(clp);
/* End curses window. */
(void)endwin();
/*
* XXX
* The screen TE sequence just got sent. See the comment in
* cl_funcs.c:cl_attr().
*/
clp->ti_te = TE_SENT;
return (0);
}
/*
* cl_ex_init --
* Initialize the ex screen.
*/
static int
cl_ex_init(SCR *sp)
{
CL_PRIVATE *clp;
clp = CLP(sp);
/* If already initialized, just set the terminal modes. */
if (F_ISSET(clp, CL_SCR_EX_INIT))
goto fast;
/* If not reading from a file, we're done. */
if (!F_ISSET(clp, CL_STDIN_TTY))
return (0);
/* Get the ex termcap/terminfo strings. */
(void)cl_getcap(sp, "cup", &clp->cup);
(void)cl_getcap(sp, "smso", &clp->smso);
(void)cl_getcap(sp, "rmso", &clp->rmso);
(void)cl_getcap(sp, "el", &clp->el);
(void)cl_getcap(sp, "cuu1", &clp->cuu1);
/* Enter_standout_mode and exit_standout_mode are paired. */
if (clp->smso == NULL || clp->rmso == NULL) {
if (clp->smso != NULL) {
free(clp->smso);
clp->smso = NULL;
}
if (clp->rmso != NULL) {
free(clp->rmso);
clp->rmso = NULL;
}
}
/*
* Turn on canonical mode, with normal input and output processing.
* Start with the original terminal settings as the user probably
* had them (including any local extensions) set correctly for the
* current terminal.
*
* !!!
* We can't get everything that we need portably; for example, ONLCR,
* mapping <newline> to <carriage-return> on output isn't required
* by POSIX 1003.1b-1993. If this turns out to be a problem, then
* we'll either have to play some games on the mapping, or we'll have
* to make all ex printf's output \r\n instead of \n.
*/
clp->ex_enter = clp->orig;
clp->ex_enter.c_lflag |= ECHO | ECHOE | ECHOK | ICANON | IEXTEN | ISIG;
#ifdef ECHOCTL
clp->ex_enter.c_lflag |= ECHOCTL;
#endif
#ifdef ECHOKE
clp->ex_enter.c_lflag |= ECHOKE;
#endif
clp->ex_enter.c_iflag |= ICRNL;
clp->ex_enter.c_oflag |= OPOST;
#ifdef ONLCR
clp->ex_enter.c_oflag |= ONLCR;
#endif
fast: if (tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &clp->ex_enter)) {
if (errno == EINTR)
goto fast;
msgq(sp, M_SYSERR, "tcsetattr");
return (1);
}
return (0);
}
/*
* cl_ex_end --
* Shutdown the ex screen.
*/
static int
cl_ex_end(GS *gp)
{
CL_PRIVATE *clp;
clp = GCLP(gp);
cl_freecap(clp);
return (0);
}
/*
* cl_getcap --
* Retrieve termcap/terminfo strings.
*
* PUBLIC: int cl_getcap __P((SCR *, char *, char **));
*/
int
cl_getcap(SCR *sp, const char *name, char **elementp)
{
size_t len;
char *t;
if ((t = tigetstr(name)) != NULL &&
t != (char *)-1 && (len = strlen(t)) != 0) {
MALLOC_RET(sp, *elementp, char *, len + 1);
memmove(*elementp, t, len + 1);
}
return (0);
}
/*
* cl_freecap --
* Free any allocated termcap/terminfo strings.
*/
static void
cl_freecap(CL_PRIVATE *clp)
{
if (clp->el != NULL) {
free(clp->el);
clp->el = NULL;
}
if (clp->cup != NULL) {
free(clp->cup);
clp->cup = NULL;
}
if (clp->cuu1 != NULL) {
free(clp->cuu1);
clp->cuu1 = NULL;
}
if (clp->rmso != NULL) {
free(clp->rmso);
clp->rmso = NULL;
}
if (clp->smso != NULL) {
free(clp->smso);
clp->smso = NULL;
}
}
/*
* cl_putenv --
* Put a value into the environment.
*/
static int
cl_putenv(SCR *sp, const char *name, const char *str, u_long value)
{
char buf[40];
if (str == NULL) {
(void)snprintf(buf, sizeof(buf), "%lu", value);
return (cl_setenv(sp, name, buf));
} else
return (cl_setenv(sp, name, str));
}

481
dist/nvi/cl/cl_term.c vendored Normal file
View File

@@ -0,0 +1,481 @@
/* $NetBSD: cl_term.c,v 1.4 2011/11/23 19:25:27 tnozaki Exp $ */
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: cl_term.c,v 10.31 2001/07/08 13:06:56 skimo Exp (Berkeley) Date: 2001/07/08 13:06:56";
#endif /* not lint */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/queue.h>
#include <sys/stat.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 "../common/common.h"
#include "cl.h"
static int cl_pfmap __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t));
/*
* XXX
* THIS REQUIRES THAT ALL SCREENS SHARE A TERMINAL TYPE.
*/
typedef struct _tklist {
const char *ts; /* Key's termcap string. */
const char *output; /* Corresponding vi command. */
const char *name; /* Name. */
u_char value; /* Special value (for lookup). */
} TKLIST;
#define TKINIT(a, b, c) { a, b, c, 0 }
static TKLIST const c_tklist[] = { /* Command mappings. */
TKINIT("kil1", "O", "insert line"),
TKINIT("kdch1", "x", "delete character"),
TKINIT("kcud1", "j", "cursor down"),
TKINIT("kel", "D", "delete to eol"),
TKINIT("kind", "\004", "scroll down"), /* ^D */
TKINIT("kll", "$", "go to eol"),
TKINIT("kend", "$", "go to eol"),
TKINIT("khome", "^", "go to sol"),
TKINIT("kich1", "i", "insert at cursor"),
TKINIT("kdl1", "dd", "delete line"),
TKINIT("kcub1", "h", "cursor left"),
TKINIT("knp", "\006", "page down"), /* ^F */
TKINIT("kpp", "\002", "page up"), /* ^B */
TKINIT("kri", "\025", "scroll up"), /* ^U */
TKINIT("ked", "dG", "delete to end of screen"),
TKINIT("kcuf1", "l", "cursor right"),
TKINIT("kcuu1", "k", "cursor up"),
TKINIT(NULL, NULL, NULL),
};
static TKLIST const m1_tklist[] = { /* Input mappings (lookup). */
TKINIT(NULL, NULL, NULL),
};
static TKLIST const m2_tklist[] = { /* Input mappings (set or delete). */
TKINIT("kcud1", "\033ja", "cursor down"), /* ^[ja */
TKINIT("kcub1", "\033ha", "cursor left"), /* ^[ha */
TKINIT("kcuu1", "\033ka", "cursor up"), /* ^[ka */
TKINIT("kcuf1", "\033la", "cursor right"), /* ^[la */
TKINIT(NULL, NULL, NULL),
};
/*
* cl_term_init --
* Initialize the special keys defined by the termcap/terminfo entry.
*
* PUBLIC: int cl_term_init __P((SCR *));
*/
int
cl_term_init(SCR *sp)
{
KEYLIST *kp;
SEQ *qp;
TKLIST const *tkp;
char *t;
CHAR_T name[60];
CHAR_T output[5];
CHAR_T ts[20];
const CHAR_T *wp;
size_t wlen;
/* Command mappings. */
for (tkp = c_tklist; tkp->name != NULL; ++tkp) {
if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
continue;
CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen);
MEMCPYW(name, wp, wlen);
CHAR2INT(sp, t, strlen(t), wp, wlen);
MEMCPYW(ts, wp, wlen);
CHAR2INT(sp, tkp->output, strlen(tkp->output), wp, wlen);
MEMCPYW(output, wp, wlen);
if (seq_set(sp, name, strlen(tkp->name), ts, strlen(t),
output, strlen(tkp->output), SEQ_COMMAND,
SEQ_NOOVERWRITE | SEQ_SCREEN))
return (1);
}
/* Input mappings needing to be looked up. */
for (tkp = m1_tklist; tkp->name != NULL; ++tkp) {
if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
continue;
for (kp = keylist;; ++kp)
if (kp->value == tkp->value)
break;
if (kp == NULL)
continue;
CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen);
MEMCPYW(name, wp, wlen);
CHAR2INT(sp, t, strlen(t), wp, wlen);
MEMCPYW(ts, wp, wlen);
output[0] = (UCHAR_T)kp->ch;
if (seq_set(sp, name, strlen(tkp->name), ts, strlen(t),
output, 1, SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN))
return (1);
}
/* Input mappings that are already set or are text deletions. */
for (tkp = m2_tklist; tkp->name != NULL; ++tkp) {
if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
continue;
/*
* !!!
* Some terminals' <cursor_left> keys send single <backspace>
* characters. This is okay in command mapping, but not okay
* in input mapping. That combination is the only one we'll
* ever see, hopefully, so kluge it here for now.
*/
if (!strcmp(t, "\b"))
continue;
if (tkp->output == NULL) {
CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen);
MEMCPYW(name, wp, wlen);
CHAR2INT(sp, t, strlen(t), wp, wlen);
MEMCPYW(ts, wp, wlen);
if (seq_set(sp, name, strlen(tkp->name),
ts, strlen(t), NULL, 0,
SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN))
return (1);
} else {
CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen);
MEMCPYW(name, wp, wlen);
CHAR2INT(sp, t, strlen(t), wp, wlen);
MEMCPYW(ts, wp, wlen);
CHAR2INT(sp, tkp->output, strlen(tkp->output), wp, wlen);
MEMCPYW(output, wp, wlen);
if (seq_set(sp, name, strlen(tkp->name),
ts, strlen(t), output, strlen(tkp->output),
SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN))
return (1);
}
}
/*
* Rework any function key mappings that were set before the
* screen was initialized.
*/
for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next)
if (F_ISSET(qp, SEQ_FUNCMAP))
(void)cl_pfmap(sp, qp->stype,
qp->input, qp->ilen, qp->output, qp->olen);
return (0);
}
/*
* cl_term_end --
* End the special keys defined by the termcap/terminfo entry.
*
* PUBLIC: int cl_term_end __P((GS *));
*/
int
cl_term_end(GS *gp)
{
SEQ *qp, *nqp;
/* Delete screen specific mappings. */
for (qp = gp->seqq.lh_first; qp != NULL; qp = nqp) {
nqp = qp->q.le_next;
if (F_ISSET(qp, SEQ_SCREEN))
(void)seq_mdel(qp);
}
return (0);
}
/*
* cl_fmap --
* Map a function key.
*
* PUBLIC: int cl_fmap __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t));
*/
int
cl_fmap(SCR *sp, seq_t stype, CHAR_T *from, size_t flen, CHAR_T *to, size_t tlen)
{
/* Ignore until the screen is running, do the real work then. */
if (F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_SCR_VI))
return (0);
if (F_ISSET(sp, SC_EX) && !F_ISSET(sp, SC_SCR_EX))
return (0);
return (cl_pfmap(sp, stype, from, flen, to, tlen));
}
/*
* cl_pfmap --
* Map a function key (private version).
*/
static int
cl_pfmap(SCR *sp, seq_t stype, CHAR_T *from, size_t flen, CHAR_T *to, size_t tlen)
{
size_t nlen;
char *p;
char name[64];
CHAR_T mykeyname[64];
CHAR_T ts[20];
const CHAR_T *wp;
size_t wlen;
(void)snprintf(name, sizeof(name), "kf%d",
(int)STRTOL(from+1,NULL,10));
if ((p = tigetstr(name)) == NULL ||
p == (char *)-1 || strlen(p) == 0)
p = NULL;
if (p == NULL) {
msgq_wstr(sp, M_ERR, from, "233|This terminal has no %s key");
return (1);
}
nlen = SPRINTF(mykeyname,
SIZE(mykeyname), L("function key %d"),
(int)STRTOL(from+1,NULL,10));
CHAR2INT(sp, p, strlen(p), wp, wlen);
MEMCPYW(ts, wp, wlen);
return (seq_set(sp, mykeyname, nlen,
ts, strlen(p), to, tlen, stype, SEQ_NOOVERWRITE | SEQ_SCREEN));
}
/*
* cl_optchange --
* Curses screen specific "option changed" routine.
*
* PUBLIC: int cl_optchange __P((SCR *, int, const char *, u_long *));
*/
int
cl_optchange(SCR *sp, int opt, const char *str, u_long *valp)
{
CL_PRIVATE *clp;
clp = CLP(sp);
switch (opt) {
case O_COLUMNS:
case O_LINES:
case O_TERM:
/*
* Changing the columns, lines or terminal require that
* we restart the screen.
*/
F_SET(sp->gp, G_SRESTART);
F_CLR(sp, SC_SCR_EX | SC_SCR_VI);
break;
case O_MESG:
(void)cl_omesg(sp, clp, *valp);
break;
case O_WINDOWNAME:
if (*valp) {
F_SET(clp, CL_RENAME_OK);
/*
* If the screen is live, i.e. we're not reading the
* .exrc file, update the window.
*/
if (sp->frp != NULL && sp->frp->name != NULL)
(void)cl_rename(sp, sp->frp->name, 1);
} else {
F_CLR(clp, CL_RENAME_OK);
(void)cl_rename(sp, NULL, 0);
}
break;
}
return (0);
}
/*
* cl_omesg --
* Turn the tty write permission on or off.
*
* PUBLIC: int cl_omesg __P((SCR *, CL_PRIVATE *, int));
*/
int
cl_omesg(SCR *sp, CL_PRIVATE *clp, int on)
{
struct stat sb;
char *tty;
/* Find the tty, get the current permissions. */
if ((tty = ttyname(STDERR_FILENO)) == NULL) {
if (sp != NULL)
msgq(sp, M_SYSERR, "stderr");
return (1);
}
if (stat(tty, &sb) < 0) {
if (sp != NULL)
msgq(sp, M_SYSERR, "%s", tty);
return (1);
}
/* Save the original status if it's unknown. */
if (clp->tgw == TGW_UNKNOWN)
clp->tgw = sb.st_mode & S_IWGRP ? TGW_SET : TGW_UNSET;
/* Toggle the permissions. */
if (on) {
if (chmod(tty, sb.st_mode | S_IWGRP) < 0) {
if (sp != NULL)
msgq(sp, M_SYSERR,
"046|messages not turned on: %s", tty);
return (1);
}
} else
if (chmod(tty, sb.st_mode & ~S_IWGRP) < 0) {
if (sp != NULL)
msgq(sp, M_SYSERR,
"045|messages not turned off: %s", tty);
return (1);
}
return (0);
}
/*
* cl_ssize --
* Return the terminal size.
*
* PUBLIC: int cl_ssize __P((SCR *, int, size_t *, size_t *, int *));
*/
int
cl_ssize(SCR *sp, int sigwinch, size_t *rowp, size_t *colp, int *changedp)
{
#ifdef TIOCGWINSZ
struct winsize win;
#endif
size_t col, row;
int rval;
char *p;
/* Assume it's changed. */
if (changedp != NULL)
*changedp = 1;
/*
* !!!
* sp may be NULL.
*
* Get the screen rows and columns. If the values are wrong, it's
* not a big deal -- as soon as the user sets them explicitly the
* environment will be set and the screen package will use the new
* values.
*
* Try TIOCGWINSZ.
*/
row = col = 0;
#ifdef TIOCGWINSZ
if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) {
row = win.ws_row;
col = win.ws_col;
}
#endif
/* If here because of suspend or a signal, only trust TIOCGWINSZ. */
if (sigwinch) {
/*
* Somebody didn't get TIOCGWINSZ right, or has suspend
* without window resizing support. The user just lost,
* but there's nothing we can do.
*/
if (row == 0 || col == 0) {
if (changedp != NULL)
*changedp = 0;
return (0);
}
/*
* SunOS systems deliver SIGWINCH when windows are uncovered
* as well as when they change size. In addition, we call
* here when continuing after being suspended since the window
* may have changed size. Since we don't want to background
* all of the screens just because the window was uncovered,
* ignore the signal if there's no change.
*/
if (sp != NULL &&
row == O_VAL(sp, O_LINES) && col == O_VAL(sp, O_COLUMNS)) {
if (changedp != NULL)
*changedp = 0;
return (0);
}
if (rowp != NULL)
*rowp = row;
if (colp != NULL)
*colp = col;
resizeterm(row, col);
return (0);
}
/*
* !!!
* If TIOCGWINSZ failed, or had entries of 0, try termcap. This
* routine is called before any termcap or terminal information
* has been set up. If there's no TERM environmental variable set,
* let it go, at least ex can run.
*/
if (row == 0 || col == 0) {
if ((p = getenv("TERM")) == NULL)
goto noterm;
if (row == 0) {
if ((rval = tigetnum("lines")) < 0)
msgq(sp, M_SYSERR, "tigetnum: lines");
else
row = rval;
}
if (col == 0) {
if ((rval = tigetnum("cols")) < 0)
msgq(sp, M_SYSERR, "tigetnum: cols");
else
col = rval;
}
}
/* If nothing else, well, it's probably a VT100. */
noterm: if (row == 0)
row = 24;
if (col == 0)
col = 80;
/*
* !!!
* POSIX 1003.2 requires the environment to override everything.
* Often, people can get nvi to stop messing up their screen by
* deleting the LINES and COLUMNS environment variables from their
* dot-files.
*/
if ((p = getenv("LINES")) != NULL)
row = strtol(p, NULL, 10);
if ((p = getenv("COLUMNS")) != NULL)
col = strtol(p, NULL, 10);
if (rowp != NULL)
*rowp = row;
if (colp != NULL)
*colp = col;
return (0);
}
/*
* cl_putchar --
* Function version of putchar, for tputs.
*
* PUBLIC: int cl_putchar __P((int));
*/
int
cl_putchar(int ch)
{
return (putchar(ch));
}

59
dist/nvi/cl/extern.h vendored Normal file
View File

@@ -0,0 +1,59 @@
/* $NetBSD: extern.h,v 1.3 2010/02/03 15:34:37 roy Exp $ */
/* Do not edit: automatically built by build/distrib. */
#ifndef HAVE_CURSES_WADDNSTR
int waddnstr __P((WINDOW*, char *, int));
#endif
#ifndef HAVE_CURSES_BEEP
void beep __P((void));
#endif
#ifndef HAVE_CURSES_FLASH
void flash __P((void));
#endif
#ifndef HAVE_CURSES_IDLOK
void idlok __P((WINDOW *, int));
#endif
#ifndef HAVE_CURSES_KEYPAD
int keypad __P((void *, int));
#endif
#ifndef HAVE_CURSES_NEWTERM
void *newterm __P((const char *, FILE *, FILE *));
#endif
#ifndef HAVE_CURSES_SETUPTERM
void setupterm __P((char *, int, int *));
#endif
#ifndef HAVE_CURSES_TIGETSTR
char *tigetstr __P((const char *));
int tigetnum __P((const char *));
#endif
int cl_waddstr __P((SCR *, const CHAR_T *, size_t));
int cl_addstr __P((SCR *, const char *, size_t));
int cl_attr __P((SCR *, scr_attr_t, int));
int cl_baud __P((SCR *, u_long *));
int cl_bell __P((SCR *));
int cl_clrtoeol __P((SCR *));
int cl_cursor __P((SCR *, size_t *, size_t *));
int cl_deleteln __P((SCR *));
int cl_discard __P((SCR *, SCR **));
int cl_ex_adjust __P((SCR *, exadj_t));
int cl_insertln __P((SCR *));
int cl_keyval __P((SCR *, scr_keyval_t, CHAR_T *, int *));
int cl_move __P((SCR *, size_t, size_t));
int cl_refresh __P((SCR *, int));
int cl_rename __P((SCR *, char *, int));
void cl_setname __P((GS *, char *));
int cl_split __P((SCR *, SCR *));
int cl_suspend __P((SCR *, int *));
void cl_usage __P((void));
int sig_init __P((GS *, SCR *));
int cl_event __P((SCR *, EVENT *, u_int32_t, int));
int cl_screen __P((SCR *, u_int32_t));
int cl_quit __P((GS *));
int cl_getcap __P((SCR *, const char *, char **));
int cl_term_init __P((SCR *));
int cl_term_end __P((GS *));
int cl_fmap __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t));
int cl_optchange __P((SCR *, int, const char *, u_long *));
int cl_omesg __P((SCR *, CL_PRIVATE *, int));
int cl_ssize __P((SCR *, int, size_t *, size_t *, int *));
int cl_putchar __P((int));

87
dist/nvi/clib/bsearch.c vendored Normal file
View File

@@ -0,0 +1,87 @@
/* $NetBSD: bsearch.c,v 1.1.1.2 2008/05/18 14:29:38 aymeric Exp $ */
/*
* Copyright (c) 1990, 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.
*/
#include "config.h"
#if defined(LIBC_SCCS) && !defined(lint)
static const char sccsid[] = "@(#)bsearch.c 8.1 (Berkeley) 6/4/93";
#endif /* LIBC_SCCS and not lint */
#include <stddef.h>
#include <stdlib.h>
#include "port.h"
/*
* Perform a binary search.
*
* The code below is a bit sneaky. After a comparison fails, we
* divide the work in half by moving either left or right. If lim
* is odd, moving left simply involves halving lim: e.g., when lim
* is 5 we look at item 2, so we change lim to 2 so that we will
* look at items 0 & 1. If lim is even, the same applies. If lim
* is odd, moving right again involes halving lim, this time moving
* the base up one item past p: e.g., when lim is 5 we change base
* to item 3 and make lim 2 so that we will look at items 3 and 4.
* If lim is even, however, we have to shrink it by one before
* halving: e.g., when lim is 4, we still looked at item 2, so we
* have to make lim 3, then halve, obtaining 1, so that we will only
* look at item 3.
*
* PUBLIC: #ifndef HAVE_BSEARCH
* PUBLIC: void *bsearch __P((const void *, const void *, size_t,
* PUBLIC: size_t, int (*)(const void *, const void *)));
* PUBLIC: #endif
*/
void *
bsearch(register const void *key, const void *base0, size_t nmemb, register size_t size, register int (*compar) (const void *, const void *))
{
register const char *base = base0;
register size_t lim;
register int cmp;
register const void *p;
for (lim = nmemb; lim != 0; lim >>= 1) {
p = base + (lim >> 1) * size;
cmp = (*compar)(key, p);
if (cmp == 0)
return ((void *)p);
if (cmp > 0) { /* key > p: move right */
base = (char *)p + size;
lim--;
} /* else move left */
}
return (NULL);
}

160
dist/nvi/clib/env.c vendored Normal file
View File

@@ -0,0 +1,160 @@
/* $NetBSD: env.c,v 1.1.1.2 2008/05/18 14:29:38 aymeric Exp $ */
/*
* Copyright (c) 1987, 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.
*/
#include "config.h"
#if defined(LIBC_SCCS) && !defined(lint)
static const char sccsid[] = "@(#)getenv.c 8.1 (Berkeley) 6/4/93";
static const char sccsid[] = "@(#)setenv.c 8.1 (Berkeley) 6/4/93";
#endif /* LIBC_SCCS and not lint */
#include <stdlib.h>
#include <string.h>
/*
* __findenv --
* Returns pointer to value associated with name, if any, else NULL.
* Sets offset to be the offset of the name/value combination in the
* environmental array, for use by setenv(3) and unsetenv(3).
* Explicitly removes '=' in argument name.
*
* This routine *should* be a static; don't use it.
*/
static char *
__findenv(register char *name, int *offset)
{
extern char **environ;
register int len;
register char *np;
register char **p, *c;
if (name == NULL || environ == NULL)
return (NULL);
for (np = name; *np && *np != '='; ++np)
continue;
len = np - name;
for (p = environ; (c = *p) != NULL; ++p)
if (strncmp(c, name, len) == 0 && c[len] == '=') {
*offset = p - environ;
return (c + len + 1);
}
return (NULL);
}
#ifndef HAVE_SETENV
/*
* setenv --
* Set the value of the environmental variable "name" to be
* "value". If rewrite is set, replace any current value.
*
* PUBLIC: #ifndef HAVE_SETENV
* PUBLIC: int setenv __P((const char *, const char *, int));
* PUBLIC: #endif
*/
setenv(name, value, rewrite)
register char *name;
register char *value;
int rewrite;
{
extern char **environ;
static int alloced; /* if allocated space before */
register char *c;
int l_value, offset;
if (*value == '=') /* no `=' in value */
++value;
l_value = strlen(value);
if ((c = __findenv(name, &offset))) { /* find if already exists */
if (!rewrite)
return (0);
if (strlen(c) >= l_value) { /* old larger; copy over */
while (*c++ = *value++);
return (0);
}
} else { /* create new slot */
register int cnt;
register char **p;
for (p = environ, cnt = 0; *p; ++p, ++cnt);
if (alloced) { /* just increase size */
environ = (char **)realloc((char *)environ,
(size_t)(sizeof(char *) * (cnt + 2)));
if (!environ)
return (-1);
}
else { /* get new space */
alloced = 1; /* copy old entries into it */
p = malloc((size_t)(sizeof(char *) * (cnt + 2)));
if (!p)
return (-1);
memmove(p, environ, cnt * sizeof(char *));
environ = p;
}
environ[cnt + 1] = NULL;
offset = cnt;
}
for (c = (char *)name; *c && *c != '='; ++c); /* no `=' in name */
if (!(environ[offset] = /* name + `=' + value */
malloc((size_t)((int)(c - name) + l_value + 2))))
return (-1);
for (c = environ[offset]; (*c = *name++) && *c != '='; ++c);
for (*c++ = '='; *c++ = *value++;);
return (0);
}
#endif
#ifndef HAVE_UNSETENV
/*
* unsetenv(name) --
* Delete environmental variable "name".
*
* PUBLIC: #ifndef HAVE_UNSETENV
* PUBLIC: void unsetenv __P((const char *));
* PUBLIC: #endif
*/
void
unsetenv(name)
char *name;
{
extern char **environ;
register char **p;
int offset;
while (__findenv(name, &offset)) /* if set multiple times */
for (p = &environ[offset];; ++p)
if (!(*p = *(p + 1)))
break;
}
#endif

24
dist/nvi/clib/gethostname.c vendored Normal file
View File

@@ -0,0 +1,24 @@
/* $NetBSD: gethostname.c,v 1.1.1.2 2008/05/18 14:29:38 aymeric Exp $ */
#include "config.h"
/*
* Solaris doesn't include the gethostname call by default.
*/
#include <sys/utsname.h>
#include <sys/systeminfo.h>
#include <netdb.h>
/*
* PUBLIC: #ifndef HAVE_GETHOSTNAME
* PUBLIC: int gethostname __P((char *, int));
* PUBLIC: #endif
*/
int
gethostname(host, len)
char *host;
int len;
{
return (sysinfo(SI_HOSTNAME, host, len) == -1 ? -1 : 0);
}

16
dist/nvi/clib/iswblank.c vendored Normal file
View File

@@ -0,0 +1,16 @@
/* $NetBSD: iswblank.c,v 1.1.1.2 2008/05/18 14:29:38 aymeric Exp $ */
#include "config.h"
#if defined(LIBC_SCCS) && !defined(lint)
static const char sccsid[] = "Id: iswblank.c,v 1.1 2001/10/11 19:22:29 skimo Exp";
#endif /* LIBC_SCCS and not lint */
#include <wchar.h>
#include <wctype.h>
int
iswblank (wint_t wc)
{
return iswctype(wc, wctype("blank"));
}

64
dist/nvi/clib/memchr.c vendored Normal file
View File

@@ -0,0 +1,64 @@
/* $NetBSD: memchr.c,v 1.1.1.2 2008/05/18 14:29:38 aymeric Exp $ */
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Chris Torek.
*
* 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.
*/
#include "config.h"
#if defined(LIBC_SCCS) && !defined(lint)
static const char sccsid[] = "@(#)memchr.c 8.1 (Berkeley) 6/4/93";
#endif /* LIBC_SCCS and not lint */
#include <string.h>
/*
* PUBLIC: #ifndef HAVE_MEMCHR
* PUBLIC: void *memchr __P((const void *, int, size_t));
* PUBLIC: #endif
*/
void *
memchr(const void *s, register unsigned char c, register size_t n)
{
if (n != 0) {
register const unsigned char *p = s;
do {
if (*p++ == c)
return ((void *)(p - 1));
} while (--n != 0);
}
return (NULL);
}

139
dist/nvi/clib/memset.c vendored Normal file
View File

@@ -0,0 +1,139 @@
/* $NetBSD: memset.c,v 1.1.1.2 2008/05/18 14:29:38 aymeric Exp $ */
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Mike Hibler and Chris Torek.
*
* 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.
*/
#include "config.h"
#if defined(LIBC_SCCS) && !defined(lint)
static const char sccsid[] = "@(#)memset.c 8.1 (Berkeley) 6/4/93";
#endif /* LIBC_SCCS and not lint */
#include <sys/types.h>
#include <limits.h>
#include <string.h>
/*
* PUBLIC: #ifndef HAVE_MEMSET
* PUBLIC: void *memset __P((void *, int, size_t));
* PUBLIC: #endif
*/
#define wsize sizeof(u_int)
#define wmask (wsize - 1)
#ifdef BZERO
#define RETURN return
#define VAL 0
#define WIDEVAL 0
void
bzero(dst0, length)
void *dst0;
register size_t length;
#else
#define RETURN return (dst0)
#define VAL c0
#define WIDEVAL c
void *
memset(void *dst0, register int c0, register size_t length)
#endif
{
register size_t t;
register u_int c;
register u_char *dst;
dst = dst0;
/*
* If not enough words, just fill bytes. A length >= 2 words
* guarantees that at least one of them is `complete' after
* any necessary alignment. For instance:
*
* |-----------|-----------|-----------|
* |00|01|02|03|04|05|06|07|08|09|0A|00|
* ^---------------------^
* dst dst+length-1
*
* but we use a minimum of 3 here since the overhead of the code
* to do word writes is substantial.
*/
if (length < 3 * wsize) {
while (length != 0) {
*dst++ = VAL;
--length;
}
RETURN;
}
#ifndef BZERO
if ((c = (u_char)c0) != 0) { /* Fill the word. */
c = (c << 8) | c; /* u_int is 16 bits. */
#if UINT_MAX > 0xffff
c = (c << 16) | c; /* u_int is 32 bits. */
#endif
#if UINT_MAX > 0xffffffff
c = (c << 32) | c; /* u_int is 64 bits. */
#endif
}
#endif
/* Align destination by filling in bytes. */
if ((t = (int)dst & wmask) != 0) {
t = wsize - t;
length -= t;
do {
*dst++ = VAL;
} while (--t != 0);
}
/* Fill words. Length was >= 2*words so we know t >= 1 here. */
t = length / wsize;
do {
*(u_int *)dst = WIDEVAL;
dst += wsize;
} while (--t != 0);
/* Mop up trailing bytes, if any. */
t = length & wmask;
if (t != 0)
do {
*dst++ = VAL;
} while (--t != 0);
RETURN;
}

131
dist/nvi/clib/mkstemp.c vendored Normal file
View File

@@ -0,0 +1,131 @@
/* $NetBSD: mkstemp.c,v 1.2 2011/03/21 14:53:02 tnozaki Exp $ */
/*
* Copyright (c) 1987, 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.
*/
#include "config.h"
#if defined(LIBC_SCCS) && !defined(lint)
static const char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93";
#endif /* LIBC_SCCS and not lint */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <ctype.h>
static int _gettemp(char *path, register int *doopen);
/*
* PUBLIC: #ifndef HAVE_MKSTEMP
* PUBLIC: int mkstemp __P((char *));
* PUBLIC: #endif
*/
mkstemp(char *path)
{
int fd;
return (_gettemp(path, &fd) ? fd : -1);
}
char *
mktemp(char *path)
{
return(_gettemp(path, (int *)NULL) ? path : (char *)NULL);
}
static
_gettemp(char *path, register int *doopen)
{
extern int errno;
register char *start, *trv;
struct stat sbuf;
u_int pid;
pid = getpid();
for (trv = path; *trv; ++trv); /* extra X's get set to 0's */
while (*--trv == 'X') {
*trv = (pid % 10) + '0';
pid /= 10;
}
/*
* check the target directory; if you have six X's and it
* doesn't exist this runs for a *very* long time.
*/
for (start = trv + 1;; --trv) {
if (trv <= path)
break;
if (*trv == '/') {
*trv = '\0';
if (stat(path, &sbuf))
return(0);
if (!S_ISDIR(sbuf.st_mode)) {
errno = ENOTDIR;
return(0);
}
*trv = '/';
break;
}
}
for (;;) {
if (doopen) {
if ((*doopen =
open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
return(1);
if (errno != EEXIST)
return(0);
}
else if (stat(path, &sbuf))
return(errno == ENOENT ? 1 : 0);
/* tricky little algorithm for backward compatibility */
for (trv = start;;) {
if (!*trv)
return(0);
if (*trv == 'z')
*trv++ = 'a';
else {
if (isdigit((unsigned char)*trv))
*trv = 'a';
else
++*trv;
break;
}
}
}
/*NOTREACHED*/
}

46
dist/nvi/clib/mmap.c vendored Normal file
View File

@@ -0,0 +1,46 @@
/* $NetBSD: mmap.c,v 1.1.1.2 2008/05/18 14:29:38 aymeric Exp $ */
#include "config.h"
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
/*
* This function fakes mmap() by reading `len' bytes from the file descriptor
* `fd' and returning a pointer to that memory. The "mapped" region can later
* be deallocated with munmap().
*
* Note: ONLY reading is supported and only reading of the exact size of the
* file will work.
*
* PUBLIC: #ifndef HAVE_MMAP
* PUBLIC: char *mmap __P((char *, size_t, int, int, int, off_t));
* PUBLIC: #endif
*/
char *
mmap(char *addr, size_t len, int prot, int flags, int fd, off_t off)
{
char *ptr;
if ((ptr = (char *)malloc(len)) == 0)
return ((char *)-1);
if (read(fd, ptr, len) < 0) {
free(ptr);
return ((char *)-1);
}
return (ptr);
}
/*
* PUBLIC: #ifndef HAVE_MMAP
* PUBLIC: int munmap __P((char *, size_t));
* PUBLIC: #endif
*/
int
munmap(char *addr, size_t len)
{
free(addr);
return (0);
}

47
dist/nvi/clib/snprintf.c vendored Normal file
View File

@@ -0,0 +1,47 @@
/* $NetBSD: snprintf.c,v 1.1.1.2 2008/05/18 14:29:39 aymeric Exp $ */
#include "config.h"
#include <sys/types.h>
#include <stdio.h>
#ifdef __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif
/*
* PUBLIC: #ifndef HAVE_SNPRINTF
* PUBLIC: int snprintf __P((char *, size_t, const char *, ...));
* PUBLIC: #endif
*/
int
#ifdef __STDC__
snprintf(char *str, size_t n, const char *fmt, ...)
#else
snprintf(str, n, fmt, va_alist)
char *str;
size_t n;
const char *fmt;
va_dcl
#endif
{
va_list ap;
int rval;
#ifdef __STDC__
va_start(ap, fmt);
#else
va_start(ap);
#endif
#ifdef SPRINTF_RET_CHARPNT
(void)vsprintf(str, fmt, ap);
va_end(ap);
return (strlen(str));
#else
rval = vsprintf(str, fmt, ap);
va_end(ap);
return (rval);
#endif
}

64
dist/nvi/clib/strdup.c vendored Normal file
View File

@@ -0,0 +1,64 @@
/* $NetBSD: strdup.c,v 1.1.1.2 2008/05/18 14:29:39 aymeric Exp $ */
/*
* Copyright (c) 1988, 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.
*/
#include "config.h"
#if defined(LIBC_SCCS) && !defined(lint)
static const char sccsid[] = "@(#)strdup.c 8.1 (Berkeley) 6/4/93";
#endif /* LIBC_SCCS and not lint */
#include <sys/types.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
/*
* PUBLIC: #ifndef HAVE_STRDUP
* PUBLIC: char *strdup __P((const char *));
* PUBLIC: #endif
*/
char *
strdup(const char *str)
{
size_t len;
char *copy;
len = strlen(str) + 1;
if (!(copy = malloc((u_int)len)))
return (NULL);
memcpy(copy, str, len);
return (copy);
}

63
dist/nvi/clib/strpbrk.c vendored Normal file
View File

@@ -0,0 +1,63 @@
/* $NetBSD: strpbrk.c,v 1.1.1.2 2008/05/18 14:29:39 aymeric Exp $ */
/*
* Copyright (c) 1985, 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.
*/
#include "config.h"
#if defined(LIBC_SCCS) && !defined(lint)
static const char sccsid[] = "@(#)strpbrk.c 8.1 (Berkeley) 6/4/93";
#endif /* LIBC_SCCS and not lint */
#include <string.h>
/*
* Find the first occurrence in s1 of a character in s2 (excluding NUL).
*
* PUBLIC: #ifndef HAVE_STRPBRK
* PUBLIC: char *strpbrk __P((const char *, const char *));
* PUBLIC: #endif
*/
char *
strpbrk(register const char *s1, register const char *s2)
{
register const char *scanp;
register int c, sc;
while ((c = *s1++) != 0) {
for (scanp = s2; (sc = *scanp++) != 0;)
if (sc == c)
return ((char *)(s1 - 1));
}
return (NULL);
}

85
dist/nvi/clib/strsep.c vendored Normal file
View File

@@ -0,0 +1,85 @@
/* $NetBSD: strsep.c,v 1.1.1.2 2008/05/18 14:29:39 aymeric Exp $ */
/*-
* Copyright (c) 1990, 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.
*/
#include "config.h"
#if defined(LIBC_SCCS) && !defined(lint)
static const char sccsid[] = "@(#)strsep.c 8.1 (Berkeley) 6/4/93";
#endif /* LIBC_SCCS and not lint */
#include <string.h>
#include <stdio.h>
/*
* Get next token from string *stringp, where tokens are possibly-empty
* strings separated by characters from delim.
*
* Writes NULs into the string at *stringp to end tokens.
* delim need not remain constant from call to call.
* On return, *stringp points past the last NUL written (if there might
* be further tokens), or is NULL (if there are definitely no more tokens).
*
* If *stringp is NULL, strsep returns NULL.
*
* PUBLIC: #ifndef HAVE_STRSEP
* PUBLIC: char *strsep __P((char **, const char *));
* PUBLIC: #endif
*/
char *
strsep(register char **stringp, register const char *delim)
{
register char *s;
register const char *spanp;
register int c, sc;
char *tok;
if ((s = *stringp) == NULL)
return (NULL);
for (tok = s;;) {
c = *s++;
spanp = delim;
do {
if ((sc = *spanp++) == c) {
if (c == 0)
s = NULL;
else
s[-1] = 0;
*stringp = s;
return (tok);
}
} while (sc != 0);
}
/* NOTREACHED */
}

133
dist/nvi/clib/strtol.c vendored Normal file
View File

@@ -0,0 +1,133 @@
/* $NetBSD: strtol.c,v 1.1.1.2 2008/05/18 14:29:39 aymeric Exp $ */
/*-
* Copyright (c) 1990, 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.
*/
#include "config.h"
#if defined(LIBC_SCCS) && !defined(lint)
static const char sccsid[] = "@(#)strtol.c 8.1 (Berkeley) 6/4/93";
#endif /* LIBC_SCCS and not lint */
#include <limits.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
/*
* Convert a string to a long integer.
*
* Ignores `locale' stuff. Assumes that the upper and lower case
* alphabets and digits are each contiguous.
*
* PUBLIC: #ifndef HAVE_STRTOL
* PUBLIC: long strtol __P((const char *, char **, int));
* PUBLIC: #endif
*/
long
strtol(const char *nptr, char **endptr, register int base)
{
register const char *s = nptr;
register unsigned long acc;
register int c;
register unsigned long cutoff;
register int neg = 0, any, cutlim;
/*
* Skip white space and pick up leading +/- sign if any.
* If base is 0, allow 0x for hex and 0 for octal, else
* assume decimal; if base is already 16, allow 0x.
*/
do {
c = *s++;
} while (isspace(c));
if (c == '-') {
neg = 1;
c = *s++;
} else if (c == '+')
c = *s++;
if ((base == 0 || base == 16) &&
c == '0' && (*s == 'x' || *s == 'X')) {
c = s[1];
s += 2;
base = 16;
}
if (base == 0)
base = c == '0' ? 8 : 10;
/*
* Compute the cutoff value between legal numbers and illegal
* numbers. That is the largest legal value, divided by the
* base. An input number that is greater than this value, if
* followed by a legal input character, is too big. One that
* is equal to this value may be valid or not; the limit
* between valid and invalid numbers is then based on the last
* digit. For instance, if the range for longs is
* [-2147483648..2147483647] and the input base is 10,
* cutoff will be set to 214748364 and cutlim to either
* 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
* a value > 214748364, or equal but the next digit is > 7 (or 8),
* the number is too big, and we will return a range error.
*
* Set any if any `digits' consumed; make it negative to indicate
* overflow.
*/
cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX;
cutlim = cutoff % (unsigned long)base;
cutoff /= (unsigned long)base;
for (acc = 0, any = 0;; c = *s++) {
if (isdigit(c))
c -= '0';
else if (isalpha(c))
c -= isupper(c) ? 'A' - 10 : 'a' - 10;
else
break;
if (c >= base)
break;
if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
any = -1;
else {
any = 1;
acc *= base;
acc += c;
}
}
if (any < 0) {
acc = neg ? LONG_MIN : LONG_MAX;
errno = ERANGE;
} else if (neg)
acc = -acc;
if (endptr != 0)
*endptr = (char *)(any ? s - 1 : nptr);
return (acc);
}

112
dist/nvi/clib/strtoul.c vendored Normal file
View File

@@ -0,0 +1,112 @@
/* $NetBSD: strtoul.c,v 1.1.1.2 2008/05/18 14:29:39 aymeric Exp $ */
/*
* Copyright (c) 1990, 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.
*/
#include "config.h"
#if defined(LIBC_SCCS) && !defined(lint)
static const char sccsid[] = "@(#)strtoul.c 8.1 (Berkeley) 6/4/93";
#endif /* LIBC_SCCS and not lint */
#include <limits.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
/*
* Convert a string to an unsigned long integer.
*
* Ignores `locale' stuff. Assumes that the upper and lower case
* alphabets and digits are each contiguous.
*
* PUBLIC: #ifndef HAVE_STRTOUL
* PUBLIC: unsigned long strtoul __P((const char *, char **, int));
* PUBLIC: #endif
*/
unsigned long
strtoul(const char *nptr, char **endptr, register int base)
{
register const char *s = nptr;
register unsigned long acc;
register int c;
register unsigned long cutoff;
register int neg = 0, any, cutlim;
/*
* See strtol for comments as to the logic used.
*/
do {
c = *s++;
} while (isspace(c));
if (c == '-') {
neg = 1;
c = *s++;
} else if (c == '+')
c = *s++;
if ((base == 0 || base == 16) &&
c == '0' && (*s == 'x' || *s == 'X')) {
c = s[1];
s += 2;
base = 16;
}
if (base == 0)
base = c == '0' ? 8 : 10;
cutoff = (unsigned long)ULONG_MAX / (unsigned long)base;
cutlim = (unsigned long)ULONG_MAX % (unsigned long)base;
for (acc = 0, any = 0;; c = *s++) {
if (isdigit(c))
c -= '0';
else if (isalpha(c))
c -= isupper(c) ? 'A' - 10 : 'a' - 10;
else
break;
if (c >= base)
break;
if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
any = -1;
else {
any = 1;
acc *= base;
acc += c;
}
}
if (any < 0) {
acc = ULONG_MAX;
errno = ERANGE;
} else if (neg)
acc = -acc;
if (endptr != 0)
*endptr = (char *)(any ? s - 1 : nptr);
return (acc);
}

33
dist/nvi/clib/vsnprintf.c vendored Normal file
View File

@@ -0,0 +1,33 @@
/* $NetBSD: vsnprintf.c,v 1.1.1.2 2008/05/18 14:29:39 aymeric Exp $ */
#include "config.h"
#include <sys/types.h>
#include <stdio.h>
#ifdef __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif
/*
* PUBLIC: #ifndef HAVE_VSNPRINTF
* PUBLIC: int vsnprintf __P((char *, size_t, const char *, ...));
* PUBLIC: #endif
*/
int
vsnprintf(str, n, fmt, ap)
char *str;
size_t n;
const char *fmt;
va_list ap;
{
#ifdef SPRINTF_RET_CHARPNT
(void)vsprintf(str, fmt, ap);
return (strlen(str));
#else
return (vsprintf(str, fmt, ap));
#endif
}

609
dist/nvi/common/api.c vendored Normal file
View File

@@ -0,0 +1,609 @@
/* $NetBSD: api.c,v 1.2 2008/12/05 22:51:42 christos Exp $ */
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1992, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
* Copyright (c) 1995
* George V. Neville-Neil. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: api.c,v 8.40 2002/06/08 19:30:33 skimo Exp (Berkeley) Date: 2002/06/08 19:30:33";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "../common/common.h"
#include "../ex/tag.h"
extern GS *__global_list; /* XXX */
/*
* api_fscreen --
* Return a pointer to the screen specified by the screen id
* or a file name.
*
* PUBLIC: SCR *api_fscreen __P((int, char *));
*/
SCR *
api_fscreen(int id, char *name)
{
GS *gp;
SCR *tsp;
WIN *wp;
gp = __global_list;
/* Search the displayed lists. */
for (wp = gp->dq.cqh_first;
wp != (void *)&gp->dq; wp = wp->q.cqe_next)
for (tsp = wp->scrq.cqh_first;
tsp != (void *)&wp->scrq; tsp = tsp->q.cqe_next)
if (name == NULL) {
if (id == tsp->id)
return (tsp);
} else if (!strcmp(name, tsp->frp->name))
return (tsp);
/* Search the hidden list. */
for (tsp = gp->hq.cqh_first;
tsp != (void *)&gp->hq; tsp = tsp->q.cqe_next)
if (name == NULL) {
if (id == tsp->id)
return (tsp);
} else if (!strcmp(name, tsp->frp->name))
return (tsp);
return (NULL);
}
/*
* api_aline --
* Append a line.
*
* PUBLIC: int api_aline __P((SCR *, db_recno_t, char *, size_t));
*/
int
api_aline(SCR *sp, db_recno_t lno, char *line, size_t len)
{
size_t wblen;
const CHAR_T *wbp;
CHAR2INT(sp, line, len, wbp, wblen);
return (db_append(sp, 1, lno, wbp, wblen));
}
/*
* api_extend --
* Extend file.
*
* PUBLIC: int api_extend __P((SCR *, db_recno_t));
*/
int
api_extend(SCR *sp, db_recno_t lno)
{
db_recno_t lastlno;
if (db_last(sp, &lastlno))
return 1;
while(lastlno < lno)
if (db_append(sp, 1, lastlno++, NULL, 0))
return 1;
return 0;
}
/*
* api_dline --
* Delete a line.
*
* PUBLIC: int api_dline __P((SCR *, db_recno_t));
*/
int
api_dline(SCR *sp, db_recno_t lno)
{
if (db_delete(sp, lno))
return 1;
/* change current line if deleted line is that one
* or one berfore that
*/
if (sp->lno >= lno && sp->lno > 1)
sp->lno--;
return 0;
}
/*
* api_gline --
* Get a line.
*
* PUBLIC: int api_gline __P((SCR *, db_recno_t, CHAR_T **, size_t *));
*/
int
api_gline(SCR *sp, db_recno_t lno, CHAR_T **linepp, size_t *lenp)
{
int isempty;
if (db_eget(sp, lno, linepp, lenp, &isempty)) {
if (isempty)
msgq(sp, M_ERR, "209|The file is empty");
return (1);
}
return (0);
}
/*
* api_iline --
* Insert a line.
*
* PUBLIC: int api_iline __P((SCR *, db_recno_t, CHAR_T *, size_t));
*/
int
api_iline(SCR *sp, db_recno_t lno, CHAR_T *line, size_t len)
{
return (db_insert(sp, lno, line, len));
}
/*
* api_lline --
* Return the line number of the last line in the file.
*
* PUBLIC: int api_lline __P((SCR *, db_recno_t *));
*/
int
api_lline(SCR *sp, db_recno_t *lnop)
{
return (db_last(sp, lnop));
}
/*
* api_sline --
* Set a line.
*
* PUBLIC: int api_sline __P((SCR *, db_recno_t, CHAR_T *, size_t));
*/
int
api_sline(SCR *sp, db_recno_t lno, CHAR_T *line, size_t len)
{
return (db_set(sp, lno, line, len));
}
/*
* api_getmark --
* Get the mark.
*
* PUBLIC: int api_getmark __P((SCR *, int, MARK *));
*/
int
api_getmark(SCR *sp, int markname, MARK *mp)
{
return (mark_get(sp, (ARG_CHAR_T)markname, mp, M_ERR));
}
/*
* api_setmark --
* Set the mark.
*
* PUBLIC: int api_setmark __P((SCR *, int, MARK *));
*/
int
api_setmark(SCR *sp, int markname, MARK *mp)
{
return (mark_set(sp, (ARG_CHAR_T)markname, mp, 1));
}
/*
* api_nextmark --
* Return the first mark if next not set, otherwise return the
* subsequent mark.
*
* PUBLIC: int api_nextmark __P((SCR *, int, char *));
*/
int
api_nextmark(SCR *sp, int next, char *namep)
{
LMARK *mp;
mp = sp->ep->marks.lh_first;
if (next)
for (; mp != NULL; mp = mp->q.le_next)
if (mp->name == *namep) {
mp = mp->q.le_next;
break;
}
if (mp == NULL)
return (1);
*namep = mp->name;
return (0);
}
/*
* api_getcursor --
* Get the cursor.
*
* PUBLIC: int api_getcursor __P((SCR *, MARK *));
*/
int
api_getcursor(SCR *sp, MARK *mp)
{
mp->lno = sp->lno;
mp->cno = sp->cno;
return (0);
}
/*
* api_setcursor --
* Set the cursor.
*
* PUBLIC: int api_setcursor __P((SCR *, MARK *));
*/
int
api_setcursor(SCR *sp, MARK *mp)
{
size_t len;
if (db_get(sp, mp->lno, DBG_FATAL, NULL, &len))
return (1);
if (mp->cno > len) {
msgq(sp, M_ERR, "Cursor set to nonexistent column");
return (1);
}
/* Set the cursor. */
sp->lno = mp->lno;
sp->cno = mp->cno;
return (0);
}
/*
* api_emessage --
* Print an error message.
*
* PUBLIC: void api_emessage __P((SCR *, char *));
*/
void
api_emessage(SCR *sp, char *text)
{
msgq(sp, M_ERR, "%s", text);
}
/*
* api_imessage --
* Print an informational message.
*
* PUBLIC: void api_imessage __P((SCR *, char *));
*/
void
api_imessage(SCR *sp, char *text)
{
msgq(sp, M_INFO, "%s", text);
}
/*
* api_edit
* Create a new screen and return its id
* or edit a new file in the current screen.
*
* PUBLIC: int api_edit __P((SCR *, char *, SCR **, int));
*/
int
api_edit(SCR *sp, char *file, SCR **spp, int newscreen)
{
EXCMD cmd;
size_t wlen;
const CHAR_T *wp;
if (file) {
ex_cinit(sp, &cmd, C_EDIT, 0, OOBLNO, OOBLNO, 0);
CHAR2INT(sp, file, strlen(file) + 1, wp, wlen);
argv_exp0(sp, &cmd, wp, wlen - 1 /* terminating 0 */);
} else
ex_cinit(sp, &cmd, C_EDIT, 0, OOBLNO, OOBLNO, 0);
if (newscreen)
cmd.flags |= E_NEWSCREEN; /* XXX */
if (cmd.cmd->fn(sp, &cmd))
return (1);
*spp = sp->nextdisp;
return (0);
}
/*
* api_escreen
* End a screen.
*
* PUBLIC: int api_escreen __P((SCR *));
*/
int
api_escreen(SCR *sp)
{
EXCMD cmd;
/*
* XXX
* If the interpreter exits anything other than the current
* screen, vi isn't going to update everything correctly.
*/
ex_cinit(sp, &cmd, C_QUIT, 0, OOBLNO, OOBLNO, 0);
return (cmd.cmd->fn(sp, &cmd));
}
/*
* api_swscreen --
* Switch to a new screen.
*
* PUBLIC: int api_swscreen __P((SCR *, SCR *));
*/
int
api_swscreen(SCR *sp, SCR *new)
{
/*
* XXX
* If the interpreter switches from anything other than the
* current screen, vi isn't going to update everything correctly.
*/
sp->nextdisp = new;
F_SET(sp, SC_SSWITCH);
return (0);
}
/*
* api_map --
* Map a key.
*
* PUBLIC: int api_map __P((SCR *, char *, char *, size_t));
*/
int
api_map(SCR *sp, char *name, char *map, size_t len)
{
EXCMD cmd;
size_t wlen;
const CHAR_T *wp;
ex_cinit(sp, &cmd, C_MAP, 0, OOBLNO, OOBLNO, 0);
CHAR2INT(sp, name, strlen(name) + 1, wp, wlen);
argv_exp0(sp, &cmd, wp, wlen - 1);
CHAR2INT(sp, map, len, wp, wlen);
argv_exp0(sp, &cmd, wp, wlen);
return (cmd.cmd->fn(sp, &cmd));
}
/*
* api_unmap --
* Unmap a key.
*
* PUBLIC: int api_unmap __P((SCR *, char *));
*/
int
api_unmap(SCR *sp, char *name)
{
EXCMD cmd;
size_t wlen;
const CHAR_T *wp;
ex_cinit(sp, &cmd, C_UNMAP, 0, OOBLNO, OOBLNO, 0);
CHAR2INT(sp, name, strlen(name) + 1, wp, wlen);
argv_exp0(sp, &cmd, wp, wlen - 1);
return (cmd.cmd->fn(sp, &cmd));
}
/*
* api_opts_get --
* Return a option value as a string, in allocated memory.
* If the option is of type boolean, boolvalue is (un)set
* according to the value; otherwise boolvalue is -1.
*
* PUBLIC: int api_opts_get __P((SCR *, CHAR_T *, char **, int *));
*/
int
api_opts_get(SCR *sp, const CHAR_T *name, char **value, int *boolvalue)
{
OPTLIST const *op;
int offset;
if ((op = opts_search(name)) == NULL) {
opts_nomatch(sp, name);
return (1);
}
offset = op - optlist;
if (boolvalue != NULL)
*boolvalue = -1;
switch (op->type) {
case OPT_0BOOL:
case OPT_1BOOL:
MALLOC_RET(sp, *value, char *, STRLEN(op->name) + 2 + 1);
(void)sprintf(*value,
"%s"WS, O_ISSET(sp, offset) ? "" : "no", op->name);
if (boolvalue != NULL)
*boolvalue = O_ISSET(sp, offset);
break;
case OPT_NUM:
MALLOC_RET(sp, *value, char *, 20);
(void)sprintf(*value, "%lu", (u_long)O_VAL(sp, offset));
break;
case OPT_STR:
if (O_STR(sp, offset) == NULL) {
MALLOC_RET(sp, *value, char *, 2);
value[0] = '\0';
} else {
MALLOC_RET(sp,
*value, char *, strlen(O_STR(sp, offset)) + 1);
(void)sprintf(*value, "%s", O_STR(sp, offset));
}
break;
}
return (0);
}
/*
* api_opts_set --
* Set options.
*
* PUBLIC: int api_opts_set __P((SCR *, CHAR_T *, char *, u_long, int));
*/
int
api_opts_set(SCR *sp, const CHAR_T *name,
const char *str_value, u_long num_value, int bool_value)
{
ARGS *ap[2], a, b;
OPTLIST const *op;
int rval;
size_t blen;
CHAR_T *bp;
if ((op = opts_search(name)) == NULL) {
opts_nomatch(sp, name);
return (1);
}
switch (op->type) {
case OPT_0BOOL:
case OPT_1BOOL:
GET_SPACE_RETW(sp, bp, blen, 64);
a.len = SPRINTF(bp, 64, L("%s"WS), bool_value ? "" : "no", name);
break;
case OPT_NUM:
GET_SPACE_RETW(sp, bp, blen, 64);
a.len = SPRINTF(bp, 64, L(""WS"=%lu"), name, num_value);
break;
case OPT_STR:
GET_SPACE_RETW(sp, bp, blen, 1024);
a.len = SPRINTF(bp, 1024, L(""WS"=%s"), name, str_value);
break;
default:
bp = NULL;
break;
}
a.bp = bp;
b.len = 0;
b.bp = NULL;
ap[0] = &a;
ap[1] = &b;
rval = opts_set(sp, ap, NULL);
FREE_SPACEW(sp, bp, blen);
return (rval);
}
/*
* api_run_str --
* Execute a string as an ex command.
*
* PUBLIC: int api_run_str __P((SCR *, char *));
*/
int
api_run_str(SCR *sp, char *cmd)
{
size_t wlen;
const CHAR_T *wp;
CHAR2INT(sp, cmd, strlen(cmd)+1, wp, wlen);
return (ex_run_str(sp, NULL, wp, wlen - 1, 0, 0));
}
/*
* PUBLIC: TAGQ * api_tagq_new __P((SCR*, char*));
*/
TAGQ *
api_tagq_new(SCR *sp, char *tag)
{
TAGQ *tqp;
size_t len;
/* Allocate and initialize the tag queue structure. */
len = strlen(tag);
CALLOC_GOTO(sp, tqp, TAGQ *, 1, sizeof(TAGQ) + len + 1);
CIRCLEQ_INIT(&tqp->tagq);
tqp->tag = tqp->buf;
memcpy(tqp->tag, tag, (tqp->tlen = len) + 1);
return tqp;
alloc_err:
return (NULL);
}
/*
* PUBLIC: void api_tagq_add __P((SCR*, TAGQ*, char*, char *, char *));
*/
void
api_tagq_add(SCR *sp, TAGQ *tqp, char *filename, char *search, char *msg)
{
TAG *tp;
const CHAR_T *wp;
size_t wlen;
size_t flen = strlen(filename);
size_t slen = strlen(search);
size_t mlen = strlen(msg);
CALLOC_GOTO(sp, tp, TAG *, 1,
sizeof(TAG) - 1 + flen + 1 +
(slen + 1 + mlen + 1) * sizeof(CHAR_T));
tp->fname = (char *)tp->buf;
memcpy(tp->fname, filename, flen + 1);
tp->fnlen = flen;
tp->search = (CHAR_T *)((char *)tp->fname + flen + 1);
CHAR2INT(sp, search, slen + 1, wp, wlen);
MEMCPYW(tp->search, wp, wlen);
tp->slen = slen;
tp->msg = tp->search + slen + 1;
CHAR2INT(sp, msg, mlen + 1, wp, wlen);
MEMCPYW(tp->msg, wp, wlen);
tp->mlen = mlen;
CIRCLEQ_INSERT_TAIL(&tqp->tagq, tp, q);
alloc_err:
return;
}
/*
* PUBLIC: int api_tagq_push __P((SCR*, TAGQ**));
*/
int
api_tagq_push(SCR *sp, TAGQ **tqpp)
{
TAGQ *tqp;
tqp = *tqpp;
*tqpp = 0;
/* Check to see if we found anything. */
if (tqp->tagq.cqh_first == (void *)&tqp->tagq) {
free(tqp);
return 0;
}
tqp->current = tqp->tagq.cqh_first;
if (tagq_push(sp, tqp, 0, 0))
return 1;
return (0);
}
/*
* PUBLIC: void api_tagq_free __P((SCR*, TAGQ*));
*/
void
api_tagq_free(SCR *sp, TAGQ *tqp)
{
if (tqp)
tagq_free(sp, tqp);
}

31
dist/nvi/common/args.h vendored Normal file
View File

@@ -0,0 +1,31 @@
/* $NetBSD: args.h,v 1.1.1.2 2008/05/18 14:29:40 aymeric Exp $ */
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*
* Id: args.h,v 10.2 1996/03/06 19:50:07 bostic Exp (Berkeley) Date: 1996/03/06 19:50:07
*/
/*
* 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;

112
dist/nvi/common/common.h vendored Normal file
View File

@@ -0,0 +1,112 @@
/* $NetBSD: common.h,v 1.3 2012/01/27 16:41:22 christos Exp $ */
/*-
* Copyright (c) 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1991, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*
* Id: common.h,v 10.20 2002/03/02 23:36:22 skimo Exp (Berkeley) Date: 2002/03/02 23:36:22
*/
/*
* Avoid include sys/types.h after definition of pgno_t
*/
#include <sys/types.h>
#include <sys/queue.h>
#include <bitstring.h>
/*
* Porting information built at configuration time. Included before
* any of nvi's include files.
*/
#include "port.h"
/*
* Pseudo-local includes. These are files that are unlikely to exist
* on most machines to which we're porting vi, and we want to include
* them in a very specific order, regardless.
*/
#include "db.h"
#include <regex.h>
/*
* Forward structure declarations. Not pretty, but the include files
* are far too interrelated for a clean solution.
*/
typedef struct _cb CB;
typedef struct _csc CSC;
typedef struct _conv CONV;
typedef struct _conv_win CONVWIN;
typedef struct _event EVENT;
typedef struct _excmd EXCMD;
typedef struct _exf EXF;
typedef struct _fref FREF;
typedef struct _gs GS;
typedef struct _lmark LMARK;
typedef struct _mark MARK;
typedef struct _msg MSGS;
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 _tagq TAGQ;
typedef struct _text TEXT;
typedef struct _win WIN;
/* Autoindent state. */
typedef enum { C_NOTSET, C_CARATSET, C_NOCHANGE, C_ZEROSET } carat_t;
/* Busy message types. */
typedef enum { BUSY_ON = 1, BUSY_OFF, BUSY_UPDATE } busy_t;
/*
* 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.
*/
typedef enum { CONF_NO, CONF_QUIT, CONF_YES } conf_t;
/* Directions. */
typedef enum { NOTSET, FORWARD, BACKWARD } dir_t;
/* Line operations. */
typedef enum { LINE_APPEND, LINE_DELETE, LINE_INSERT, LINE_RESET } lnop_t;
/* Lock return values. */
typedef enum { LOCK_FAILED, LOCK_SUCCESS, LOCK_UNAVAIL } lockr_t;
/* Sequence types. */
typedef enum { SEQ_ABBREV, SEQ_COMMAND, SEQ_INPUT } seq_t;
#define ENTIRE_LINE ((size_t)-1)
/*
* Local includes.
*/
#include "key.h" /* Required by args.h. */
#include "args.h" /* Required by options.h. */
#include "options.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 "util.h" /* Required by ex.h. */
#include "mark.h" /* Required by gs.h. */
#include "conv.h" /* Required by ex.h and screen.h */
#include "../ex/ex.h" /* Required by gs.h. */
#include "gs.h" /* Required by screen.h. */
#include "log.h" /* Required by screen.h */
#include "screen.h" /* Required by exf.h. */
#include "exf.h"
#include "mem.h"
#if defined(USE_DB4_LOGGING)
#include "vi_auto.h"
#endif
#include "extern.h"

414
dist/nvi/common/conv.c vendored Normal file
View File

@@ -0,0 +1,414 @@
/* $NetBSD: conv.c,v 1.6 2009/01/18 03:45:50 lukem Exp $ */
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: conv.c,v 1.27 2001/08/18 21:41:41 skimo Exp (Berkeley) Date: 2001/08/18 21:41:41";
#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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "common.h"
#ifdef USE_ICONV
#include <langinfo.h>
#include <iconv.h>
#define LANGCODESET nl_langinfo(CODESET)
#else
typedef int iconv_t;
#define LANGCODESET ""
#endif
#include <locale.h>
#ifdef USE_WIDECHAR
static int
raw2int(SCR *sp, const char * str, ssize_t len, CONVWIN *cw, size_t *tolen,
const CHAR_T **dst)
{
int i;
CHAR_T **tostr = (CHAR_T **)(void *)&cw->bp1;
size_t *blen = &cw->blen1;
BINC_RETW(NULL, *tostr, *blen, len);
*tolen = len;
for (i = 0; i < len; ++i)
(*tostr)[i] = (u_char) str[i];
*dst = cw->bp1;
return 0;
}
#define CONV_BUFFER_SIZE 512
/* fill the buffer with codeset encoding of string pointed to by str
* left has the number of bytes left in str and is adjusted
* len contains the number of bytes put in the buffer
*/
#ifdef USE_ICONV
#define CONVERT(str, left, src, len) \
do { \
size_t outleft; \
char *bp = buffer; \
outleft = CONV_BUFFER_SIZE; \
errno = 0; \
if (iconv(id, (const char **)&str, &left, &bp, &outleft) == (size_t)-1 \
/* && errno != E2BIG */) \
goto err; \
if ((len = CONV_BUFFER_SIZE - outleft) == 0) { \
error = -left; \
goto err; \
} \
src = buffer; \
} while (0)
#else
#define CONVERT(str, left, src, len)
#endif
static int
default_char2int(SCR *sp, const char * str, ssize_t len, CONVWIN *cw,
size_t *tolen, const CHAR_T **dst, const char *enc)
{
int j;
size_t i = 0;
CHAR_T **tostr = (CHAR_T **)(void *)&cw->bp1;
size_t *blen = &cw->blen1;
mbstate_t mbs;
size_t n;
ssize_t nlen = len;
const char *src = (const char *)str;
iconv_t id = (iconv_t)-1;
char buffer[CONV_BUFFER_SIZE];
size_t left = len;
int error = 1;
MEMSET(&mbs, 0, 1);
BINC_RETW(NULL, *tostr, *blen, nlen);
#ifdef USE_ICONV
if (strcmp(nl_langinfo(CODESET), enc)) {
id = iconv_open(nl_langinfo(CODESET), enc);
if (id == (iconv_t)-1)
goto err;
CONVERT(str, left, src, len);
}
#endif
for (i = 0, j = 0; j < len; ) {
n = mbrtowc((*tostr)+i, src+j, len-j, &mbs);
/* NULL character converted */
if (n == (size_t)-2) error = -(len-j);
if (n == (size_t)-1 || n == (size_t)-2) goto err;
if (n == 0) n = 1;
j += n;
if (++i >= *blen) {
nlen += 256;
BINC_RETW(NULL, *tostr, *blen, nlen);
}
if (id != (iconv_t)-1 && j == len && left) {
CONVERT(str, left, src, len);
j = 0;
}
}
*tolen = i;
if (id != (iconv_t)-1)
iconv_close(id);
*dst = cw->bp1;
return 0;
err:
*tolen = i;
if (id != (iconv_t)-1)
iconv_close(id);
*dst = cw->bp1;
return error;
}
static int
fe_char2int(SCR *sp, const char * str, ssize_t len, CONVWIN *cw,
size_t *tolen, const CHAR_T **dst)
{
return default_char2int(sp, str, len, cw, tolen, dst, O_STR(sp, O_FILEENCODING));
}
static int
ie_char2int(SCR *sp, const char * str, ssize_t len, CONVWIN *cw,
size_t *tolen, const CHAR_T **dst)
{
return default_char2int(sp, str, len, cw, tolen, dst, O_STR(sp, O_INPUTENCODING));
}
static int
cs_char2int(SCR *sp, const char * str, ssize_t len, CONVWIN *cw,
size_t *tolen, const CHAR_T **dst)
{
return default_char2int(sp, str, len, cw, tolen, dst, LANGCODESET);
}
static int
CHAR_T_int2char(SCR *sp, const CHAR_T * str, ssize_t len, CONVWIN *cw,
size_t *tolen, const char **dst)
{
*tolen = len * sizeof(CHAR_T);
*dst = (const char *)(const void *)str;
return 0;
}
static int
CHAR_T_char2int(SCR *sp, const char * str, ssize_t len, CONVWIN *cw,
size_t *tolen, const CHAR_T **dst)
{
*tolen = len / sizeof(CHAR_T);
*dst = (const CHAR_T*) str;
return 0;
}
static int
int2raw(SCR *sp, const CHAR_T * str, ssize_t len, CONVWIN *cw, size_t *tolen,
const char **dst)
{
int i;
char **tostr = (char **)(void *)&cw->bp1;
size_t *blen = &cw->blen1;
BINC_RETC(NULL, *tostr, *blen, len);
*tolen = len;
for (i = 0; i < len; ++i)
(*tostr)[i] = str[i];
*dst = cw->bp1;
return 0;
}
static int
default_int2char(SCR *sp, const CHAR_T * str, ssize_t len, CONVWIN *cw,
size_t *tolen, const char **pdst, const char *enc)
{
size_t i, j;
int offset = 0;
char **tostr = (char **)(void *)&cw->bp1;
size_t *blen = &cw->blen1;
mbstate_t mbs;
size_t n;
ssize_t nlen = len + MB_CUR_MAX;
char *dst;
size_t buflen;
char buffer[CONV_BUFFER_SIZE];
iconv_t id = (iconv_t)-1;
/* convert first len bytes of buffer and append it to cw->bp
* len is adjusted => 0
* offset contains the offset in cw->bp and is adjusted
* cw->bp is grown as required
*/
#ifdef USE_ICONV
#define CONVERT2(len, cw, offset) \
do { \
const char *bp = buffer; \
while (len != 0) { \
size_t outleft = cw->blen1 - offset; \
char *obp = (char *)cw->bp1 + offset; \
if (cw->blen1 < offset + MB_CUR_MAX) { \
nlen += 256; \
BINC_RETC(NULL, cw->bp1, cw->blen1, nlen); \
} \
errno = 0; \
if (iconv(id, &bp, &len, &obp, &outleft) == (size_t)-1 && \
errno != E2BIG) \
goto err; \
offset = cw->blen1 - outleft; \
} \
} while (0)
#else
#define CONVERT2(len, cw, offset)
#endif
MEMSET(&mbs, 0, 1);
BINC_RETC(NULL, *tostr, *blen, nlen);
dst = *tostr; buflen = *blen;
#ifdef USE_ICONV
if (strcmp(nl_langinfo(CODESET), enc)) {
id = iconv_open(enc, nl_langinfo(CODESET));
if (id == (iconv_t)-1)
goto err;
dst = buffer; buflen = CONV_BUFFER_SIZE;
}
#endif
for (i = 0, j = 0; i < (size_t)len; ++i) {
n = wcrtomb(dst+j, str[i], &mbs);
if (n == (size_t)-1) goto err;
j += n;
if (buflen < j + MB_CUR_MAX) {
if (id != (iconv_t)-1) {
CONVERT2(j, cw, offset);
} else {
nlen += 256;
BINC_RETC(NULL, *tostr, *blen, nlen);
dst = *tostr; buflen = *blen;
}
}
}
n = wcrtomb(dst+j, L'\0', &mbs);
j += n - 1; /* don't count NUL at the end */
*tolen = j;
if (id != (iconv_t)-1) {
CONVERT2(j, cw, offset);
*tolen = offset;
}
*pdst = cw->bp1;
return 0;
err:
*tolen = j;
*pdst = cw->bp1;
return 1;
}
static int
fe_int2char(SCR *sp, const CHAR_T * str, ssize_t len, CONVWIN *cw,
size_t *tolen, const char **dst)
{
return default_int2char(sp, str, len, cw, tolen, dst, O_STR(sp, O_FILEENCODING));
}
static int
cs_int2char(SCR *sp, const CHAR_T * str, ssize_t len, CONVWIN *cw,
size_t *tolen, const char **dst)
{
return default_int2char(sp, str, len, cw, tolen, dst, LANGCODESET);
}
#endif
void
conv_init (SCR *orig, SCR *sp)
{
if (orig != NULL)
MEMCPY(&sp->conv, &orig->conv, 1);
else {
setlocale(LC_ALL, "");
#ifdef USE_WIDECHAR
sp->conv.sys2int = cs_char2int;
sp->conv.int2sys = cs_int2char;
sp->conv.file2int = fe_char2int;
sp->conv.int2file = fe_int2char;
sp->conv.input2int = ie_char2int;
#endif
#ifdef USE_ICONV
o_set(sp, O_FILEENCODING, OS_STRDUP, nl_langinfo(CODESET), 0);
o_set(sp, O_INPUTENCODING, OS_STRDUP, nl_langinfo(CODESET), 0);
#endif
}
}
int
conv_enc (SCR *sp, int option, const char *enc)
{
#if defined(USE_WIDECHAR) && defined(USE_ICONV)
iconv_t id;
char2wchar_t *c2w;
wchar2char_t *w2c;
switch (option) {
case O_FILEENCODING:
c2w = &sp->conv.file2int;
w2c = &sp->conv.int2file;
break;
case O_INPUTENCODING:
c2w = &sp->conv.input2int;
w2c = NULL;
break;
default:
c2w = NULL;
w2c = NULL;
break;
}
if (!*enc) {
if (c2w) *c2w = raw2int;
if (w2c) *w2c = int2raw;
return 0;
}
if (!strcmp(enc, "WCHAR_T")) {
if (c2w) *c2w = CHAR_T_char2int;
if (w2c) *w2c = CHAR_T_int2char;
return 0;
}
id = iconv_open(enc, nl_langinfo(CODESET));
if (id == (iconv_t)-1)
goto err;
iconv_close(id);
id = iconv_open(nl_langinfo(CODESET), enc);
if (id == (iconv_t)-1)
goto err;
iconv_close(id);
switch (option) {
case O_FILEENCODING:
*c2w = fe_char2int;
*w2c = fe_int2char;
break;
case O_INPUTENCODING:
*c2w = ie_char2int;
break;
}
F_CLR(sp, SC_CONV_ERROR);
F_SET(sp, SC_SCR_REFORMAT);
return 0;
err:
switch (option) {
case O_FILEENCODING:
msgq(sp, M_ERR,
"321|File encoding conversion not supported");
break;
case O_INPUTENCODING:
msgq(sp, M_ERR,
"322|Input encoding conversion not supported");
break;
}
#endif
return 1;
}

28
dist/nvi/common/conv.h vendored Normal file
View File

@@ -0,0 +1,28 @@
/* $NetBSD: conv.h,v 1.4 2009/04/19 02:28:19 tnozaki Exp $ */
#define KEY_COL(sp, ch) \
(INTISWIDE(ch) ? \
(CHAR_WIDTH(sp, ch) >= 0) ? \
(size_t)CHAR_WIDTH(sp, ch) : 1 /* extra space */ \
: KEY_LEN(sp,ch))
struct _conv_win {
void *bp1;
size_t blen1;
};
typedef int (*char2wchar_t)
(SCR *, const char *, ssize_t, struct _conv_win *, size_t *, const CHAR_T **);
typedef int (*wchar2char_t)
(SCR *, const CHAR_T *, ssize_t, struct _conv_win *, size_t *, const char **);
struct _conv {
char2wchar_t sys2int;
wchar2char_t int2sys;
char2wchar_t file2int;
wchar2char_t int2file;
char2wchar_t input2int;
wchar2char_t int2disp;
};
void conv_init __P((SCR *, SCR *));
int conv_enc __P((SCR *, int, const char *));

354
dist/nvi/common/cut.c vendored Normal file
View File

@@ -0,0 +1,354 @@
/* $NetBSD: cut.c,v 1.9 2012/01/27 16:41:22 christos Exp $ */
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1992, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: cut.c,v 10.18 2001/06/25 15:19:09 skimo Exp (Berkeley) Date: 2001/06/25 15:19:09";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <bitstring.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
static void 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.
*
* PUBLIC: int cut __P((SCR *, ARG_CHAR_T *, MARK *, MARK *, int));
*/
int
cut(SCR *sp, ARG_CHAR_T *namep, MARK *fm, MARK *tm, int flags)
{
CB *cbp;
ARG_CHAR_T name = '\0';
db_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->wp->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->wp->cutq, cbp, q);
} else if (!append) {
text_lfree(&cbp->textq);
cbp->len = 0;
cbp->flags = 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, lno, 0, ENTIRE_LINE, cbp))
goto cut_line_err;
} else {
/*
* Get the first line. A length of ENTIRE_LINE causes cut_line
* to cut from the MARK to the end of the line.
*/
if (cut_line(sp, 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, lno, 0, ENTIRE_LINE, cbp))
goto cut_line_err;
/* Get the last line. */
if (tm->lno != fm->lno &&
cut_line(sp, lno, 0, tm->cno + 1, cbp))
goto cut_line_err;
}
append = 0; /* Only append to the named buffer. */
sp->wp->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->wp->dcb_store;
copy_def = 0;
goto copyloop;
}
return (0);
cut_line_err:
text_lfree(&cbp->textq);
cbp->len = 0;
cbp->flags = 0;
return (1);
}
/*
* cb_rotate --
* Rotate the numbered buffers up one.
*/
static void
cb_rotate(SCR *sp)
{
CB *cbp, *del_cbp;
del_cbp = NULL;
for (cbp = sp->wp->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);
}
}
/*
* cut_line --
* Cut a portion of a single line.
*
* PUBLIC: int cut_line __P((SCR *, db_recno_t, size_t, size_t, CB *));
*/
int
cut_line(SCR *sp, db_recno_t lno, size_t fcno, size_t clen, CB *cbp)
{
TEXT *tp;
size_t len;
CHAR_T *p;
/* Get the line. */
if (db_get(sp, lno, DBG_FATAL, &p, &len))
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 == ENTIRE_LINE)
clen = len - fcno;
MEMCPYW(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);
}
/*
* cut_close --
* Discard all cut buffers.
*
* PUBLIC: void cut_close __P((WIN *));
*/
void
cut_close(WIN *wp)
{
CB *cbp;
/* Free cut buffer list. */
while ((cbp = wp->cutq.lh_first) != NULL) {
if (cbp->textq.cqh_first != (void *)&cbp->textq)
text_lfree(&cbp->textq);
LIST_REMOVE(cbp, q);
free(cbp);
}
/* Free default cut storage. */
cbp = &wp->dcb_store;
if (cbp->textq.cqh_first != (void *)&cbp->textq)
text_lfree(&cbp->textq);
}
/*
* text_init --
* Allocate a new TEXT structure.
*
* PUBLIC: TEXT *text_init __P((SCR *, const CHAR_T *, size_t, size_t));
*/
TEXT *
text_init(SCR *sp, const CHAR_T *p, size_t len, size_t 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(3) for 0 bytes. */
if ((tp->lb_len = total_len * sizeof(CHAR_T)) != 0) {
MALLOC(sp, tp->lb, CHAR_T *, tp->lb_len * sizeof(CHAR_T));
if (tp->lb == NULL) {
free(tp);
return (NULL);
}
if (p != NULL && len != 0)
MEMCPYW(tp->lb, p, len);
}
tp->len = len;
return (tp);
}
/*
* text_lfree --
* Free a chain of text structures.
*
* PUBLIC: void text_lfree __P((TEXTH *));
*/
void
text_lfree(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.
*
* PUBLIC: void text_free __P((TEXT *));
*/
void
text_free(TEXT *tp)
{
if (tp->lb != NULL)
free(tp->lb);
free(tp);
}

81
dist/nvi/common/cut.h vendored Normal file
View File

@@ -0,0 +1,81 @@
/* $NetBSD: cut.h,v 1.4 2011/03/21 14:53:02 tnozaki Exp $ */
/*-
* Copyright (c) 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1991, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*
* Id: cut.h,v 10.9 2000/07/22 17:31:18 skimo Exp (Berkeley) Date: 2000/07/22 17:31:18
*/
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. */
/* XXXX Needed ? Can non ascii-chars be cut buffer names ? */
ARG_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_T *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. */
db_recno_t lno; /* 1-N: file line. */
size_t cno; /* 0-N: file character in line. */
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 returns information from the vi text input routine.
*
* The termination condition. Note, this field is only valid if the
* text input routine returns success.
* TERM_BS: User backspaced over the prompt.
* TERM_CEDIT: User entered <edit-char>.
* TERM_CR: User entered <carriage-return>; no data.
* TERM_ESC: User entered <escape>; no data.
* TERM_OK: Data available.
* TERM_SEARCH: Incremental search.
*/
enum {
TERM_BS, TERM_CEDIT, TERM_CR, TERM_ESC, TERM_OK, TERM_SEARCH
} term;
};
/*
* Get named buffer 'name'.
* Translate upper-case buffer names to lower-case buffer names.
*/
#define CBNAME(sp, cbp, nch) { \
ARG_CHAR_T L__name; \
L__name = ISUPPER(nch) ? \
TOLOWER(nch) : (nch); \
for (cbp = sp->wp->cutq.lh_first; \
cbp != NULL; cbp = cbp->q.le_next) \
if (cbp->name == L__name) \
break; \
}
/* Flags to the cut() routine. */
#define CUT_LINEMODE 0x01 /* Cut in line mode. */
#define CUT_NUMOPT 0x02 /* Numeric buffer: optional. */
#define CUT_NUMREQ 0x04 /* Numeric buffer: required. */

690
dist/nvi/common/db.c vendored Normal file
View File

@@ -0,0 +1,690 @@
/* $NetBSD: db.c,v 1.3 2008/12/09 16:50:22 christos Exp $ */
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1992, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: db.c,v 10.48 2002/06/08 19:32:52 skimo Exp (Berkeley) Date: 2002/06/08 19:32:52";
#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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "common.h"
#include "../vi/vi.h"
static int append __P((SCR*, db_recno_t, const CHAR_T*, size_t, lnop_t, int));
/*
* db_eget --
* Front-end to db_get, special case handling for empty files.
*
* PUBLIC: int db_eget __P((SCR *, db_recno_t, CHAR_T **, size_t *, int *));
*/
int
db_eget(SCR *sp, db_recno_t lno, CHAR_T **pp, size_t *lenp, int *isemptyp)
/* Line number. */
/* Pointer store. */
/* Length store. */
{
db_recno_t l1;
if (isemptyp != NULL)
*isemptyp = 0;
/* If the line exists, simply return it. */
if (!db_get(sp, lno, 0, pp, lenp))
return (0);
/*
* If the user asked for line 0 or line 1, i.e. the only possible
* line in an empty file, find the last line of the file; db_last
* fails loudly.
*/
if ((lno == 0 || lno == 1) && db_last(sp, &l1))
return (1);
/* If the file isn't empty, fail loudly. */
if ((lno != 0 && lno != 1) || l1 != 0) {
db_err(sp, lno);
return (1);
}
if (isemptyp != NULL)
*isemptyp = 1;
return (1);
}
/*
* db_get --
* Look in the text buffers for a line, followed by the cache, followed
* by the database.
*
* PUBLIC: int db_get __P((SCR *, db_recno_t, u_int32_t, CHAR_T **, size_t *));
*/
int
db_get(SCR *sp, db_recno_t lno, u_int32_t flags, CHAR_T **pp, size_t *lenp)
/* Line number. */ /* Pointer store. */ /* Length store. */
{
DBT data, key;
EXF *ep;
TEXT *tp;
db_recno_t l1, l2;
const CHAR_T *wp;
size_t wlen;
size_t nlen;
/*
* 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)
goto err1;
/* Check for no underlying file. */
if ((ep = sp->ep) == NULL) {
ex_emsg(sp, NULL, EXM_NOFILEYET);
goto err3;
}
if (LF_ISSET(DBG_NOCACHE))
goto nocache;
/*
* Look-aside into the TEXT buffers and see if the line we want
* is there.
*/
if (F_ISSET(sp, SC_TINPUT)) {
l1 = ((TEXT *)sp->tiq.cqh_first)->lno;
l2 = ((TEXT *)sp->tiq.cqh_last)->lno;
if (l1 <= lno && l2 >= lno) {
#if defined(DEBUG) && 0
vtrace(sp,
"retrieve TEXT buffer line %lu\n", (u_long)lno);
#endif
for (tp = sp->tiq.cqh_first;
tp->lno != lno; tp = tp->q.cqe_next);
if (lenp != NULL)
*lenp = tp->len;
if (pp != NULL)
*pp = tp->lb;
return (0);
}
/*
* Adjust the line number for the number of lines used
* by the text input buffers.
*/
if (lno > l2)
lno -= l2 - l1;
}
/* Look-aside into the cache, and see if the line we want is there. */
if (lno == sp->c_lno) {
#if defined(DEBUG) && 0
vtrace(sp, "retrieve cached line %lu\n", (u_long)lno);
#endif
if (lenp != NULL)
*lenp = sp->c_len;
if (pp != NULL)
*pp = sp->c_lp;
return (0);
}
sp->c_lno = OOBLNO;
nocache:
nlen = 1024;
retry:
/* data.size contains length in bytes */
BINC_GOTO(sp, CHAR_T, sp->c_lp, sp->c_blen, nlen);
/* Get the line from the underlying database. */
memset(&key, 0, sizeof(key));
key.data = &lno;
key.size = sizeof(lno);
memset(&data, 0, sizeof(data));
data.data = sp->c_lp;
data.ulen = sp->c_blen;
data.flags = DB_DBT_USERMEM;
switch (ep->db->get(ep->db, NULL, &key, &data, 0)) {
case DB_BUFFER_SMALL:
nlen = data.size;
goto retry;
default:
goto err2;
case DB_NOTFOUND:
err1: if (LF_ISSET(DBG_FATAL))
err2: db_err(sp, lno);
alloc_err:
err3: if (lenp != NULL)
*lenp = 0;
if (pp != NULL)
*pp = NULL;
return (1);
case 0:
;
}
if (FILE2INT(sp, data.data, data.size, wp, wlen)) {
if (!F_ISSET(sp, SC_CONV_ERROR)) {
F_SET(sp, SC_CONV_ERROR);
msgq(sp, M_ERR, "324|Conversion error on line %d", lno);
}
goto err3;
}
/* Reset the cache. */
if (wp != data.data) {
BINC_GOTOW(sp, sp->c_lp, sp->c_blen, wlen);
MEMCPYW(sp->c_lp, wp, wlen);
}
sp->c_lno = lno;
sp->c_len = wlen;
#if defined(DEBUG) && 0
vtrace(sp, "retrieve DB line %lu\n", (u_long)lno);
#endif
if (lenp != NULL)
*lenp = wlen;
if (pp != NULL)
*pp = sp->c_lp;
return (0);
}
/*
* db_delete --
* Delete a line from the file.
*
* PUBLIC: int db_delete __P((SCR *, db_recno_t));
*/
int
db_delete(SCR *sp, db_recno_t lno)
{
DBT key;
EXF *ep;
#if defined(DEBUG) && 0
vtrace(sp, "delete line %lu\n", (u_long)lno);
#endif
/* Check for no underlying file. */
if ((ep = sp->ep) == NULL) {
ex_emsg(sp, NULL, EXM_NOFILEYET);
return (1);
}
if (ep->l_win && ep->l_win != sp->wp) {
ex_emsg(sp, NULL, EXM_LOCKED);
return 1;
}
/* Update marks, @ and global commands. */
if (line_insdel(sp, LINE_DELETE, lno))
return 1;
/* Log before change. */
log_line(sp, lno, LOG_LINE_DELETE_B);
/* Update file. */
memset(&key, 0, sizeof(key));
key.data = &lno;
key.size = sizeof(lno);
if ((sp->db_error = ep->db->del(ep->db, NULL, &key, 0)) != 0) {
msgq(sp, M_DBERR, "003|unable to delete line %lu",
(u_long)lno);
return (1);
}
/* Flush the cache, update line count, before screen update. */
update_cache(sp, LINE_DELETE, lno);
/* File now modified. */
if (F_ISSET(ep, F_FIRSTMODIFY))
(void)rcv_init(sp);
F_SET(ep, F_MODIFIED);
/* Log after change. */
log_line(sp, lno, LOG_LINE_DELETE_F);
/* Update screen. */
return (scr_update(sp, lno, LINE_DELETE, 1));
}
/* maybe this could be simpler
*
* DB3 behaves differently from DB1
*
* if lno != 0 just go to lno and put the new line after it
* if lno == 0 then if there are any record, put in front of the first
* otherwise just append to the end thus creating the first
* line
*/
static int
append(SCR *sp, db_recno_t lno, const CHAR_T *p, size_t len, lnop_t op, int update)
{
DBT data, key;
DBC *dbcp_put;
EXF *ep;
const char *fp;
size_t flen;
int rval;
/* Check for no underlying file. */
if ((ep = sp->ep) == NULL) {
ex_emsg(sp, NULL, EXM_NOFILEYET);
return (1);
}
if (ep->l_win && ep->l_win != sp->wp) {
ex_emsg(sp, NULL, EXM_LOCKED);
return 1;
}
/* Log before change. */
log_line(sp, lno + 1, LOG_LINE_APPEND_B);
/* Update file. */
memset(&key, 0, sizeof(key));
key.data = &lno;
key.size = sizeof(lno);
memset(&data, 0, sizeof(data));
if ((sp->db_error = ep->db->cursor(ep->db, NULL, &dbcp_put, 0)) != 0)
return 1;
INT2FILE(sp, p, len, fp, flen);
if (lno != 0) {
if ((sp->db_error = dbcp_put->c_get(dbcp_put, &key, &data, DB_SET)) != 0)
goto err2;
data.data = __UNCONST(fp);
data.size = flen;
if ((sp->db_error = dbcp_put->c_put(dbcp_put, &key, &data, DB_AFTER)) != 0) {
err2:
(void)dbcp_put->c_close(dbcp_put);
msgq(sp, M_DBERR,
(op == LINE_APPEND)
? "004|unable to append to line %lu"
: "005|unable to insert at line %lu",
(u_long)lno);
return (1);
}
} else {
if ((sp->db_error = dbcp_put->c_get(dbcp_put, &key, &data, DB_FIRST)) != 0) {
if (sp->db_error != DB_NOTFOUND)
goto err2;
data.data = __UNCONST(fp);
data.size = flen;
if ((sp->db_error = ep->db->put(ep->db, NULL, &key, &data, DB_APPEND)) != 0) {
goto err2;
}
} else {
key.data = &lno;
key.size = sizeof(lno);
data.data = __UNCONST(fp);
data.size = flen;
if ((sp->db_error = dbcp_put->c_put(dbcp_put, &key, &data, DB_BEFORE)) != 0) {
goto err2;
}
}
}
(void)dbcp_put->c_close(dbcp_put);
/* Flush the cache, update line count, before screen update. */
update_cache(sp, LINE_INSERT, lno);
/* File now dirty. */
if (F_ISSET(ep, F_FIRSTMODIFY))
(void)rcv_init(sp);
F_SET(ep, F_MODIFIED);
/* Log after change. */
log_line(sp, lno + 1, LOG_LINE_APPEND_F);
/* Update marks, @ and global commands. */
rval = line_insdel(sp, LINE_INSERT, lno + 1);
/*
* Update screen.
*
* comment copied from db_append
* 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, lno + 1, LINE_INSERT, update) || rval);
}
/*
* db_append --
* Append a line into the file.
*
* PUBLIC: int db_append __P((SCR *, int, db_recno_t, CHAR_T *, size_t));
*/
int
db_append(SCR *sp, int update, db_recno_t lno, const CHAR_T *p, size_t len)
{
#if defined(DEBUG) && 0
vtrace(sp, "append to %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
#endif
/* Update file. */
return append(sp, lno, p, len, LINE_APPEND, update);
}
/*
* db_insert --
* Insert a line into the file.
*
* PUBLIC: int db_insert __P((SCR *, db_recno_t, CHAR_T *, size_t));
*/
int
db_insert(SCR *sp, db_recno_t lno, CHAR_T *p, size_t len)
{
#if defined(DEBUG) && 0
vtrace(sp, "insert before %lu: len %lu {%.*s}\n",
(u_long)lno, (u_long)len, MIN(len, 20), p);
#endif
return append(sp, lno - 1, p, len, LINE_INSERT, 1);
}
/*
* db_set --
* Store a line in the file.
*
* PUBLIC: int db_set __P((SCR *, db_recno_t, CHAR_T *, size_t));
*/
int
db_set(SCR *sp, db_recno_t lno, CHAR_T *p, size_t len)
{
DBT data, key;
EXF *ep;
const char *fp;
size_t flen;
#if defined(DEBUG) && 0
vtrace(sp, "replace line %lu: len %lu {%.*s}\n",
(u_long)lno, (u_long)len, MIN(len, 20), p);
#endif
/* Check for no underlying file. */
if ((ep = sp->ep) == NULL) {
ex_emsg(sp, NULL, EXM_NOFILEYET);
return (1);
}
if (ep->l_win && ep->l_win != sp->wp) {
ex_emsg(sp, NULL, EXM_LOCKED);
return 1;
}
/* Log before change. */
log_line(sp, lno, LOG_LINE_RESET_B);
INT2FILE(sp, p, len, fp, flen);
/* Update file. */
memset(&key, 0, sizeof(key));
key.data = &lno;
key.size = sizeof(lno);
memset(&data, 0, sizeof(data));
data.data = __UNCONST(fp);
data.size = flen;
if ((sp->db_error = ep->db->put(ep->db, NULL, &key, &data, 0)) != 0) {
msgq(sp, M_DBERR, "006|unable to store line %lu", (u_long)lno);
return (1);
}
/* Flush the cache, update line count, before screen update. */
update_cache(sp, LINE_RESET, lno);
/* File now dirty. */
if (F_ISSET(ep, F_FIRSTMODIFY))
(void)rcv_init(sp);
F_SET(ep, F_MODIFIED);
/* Log after change. */
log_line(sp, lno, LOG_LINE_RESET_F);
/* Update screen. */
return (scr_update(sp, lno, LINE_RESET, 1));
}
/*
* db_exist --
* Return if a line exists.
*
* PUBLIC: int db_exist __P((SCR *, db_recno_t));
*/
int
db_exist(SCR *sp, db_recno_t lno)
{
EXF *ep;
/* Check for no underlying file. */
if ((ep = sp->ep) == NULL) {
ex_emsg(sp, NULL, EXM_NOFILEYET);
return (1);
}
if (lno == OOBLNO)
return (0);
/*
* Check the last-line number cache. Adjust the cached line
* number for the lines used by the text input buffers.
*/
if (ep->c_nlines != OOBLNO)
return (lno <= (F_ISSET(sp, SC_TINPUT) ?
ep->c_nlines + (((TEXT *)sp->tiq.cqh_last)->lno -
((TEXT *)sp->tiq.cqh_first)->lno) : ep->c_nlines));
/* Go get the line. */
return (!db_get(sp, lno, 0, NULL, NULL));
}
/*
* db_last --
* Return the number of lines in the file.
*
* PUBLIC: int db_last __P((SCR *, db_recno_t *));
*/
int
db_last(SCR *sp, db_recno_t *lnop)
{
DBT data, key;
DBC *dbcp;
EXF *ep;
db_recno_t lno;
const CHAR_T *wp;
size_t wlen;
/* Check for no underlying file. */
if ((ep = sp->ep) == NULL) {
ex_emsg(sp, NULL, EXM_NOFILEYET);
return (1);
}
/*
* Check the last-line number cache. Adjust the cached line
* number for the lines used by the text input buffers.
*/
if (ep->c_nlines != OOBLNO) {
*lnop = ep->c_nlines;
if (F_ISSET(sp, SC_TINPUT))
*lnop += ((TEXT *)sp->tiq.cqh_last)->lno -
((TEXT *)sp->tiq.cqh_first)->lno;
return (0);
}
memset(&key, 0, sizeof(key));
key.data = &lno;
key.size = sizeof(lno);
memset(&data, 0, sizeof(data));
if ((sp->db_error = ep->db->cursor(ep->db, NULL, &dbcp, 0)) != 0)
goto err1;
switch (sp->db_error = dbcp->c_get(dbcp, &key, &data, DB_LAST)) {
case DB_NOTFOUND:
*lnop = 0;
return (0);
default:
(void)dbcp->c_close(dbcp);
alloc_err:
err1:
msgq(sp, M_DBERR, "007|unable to get last line");
*lnop = 0;
return (1);
case 0:
;
}
memcpy(&lno, key.data, sizeof(lno));
if (lno != sp->c_lno) {
FILE2INT(sp, data.data, data.size, wp, wlen);
/* Fill the cache. */
BINC_GOTOW(sp, sp->c_lp, sp->c_blen, wlen);
MEMCPYW(sp->c_lp, wp, wlen);
sp->c_lno = lno;
sp->c_len = wlen;
}
ep->c_nlines = lno;
(void)dbcp->c_close(dbcp);
/* Return the value. */
*lnop = (F_ISSET(sp, SC_TINPUT) &&
((TEXT *)sp->tiq.cqh_last)->lno > lno ?
((TEXT *)sp->tiq.cqh_last)->lno : lno);
return (0);
}
/*
* db_err --
* Report a line error.
*
* PUBLIC: void db_err __P((SCR *, db_recno_t));
*/
void
db_err(SCR *sp, db_recno_t lno)
{
msgq(sp, M_ERR,
"008|Error: unable to retrieve line %lu", (u_long)lno);
}
/*
* scr_update --
* Update all of the screens that are backed by the file that
* just changed.
*
* PUBLIC: int scr_update __P((SCR *sp, db_recno_t lno,
* PUBLIC: lnop_t op, int current));
*/
int
scr_update(SCR *sp, db_recno_t lno, lnop_t op, int current)
{
EXF *ep;
SCR *tsp;
WIN *wp;
if (F_ISSET(sp, SC_EX))
return (0);
/* XXXX goes outside of window */
ep = sp->ep;
if (ep->refcnt != 1)
for (wp = sp->gp->dq.cqh_first; wp != (void *)&sp->gp->dq;
wp = wp->q.cqe_next)
for (tsp = wp->scrq.cqh_first;
tsp != (void *)&wp->scrq; tsp = tsp->q.cqe_next)
if (sp != tsp && tsp->ep == ep)
if (vs_change(tsp, lno, op))
return (1);
return (current ? vs_change(sp, lno, op) : 0);
}
/*
* PUBLIC: void update_cache __P((SCR *sp, lnop_t op, db_recno_t lno));
*/
void
update_cache(SCR *sp, lnop_t op, db_recno_t lno)
{
SCR* scrp;
EXF *ep;
ep = sp->ep;
/* Flush the cache, update line count, before screen update. */
/* The flushing is probably not needed, since it was incorrect
* for db_insert. It might be better to adjust it, like
* marks, @ and global
*/
for (scrp = ep->scrq.cqh_first; scrp != (void *)&ep->scrq;
scrp = scrp->eq.cqe_next)
switch (op) {
case LINE_INSERT:
case LINE_DELETE:
if (lno <= scrp->c_lno)
scrp->c_lno = OOBLNO;
break;
case LINE_RESET:
if (lno == scrp->c_lno)
scrp->c_lno = OOBLNO;
/*FALLTHROUGH*/
case LINE_APPEND:
break;
}
if (ep->c_nlines != OOBLNO)
switch (op) {
case LINE_INSERT:
++ep->c_nlines;
break;
case LINE_DELETE:
--ep->c_nlines;
/*FALLTHROUGH*/
case LINE_APPEND:
case LINE_RESET:
break;
}
}
/*
* PUBLIC: int line_insdel __P((SCR *sp, lnop_t op, db_recno_t lno));
*/
int
line_insdel(SCR *sp, lnop_t op, db_recno_t lno)
{
int rval;
/* Update marks, @ and global commands. */
rval = 0;
if (mark_insdel(sp, op, lno))
rval = 1;
if (ex_g_insdel(sp, op, lno))
rval = 1;
return rval;
}

146
dist/nvi/common/db.h vendored Normal file
View File

@@ -0,0 +1,146 @@
/* $NetBSD: db.h,v 1.3 2008/08/05 15:49:18 aymeric Exp $ */
#include <db.h>
#ifndef DB_BUFFER_SMALL
#define DB_BUFFER_SMALL ENOMEM
#endif
#if USE_DB1 || (DB_VERSION_MAJOR >= 3 && DB_VERSION_MINOR >= 1)
#define db_env_open(env,path,flags,mode) \
(env)->open(env, path, flags, mode)
#define db_env_remove(env,path,flags) \
(env)->remove(env, path, flags)
#else
#define db_env_open(env,path,flags,mode) \
(env)->open(env, path, NULL, flags, mode)
#define db_env_remove(env,path,flags) \
(env)->remove(env, path, NULL, flags)
#endif
#if DB_VERSION_MAJOR >= 4 && DB_VERSION_MINOR >= 1
#define db_open(db,file,type,flags,mode) \
(db)->open(db, NULL, file, NULL, type, flags, mode)
#else
#define db_open(db,file,type,flags,mode) \
(db)->open(db, file, NULL, type, flags, mode)
#endif
#ifdef USE_DYNAMIC_LOADING
#define db_create nvi_db_create
#define db_env_create nvi_db_env_create
#define db_strerror nvi_db_strerror
extern int (*nvi_db_create) __P((DB **, DB_ENV *, u_int32_t));
extern int (*nvi_db_env_create) __P((DB_ENV **, u_int32_t));
extern char *(*nvi_db_strerror) __P((int));
#endif
#ifdef USE_DB1
#define DB_AFTER 1
#define DB_APPEND 2
#define DB_BEFORE 3
#define DB_FIRST 7
#define DB_LAST 15
#define DB_SET 25
#define DB_NOTFOUND (-30989)
/* DBT emulation */
typedef DBT DBT_v1;
#undef DBT
#define DBT DBT_new
typedef struct {
void *data;
size_t size;
u_int32_t ulen;
#define DB_DBT_USERMEM 0x040
u_int32_t flags;
} DBT;
/* DB_ENV emulation */
struct __db_env_new;
typedef struct __db_env_new DB_ENV;
struct __db_env_new {
int (*close)(DB_ENV *, u_int32_t);
int (*open)(DB_ENV *, char *, u_int32_t, int);
#define DB_INIT_MPOOL 0x004000
#define DB_PRIVATE 0x200000
int (*remove)(DB_ENV *, char *, u_int32_t);
char *base_path;
int mode;
};
/* DBC emulation */
struct __dbc_new;
typedef struct __dbc_new DBC;
typedef recno_t db_recno_t;
#define DB_MAX_RECORDS MAX_REC_NUMBER
#define DB_UNKNOWN (-1)
/* DB emulation */
typedef DB DB_old;
#undef DB
#define DB DB_new
typedef struct __db_new DB;
#undef DB_TXN
typedef void DB_TXN;
#undef DB_LSN
typedef struct {
int dummy;
} DB_LSN;
struct __db_new {
DB_old *actual_db;
int type;
int (*close)(DB *, u_int32_t);
#define DB_NOSYNC 26 /* close() */
int (*open)(DB *, const char *, const char *, DBTYPE, u_int32_t, int);
#define DB_CREATE 0x000001 /* Create file as necessary. */
#define DB_TRUNCATE 0x004000 /* Discard existing DB (O_TRUNC) */
int (*sync)(DB *, u_int32_t);
int (*get)(DB *, DB_TXN *, DBT *, DBT *, u_int32_t);
int (*put)(DB *, DB_TXN *, DBT *, DBT *, u_int32_t);
int (*del)(DB *, DB_TXN *, DBT *, u_int32_t);
int (*cursor)(DB *, DB_TXN *, DBC **, u_int32_t);
int (*set_flags)(DB *, u_int32_t);
#define DB_RENUMBER 0x0008 /* Recno: renumber on insert/delete. */
#define DB_SNAPSHOT 0x0020 /* Recno: snapshot the input. */
int (*set_pagesize)(DB *, u_int32_t);
int (*set_re_delim)(DB *, int);
int (*set_re_source)(DB *, const char *);
RECNOINFO _recno_info;
u_int32_t _pagesize;
u_int32_t _flags;
};
struct __dbc_new {
DB *db;
db_recno_t pos;
DBT_v1 pos_key;
int (*c_close)(DBC *);
int (*c_get)(DBC *, DBT *, DBT *, u_int32_t);
int (*c_put)(DBC *, DBT *, DBT *, u_int32_t);
};
#endif /* USE_DB1 */

360
dist/nvi/common/db1.c vendored Normal file
View File

@@ -0,0 +1,360 @@
/*
* DB1->3 compatibility layer
*/
#include "config.h"
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include "../common/db.h"
#include "../common/dbinternal.h"
/*
* DB_ENV emulation
*/
static int db1_dbenv_close(DB_ENV *, u_int32_t);
static int db1_dbenv_open(DB_ENV *, char *, u_int32_t, int);
static int db1_dbenv_remove(DB_ENV *, char *, u_int32_t);
int
db_env_create(DB_ENV **dbenvp, u_int32_t flags) {
DB_ENV *dbenv;
assert(flags == 0);
dbenv = malloc(sizeof *dbenv);
if (dbenv == NULL)
return -1;
dbenv->close = db1_dbenv_close;
dbenv->open = db1_dbenv_open;
dbenv->remove = db1_dbenv_remove;
dbenv->base_path = NULL;
dbenv->mode = 0;
*dbenvp = dbenv;
return 0;
}
static int
db1_dbenv_close(DB_ENV *dbenv, u_int32_t flags) {
assert(flags == 0);
if (dbenv->base_path != NULL)
free(dbenv->base_path);
free(dbenv);
return 0;
}
static int
db1_dbenv_open(DB_ENV *dbenv, char *base_path, u_int32_t flags, int mode) {
/* We ignore flags on purpose */
dbenv->base_path = strdup(base_path);
if (dbenv->base_path == NULL)
return ENOSPC;
dbenv->mode = mode != 0? mode : 0660;
return 0;
}
static int
db1_dbenv_remove(DB_ENV *dbenv_fake, char *base_path, u_int32_t flags) {
/* dbenv_fake is not a useful environment */
/* XXX check if we have to remove files here */
return 0;
}
/*
* DB emulation
*/
static int db1_db_close(DB *, u_int32_t);
static int db1_db_open(DB *, const char *, const char *, DBTYPE, u_int32_t, int);
static int db1_db_sync(DB *, u_int32_t);
static int db1_db_get(DB *, DB_TXN *, DBT *, DBT *, u_int32_t);
static int db1_db_put(DB *, DB_TXN *, DBT *, DBT *, u_int32_t);
static int db1_db_del(DB *, DB_TXN *, DBT *, u_int32_t);
static int db1_db_set_flags(DB *, u_int32_t);
static int db1_db_set_pagesize(DB *, u_int32_t);
static int db1_db_set_re_delim(DB *, int);
static int db1_db_set_re_source(DB *, const char *);
static int db1_db_cursor(DB *, DB_TXN *, DBC **, u_int32_t);
int
db_create(DB **dbp, DB_ENV *dbenv, u_int32_t flags) {
assert(flags == 0);
*dbp = malloc(sizeof **dbp);
if (*dbp == NULL)
return -1;
(*dbp)->type = DB_UNKNOWN;
(*dbp)->actual_db = NULL;
(*dbp)->_pagesize = 0;
(*dbp)->_flags = 0;
memset(&(*dbp)->_recno_info, 0, sizeof (RECNOINFO));
(*dbp)->close = db1_db_close;
(*dbp)->open = db1_db_open;
(*dbp)->sync = db1_db_sync;
(*dbp)->get = db1_db_get;
(*dbp)->put = db1_db_put;
(*dbp)->del = db1_db_del;
(*dbp)->set_flags = db1_db_set_flags;
(*dbp)->set_pagesize = db1_db_set_pagesize;
(*dbp)->set_re_delim = db1_db_set_re_delim;
(*dbp)->set_re_source = db1_db_set_re_source;
(*dbp)->cursor = db1_db_cursor;
return 0;
}
const char *
db_strerror(int error) {
return error > 0? strerror(error) : "record not found";
}
static int
db1_db_close(DB *db, u_int32_t flags) {
if (flags & DB_NOSYNC) {
/* XXX warn user? */
}
db->actual_db->close(db->actual_db);
db->type = DB_UNKNOWN;
db->actual_db = NULL;
db->_pagesize = 0;
db->_flags = 0;
memset(&db->_recno_info, 0, sizeof (RECNOINFO));
return 0;
}
static int
db1_db_open(DB *db, const char *file, const char *database, DBTYPE type,
u_int32_t flags, int mode) {
int oldflags = 0;
assert(database == NULL && !(flags & ~(DB_CREATE | DB_TRUNCATE)));
db->type = type;
if (flags & DB_CREATE)
oldflags |= O_CREAT;
if (flags & DB_TRUNCATE)
oldflags |= O_TRUNC;
if (type == DB_RECNO) {
const char *tmp = file;
/* The interface is reversed in DB3 */
file = db->_recno_info.bfname;
db->_recno_info.bfname = __UNCONST(tmp);
/* ... and so, we should avoid to truncate the main file! */
oldflags &= ~O_TRUNC;
db->_recno_info.flags =
db->_flags & DB_SNAPSHOT? R_SNAPSHOT : 0;
db->_recno_info.psize = db->_pagesize;
}
db->actual_db = dbopen(file, oldflags, mode, type,
type == DB_RECNO? &db->_recno_info : NULL);
return db->actual_db == NULL? errno : 0;
}
static int
db1_db_sync(DB *db, u_int32_t flags) {
assert(flags == 0);
return db->actual_db->sync(db->actual_db, db->type == DB_UNKNOWN?
R_RECNOSYNC : 0) == 0? 0 : errno;
}
static int
db1_db_get(DB *db, DB_TXN *txnid, DBT *key, DBT *data, u_int32_t flags) {
int err;
DBT_v1 data1;
assert(flags == 0 && txnid == NULL);
err = db->actual_db->get(db->actual_db, (DBT_v1 *) key, &data1, flags);
if (err == 1)
return DB_NOTFOUND;
else if (err == -1)
return errno;
if (data->flags & DB_DBT_USERMEM) {
data->size = data1.size;
if (data1.size > data->ulen)
return DB_BUFFER_SMALL;
memcpy(data->data, data1.data, data1.size);
}
return 0;
}
static int
db1_db_put(DB *db, DB_TXN *txnid, DBT *key, DBT *data, u_int32_t flags) {
int err;
DB_old *db_v1 = db->actual_db;
DBT data1;
DBT key1;
recno_t recno = 1;
assert((flags & ~DB_APPEND) == 0 && txnid == NULL);
key1 = *key;
if (flags & DB_APPEND) {
if (db_v1->seq(db_v1, (DBT_v1 *)(void *)key,
(DBT_v1 *)(void *)&data1, R_LAST) == 1) {
key1.data = &recno;
key1.size = sizeof recno;
}
}
err = db_v1->put(db_v1, (DBT_v1 *)(void *)&key1, (DBT_v1 *)(void *)data,
0);
return err == -1? errno : err;
}
static int
db1_db_del(DB *db, DB_TXN *txnid, DBT *key, u_int32_t flags) {
int err;
DB_old *db_v1 = db->actual_db;
assert(txnid == NULL && flags == 0);
err = db_v1->del(db_v1, (DBT_v1 *) key, 0);
return err == -1? errno : err;
}
static int
db1_db_set_flags(DB *db, u_int32_t flags) {
assert((flags & ~(DB_RENUMBER | DB_SNAPSHOT)) == 0);
/* Can't prevent renumbering from happening with DB1 */
assert((flags | db->_flags) & DB_RENUMBER);
db->_flags |= flags;
return 0;
}
static int
db1_db_set_pagesize(DB *db, u_int32_t pagesize) {
db->_pagesize = pagesize;
return 0;
}
static int
db1_db_set_re_delim(DB *db, int re_delim) {
db->_recno_info.bval = re_delim;
return 0;
}
static int
db1_db_set_re_source(DB *db, const char *re_source) {
db->_recno_info.bfname = __UNCONST(re_source);
return 0;
}
/* DBC emulation. Very basic, only one cursor at a time, enough for vi */
static int db1_dbc_close(DBC *);
static int db1_dbc_get(DBC *, DBT *, DBT *, u_int32_t);
static int db1_dbc_put(DBC *, DBT *, DBT *, u_int32_t);
static int
db1_db_cursor(DB *db, DB_TXN *txn, DBC **cursorp, u_int32_t flags) {
DBC *cursor;
assert(txn == NULL && flags == 0);
cursor = malloc(sizeof *cursor);
if (cursor == NULL)
return -1;
cursor->db = db;
cursor->pos_key.data = &cursor->pos;
cursor->pos_key.size = sizeof cursor->pos;
cursor->c_close = db1_dbc_close;
cursor->c_get = db1_dbc_get;
cursor->c_put = db1_dbc_put;
*cursorp = cursor;
return 0;
}
static int
db1_dbc_close(DBC *cursor) {
free(cursor);
return 0;
}
static int
db1_dbc_get(DBC *cursor, DBT *key, DBT *data, u_int32_t flags) {
DB *db = cursor->db;
DB_old *db_v1 = db->actual_db;
int ret = 0;
switch(flags) {
case DB_SET:
ret = db_v1->seq(db_v1, (DBT_v1 *) key, (DBT_v1 *) data,
R_CURSOR);
cursor->pos = * (db_recno_t *) key->data;
break;
case DB_FIRST:
ret = db_v1->seq(db_v1, (DBT_v1 *) key, (DBT_v1 *) data,
R_FIRST);
if (ret == 1)
ret = DB_NOTFOUND;
cursor->pos = * (db_recno_t *) key->data;
break;
case DB_LAST:
ret = db_v1->seq(db_v1, (DBT_v1 *) key, (DBT_v1 *) data,
R_LAST);
if (ret == 1)
ret = DB_NOTFOUND;
cursor->pos = * (db_recno_t *) key->data;
break;
default:
abort();
}
return ret;
}
static int
db1_dbc_put(DBC *cursor, DBT *key, DBT *data, u_int32_t flags) {
DB *db = cursor->db;
DB_old *db_v1 = db->actual_db;
int ret = 0;
assert((flags & ~(DB_BEFORE | DB_AFTER)) == 0);
ret = db_v1->put(db_v1, &cursor->pos_key, (DBT_v1 *) data,
flags == DB_BEFORE? R_IBEFORE : R_IAFTER);
return ret == -1? errno : ret;
}

3
dist/nvi/common/dbinternal.h vendored Normal file
View File

@@ -0,0 +1,3 @@
int db_env_create(DB_ENV **, u_int32_t);
int db_create(DB **, DB_ENV *, u_int32_t);
const char *db_strerror(int);

160
dist/nvi/common/delete.c vendored Normal file
View File

@@ -0,0 +1,160 @@
/* $NetBSD: delete.c,v 1.6 2012/01/27 16:41:22 christos Exp $ */
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1992, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: delete.c,v 10.17 2001/06/25 15:19:09 skimo Exp (Berkeley) Date: 2001/06/25 15:19:09";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <bitstring.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
/*
* del --
* Delete a range of text.
*
* PUBLIC: int del __P((SCR *, MARK *, MARK *, int));
*/
int
del(SCR *sp, MARK *fm, MARK *tm, int lmode)
{
db_recno_t lno;
size_t blen, len, nlen, tlen;
CHAR_T *bp, *p;
int eof, rval;
bp = NULL;
/* Case 1 -- delete in line mode. */
if (lmode) {
for (lno = tm->lno; lno >= fm->lno; --lno) {
if (db_delete(sp, lno))
return (1);
++sp->rptlines[L_DELETED];
if (lno % INTERRUPT_CHECK == 0 && INTERRUPTED(sp))
break;
}
goto done;
}
/*
* 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 (db_last(sp, &lno))
return (1);
if (tm->lno >= lno) {
if (tm->lno == lno) {
if (db_get(sp, lno, DBG_FATAL, &p, &len))
return (1);
eof = tm->cno != ENTIRE_LINE && tm->cno >= len ? 1 : 0;
} else
eof = 1;
if (eof) {
for (lno = tm->lno; lno > fm->lno; --lno) {
if (db_delete(sp, lno))
return (1);
++sp->rptlines[L_DELETED];
if (lno %
INTERRUPT_CHECK == 0 && INTERRUPTED(sp))
break;
}
if (db_get(sp, fm->lno, DBG_FATAL, &p, &len))
return (1);
GET_SPACE_RETW(sp, bp, blen, fm->cno);
MEMCPYW(bp, p, fm->cno);
if (db_set(sp, fm->lno, bp, fm->cno))
return (1);
goto done;
}
}
/* Case 3 -- delete within a single line. */
if (tm->lno == fm->lno) {
if (db_get(sp, fm->lno, DBG_FATAL, &p, &len))
return (1);
GET_SPACE_RETW(sp, bp, blen, len);
if (fm->cno != 0)
MEMCPYW(bp, p, fm->cno);
MEMCPYW(bp + fm->cno, p + (tm->cno + 1),
len - (tm->cno + 1));
if (db_set(sp, 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 (db_get(sp, fm->lno, DBG_FATAL, &p, NULL))
return (1);
GET_SPACE_RETW(sp, bp, blen, tlen + 256);
MEMCPYW(bp, p, tlen);
}
/* Copy the end partial line into place. */
if (db_get(sp, tm->lno, DBG_FATAL, &p, &len))
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, "002|Line length overflow");
goto err;
}
if (tlen == 0) {
GET_SPACE_RETW(sp, bp, blen, nlen);
} else
ADD_SPACE_RETW(sp, bp, blen, nlen);
MEMCPYW(bp + tlen, p + (tm->cno + 1), len - (tm->cno + 1));
tlen += len - (tm->cno + 1);
}
/* Set the current line. */
if (db_set(sp, fm->lno, bp, tlen))
goto err;
/* Delete the last and intermediate lines. */
for (lno = tm->lno; lno > fm->lno; --lno) {
if (db_delete(sp, lno))
goto err;
++sp->rptlines[L_DELETED];
if (lno % INTERRUPT_CHECK == 0 && INTERRUPTED(sp))
break;
}
done: rval = 0;
if (0)
err: rval = 1;
if (bp != NULL)
FREE_SPACEW(sp, bp, blen);
return (rval);
}

47
dist/nvi/common/dldb.c vendored Normal file
View File

@@ -0,0 +1,47 @@
/* $NetBSD: dldb.c,v 1.1.1.2 2008/05/18 14:29:41 aymeric Exp $ */
#include "config.h"
#include <dlfcn.h>
#include "common.h"
#include "pathnames.h"
static void relocate __P(());
#define RELOC(func,returntype,args,proto,types) \
static returntype reloc_##func __P(proto); \
returntype (*nvi_##func) __P(proto) = reloc_##func; \
static returntype reloc_##func args \
types \
{ \
relocate(); \
return nvi_##func args; \
}
RELOC(db_create,int,(a,b,c),(DB **, DB_ENV *, u_int32_t),
DB**a;DB_ENV*b;u_int32_t c;)
RELOC(db_env_create,int,(a,b),(DB_ENV **, u_int32_t),DB_ENV ** a;u_int32_t b;);
RELOC(db_strerror,char *,(a),(int),int a;)
#define LOADSYM(func) \
if ((nvi_##func = dlsym(handle, #func)) == NULL) \
goto error;
static void
relocate()
{
void *handle = dlopen(_PATH_DB3, RTLD_LAZY);
if (!handle)
goto error;
LOADSYM(db_create)
LOADSYM(db_env_create)
LOADSYM(db_strerror)
return;
error:
fprintf(stderr, "Relocation error: %s\n", dlerror());
abort();
}

1650
dist/nvi/common/exf.c vendored Normal file

File diff suppressed because it is too large Load Diff

92
dist/nvi/common/exf.h vendored Normal file
View File

@@ -0,0 +1,92 @@
/* $NetBSD: exf.h,v 1.1.1.2 2008/05/18 14:29:45 aymeric Exp $ */
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1992, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*
* Id: exf.h,v 10.19 2002/03/02 23:36:23 skimo Exp (Berkeley) Date: 2002/03/02 23:36:23
*/
/* Undo direction. */
/*
* exf --
* The file structure.
*/
struct _exf {
CIRCLEQ_ENTRY(_exf) q; /* Linked list of file structures. */
int refcnt; /* Reference count. */
CIRCLEQ_HEAD(_escrh, _scr) scrq; /* Attached screens */
/* Underlying database state. */
DB_ENV *env; /* The DB environment. */
char *env_path; /* DB environment directory. */
DB *db; /* File db structure. */
db_recno_t c_nlines; /* Cached lines in the file. */
DB *log; /* Log db structure. */
db_recno_t l_high; /* Log last + 1 record number. */
db_recno_t l_cur; /* Log current record number. */
#ifdef USE_DB4_LOGGING
DB_LSN lsn_first;
DB_LSN lsn_high; /* LSN of last record. */
DB_LSN lsn_cur; /* LSN of first record to undo. */
#endif
MARK l_cursor; /* Log cursor position. */
dir_t lundo; /* Last undo direction. */
WIN *l_win; /* Window owning transaction. */
LIST_HEAD(_markh, _lmark) marks;/* Linked list of file MARK's. */
/*
* XXX
* Mtime should be a struct timespec, but time_t is more portable.
*/
dev_t mdev; /* Device. */
ino_t minode; /* Inode. */
time_t mtime; /* Last modification time. */
int fcntl_fd; /* Fcntl locking fd; see exf.c. */
int fd; /* File descriptor */
/*
* 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. */
void *lock; /* Lock for log. */
#define F_DEVSET 0x001 /* mdev/minode fields initialized. */
#define F_FIRSTMODIFY 0x002 /* File not yet modified. */
#define F_MODIFIED 0x004 /* File is currently dirty. */
#define F_MULTILOCK 0x008 /* Multiple processes running, lock. */
#define F_NOLOG 0x010 /* Logging turned off. */
#define F_RCV_NORM 0x020 /* Don't delete recovery files. */
#define F_RCV_ON 0x040 /* Recovery is possible. */
#define F_UNDO 0x080 /* No change since last undo. */
u_int8_t flags;
};
/* Flags to db_get(). */
#define DBG_FATAL 0x001 /* If DNE, error message. */
#define DBG_NOCACHE 0x002 /* Ignore the front-end cache. */
/* Flags to file_init() and file_write(). */
#define FS_ALL 0x001 /* Write the entire file. */
#define FS_APPEND 0x002 /* Append to the file. */
#define FS_FORCE 0x004 /* Force is set. */
#define FS_OPENERR 0x008 /* Open failed, try it again. */
#define FS_POSSIBLE 0x010 /* Force could have been set. */
#define FS_SETALT 0x020 /* Set alternate file name. */
/* Flags to rcv_sync(). */
#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. */

218
dist/nvi/common/extern.h vendored Normal file
View File

@@ -0,0 +1,218 @@
/* $NetBSD: extern.h,v 1.5 2011/11/23 19:25:28 tnozaki Exp $ */
/* Do not edit: automatically built by build/distrib. */
SCR *api_fscreen __P((int, char *));
int api_aline __P((SCR *, db_recno_t, char *, size_t));
int api_extend __P((SCR *, db_recno_t));
int api_dline __P((SCR *, db_recno_t));
int api_gline __P((SCR *, db_recno_t, CHAR_T **, size_t *));
int api_iline __P((SCR *, db_recno_t, CHAR_T *, size_t));
int api_lline __P((SCR *, db_recno_t *));
int api_sline __P((SCR *, db_recno_t, CHAR_T *, size_t));
int api_getmark __P((SCR *, int, MARK *));
int api_setmark __P((SCR *, int, MARK *));
int api_nextmark __P((SCR *, int, char *));
int api_getcursor __P((SCR *, MARK *));
int api_setcursor __P((SCR *, MARK *));
void api_emessage __P((SCR *, char *));
void api_imessage __P((SCR *, char *));
int api_edit __P((SCR *, char *, SCR **, int));
int api_escreen __P((SCR *));
int api_swscreen __P((SCR *, SCR *));
int api_map __P((SCR *, char *, char *, size_t));
int api_unmap __P((SCR *, char *));
int api_opts_get __P((SCR *, const CHAR_T *, char **, int *));
int api_opts_set __P((SCR *, const CHAR_T *, const char *, u_long, int));
int api_run_str __P((SCR *, char *));
TAGQ * api_tagq_new __P((SCR*, char*));
void api_tagq_add __P((SCR*, TAGQ*, char*, char *, char *));
int api_tagq_push __P((SCR*, TAGQ**));
void api_tagq_free __P((SCR*, TAGQ*));
int cut __P((SCR *, ARG_CHAR_T *, MARK *, MARK *, int));
int cut_line __P((SCR *, db_recno_t, size_t, size_t, CB *));
void cut_close __P((WIN *));
TEXT *text_init __P((SCR *, const CHAR_T *, size_t, size_t));
void text_lfree __P((TEXTH *));
void text_free __P((TEXT *));
int db_eget __P((SCR *, db_recno_t, CHAR_T **, size_t *, int *));
int db_get __P((SCR *, db_recno_t, u_int32_t, CHAR_T **, size_t *));
int db_delete __P((SCR *, db_recno_t));
int db_append __P((SCR *, int, db_recno_t, const CHAR_T *, size_t));
int db_insert __P((SCR *, db_recno_t, CHAR_T *, size_t));
int db_set __P((SCR *, db_recno_t, CHAR_T *, size_t));
int db_exist __P((SCR *, db_recno_t));
int db_last __P((SCR *, db_recno_t *));
void db_err __P((SCR *, db_recno_t));
int scr_update __P((SCR *sp, db_recno_t lno,
lnop_t op, int current));
void update_cache __P((SCR *sp, lnop_t op, db_recno_t lno));
int line_insdel __P((SCR *sp, lnop_t op, db_recno_t lno));
int del __P((SCR *, MARK *, MARK *, int));
FREF *file_add __P((SCR *, const char *));
int file_init __P((SCR *, FREF *, char *, int));
int file_end __P((SCR *, EXF *, int));
int file_write __P((SCR *, MARK *, MARK *, char *, int));
int file_m1 __P((SCR *, int, int));
int file_m2 __P((SCR *, int));
int file_m3 __P((SCR *, int));
int file_aw __P((SCR *, int));
void set_alt_name __P((SCR *, const char *));
lockr_t file_lock __P((SCR *, char *, int *, int, int));
GS * gs_init __P((char*));
WIN * gs_new_win __P((GS *gp));
int win_end __P((WIN *wp));
void gs_end __P((GS *));
int v_key_init __P((SCR *));
void v_key_ilookup __P((SCR *));
size_t v_key_len __P((SCR *, ARG_CHAR_T));
u_char *v_key_name __P((SCR *, ARG_CHAR_T));
e_key_t v_key_val __P((SCR *, ARG_CHAR_T));
int v_event_push __P((SCR *, EVENT *, const CHAR_T *, size_t, u_int));
int v_event_get __P((SCR *, EVENT *, int, u_int32_t));
void v_event_err __P((SCR *, EVENT *));
int v_event_flush __P((SCR *, u_int));
int log_init __P((SCR *, EXF *));
int log_end __P((SCR *, EXF *));
int log_cursor __P((SCR *));
int log_line __P((SCR *, db_recno_t, u_int));
int log_mark __P((SCR *, LMARK *));
int log_backward __P((SCR *, MARK *));
int log_setline __P((SCR *));
int log_forward __P((SCR *, MARK *));
int log_init __P((SCR *, EXF *));
int log_end __P((SCR *, EXF *));
int log_cursor __P((SCR *));
int log_line __P((SCR *, db_recno_t, u_int));
int log_mark __P((SCR *, LMARK *));
int log_backward __P((SCR *, MARK *));
int log_setline __P((SCR *));
int log_forward __P((SCR *, MARK *));
int editor __P((WIN *, int, char *[]));
int mark_init __P((SCR *, EXF *));
int mark_end __P((SCR *, EXF *));
int mark_get __P((SCR *, ARG_CHAR_T, MARK *, mtype_t));
int mark_set __P((SCR *, ARG_CHAR_T, MARK *, int));
int mark_insdel __P((SCR *, lnop_t, db_recno_t));
void msgq __P((SCR *, mtype_t, const char *, ...))
__attribute__((__format__(__printf__, 3, 4)));
void msgq_wstr __P((SCR *, mtype_t, const CHAR_T *, const char *));
void msgq_str __P((SCR *, mtype_t, const char *, const char *));
void mod_rpt __P((SCR *));
void msgq_status __P((SCR *, db_recno_t, u_int));
int msg_open __P((SCR *, const char *));
void msg_close __P((GS *));
const char *msg_cmsg __P((SCR *, cmsg_t, size_t *));
const char *msg_cat __P((SCR *, const char *, size_t *));
char *msg_print __P((SCR *, const char *, int *));
void thread_init __P((GS *gp));
int opts_init __P((SCR *, int *));
int opts_set __P((SCR *, ARGS *[], const char *));
int o_set __P((SCR *, int, u_int, const char *, u_long));
int opts_empty __P((SCR *, int, int));
void opts_dump __P((SCR *, enum optdisp));
int opts_save __P((SCR *, FILE *));
OPTLIST const *opts_search __P((const CHAR_T *));
void opts_nomatch __P((SCR *, const CHAR_T *));
int opts_copy __P((SCR *, SCR *));
void opts_free __P((SCR *));
int f_altwerase __P((SCR *, OPTION *, const char *, u_long *));
int f_columns __P((SCR *, OPTION *, const char *, u_long *));
int f_lines __P((SCR *, OPTION *, const char *, u_long *));
int f_lisp __P((SCR *, OPTION *, const char *, u_long *));
int f_msgcat __P((SCR *, OPTION *, const char *, u_long *));
int f_paragraph __P((SCR *, OPTION *, const char *, u_long *));
int f_print __P((SCR *, OPTION *, const char *, u_long *));
int f_readonly __P((SCR *, OPTION *, const char *, u_long *));
int f_recompile __P((SCR *, OPTION *, const char *, u_long *));
int f_reformat __P((SCR *, OPTION *, const char *, u_long *));
int f_section __P((SCR *, OPTION *, const char *, u_long *));
int f_ttywerase __P((SCR *, OPTION *, const char *, u_long *));
int f_w300 __P((SCR *, OPTION *, const char *, u_long *));
int f_w1200 __P((SCR *, OPTION *, const char *, u_long *));
int f_w9600 __P((SCR *, OPTION *, const char *, u_long *));
int f_window __P((SCR *, OPTION *, const char *, u_long *));
int f_encoding __P((SCR *, OPTION *, const char *, u_long *));
void thread_init __P((GS *gp));
int put __P((SCR *, CB *, ARG_CHAR_T *, MARK *, MARK *, int));
int rcv_tmp __P((SCR *, EXF *, char *));
int rcv_init __P((SCR *));
int rcv_sync __P((SCR *, u_int));
int rcv_list __P((SCR *));
int rcv_read __P((SCR *, FREF *));
int screen_init __P((GS *, SCR *, SCR **));
int screen_end __P((SCR *));
SCR *screen_next __P((SCR *));
int f_search __P((SCR *,
MARK *, MARK *, CHAR_T *, size_t, CHAR_T **, u_int));
int b_search __P((SCR *,
MARK *, MARK *, CHAR_T *, size_t, CHAR_T **, u_int));
void search_busy __P((SCR *, busy_t));
int seq_set __P((SCR *, CHAR_T *,
size_t, CHAR_T *, size_t, CHAR_T *, size_t, seq_t, int));
int seq_delete __P((SCR *, CHAR_T *, size_t, seq_t));
int seq_mdel __P((SEQ *));
SEQ *seq_find
__P((SCR *, SEQ **, EVENT *, CHAR_T *, size_t, seq_t, int *));
void seq_close __P((GS *));
int seq_dump __P((SCR *, seq_t, int));
int seq_save __P((SCR *, FILE *, const char *, seq_t));
int e_memcmp __P((CHAR_T *, EVENT *, size_t));
void vtrace_end __P((void));
void vtrace_init __P((char *));
void vtrace __P((const char *, ...))
__attribute__((__format__(__printf__, 1, 2)));
void *binc __P((SCR *, void *, size_t *, size_t));
int nonblank __P((SCR *, db_recno_t, size_t *));
const char *tail __P((const char *));
char *v_strdup __P((SCR *, const char *, size_t));
CHAR_T *v_wstrdup __P((SCR *, const CHAR_T *, size_t));
enum nresult nget_uslong __P((SCR *, u_long *, const CHAR_T *, CHAR_T **, int));
enum nresult nget_slong __P((SCR *, long *, const CHAR_T *, CHAR_T **, int));
#ifdef USE_DB4_LOGGING
int __vi_marker_log __P((DB_ENV *, DB_TXN *, DB_LSN *, u_int32_t,
u_int32_t));
int __vi_marker_getpgnos __P((DB_ENV *, DBT *, DB_LSN *, db_recops,
void *));
int __vi_marker_print __P((DB_ENV *, DBT *, DB_LSN *, db_recops,
void *));
int __vi_marker_read __P((DB_ENV *, void *, __vi_marker_args **));
int __vi_cursor_log __P((DB_ENV *, DB_TXN *, DB_LSN *, u_int32_t,
u_int32_t, db_recno_t, size_t));
int __vi_cursor_getpgnos __P((DB_ENV *, DBT *, DB_LSN *, db_recops,
void *));
int __vi_cursor_print __P((DB_ENV *, DBT *, DB_LSN *, db_recops,
void *));
int __vi_cursor_read __P((DB_ENV *, void *, __vi_cursor_args **));
int __vi_mark_log __P((DB_ENV *, DB_TXN *, DB_LSN *, u_int32_t,
LMARK *));
int __vi_mark_getpgnos __P((DB_ENV *, DBT *, DB_LSN *, db_recops,
void *));
int __vi_mark_print __P((DB_ENV *, DBT *, DB_LSN *, db_recops,
void *));
int __vi_mark_read __P((DB_ENV *, void *, __vi_mark_args **));
int __vi_change_log __P((DB_ENV *, DB_TXN *, DB_LSN *, u_int32_t,
u_int32_t, db_recno_t));
int __vi_change_getpgnos __P((DB_ENV *, DBT *, DB_LSN *, db_recops,
void *));
int __vi_change_print __P((DB_ENV *, DBT *, DB_LSN *, db_recops,
void *));
int __vi_change_read __P((DB_ENV *, void *, __vi_change_args **));
int __vi_init_print __P((DB_ENV *, int (***)(DB_ENV *, DBT *,
DB_LSN *, db_recops, void *), size_t *));
int __vi_init_getpgnos __P((DB_ENV *, int (***)(DB_ENV *, DBT *,
DB_LSN *, db_recops, void *), size_t *));
int __vi_init_recover __P((DB_ENV *));
#endif
#ifdef USE_DB4_LOGGING
int __vi_marker_recover
__P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
int __vi_cursor_recover
__P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
int __vi_mark_recover
__P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
int __vi_change_recover
__P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
int __vi_log_truncate __P((EXF *ep));
int __vi_log_dispatch __P((DB_ENV *dbenv, DBT *data, DB_LSN *lsn, db_recops ops));
int __vi_log_traverse __P((SCR *sp, undo_t undo, MARK *));
#endif

218
dist/nvi/common/gs.c vendored Normal file
View File

@@ -0,0 +1,218 @@
/* $NetBSD: gs.c,v 1.2 2011/09/16 16:13:41 joerg Exp $ */
/*-
* Copyright (c) 2000
* Sven Verdoolaege. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#include <sys/types.h>
#include <sys/queue.h>
#include <bitstring.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "../common/common.h"
#include "../perl_api/extern.h"
__dead static void perr __P((char *, char *));
/*
* gs_init --
* Create and partially initialize the GS structure.
* PUBLIC: GS * gs_init __P((char*));
*/
GS *
gs_init(char *name)
{
GS *gp;
char *p;
/* Figure out what our name is. */
if ((p = strrchr(name, '/')) != NULL)
name = p + 1;
/* Allocate the global structure. */
CALLOC_NOMSG(NULL, gp, GS *, 1, sizeof(GS));
if (gp == NULL)
perr(name, NULL);
gp->progname = name;
/* Common global structure initialization. */
/* others will need to be copied from main.c */
CIRCLEQ_INIT(&gp->dq);
CIRCLEQ_INIT(&gp->hq);
gp->noprint = DEFAULT_NOPRINT;
/* Structures shared by screens so stored in the GS structure. */
CIRCLEQ_INIT(&gp->frefq);
CIRCLEQ_INIT(&gp->exfq);
LIST_INIT(&gp->seqq);
thread_init(gp);
return (gp);
}
/*
* gs_new_win
* Create new window
* PUBLIC: WIN * gs_new_win __P((GS *gp));
*/
WIN *
gs_new_win(GS *gp)
{
WIN *wp;
CALLOC_NOMSG(NULL, wp, WIN *, 1, sizeof(*wp));
if (!wp)
return NULL;
/* Common global structure initialization. */
LIST_INIT(&wp->ecq);
LIST_INSERT_HEAD(&wp->ecq, &wp->excmd, q);
CIRCLEQ_INSERT_TAIL(&gp->dq, wp, q);
CIRCLEQ_INIT(&wp->scrq);
CIRCLEQ_INIT(&wp->dcb_store.textq);
LIST_INIT(&wp->cutq);
wp->gp = gp;
return wp;
}
/*
* win_end --
* Remove window.
*
* PUBLIC: int win_end __P((WIN *wp));
*/
int
win_end(WIN *wp)
{
SCR *sp;
CIRCLEQ_REMOVE(&wp->gp->dq, wp, q);
if (wp->ccl_sp != NULL) {
(void)file_end(wp->ccl_sp, NULL, 1);
(void)screen_end(wp->ccl_sp);
}
while ((sp = wp->scrq.cqh_first) != (void *)&wp->scrq)
(void)screen_end(sp);
/* Free key input queue. */
if (wp->i_event != NULL)
free(wp->i_event);
/* Free cut buffers. */
cut_close(wp);
/* Free default buffer storage. */
(void)text_lfree(&wp->dcb_store.textq);
#if defined(DEBUG) || defined(PURIFY) || defined(LIBRARY)
/* Free any temporary space. */
if (wp->tmp_bp != NULL)
free(wp->tmp_bp);
#endif
return 0;
}
/*
* gs_end --
* End the program, discarding screens and most of the global area.
*
* PUBLIC: void gs_end __P((GS *));
*/
void
gs_end(GS *gp)
{
MSGS *mp;
SCR *sp;
WIN *wp;
/* If there are any remaining screens, kill them off. */
while ((wp = gp->dq.cqh_first) != (void *)&gp->dq)
(void)win_end(wp);
while ((sp = gp->hq.cqh_first) != (void *)&gp->hq)
(void)screen_end(sp);
#ifdef HAVE_PERL_INTERP
perl_end(gp);
#endif
#if defined(DEBUG) || defined(PURIFY) || defined(LIBRARY)
{ FREF *frp;
/* Free FREF's. */
while ((frp = gp->frefq.cqh_first) != (FREF *)&gp->frefq) {
CIRCLEQ_REMOVE(&gp->frefq, frp, q);
if (frp->name != NULL)
free(frp->name);
if (frp->tname != NULL)
free(frp->tname);
free(frp);
}
}
/* Free map sequences. */
seq_close(gp);
/* Close message catalogs. */
msg_close(gp);
#endif
/* Ring the bell if scheduled. */
if (F_ISSET(gp, G_BELLSCHED))
(void)fprintf(stderr, "\07"); /* \a */
/*
* Flush any remaining messages. If a message is here, it's almost
* certainly the message about the event that killed us (although
* it's possible that the user is sourcing a file that exits from the
* editor).
*/
while ((mp = gp->msgq.lh_first) != NULL) {
(void)fprintf(stderr, "%s%.*s",
mp->mtype == M_ERR ? "ex/vi: " : "", (int)mp->len, mp->buf);
LIST_REMOVE(mp, q);
#if defined(DEBUG) || defined(PURIFY) || defined(LIBRARY)
free(mp->buf);
free(mp);
#endif
}
#if defined(TRACE)
/* Close tracing file descriptor. */
vtrace_end();
#endif
}
/*
* perr --
* Print system error.
*/
static void
perr(char *name, char *msg)
{
(void)fprintf(stderr, "%s:", name);
if (msg != NULL)
(void)fprintf(stderr, "%s:", msg);
(void)fprintf(stderr, "%s\n", strerror(errno));
exit(1);
}

212
dist/nvi/common/gs.h vendored Normal file
View File

@@ -0,0 +1,212 @@
/* $NetBSD: gs.h,v 1.5 2011/03/21 14:53:02 tnozaki Exp $ */
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*
* Id: gs.h,v 10.55 2001/11/01 10:28:25 skimo Exp (Berkeley) Date: 2001/11/01 10:28:25
*/
#define TEMPORARY_FILE_STRING "/tmp" /* Default temporary file name. */
/*
* File reference structure (FREF). 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.
*/
struct _fref {
CIRCLEQ_ENTRY(_fref) q; /* Linked list of file references. */
char *name; /* File name. */
char *tname; /* Backing temporary file name. */
db_recno_t lno; /* 1-N: file cursor line. */
size_t cno; /* 0-N: file cursor column. */
#define FR_CURSORSET 0x0001 /* If lno/cno values valid. */
#define FR_DONTDELETE 0x0002 /* Don't delete the temporary file. */
#define FR_EXNAMED 0x0004 /* Read/write renamed the file. */
#define FR_NAMECHANGE 0x0008 /* If the name changed. */
#define FR_NEWFILE 0x0010 /* File doesn't really exist yet. */
#define FR_RECOVER 0x0020 /* File is being recovered. */
#define FR_TMPEXIT 0x0040 /* Modified temporary file, no exit. */
#define FR_TMPFILE 0x0080 /* If file has no name. */
#define FR_UNLOCKED 0x0100 /* File couldn't be locked. */
u_int16_t flags;
};
/* Action arguments to scr_exadjust(). */
typedef enum { EX_TERM_CE, EX_TERM_SCROLL } exadj_t;
/* Screen attribute arguments to scr_attr(). */
typedef enum { SA_ALTERNATE, SA_INVERSE } scr_attr_t;
/* Key type arguments to scr_keyval(). */
typedef enum { KEY_VEOF, KEY_VERASE, KEY_VKILL, KEY_VWERASE } scr_keyval_t;
/*
* GS:
*
* Structure that describes global state of the running program.
*/
struct _gs {
char *progname; /* Programe name. */
int id; /* Last allocated screen id. */
CIRCLEQ_HEAD(_dqh, _win) dq; /* Displayed windows. */
CIRCLEQ_HEAD(_hqh, _scr) hq; /* Hidden screens. */
void *perl_interp; /* Perl interpreter. */
void *tcl_interp; /* Tcl_Interp *: Tcl interpreter. */
void *cl_private; /* Curses support private area. */
void *tk_private; /* Tk/Tcl support private area. */
/* File references. */
CIRCLEQ_HEAD(_frefh, _fref) frefq;
/* File structures. */
CIRCLEQ_HEAD(_exfh, _exf) exfq;
#define GO_COLUMNS 0 /* Global options: columns. */
#define GO_LINES 1 /* Global options: lines. */
#define GO_SECURE 2 /* Global options: secure. */
#define GO_TERM 3 /* Global options: terminal type. */
OPTION opts[GO_TERM + 1];
DB *msg; /* Message catalog DB. */
MSGH msgq; /* User message list. */
#define DEFAULT_NOPRINT '\1' /* Emergency non-printable character. */
int noprint; /* Cached, unprintable character. */
char *c_option; /* Ex initial, command-line command. */
#ifdef DEBUG
FILE *tracefp; /* Trace file pointer. */
#endif
#define MAX_BIT_SEQ 0x7f /* Max + 1 fast check character. */
LIST_HEAD(_seqh, _seq) seqq; /* Linked list of maps, abbrevs. */
bitstr_t bit_decl(seqb, MAX_BIT_SEQ + 1);
#define MAX_FAST_KEY 0xff /* Max fast check character.*/
#define KEY_LEN(sp, ch) \
(((ch) & ~MAX_FAST_KEY) == 0 ? \
sp->gp->cname[(unsigned char)ch].len : v_key_len(sp, ch))
#define KEY_NAME(sp, ch) \
(((ch) & ~MAX_FAST_KEY) == 0 ? \
sp->gp->cname[(unsigned char)ch].name : v_key_name(sp, ch))
struct {
u_char 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) == 0 ? \
sp->gp->special_key[(unsigned char)ch] : v_key_val(sp,ch))
e_key_t /* Fast lookup table. */
special_key[MAX_FAST_KEY + 1];
/* Flags. */
#define G_ABBREV 0x0001 /* If have abbreviations. */
#define G_BELLSCHED 0x0002 /* Bell scheduled. */
#define G_INTERRUPTED 0x0004 /* Interrupted. */
#define G_RECOVER_SET 0x0008 /* Recover system initialized. */
#define G_SCRIPTED 0x0010 /* Ex script session. */
#define G_SCRWIN 0x0020 /* Scripting windows running. */
#define G_SNAPSHOT 0x0040 /* Always snapshot files. */
#define G_SRESTART 0x0080 /* Screen restarted. */
u_int32_t flags;
/* Screen interface functions. */
/* Add a string to the screen. */
int (*scr_addstr) __P((SCR *, const char *, size_t));
/* Add a string to the screen. */
int (*scr_waddstr) __P((SCR *, const CHAR_T *, size_t));
/* Toggle a screen attribute. */
int (*scr_attr) __P((SCR *, scr_attr_t, int));
/* Terminal baud rate. */
int (*scr_baud) __P((SCR *, u_long *));
/* Beep/bell/flash the terminal. */
int (*scr_bell) __P((SCR *));
/* Display a busy message. */
void (*scr_busy) __P((SCR *, const char *, busy_t));
/* Prepare child. */
int (*scr_child) __P((SCR *));
/* Clear to the end of the line. */
int (*scr_clrtoeol) __P((SCR *));
/* Return the cursor location. */
int (*scr_cursor) __P((SCR *, size_t *, size_t *));
/* Delete a line. */
int (*scr_deleteln) __P((SCR *));
/* Discard a screen. */
int (*scr_discard) __P((SCR *, SCR **));
/* Get a keyboard event. */
int (*scr_event) __P((SCR *, EVENT *, u_int32_t, int));
/* Ex: screen adjustment routine. */
int (*scr_ex_adjust) __P((SCR *, exadj_t));
int (*scr_fmap) /* Set a function key. */
__P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t));
/* Get terminal key value. */
int (*scr_keyval) __P((SCR *, scr_keyval_t, CHAR_T *, int *));
/* Insert a line. */
int (*scr_insertln) __P((SCR *));
/* Handle an option change. */
int (*scr_optchange) __P((SCR *, int, const char *, u_long *));
/* Move the cursor. */
int (*scr_move) __P((SCR *, size_t, size_t));
/* Refresh the screen. */
int (*scr_refresh) __P((SCR *, int));
/* Rename the file. */
int (*scr_rename) __P((SCR *, char *, int));
/* Reply to an event. */
int (*scr_reply) __P((SCR *, int, char *));
/* Set the screen type. */
int (*scr_screen) __P((SCR *, u_int32_t));
/* Split the screen. */
int (*scr_split) __P((SCR *, SCR *));
/* Suspend the editor. */
int (*scr_suspend) __P((SCR *, int *));
/* Print usage message. */
void (*scr_usage) __P((void));
/* Threading stuff */
void *th_private;
int (*run) __P((WIN *, void *(*)(void*), void *));
int (*lock_init) __P((WIN *, void **));
#define LOCK_INIT(wp,s) \
wp->gp->lock_init(wp, &s->lock)
int (*lock_try) __P((WIN *, void **));
#define LOCK_TRY(wp,s) \
wp->gp->lock_try(wp, &s->lock)
int (*lock_unlock) __P((WIN *, void **));
#define LOCK_UNLOCK(wp,s) \
wp->gp->lock_unlock(wp, &s->lock)
int (*lock_end) __P((WIN *, void **));
#define LOCK_END(wp,s) \
wp->gp->lock_end(wp, &s->lock)
};
/*
* XXX
* Block signals if there are asynchronous events. Used to keep DB system calls
* from being interrupted and not restarted, as that will result in consistency
* problems. This should be handled by DB.
*/
#ifdef BLOCK_SIGNALS
#include <signal.h>
extern sigset_t __sigblockset;
#define SIGBLOCK \
(void)sigprocmask(SIG_BLOCK, &__sigblockset, NULL)
#define SIGUNBLOCK \
(void)sigprocmask(SIG_UNBLOCK, &__sigblockset, NULL);
#else
#define SIGBLOCK
#define SIGUNBLOCK
#endif

844
dist/nvi/common/key.c vendored Normal file
View File

@@ -0,0 +1,844 @@
/* $NetBSD: key.c,v 1.6 2011/03/21 14:53:02 tnozaki Exp $ */
/*-
* Copyright (c) 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1991, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: key.c,v 10.48 2001/06/25 15:19:10 skimo Exp (Berkeley) Date: 2001/06/25 15:19:10";
#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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "common.h"
#include "../vi/vi.h"
static int v_event_append __P((SCR *, EVENT *));
static int v_event_grow __P((SCR *, int));
static int v_key_cmp __P((const void *, const void *));
static void v_keyval __P((SCR *, int, scr_keyval_t));
static void v_sync __P((SCR *, 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.
*
* !!!
* This means that all screens share a special key set.
*/
KEYLIST keylist[] = {
{K_BACKSLASH, '\\'}, /* \ */
{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 */
#define ADDITIONAL_CHARACTERS 4
{K_NOTUSED, 0}, /* VEOF, VERASE, VKILL, VWERASE */
{K_NOTUSED, 0},
{K_NOTUSED, 0},
{K_NOTUSED, 0},
};
static int nkeylist =
(sizeof(keylist) / sizeof(keylist[0])) - ADDITIONAL_CHARACTERS;
/*
* v_key_init --
* Initialize the special key lookup table.
*
* PUBLIC: int v_key_init __P((SCR *));
*/
int
v_key_init(SCR *sp)
{
int ch;
GS *gp;
KEYLIST *kp;
int cnt;
gp = sp->gp;
/*
* 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, "");
#if __linux__
/*
* In libc 4.5.26, setlocale(LC_ALL, ""), doesn't setup the table
* for ctype(3c) correctly. This bug is fixed in libc 4.6.x.
*
* This code works around this problem for libc 4.5.x users.
* Note that this code is harmless if you're using libc 4.6.x.
*/
(void)setlocale(LC_CTYPE, "");
#endif
v_key_ilookup(sp);
v_keyval(sp, K_CNTRLD, KEY_VEOF);
v_keyval(sp, K_VERASE, KEY_VERASE);
v_keyval(sp, K_VKILL, KEY_VKILL);
v_keyval(sp, K_VWERASE, KEY_VWERASE);
/* Sort the special key list. */
qsort(keylist, nkeylist, sizeof(keylist[0]), v_key_cmp);
/* Initialize the fast lookup table. */
for (kp = keylist, cnt = nkeylist; cnt--; ++kp)
gp->special_key[kp->ch] = kp->value;
/* Find a non-printable character to use as a message separator. */
for (ch = 1; ch <= UCHAR_MAX; ++ch)
if (!isprint(ch)) {
gp->noprint = ch;
break;
}
if (ch != gp->noprint) {
msgq(sp, M_ERR, "079|No non-printable character found");
return (1);
}
return (0);
}
/*
* v_keyval --
* Set key values.
*
* We've left some open slots in the keylist table, and if these values exist,
* we put them into place. Note, they may reset (or duplicate) values already
* in the table, so we check for that first.
*/
static void
v_keyval(SCR *sp, int val, scr_keyval_t name)
{
KEYLIST *kp;
CHAR_T ch;
int dne;
/* Get the key's value from the screen. */
if (sp->gp->scr_keyval(sp, name, &ch, &dne))
return;
if (dne)
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;
}
}
/*
* v_key_ilookup --
* Build the fast-lookup key display array.
*
* PUBLIC: void v_key_ilookup __P((SCR *));
*/
void
v_key_ilookup(SCR *sp)
{
UCHAR_T ch;
unsigned char *p, *t;
GS *gp;
size_t len;
for (gp = sp->gp, ch = 0;; ++ch) {
for (p = gp->cname[ch].name, t = v_key_name(sp, ch),
len = gp->cname[ch].len = sp->clen; len--;)
*p++ = *t++;
if (ch == MAX_FAST_KEY)
break;
}
}
/*
* v_key_len --
* Return the length of the string that will display the key.
* This routine is the backup for the KEY_LEN() macro.
*
* PUBLIC: size_t v_key_len __P((SCR *, ARG_CHAR_T));
*/
size_t
v_key_len(SCR *sp, ARG_CHAR_T ch)
{
(void)v_key_name(sp, ch);
return (sp->clen);
}
/*
* v_key_name --
* Return the string that will display the key. This routine
* is the backup for the KEY_NAME() macro.
*
* PUBLIC: u_char *v_key_name __P((SCR *, ARG_CHAR_T));
*/
u_char *
v_key_name(SCR *sp, ARG_CHAR_T ach)
{
static const char hexdigit[] = "0123456789abcdef";
static const char octdigit[] = "01234567";
int ch;
size_t len, i;
const char *chp;
if (INTISWIDE(ach))
goto vis;
ch = (unsigned char)ach;
/* See if the character was explicitly declared printable or not. */
if ((chp = O_STR(sp, O_PRINT)) != NULL)
for (; *chp != '\0'; ++chp)
if (*chp == ch)
goto pr;
if ((chp = O_STR(sp, O_NOPRINT)) != NULL)
for (; *chp != '\0'; ++chp)
if (*chp == ch)
goto nopr;
/*
* Historical (ARPA standard) mappings. Printable characters are left
* alone. Control characters less than 0x20 are represented as '^'
* followed by the character offset from the '@' character in the ASCII
* character set. Del (0x7f) is represented as '^' followed by '?'.
*
* XXX
* The following code depends on the current locale being identical to
* the ASCII map from 0x40 to 0x5f (since 0x1f + 0x40 == 0x5f). 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.
*/
if (isprint(ch)) {
pr: sp->cname[0] = ch;
len = 1;
goto done;
}
nopr: if (iscntrl(ch) && (ch < 0x20 || ch == 0x7f)) {
sp->cname[0] = '^';
sp->cname[1] = ch == 0x7f ? '?' : '@' + ch;
len = 2;
goto done;
}
vis: for (i = 1; i <= sizeof(CHAR_T); ++i)
if ((ach >> i * CHAR_BIT) == 0)
break;
ch = (ach >> --i * CHAR_BIT) & UCHAR_MAX;
if (O_ISSET(sp, O_OCTAL)) {
sp->cname[0] = '\\';
sp->cname[1] = octdigit[(ch & 0300) >> 6];
sp->cname[2] = octdigit[(ch & 070) >> 3];
sp->cname[3] = octdigit[ ch & 07 ];
} else {
sp->cname[0] = '\\';
sp->cname[1] = 'x';
sp->cname[2] = hexdigit[(ch & 0xf0) >> 4];
sp->cname[3] = hexdigit[ ch & 0x0f ];
}
len = 4;
done: sp->cname[sp->clen = len] = '\0';
return (sp->cname);
}
/*
* v_key_val --
* Fill in the value for a key. This routine is the backup
* for the KEY_VAL() macro.
*
* PUBLIC: int v_key_val __P((SCR *, ARG_CHAR_T));
*/
e_key_t
v_key_val(SCR *sp, ARG_CHAR_T ch)
{
KEYLIST k, *kp;
k.ch = ch;
kp = bsearch(&k, keylist, nkeylist, sizeof(keylist[0]), v_key_cmp);
return (kp == NULL ? K_NOTUSED : kp->value);
}
/*
* v_event_push --
* Push events/keys onto the front of the buffer.
*
* There is a single input buffer in ex/vi. Characters are put 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,
* and if it is the result of a mapping or an abbreviation.
*
* PUBLIC: int v_event_push __P((SCR *, EVENT *, const CHAR_T *, size_t, u_int));
*/
int
v_event_push(SCR *sp, EVENT *p_evp, const CHAR_T *p_s, size_t nitems, u_int flags)
/* Push event. */
/* Push characters. */
/* Number of items to push. */
/* CH_* flags. */
{
EVENT *evp;
GS *gp;
WIN *wp;
size_t total;
/* If we have room, stuff the items into the buffer. */
gp = sp->gp;
wp = sp->wp;
if (nitems <= wp->i_next ||
(wp->i_event != NULL && wp->i_cnt == 0 && nitems <= wp->i_nelem)) {
if (wp->i_cnt != 0)
wp->i_next -= nitems;
goto copy;
}
/*
* If there are currently items in the queue, shift them up,
* leaving some extra room. Get enough space plus a little
* extra.
*/
#define TERM_PUSH_SHIFT 30
total = wp->i_cnt + wp->i_next + nitems + TERM_PUSH_SHIFT;
if (total >= wp->i_nelem && v_event_grow(sp, MAX(total, 64)))
return (1);
if (wp->i_cnt)
MEMMOVE(wp->i_event + TERM_PUSH_SHIFT + nitems,
wp->i_event + wp->i_next, wp->i_cnt);
wp->i_next = TERM_PUSH_SHIFT;
/* Put the new items into the queue. */
copy: wp->i_cnt += nitems;
for (evp = wp->i_event + wp->i_next; nitems--; ++evp) {
if (p_evp != NULL)
*evp = *p_evp++;
else {
evp->e_event = E_CHARACTER;
evp->e_c = *p_s++;
evp->e_value = KEY_VAL(sp, evp->e_c);
FL_INIT(evp->e_flags, flags);
}
}
return (0);
}
/*
* v_event_append --
* Append events onto the tail of the buffer.
*/
static int
v_event_append(SCR *sp, EVENT *argp)
{
CHAR_T *s; /* Characters. */
EVENT *evp;
WIN *wp;
size_t nevents; /* Number of events. */
/* Grow the buffer as necessary. */
nevents = argp->e_event == E_STRING ? argp->e_len : 1;
wp = sp->wp;
if (wp->i_event == NULL ||
nevents > wp->i_nelem - (wp->i_next + wp->i_cnt))
v_event_grow(sp, MAX(nevents, 64));
evp = wp->i_event + wp->i_next + wp->i_cnt;
wp->i_cnt += nevents;
/* Transform strings of characters into single events. */
if (argp->e_event == E_STRING)
for (s = argp->e_csp; nevents--; ++evp) {
evp->e_event = E_CHARACTER;
evp->e_c = *s++;
evp->e_value = KEY_VAL(sp, evp->e_c);
evp->e_flags = 0;
}
else
*evp = *argp;
return (0);
}
/* Remove events from the queue. */
#define QREM(len) { \
if ((wp->i_cnt -= len) == 0) \
wp->i_next = 0; \
else \
wp->i_next += len; \
}
/*
* v_event_get --
* Return the next event.
*
* !!!
* The flag EC_NODIGIT 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 (EC_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 EC_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 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.
*
* !!!
* It is also historic practice that the macro "map ] ]]^" caused a single ]
* keypress to behave as the command ]] (the ^ got the map past the vi check
* for "tail recursion"). Conversely, the mapping "map n nn^" went recursive.
* What happened was that, in the historic vi, maps were expanded as the keys
* were retrieved, but not all at once and not centrally. So, the keypress ]
* pushed ]]^ on the stack, and then the first ] from the stack was passed to
* the ]] command code. The ]] command then retrieved a key without entering
* the mapping code. This could bite us anytime a user has a map that depends
* on secondary keys NOT being mapped. I can't see any possible way to make
* this work in here without the complete abandonment of Rationality Itself.
*
* 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.
*
* PUBLIC: int v_event_get __P((SCR *, EVENT *, int, u_int32_t));
*/
int
v_event_get(SCR *sp, EVENT *argp, int timeout, u_int32_t flags)
{
EVENT *evp, ev;
GS *gp;
SEQ *qp;
int init_nomap, ispartial, istimeout, remap_cnt;
WIN *wp;
gp = sp->gp;
wp = sp->wp;
/* If simply checking for interrupts, argp may be NULL. */
if (argp == NULL)
argp = &ev;
retry: istimeout = remap_cnt = 0;
/*
* If the queue isn't empty and we're timing out for characters,
* return immediately.
*/
if (wp->i_cnt != 0 && LF_ISSET(EC_TIMEOUT))
return (0);
/*
* If the queue is empty, we're checking for interrupts, or we're
* timing out for characters, get more events.
*/
if (wp->i_cnt == 0 || LF_ISSET(EC_INTERRUPT | EC_TIMEOUT)) {
/*
* If we're reading new characters, check any scripting
* windows for input.
*/
if (F_ISSET(gp, G_SCRWIN) && sscr_input(sp))
return (1);
loop: if (gp->scr_event(sp, argp,
LF_ISSET(EC_INTERRUPT | EC_QUOTED | EC_RAW), timeout))
return (1);
switch (argp->e_event) {
case E_ERR:
case E_SIGHUP:
case E_SIGTERM:
/*
* Fatal conditions cause the file to be synced to
* disk immediately.
*/
v_sync(sp, RCV_ENDSESSION | RCV_PRESERVE |
(argp->e_event == E_SIGTERM ? 0: RCV_EMAIL));
return (1);
case E_TIMEOUT:
istimeout = 1;
break;
case E_INTERRUPT:
/* Set the global interrupt flag. */
F_SET(sp->gp, G_INTERRUPTED);
/*
* If the caller was interested in interrupts, return
* immediately.
*/
if (LF_ISSET(EC_INTERRUPT))
return (0);
goto append;
default:
append: if (v_event_append(sp, argp))
return (1);
break;
}
}
/*
* If the caller was only interested in interrupts or timeouts, return
* immediately. (We may have gotten characters, and that's okay, they
* were queued up for later use.)
*/
if (LF_ISSET(EC_INTERRUPT | EC_TIMEOUT))
return (0);
newmap: evp = &wp->i_event[wp->i_next];
/*
* If the next event in the queue isn't a character event, return
* it, we're done.
*/
if (evp->e_event != E_CHARACTER) {
*argp = *evp;
QREM(1);
return (0);
}
/*
* If the key isn't mappable because:
*
* + ... the timeout has expired
* + ... it's not a mappable key
* + ... neither the command or input map flags are set
* + ... there are no maps that can apply to it
*
* return it forthwith.
*/
if (istimeout || FL_ISSET(evp->e_flags, CH_NOMAP) ||
!LF_ISSET(EC_MAPCOMMAND | EC_MAPINPUT) ||
((evp->e_c & ~MAX_BIT_SEQ) == 0 &&
!bit_test(gp->seqb, evp->e_c)))
goto nomap;
/* Search the map. */
qp = seq_find(sp, NULL, evp, NULL, wp->i_cnt,
LF_ISSET(EC_MAPCOMMAND) ? SEQ_COMMAND : SEQ_INPUT, &ispartial);
/*
* If get a partial match, get more characters and retry the map.
* If time out without further characters, return the characters
* unmapped.
*
* !!!
* <escape> characters are a problem. Cursor keys start with <escape>
* characters, so there's almost always a map in place that begins with
* an <escape> character. If we timeout <escape> keys in the same way
* that we timeout other keys, the user will get a noticeable pause as
* they enter <escape> to terminate input mode. If key timeout is set
* for a slow link, users will get an even longer pause. Nvi used to
* simply timeout <escape> characters at 1/10th of a second, but this
* loses over PPP links where the latency is greater than 100Ms.
*/
if (ispartial) {
if (O_ISSET(sp, O_TIMEOUT))
timeout = (evp->e_value == K_ESCAPE ?
O_VAL(sp, O_ESCAPETIME) :
O_VAL(sp, O_KEYTIME)) * 100;
else
timeout = 0;
goto loop;
}
/* If no map, return the character. */
if (qp == NULL) {
nomap: if (!ISDIGIT(evp->e_c) && LF_ISSET(EC_MAPNODIGIT))
goto not_digit;
*argp = *evp;
QREM(1);
return (0);
}
/*
* 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(EC_MAPNODIGIT) &&
qp->output != NULL && !ISDIGIT(qp->output[0])) {
not_digit: argp->e_c = CH_NOT_DIGIT;
argp->e_value = K_NOTUSED;
argp->e_event = E_CHARACTER;
FL_INIT(argp->e_flags, 0);
return (0);
}
/* Find out if the initial segments are identical. */
init_nomap = !e_memcmp(qp->output, &wp->i_event[wp->i_next], qp->ilen);
/* Delete the mapped characters from the queue. */
QREM(qp->ilen);
/* If keys mapped to nothing, go get more. */
if (qp->output == NULL)
goto retry;
/* If remapping characters... */
if (O_ISSET(sp, O_REMAP)) {
/*
* Periodically check for interrupts. Always check the first
* time through, because it's possible to set up a map that
* will return a character every time, but will expand to more,
* e.g. "map! a aaaa" will always return a 'a', but we'll never
* get anywhere useful.
*/
if ((++remap_cnt == 1 || remap_cnt % 10 == 0) &&
(gp->scr_event(sp, &ev,
EC_INTERRUPT, 0) || ev.e_event == E_INTERRUPT)) {
F_SET(sp->gp, G_INTERRUPTED);
argp->e_event = E_INTERRUPT;
return (0);
}
/*
* If an initial part of the characters mapped, they are not
* further remapped -- return the first one. Push the rest
* of the characters, or all of the characters if no initial
* part mapped, back on the queue.
*/
if (init_nomap) {
if (v_event_push(sp, NULL, qp->output + qp->ilen,
qp->olen - qp->ilen, CH_MAPPED))
return (1);
if (v_event_push(sp, NULL,
qp->output, qp->ilen, CH_NOMAP | CH_MAPPED))
return (1);
evp = &wp->i_event[wp->i_next];
goto nomap;
}
if (v_event_push(sp, NULL, qp->output, qp->olen, CH_MAPPED))
return (1);
goto newmap;
}
/* Else, push the characters on the queue and return one. */
if (v_event_push(sp, NULL, qp->output, qp->olen, CH_MAPPED | CH_NOMAP))
return (1);
goto nomap;
}
/*
* v_sync --
* Walk the screen lists, sync'ing files to their backup copies.
*/
static void
v_sync(SCR *sp, int flags)
{
GS *gp;
WIN *wp;
gp = sp->gp;
for (wp = gp->dq.cqh_first; wp != (void *)&gp->dq;
wp = wp->q.cqe_next)
for (sp = wp->scrq.cqh_first; sp != (void *)&wp->scrq;
sp = sp->q.cqe_next)
rcv_sync(sp, flags);
for (sp = gp->hq.cqh_first; sp != (void *)&gp->hq; sp = sp->q.cqe_next)
rcv_sync(sp, flags);
}
/*
* v_event_err --
* Unexpected event.
*
* PUBLIC: void v_event_err __P((SCR *, EVENT *));
*/
void
v_event_err(SCR *sp, EVENT *evp)
{
switch (evp->e_event) {
case E_CHARACTER:
msgq(sp, M_ERR, "276|Unexpected character event");
break;
case E_EOF:
msgq(sp, M_ERR, "277|Unexpected end-of-file event");
break;
case E_INTERRUPT:
msgq(sp, M_ERR, "279|Unexpected interrupt event");
break;
case E_IPCOMMAND:
msgq(sp, M_ERR, "318|Unexpected command or input");
break;
case E_REPAINT:
msgq(sp, M_ERR, "281|Unexpected repaint event");
break;
case E_STRING:
msgq(sp, M_ERR, "285|Unexpected string event");
break;
case E_TIMEOUT:
msgq(sp, M_ERR, "286|Unexpected timeout event");
break;
case E_WRESIZE:
msgq(sp, M_ERR, "316|Unexpected resize event");
break;
/*
* Theoretically, none of these can occur, as they're handled at the
* top editor level.
*/
case E_ERR:
case E_SIGHUP:
case E_SIGTERM:
default:
abort();
}
}
/*
* v_event_flush --
* Flush any flagged keys, returning if any keys were flushed.
*
* PUBLIC: int v_event_flush __P((SCR *, u_int));
*/
int
v_event_flush(SCR *sp, u_int flags)
{
WIN *wp;
int rval;
for (rval = 0, wp = sp->wp; wp->i_cnt != 0 &&
FL_ISSET(wp->i_event[wp->i_next].e_flags, flags); rval = 1)
QREM(1);
return (rval);
}
/*
* v_event_grow --
* Grow the terminal queue.
*/
static int
v_event_grow(SCR *sp, int add)
{
WIN *wp;
size_t new_nelem, olen;
wp = sp->wp;
new_nelem = wp->i_nelem + add;
olen = wp->i_nelem * sizeof(wp->i_event[0]);
BINC_RET(sp, EVENT, wp->i_event, olen, new_nelem * sizeof(EVENT));
wp->i_nelem = olen / sizeof(wp->i_event[0]);
return (0);
}
/*
* v_key_cmp --
* Compare two keys for sorting.
*/
static int
v_key_cmp(const void *ap, const void *bp)
{
return (((const KEYLIST *)ap)->ch - ((const KEYLIST *)bp)->ch);
}

229
dist/nvi/common/key.h vendored Normal file
View File

@@ -0,0 +1,229 @@
/* $NetBSD: key.h,v 1.3 2011/11/14 13:29:07 tnozaki Exp $ */
/*-
* Copyright (c) 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1991, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*
* Id: key.h,v 10.50 2001/06/28 17:53:58 skimo Exp (Berkeley) Date: 2001/06/28 17:53:58
*/
#include "multibyte.h"
#ifdef USE_WIDECHAR
#define FILE2INT5(sp,buf,n,nlen,w,wlen) \
sp->conv.file2int(sp, n, nlen, &buf, &wlen, &w)
#define INT2FILE(sp,w,wlen,n,nlen) \
sp->conv.int2file(sp, w, wlen, &sp->wp->cw, &nlen, &n)
#define CHAR2INT5(sp,buf,n,nlen,w,wlen) \
sp->conv.sys2int(sp, n, nlen, &buf, &wlen, &w)
#define INT2CHAR(sp,w,wlen,n,nlen) \
sp->conv.int2sys(sp, w, wlen, &sp->wp->cw, &nlen, &n)
#define INT2SYS(sp,w,wlen,n,nlen) \
sp->conv.int2sys(sp, w, wlen, &sp->wp->cw, &nlen, &n)
#define INPUT2INT5(sp,cw,n,nlen,w,wlen) \
sp->conv.input2int(sp, n, nlen, &(cw), &wlen, &w)
#define CONST
#define CHAR_WIDTH(sp, ch) wcwidth(ch)
#define INTISWIDE(c) (wctob(c) == EOF) /* XXX wrong name */
#else
#define FILE2INT5(sp,buf,n,nlen,w,wlen) \
(w = n, wlen = nlen, 0)
#define INT2FILE(sp,w,wlen,n,nlen) \
(n = w, nlen = wlen, 0)
#define CHAR2INT5(sp,buf,n,nlen,w,wlen) \
(w = n, wlen = nlen, 0)
#define INT2CHAR(sp,w,wlen,n,nlen) \
(n = w, nlen = wlen, 0)
#define INT2SYS(sp,w,wlen,n,nlen) \
(n = w, nlen = wlen, 0)
#define INPUT2INT5(sp,buf,n,nlen,w,wlen) \
(w = n, wlen = nlen, 0)
#define CONST const
#define INTISWIDE(c) 0
#define CHAR_WIDTH(sp, ch) 1
#endif
#define FILE2INT(sp,n,nlen,w,wlen) \
FILE2INT5(sp,sp->wp->cw,n,nlen,w,wlen)
#define CHAR2INT(sp,n,nlen,w,wlen) \
CHAR2INT5(sp,sp->wp->cw,n,nlen,w,wlen)
#define MEMCPYW(to, from, n) \
memcpy(to, from, (n) * sizeof(CHAR_T))
#define MEMMOVEW(to, from, n) \
memmove(to, from, (n) * sizeof(CHAR_T))
/* The maximum number of columns any character can take up on a screen. */
#define MAX_CHARACTER_COLUMNS 4
/*
* Event types.
*
* The program structure depends on the event loop being able to return
* E_EOF/E_ERR multiple times -- eventually enough things will end due
* to the events that vi will reach the command level for the screen, at
* which point the exit flags will be set and vi will exit.
*/
typedef enum {
E_NOTUSED = 0, /* Not set. */
E_CHARACTER, /* Input character: e_c set. */
E_EOF, /* End of input (NOT ^D). */
E_ERR, /* Input error. */
E_INTERRUPT, /* Interrupt. */
E_IPCOMMAND, /* IP command: e_ipcom set. */
E_REPAINT, /* Repaint: e_flno, e_tlno set. */
E_SIGHUP, /* SIGHUP. */
E_SIGTERM, /* SIGTERM. */
E_STRING, /* Input string: e_csp, e_len set. */
E_TIMEOUT, /* Timeout. */
E_WRESIZE, /* Window resize. */
E_FLAGS /* Flags */
} e_event_t;
/*
* Character values.
*/
typedef enum {
K_NOTUSED = 0, /* Not set. */
K_BACKSLASH, /* \ */
K_CARAT, /* ^ */
K_CNTRLD, /* ^D */
K_CNTRLR, /* ^R */
K_CNTRLT, /* ^T */
K_CNTRLZ, /* ^Z */
K_COLON, /* : */
K_CR, /* \r */
K_ESCAPE, /* ^[ */
K_FORMFEED, /* \f */
K_HEXCHAR, /* ^X */
K_NL, /* \n */
K_RIGHTBRACE, /* } */
K_RIGHTPAREN, /* ) */
K_TAB, /* \t */
K_VERASE, /* set from tty: default ^H */
K_VKILL, /* set from tty: default ^U */
K_VLNEXT, /* set from tty: default ^V */
K_VWERASE, /* set from tty: default ^W */
K_ZERO /* 0 */
} e_key_t;
struct _event {
TAILQ_ENTRY(_event) q; /* Linked list of events. */
e_event_t e_event; /* Event type. */
int e_ipcom; /* IP command. */
#define CH_ABBREVIATED 0x01 /* Character is from an abbreviation. */
#define CH_MAPPED 0x02 /* Character is from a map. */
#define CH_NOMAP 0x04 /* Do not map the character. */
#define CH_QUOTED 0x08 /* Character is already quoted. */
ARG_CHAR_T e_c; /* Character. */
e_key_t e_value; /* Key type. */
#define e_flags e_val1 /* Flags. */
#define e_lno e_val1 /* Single location. */
#define e_cno e_val2
#define e_flno e_val1 /* Text region. */
#define e_fcno e_val2
#define e_tlno e_val3
#define e_tcno e_val4
size_t e_val1; /* Value #1. */
size_t e_val2; /* Value #2. */
size_t e_val3; /* Value #3. */
size_t e_val4; /* Value #4. */
#define e_csp e_str1
#define e_len e_len1
CHAR_T *e_str1; /* String #1. */
size_t e_len1; /* String #1 length. */
CHAR_T *e_str2; /* String #2. */
size_t e_len2; /* String #2 length. */
};
typedef struct _keylist {
e_key_t value; /* Special value. */
int ch; /* Key. */
} KEYLIST;
extern KEYLIST keylist[];
/* Return if more keys in queue. */
#define KEYS_WAITING(sp) ((sp)->wp->i_cnt != 0)
#define MAPPED_KEYS_WAITING(sp) \
(KEYS_WAITING(sp) && \
FL_ISSET((sp)->wp->i_event[(sp)->wp->i_next].e_flags, CH_MAPPED))
/* 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. */
/*
* Checking for interrupts means that we look at the bit that gets set if the
* screen code supports asynchronous events, and call back into the event code
* so that non-asynchronous screens get a chance to post the interrupt.
*
* INTERRUPT_CHECK is the number of lines "operated" on before checking for
* interrupts.
*/
#define INTERRUPT_CHECK 100
#define INTERRUPTED(sp) \
(F_ISSET((sp)->gp, G_INTERRUPTED) || \
(!v_event_get(sp, NULL, 0, EC_INTERRUPT) && \
F_ISSET((sp)->gp, G_INTERRUPTED)))
#define CLR_INTERRUPT(sp) \
F_CLR((sp)->gp, G_INTERRUPTED)
/* Flags describing types of characters being requested. */
#define EC_INTERRUPT 0x001 /* Checking for interrupts. */
#define EC_MAPCOMMAND 0x002 /* Apply the command map. */
#define EC_MAPINPUT 0x004 /* Apply the input map. */
#define EC_MAPNODIGIT 0x008 /* Return to a digit. */
#define EC_QUOTED 0x010 /* Try to quote next character */
#define EC_RAW 0x020 /* Any next character. XXX: not used. */
#define EC_TIMEOUT 0x040 /* Timeout to next character. */
/* Flags describing text input special cases. */
#define TXT_ADDNEWLINE 0x00000001 /* Replay starts on a new line. */
#define TXT_AICHARS 0x00000002 /* Leading autoindent chars. */
#define TXT_ALTWERASE 0x00000004 /* Option: altwerase. */
#define TXT_APPENDEOL 0x00000008 /* Appending after EOL. */
#define TXT_AUTOINDENT 0x00000010 /* Autoindent set this line. */
#define TXT_BACKSLASH 0x00000020 /* Backslashes escape characters. */
#define TXT_BEAUTIFY 0x00000040 /* Only printable characters. */
#define TXT_BS 0x00000080 /* Backspace returns the buffer. */
#define TXT_CEDIT 0x00000100 /* Can return TERM_CEDIT. */
#define TXT_CNTRLD 0x00000200 /* Control-D is a command. */
#define TXT_CNTRLT 0x00000400 /* Control-T is an indent special. */
#define TXT_CR 0x00000800 /* CR returns the buffer. */
#define TXT_DOTTERM 0x00001000 /* Leading '.' terminates the input. */
#define TXT_EMARK 0x00002000 /* End of replacement mark. */
#define TXT_EOFCHAR 0x00004000 /* ICANON set, return EOF character. */
#define TXT_ESCAPE 0x00008000 /* Escape returns the buffer. */
#define TXT_FILEC 0x00010000 /* Option: filec. */
#define TXT_INFOLINE 0x00020000 /* Editing the info line. */
#define TXT_MAPINPUT 0x00040000 /* Apply the input map. */
#define TXT_NLECHO 0x00080000 /* Echo the newline. */
#define TXT_NUMBER 0x00100000 /* Number the line. */
#define TXT_OVERWRITE 0x00200000 /* Overwrite characters. */
#define TXT_PROMPT 0x00400000 /* Display a prompt. */
#define TXT_RECORD 0x00800000 /* Record for replay. */
#define TXT_REPLACE 0x01000000 /* Replace; don't delete overwrite. */
#define TXT_REPLAY 0x02000000 /* Replay the last input. */
#define TXT_RESOLVE 0x04000000 /* Resolve the text into the file. */
#define TXT_SEARCHINCR 0x08000000 /* Incremental search. */
#define TXT_SHOWMATCH 0x10000000 /* Option: showmatch. */
#define TXT_TTYWERASE 0x20000000 /* Option: ttywerase. */
#define TXT_WRAPMARGIN 0x40000000 /* Option: wrapmargin. */

806
dist/nvi/common/log.c vendored Normal file
View File

@@ -0,0 +1,806 @@
/* $NetBSD: log.c,v 1.2 2008/12/05 22:51:42 christos Exp $ */
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1992, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: log.c,v 10.26 2002/03/02 23:12:13 skimo Exp (Berkeley) Date: 2002/03/02 23:12:13";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <bitstring.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
#include "dbinternal.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_F db_recno_t char *
* LOG_LINE_APPEND_B db_recno_t char *
* LOG_LINE_DELETE_F db_recno_t char *
* LOG_LINE_DELETE_B db_recno_t char *
* LOG_LINE_RESET_F db_recno_t char *
* LOG_LINE_RESET_B db_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 vi_log_get __P((SCR *sp, db_recno_t *lnop, size_t *size));
static int log_cursor1 __P((SCR *, int));
static void log_err __P((SCR *, const char *, int));
#if defined(DEBUG) && 0
static void log_trace __P((SCR *, const char *, db_recno_t, u_char *));
#endif
/* Try and restart the log on failure, i.e. if we run out of memory. */
#define LOG_ERR { \
log_err(sp, __FILE__, __LINE__); \
return (1); \
}
/* offset of CHAR_T string in log needs to be aligned on some systems
* because it is passed to db_set as a string
*/
typedef struct {
char data[sizeof(u_char) /* type */ + sizeof(db_recno_t)];
CHAR_T str[1];
} log_t;
#define CHAR_T_OFFSET ((char *)(((log_t*)0)->str) - (char *)0)
/*
* log_init --
* Initialize the logging subsystem.
*
* PUBLIC: int log_init __P((SCR *, EXF *));
*/
int
log_init(SCR *sp, EXF *ep)
{
/*
* !!!
* ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
*
* 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.
*/
sp->wp->l_lp = NULL;
sp->wp->l_len = 0;
ep->l_cursor.lno = 1; /* XXX Any valid recno. */
ep->l_cursor.cno = 0;
ep->l_high = ep->l_cur = 1;
if (db_create(&ep->log, 0, 0) != 0 ||
db_open(ep->log, NULL, DB_RECNO,
DB_CREATE | VI_DB_THREAD, S_IRUSR | S_IWUSR) != 0) {
msgq(sp, M_SYSERR, "009|Log file");
F_SET(ep, F_NOLOG);
return (1);
}
ep->l_win = NULL;
/*LOCK_INIT(sp->wp, ep);*/
return (0);
}
/*
* log_end --
* Close the logging subsystem.
*
* PUBLIC: int log_end __P((SCR *, EXF *));
*/
int
log_end(SCR *sp, EXF *ep)
{
/*
* !!!
* ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
*/
/*LOCK_END(sp->wp, ep);*/
if (ep->log != NULL) {
(void)(ep->log->close)(ep->log,DB_NOSYNC);
ep->log = NULL;
}
if (sp->wp->l_lp != NULL) {
free(sp->wp->l_lp);
sp->wp->l_lp = NULL;
}
sp->wp->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.
*
* PUBLIC: int log_cursor __P((SCR *));
*/
int
log_cursor(SCR *sp)
{
EXF *ep;
ep = sp->ep;
if (F_ISSET(ep, F_NOLOG))
return (0);
/*
* If any changes were made since the last cursor init,
* put out the ending cursor record.
*/
if (ep->l_cursor.lno == OOBLNO) {
if (ep->l_win && ep->l_win != sp->wp)
return 0;
ep->l_cursor.lno = sp->lno;
ep->l_cursor.cno = sp->cno;
ep->l_win = NULL;
return (log_cursor1(sp, 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(SCR *sp, int type)
{
DBT data, key;
EXF *ep;
ep = sp->ep;
/*
if (type == LOG_CURSOR_INIT &&
LOCK_TRY(sp->wp, ep))
return 1;
*/
BINC_RETC(sp, sp->wp->l_lp, sp->wp->l_len, sizeof(u_char) + sizeof(MARK));
sp->wp->l_lp[0] = type;
memmove(sp->wp->l_lp + sizeof(u_char), &ep->l_cursor, sizeof(MARK));
memset(&key, 0, sizeof(key));
key.data = &ep->l_cur;
key.size = sizeof(db_recno_t);
memset(&data, 0, sizeof(data));
data.data = sp->wp->l_lp;
data.size = sizeof(u_char) + sizeof(MARK);
if (ep->log->put(ep->log, NULL, &key, &data, 0) == -1)
LOG_ERR;
#if defined(DEBUG) && 0
vtrace(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;
/*
if (type == LOG_CURSOR_END)
LOCK_UNLOCK(sp->wp, ep);
*/
return (0);
}
/*
* log_line --
* Log a line change.
*
* PUBLIC: int log_line __P((SCR *, db_recno_t, u_int));
*/
int
log_line(SCR *sp, db_recno_t lno, u_int action)
{
DBT data, key;
EXF *ep;
size_t len;
CHAR_T *lp;
db_recno_t lcur;
ep = sp->ep;
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, LOG_CURSOR_INIT))
return (1);
ep->l_cursor.lno = OOBLNO;
ep->l_win = sp->wp;
} /*else if (ep->l_win != sp->wp) {
printf("log_line own: %p, this: %p\n", ep->l_win, sp->wp);
return 1;
}*/
switch (action) {
/* newly added for DB4 logging */
case LOG_LINE_APPEND_B:
case LOG_LINE_DELETE_F:
return 0;
}
/*
* 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 (db_get(sp, lno, DBG_NOCACHE, &lp, &len)) {
static CHAR_T nul = 0;
if (lno != 1) {
db_err(sp, lno);
return (1);
}
len = 0;
lp = &nul;
}
} else
if (db_get(sp, lno, DBG_FATAL, &lp, &len))
return (1);
BINC_RETC(sp,
sp->wp->l_lp, sp->wp->l_len,
len * sizeof(CHAR_T) + CHAR_T_OFFSET);
sp->wp->l_lp[0] = action;
memmove(sp->wp->l_lp + sizeof(u_char), &lno, sizeof(db_recno_t));
MEMMOVEW(sp->wp->l_lp + CHAR_T_OFFSET, lp, len);
lcur = ep->l_cur;
memset(&key, 0, sizeof(key));
key.data = &lcur;
key.size = sizeof(db_recno_t);
memset(&data, 0, sizeof(data));
data.data = sp->wp->l_lp;
data.size = len * sizeof(CHAR_T) + CHAR_T_OFFSET;
if (ep->log->put(ep->log, NULL, &key, &data, 0) == -1)
LOG_ERR;
#if defined(DEBUG) && 0
switch (action) {
case LOG_LINE_APPEND_F:
vtrace(sp, "%u: log_line: append_f: %lu {%u}\n",
ep->l_cur, lno, len);
break;
case LOG_LINE_APPEND_B:
vtrace(sp, "%u: log_line: append_b: %lu {%u}\n",
ep->l_cur, lno, len);
break;
case LOG_LINE_DELETE_F:
vtrace(sp, "%lu: log_line: delete_f: %lu {%u}\n",
ep->l_cur, lno, len);
break;
case LOG_LINE_DELETE_B:
vtrace(sp, "%lu: log_line: delete_b: %lu {%u}\n",
ep->l_cur, lno, len);
break;
case LOG_LINE_RESET_F:
vtrace(sp, "%lu: log_line: reset_f: %lu {%u}\n",
ep->l_cur, lno, len);
break;
case LOG_LINE_RESET_B:
vtrace(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.
*
* PUBLIC: int log_mark __P((SCR *, LMARK *));
*/
int
log_mark(SCR *sp, LMARK *lmp)
{
DBT data, key;
EXF *ep;
ep = sp->ep;
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, LOG_CURSOR_INIT))
return (1);
ep->l_cursor.lno = OOBLNO;
ep->l_win = sp->wp;
}
BINC_RETC(sp, sp->wp->l_lp,
sp->wp->l_len, sizeof(u_char) + sizeof(LMARK));
sp->wp->l_lp[0] = LOG_MARK;
memmove(sp->wp->l_lp + sizeof(u_char), lmp, sizeof(LMARK));
memset(&key, 0, sizeof(key));
key.data = &ep->l_cur;
key.size = sizeof(db_recno_t);
memset(&data, 0, sizeof(data));
data.data = sp->wp->l_lp;
data.size = sizeof(u_char) + sizeof(LMARK);
if (ep->log->put(ep->log, NULL, &key, &data, 0) == -1)
LOG_ERR;
#if defined(DEBUG) && 0
vtrace(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);
}
/*
* vi_log_get --
* Get a line from the log in log buffer.
*/
static int
vi_log_get(SCR *sp, db_recno_t *lnop, size_t *size)
{
DBT key, data;
size_t nlen;
EXF *ep;
ep = sp->ep;
nlen = 1024;
retry:
BINC_RETC(sp, sp->wp->l_lp, sp->wp->l_len, nlen);
memset(&key, 0, sizeof(key));
key.data = lnop; /* Initialize db request. */
key.size = sizeof(db_recno_t);
memset(&data, 0, sizeof(data));
data.data = sp->wp->l_lp;
data.ulen = sp->wp->l_len;
data.flags = DB_DBT_USERMEM;
switch (ep->log->get(ep->log, NULL, &key, &data, 0)) {
case ENOMEM:
nlen = data.size;
goto retry;
case 0:
*size = data.size;
return 0;
default:
return 1;
}
}
/*
* Log_backward --
* Roll the log backward one operation.
*
* PUBLIC: int log_backward __P((SCR *, MARK *));
*/
int
log_backward(SCR *sp, MARK *rp)
{
EXF *ep;
LMARK lm;
MARK m;
db_recno_t lno;
int didop;
u_char *p;
size_t size;
ep = sp->ep;
if (F_ISSET(ep, F_NOLOG)) {
msgq(sp, M_ERR,
"010|Logging not being performed, undo not possible");
return (1);
}
if (ep->l_cur == 1) {
msgq(sp, M_BERR, "011|No changes to undo");
return (1);
}
if (ep->l_win && ep->l_win != sp->wp) {
ex_emsg(sp, NULL, EXM_LOCKED);
return 1;
}
ep->l_win = sp->wp;
F_SET(ep, F_NOLOG); /* Turn off logging. */
for (didop = 0;;) {
--ep->l_cur;
if (vi_log_get(sp, &ep->l_cur, &size))
LOG_ERR;
#if defined(DEBUG) && 0
log_trace(sp, "log_backward", ep->l_cur, data.data);
#endif
switch (*(p = (u_char *)sp->wp->l_lp)) {
case LOG_CURSOR_INIT:
if (didop) {
memmove(rp, p + sizeof(u_char), sizeof(MARK));
F_CLR(ep, F_NOLOG);
ep->l_win = NULL;
return (0);
}
break;
case LOG_CURSOR_END:
break;
case LOG_LINE_APPEND_F:
didop = 1;
memmove(&lno, p + sizeof(u_char), sizeof(db_recno_t));
if (db_delete(sp, lno))
goto err;
++sp->rptlines[L_DELETED];
break;
case LOG_LINE_DELETE_B:
didop = 1;
memmove(&lno, p + sizeof(u_char), sizeof(db_recno_t));
if (db_insert(sp, lno,
(CHAR_T *)(p + CHAR_T_OFFSET),
(size - CHAR_T_OFFSET) / sizeof(CHAR_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(db_recno_t));
if (db_set(sp, lno,
(CHAR_T *)(p + CHAR_T_OFFSET),
(size - CHAR_T_OFFSET) / sizeof(CHAR_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, lm.name, &m, 0))
goto err;
break;
default:
abort();
}
}
err: F_CLR(ep, F_NOLOG);
ep->l_win = NULL;
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.
*
* PUBLIC: int log_setline __P((SCR *));
*/
int
log_setline(SCR *sp)
{
EXF *ep;
LMARK lm;
MARK m;
db_recno_t lno;
u_char *p;
size_t size;
ep = sp->ep;
if (F_ISSET(ep, F_NOLOG)) {
msgq(sp, M_ERR,
"012|Logging not being performed, undo not possible");
return (1);
}
if (ep->l_cur == 1)
return (1);
if (ep->l_win && ep->l_win != sp->wp) {
ex_emsg(sp, NULL, EXM_LOCKED);
return 1;
}
ep->l_win = sp->wp;
F_SET(ep, F_NOLOG); /* Turn off logging. */
for (;;) {
--ep->l_cur;
if (vi_log_get(sp, &ep->l_cur, &size))
LOG_ERR;
#if defined(DEBUG) && 0
log_trace(sp, "log_setline", ep->l_cur, data.data);
#endif
switch (*(p = (u_char *)sp->wp->l_lp)) {
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);
ep->l_win = NULL;
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);
ep->l_win = NULL;
return (0);
}
break;
case LOG_LINE_APPEND_F:
case LOG_LINE_DELETE_B:
case LOG_LINE_RESET_F:
break;
case LOG_LINE_RESET_B:
memmove(&lno, p + sizeof(u_char), sizeof(db_recno_t));
if (lno == sp->lno &&
db_set(sp, lno, (CHAR_T *)(p + CHAR_T_OFFSET),
(size - CHAR_T_OFFSET) / sizeof(CHAR_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, lm.name, &m, 0))
goto err;
break;
default:
abort();
}
}
err: F_CLR(ep, F_NOLOG);
ep->l_win = NULL;
return (1);
}
/*
* Log_forward --
* Roll the log forward one operation.
*
* PUBLIC: int log_forward __P((SCR *, MARK *));
*/
int
log_forward(SCR *sp, MARK *rp)
{
EXF *ep;
LMARK lm;
MARK m;
db_recno_t lno;
int didop;
u_char *p;
size_t size;
ep = sp->ep;
if (F_ISSET(ep, F_NOLOG)) {
msgq(sp, M_ERR,
"013|Logging not being performed, roll-forward not possible");
return (1);
}
if (ep->l_cur == ep->l_high) {
msgq(sp, M_BERR, "014|No changes to re-do");
return (1);
}
if (ep->l_win && ep->l_win != sp->wp) {
ex_emsg(sp, NULL, EXM_LOCKED);
return 1;
}
ep->l_win = sp->wp;
F_SET(ep, F_NOLOG); /* Turn off logging. */
for (didop = 0;;) {
++ep->l_cur;
if (vi_log_get(sp, &ep->l_cur, &size))
LOG_ERR;
#if defined(DEBUG) && 0
log_trace(sp, "log_forward", ep->l_cur, data.data);
#endif
switch (*(p = (u_char *)sp->wp->l_lp)) {
case LOG_CURSOR_END:
if (didop) {
++ep->l_cur;
memmove(rp, p + sizeof(u_char), sizeof(MARK));
F_CLR(ep, F_NOLOG);
ep->l_win = NULL;
return (0);
}
break;
case LOG_CURSOR_INIT:
break;
case LOG_LINE_APPEND_F:
didop = 1;
memmove(&lno, p + sizeof(u_char), sizeof(db_recno_t));
if (db_insert(sp, lno,
(CHAR_T *)(p + CHAR_T_OFFSET),
(size - CHAR_T_OFFSET) / sizeof(CHAR_T)))
goto err;
++sp->rptlines[L_ADDED];
break;
case LOG_LINE_DELETE_B:
didop = 1;
memmove(&lno, p + sizeof(u_char), sizeof(db_recno_t));
if (db_delete(sp, 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(db_recno_t));
if (db_set(sp, lno,
(CHAR_T *)(p + CHAR_T_OFFSET),
(size - CHAR_T_OFFSET) / sizeof(CHAR_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, lm.name, &m, 0))
goto err;
break;
default:
abort();
}
}
err: F_CLR(ep, F_NOLOG);
ep->l_win = NULL;
return (1);
}
/*
* log_err --
* Try and restart the log on failure, i.e. if we run out of memory.
*/
static void
log_err(SCR *sp, const char *file, int line)
{
EXF *ep;
msgq(sp, M_SYSERR, "015|%s/%d: log put error", tail(file), line);
ep = sp->ep;
(void)ep->log->close(ep->log, DB_NOSYNC);
if (!log_init(sp, ep))
msgq(sp, M_ERR, "267|Log restarted");
}
#if defined(DEBUG) && 0
static void
log_trace(SCR *sp, const char *msg, db_recno_t rno, u_char *p)
{
LMARK lm;
MARK m;
db_recno_t lno;
switch (*p) {
case LOG_CURSOR_INIT:
memmove(&m, p + sizeof(u_char), sizeof(MARK));
vtrace(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));
vtrace(sp, "%lu: %s: C_END: %u/%u\n", rno, msg, m.lno, m.cno);
break;
case LOG_LINE_APPEND_F:
memmove(&lno, p + sizeof(u_char), sizeof(db_recno_t));
vtrace(sp, "%lu: %s: APPEND_F: %lu\n", rno, msg, lno);
break;
case LOG_LINE_APPEND_B:
memmove(&lno, p + sizeof(u_char), sizeof(db_recno_t));
vtrace(sp, "%lu: %s: APPEND_B: %lu\n", rno, msg, lno);
break;
case LOG_LINE_DELETE_F:
memmove(&lno, p + sizeof(u_char), sizeof(db_recno_t));
vtrace(sp, "%lu: %s: DELETE_F: %lu\n", rno, msg, lno);
break;
case LOG_LINE_DELETE_B:
memmove(&lno, p + sizeof(u_char), sizeof(db_recno_t));
vtrace(sp, "%lu: %s: DELETE_B: %lu\n", rno, msg, lno);
break;
case LOG_LINE_RESET_F:
memmove(&lno, p + sizeof(u_char), sizeof(db_recno_t));
vtrace(sp, "%lu: %s: RESET_F: %lu\n", rno, msg, lno);
break;
case LOG_LINE_RESET_B:
memmove(&lno, p + sizeof(u_char), sizeof(db_recno_t));
vtrace(sp, "%lu: %s: RESET_B: %lu\n", rno, msg, lno);
break;
case LOG_MARK:
memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
vtrace(sp,
"%lu: %s: MARK: %u/%u\n", rno, msg, lm.lno, lm.cno);
break;
default:
abort();
}
}
#endif

31
dist/nvi/common/log.h vendored Normal file
View File

@@ -0,0 +1,31 @@
/* $NetBSD: log.h,v 1.1.1.2 2008/05/18 14:29:46 aymeric Exp $ */
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1992, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*
* Id: log.h,v 10.4 2002/06/08 21:00:33 skimo Exp (Berkeley) Date: 2002/06/08 21:00:33
*/
#define LOG_NOTYPE 0
#define LOG_CURSOR_INIT 2
#define LOG_CURSOR_END 3
#define LOG_LINE_APPEND_B 4
#define LOG_LINE_APPEND_F 5
#define LOG_LINE_DELETE_B 6
#define LOG_LINE_DELETE_F 7
#define LOG_LINE_RESET_B 8
#define LOG_LINE_RESET_F 9
#define LOG_MARK 10
typedef enum { UNDO_FORWARD, UNDO_BACKWARD, UNDO_SETLINE } undo_t;
struct _log_state {
int didop;
MARK pos;
undo_t undo;
};

458
dist/nvi/common/log4.c vendored Normal file
View File

@@ -0,0 +1,458 @@
/* $NetBSD: log4.c,v 1.1.1.2 2008/05/18 14:29:47 aymeric Exp $ */
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1992, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: log4.c,v 10.3 2002/06/08 21:00:33 skimo Exp";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <bitstring.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.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_F db_recno_t char *
* LOG_LINE_APPEND_B db_recno_t char *
* LOG_LINE_DELETE_F db_recno_t char *
* LOG_LINE_DELETE_B db_recno_t char *
* LOG_LINE_RESET_F db_recno_t char *
* LOG_LINE_RESET_B db_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 *, int));
/*
* log_init --
* Initialize the logging subsystem.
*
* PUBLIC: int log_init __P((SCR *, EXF *));
*/
int
log_init(SCR *sp, EXF *ep)
{
DB_LOGC *logc;
DBT data;
size_t nlen;
/*
* !!!
* ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
*
* 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.
*/
sp->wp->l_lp = NULL;
sp->wp->l_len = 0;
ep->l_cursor.lno = 1; /* XXX Any valid recno. */
ep->l_cursor.cno = 0;
ep->l_high = ep->l_cur = 1;
if ((sp->db_error = ep->env->log_cursor(ep->env, &logc, 0))
!= 0) {
msgq(sp, M_DBERR, "env->log_cursor");
F_SET(ep, F_NOLOG);
return (1);
}
nlen = 1024;
retry:
BINC_GOTO(sp, sp->wp->l_lp, sp->wp->l_len, nlen);
memset(&data, 0, sizeof(data));
data.data = sp->wp->l_lp;
data.ulen = sp->wp->l_len;
data.flags = DB_DBT_USERMEM;
switch ((sp->db_error =
logc->get(logc, &ep->lsn_first, &data, DB_LAST))) {
case ENOMEM:
nlen = data.size;
goto retry;
default:
alloc_err:
msgq(sp, M_DBERR, "logc->get");
F_SET(ep, F_NOLOG);
return (1);
case 0:
;
}
MEMCPY(&ep->lsn_cur, &ep->lsn_first, 1);
MEMCPY(&ep->lsn_high, &ep->lsn_first, 1);
logc->close(logc, 0);
ep->l_win = NULL;
/*LOCK_INIT(sp->wp, ep);*/
return (0);
}
/*
* log_end --
* Close the logging subsystem.
*
* PUBLIC: int log_end __P((SCR *, EXF *));
*/
int
log_end(SCR *sp, EXF *ep)
{
/*
* !!!
* ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
*/
/*LOCK_END(sp->wp, ep);*/
if (sp->wp->l_lp != NULL) {
free(sp->wp->l_lp);
sp->wp->l_lp = NULL;
}
sp->wp->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.
*
* PUBLIC: int log_cursor __P((SCR *));
*/
int
log_cursor(SCR *sp)
{
EXF *ep;
ep = sp->ep;
if (F_ISSET(ep, F_NOLOG))
return (0);
/*
* If any changes were made since the last cursor init,
* put out the ending cursor record.
*/
if (ep->l_cursor.lno == OOBLNO) {
if (ep->l_win && ep->l_win != sp->wp)
return 0;
ep->l_cursor.lno = sp->lno;
ep->l_cursor.cno = sp->cno;
ep->l_win = NULL;
return (log_cursor1(sp, 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(SCR *sp, int type)
{
DBT data, key;
EXF *ep;
ep = sp->ep;
/*
if (type == LOG_CURSOR_INIT &&
LOCK_TRY(sp->wp, ep))
return 1;
*/
if (type == LOG_CURSOR_INIT &&
(sp->db_error = __vi_log_truncate(ep)) != 0) {
msgq(sp, M_DBERR, "truncate");
return 1;
}
if ((sp->db_error =
__vi_cursor_log(ep->env, NULL, &ep->lsn_cur, 0, type,
ep->l_cursor.lno, ep->l_cursor.cno)) != 0) {
msgq(sp, M_DBERR, "cursor_log");
return 1;
}
if (type == LOG_CURSOR_END) {
MEMCPY(&ep->lsn_high, &ep->lsn_cur, 1);
/* XXXX should not be needed */
ep->env->log_flush(ep->env, NULL);
}
#if defined(DEBUG) && 0
vtrace(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;
/*
if (type == LOG_CURSOR_END)
LOCK_UNLOCK(sp->wp, ep);
*/
return (0);
}
/*
* log_line --
* Log a line change.
*
* PUBLIC: int log_line __P((SCR *, db_recno_t, u_int));
*/
int
log_line(SCR *sp, db_recno_t lno, u_int action)
{
DBT data, key;
EXF *ep;
size_t len;
CHAR_T *lp;
db_recno_t lcur;
ep = sp->ep;
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, LOG_CURSOR_INIT))
return (1);
ep->l_cursor.lno = OOBLNO;
ep->l_win = sp->wp;
} /*else if (ep->l_win != sp->wp) {
printf("log_line own: %p, this: %p\n", ep->l_win, sp->wp);
return 1;
}*/
if ((sp->db_error =
__vi_change_log(ep->env, NULL, &ep->lsn_cur, 0, action,
lno)) != 0) {
msgq(sp, M_DBERR, "change_log");
return 1;
}
#if defined(DEBUG) && 0
switch (action) {
case LOG_LINE_APPEND_F:
vtrace(sp, "%u: log_line: append_f: %lu {%u}\n",
ep->l_cur, lno, len);
break;
case LOG_LINE_APPEND_B:
vtrace(sp, "%u: log_line: append_b: %lu {%u}\n",
ep->l_cur, lno, len);
break;
case LOG_LINE_DELETE_F:
vtrace(sp, "%lu: log_line: delete_f: %lu {%u}\n",
ep->l_cur, lno, len);
break;
case LOG_LINE_DELETE_B:
vtrace(sp, "%lu: log_line: delete_b: %lu {%u}\n",
ep->l_cur, lno, len);
break;
case LOG_LINE_RESET_F:
vtrace(sp, "%lu: log_line: reset_f: %lu {%u}\n",
ep->l_cur, lno, len);
break;
case LOG_LINE_RESET_B:
vtrace(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.
*
* PUBLIC: int log_mark __P((SCR *, LMARK *));
*/
int
log_mark(SCR *sp, LMARK *lmp)
{
DBT data, key;
EXF *ep;
ep = sp->ep;
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, LOG_CURSOR_INIT))
return (1);
ep->l_cursor.lno = OOBLNO;
ep->l_win = sp->wp;
}
if ((sp->db_error =
__vi_mark_log(ep->env, NULL, &ep->lsn_cur, 0,
lmp)) != 0) {
msgq(sp, M_DBERR, "cursor_log");
return 1;
}
#if defined(DEBUG) && 0
vtrace(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.
*
* PUBLIC: int log_backward __P((SCR *, MARK *));
*/
int
log_backward(SCR *sp, MARK *rp)
{
EXF *ep;
LMARK lm;
MARK m;
db_recno_t lno;
int didop;
u_char *p;
size_t size;
ep = sp->ep;
if (F_ISSET(ep, F_NOLOG)) {
msgq(sp, M_ERR,
"010|Logging not being performed, undo not possible");
return (1);
}
if (log_compare(&ep->lsn_cur, &ep->lsn_first) <= 0) {
msgq(sp, M_BERR, "011|No changes to undo");
return (1);
}
return __vi_log_traverse(sp, UNDO_BACKWARD, rp);
}
/*
* 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.
*
* PUBLIC: int log_setline __P((SCR *));
*/
int
log_setline(SCR *sp)
{
EXF *ep;
LMARK lm;
MARK m;
db_recno_t lno;
u_char *p;
size_t size;
ep = sp->ep;
if (F_ISSET(ep, F_NOLOG)) {
msgq(sp, M_ERR,
"012|Logging not being performed, undo not possible");
return (1);
}
if (log_compare(&ep->lsn_cur, &ep->lsn_first) <= 0) {
msgq(sp, M_BERR, "011|No changes to undo");
return (1);
}
return __vi_log_traverse(sp, UNDO_SETLINE, &m);
}
/*
* Log_forward --
* Roll the log forward one operation.
*
* PUBLIC: int log_forward __P((SCR *, MARK *));
*/
int
log_forward(SCR *sp, MARK *rp)
{
EXF *ep;
LMARK lm;
MARK m;
db_recno_t lno;
int didop;
u_char *p;
size_t size;
ep = sp->ep;
if (F_ISSET(ep, F_NOLOG)) {
msgq(sp, M_ERR,
"013|Logging not being performed, roll-forward not possible");
return (1);
}
if (log_compare(&ep->lsn_cur, &ep->lsn_high) >= 0) {
msgq(sp, M_BERR, "014|No changes to re-do");
return (1);
}
return __vi_log_traverse(sp, UNDO_FORWARD, rp);
}

529
dist/nvi/common/main.c vendored Normal file
View File

@@ -0,0 +1,529 @@
/* $NetBSD: main.c,v 1.3 2008/12/05 22:51:42 christos Exp $ */
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1992, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char copyright[] =
"%Z% Copyright (c) 1992, 1993, 1994\n\
The Regents of the University of California. All rights reserved.\n\
%Z% Copyright (c) 1992, 1993, 1994, 1995, 1996\n\
Keith Bostic. All rights reserved.\n";
#endif /* not lint */
#ifndef lint
static const char sccsid[] = "Id: main.c,v 10.63 2001/11/01 15:24:43 skimo Exp (Berkeley) Date: 2001/11/01 15:24:43";
#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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "common.h"
#include "../vi/vi.h"
#include "pathnames.h"
static void v_estr __P((const char *, int, const char *));
static int v_obsolete __P((char *, char *[]));
/*
* editor --
* Main editor routine.
*
* PUBLIC: int editor __P((WIN *, int, char *[]));
*/
int
editor(WIN *wp, int argc, char **argv)
{
extern int optind;
extern char *optarg;
const char *p;
EVENT ev;
FREF *frp;
SCR *sp;
GS *gp;
size_t len;
u_int flags;
int ch, flagchk, lflag, secure, startup, readonly, rval, silent;
#ifdef GTAGS
int gtags = 0;
#endif
char *tag_f, *wsizearg, path[256];
const CHAR_T *w;
size_t wlen;
gp = wp->gp;
/* Initialize the busy routine, if not defined by the screen. */
if (gp->scr_busy == NULL)
gp->scr_busy = vs_busy;
/* Initialize the message routine, if not defined by the screen. */
if (wp->scr_msg == NULL)
wp->scr_msg = vs_msg;
/* Set initial screen type and mode based on the program name. */
readonly = 0;
if (!strcmp(gp->progname, "ex") || !strcmp(gp->progname, "nex"))
LF_INIT(SC_EX);
else {
/* Nview, view are readonly. */
if (!strcmp(gp->progname, "nview") ||
!strcmp(gp->progname, "view"))
readonly = 1;
/* Vi is the default. */
LF_INIT(SC_VI);
}
/* Convert old-style arguments into new-style ones. */
if (v_obsolete(gp->progname, argv))
return (1);
/* Parse the arguments. */
flagchk = '\0';
tag_f = wsizearg = NULL;
lflag = secure = silent = 0;
startup = 1;
/* Set the file snapshot flag. */
F_SET(gp, G_SNAPSHOT);
while ((ch = getopt(argc, argv, "c:"
#ifdef DEBUG
"D:"
#endif
"eF"
#ifdef GTAGS
"G"
#endif
"lRrSsT:t:vw:")) != EOF)
switch (ch) {
case 'c': /* Run the command. */
/*
* XXX
* We should support multiple -c options.
*/
if (gp->c_option != NULL) {
v_estr(gp->progname, 0,
"only one -c command may be specified.");
return (1);
}
gp->c_option = optarg;
break;
#ifdef DEBUG
case 'D':
switch (optarg[0]) {
case 's':
startup = 0;
break;
case 'w':
attach(gp);
break;
default:
v_estr(gp->progname, 0,
"usage: -D requires s or w argument.");
return (1);
}
break;
#endif
case 'e': /* Ex mode. */
LF_CLR(SC_VI);
LF_SET(SC_EX);
break;
case 'F': /* No snapshot. */
v_estr(gp->progname, 0,
"-F option no longer supported.");
break;
case 'l': /* Set lisp, showmatch options. */
lflag = 1;
break;
#ifdef GTAGS
case 'G': /* gtags mode. */
gtags = 1;
break;
#endif
case 'R': /* Readonly. */
readonly = 1;
break;
case 'r': /* Recover. */
if (flagchk == 't') {
v_estr(gp->progname, 0,
"only one of -r and -t may be specified.");
return (1);
}
flagchk = 'r';
break;
case 'S':
secure = 1;
break;
case 's':
silent = 1;
break;
#ifdef TRACE
case 'T': /* Trace. */
(void)vtrace_init(optarg);
break;
#endif
case 't': /* Tag. */
if (flagchk == 'r') {
v_estr(gp->progname, 0,
"only one of -r and -t may be specified.");
return (1);
}
if (flagchk == 't') {
v_estr(gp->progname, 0,
"only one tag file may be specified.");
return (1);
}
flagchk = 't';
tag_f = optarg;
break;
case 'v': /* Vi mode. */
LF_CLR(SC_EX);
LF_SET(SC_VI);
break;
case 'w':
wsizearg = optarg;
break;
case '?':
default:
(void)gp->scr_usage();
return (1);
}
argc -= optind;
argv += optind;
/*
* -s option is only meaningful to ex.
*
* If not reading from a terminal, it's like -s was specified.
*/
if (silent && !LF_ISSET(SC_EX)) {
v_estr(gp->progname, 0, "-s option is only applicable to ex.");
goto err;
}
if (LF_ISSET(SC_EX) && F_ISSET(gp, G_SCRIPTED))
silent = 1;
/*
* 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.
*
* !!!
* Everything we do until we go interactive is done in ex mode.
*/
if (screen_init(gp, NULL, &sp)) {
if (sp != NULL) {
CIRCLEQ_INSERT_HEAD(&wp->scrq, sp, q);
sp->wp = wp;
}
goto err;
}
F_SET(sp, SC_EX);
CIRCLEQ_INSERT_HEAD(&wp->scrq, sp, q);
sp->wp = wp;
if (v_key_init(sp)) /* Special key initialization. */
goto err;
{ int oargs[5], *oargp = oargs;
if (lflag) { /* Command-line options. */
*oargp++ = O_LISP;
*oargp++ = O_SHOWMATCH;
}
if (readonly)
*oargp++ = O_READONLY;
#ifdef GTAGS
if (gtags)
*oargp++ = O_GTAGSMODE;
#endif
if (secure)
*oargp++ = O_SECURE;
*oargp = -1; /* Options initialization. */
if (opts_init(sp, oargs))
goto err;
}
if (wsizearg != NULL) {
ARGS *av[2], a, b;
(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;
(void)opts_set(sp, av, NULL);
}
if (silent) { /* Ex batch mode option values. */
O_CLR(sp, O_AUTOPRINT);
O_CLR(sp, O_PROMPT);
O_CLR(sp, O_VERBOSE);
O_CLR(sp, O_WARN);
F_SET(sp, SC_EX_SILENT);
}
sp->rows = O_VAL(sp, O_LINES); /* Make ex formatting work. */
sp->cols = O_VAL(sp, O_COLUMNS);
if (!silent && startup) { /* Read EXINIT, exrc files. */
if (ex_exrc(sp))
goto err;
if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
if (screen_end(sp))
goto err;
goto done;
}
}
/*
* List recovery files if -r specified without file arguments.
* Note, options must be initialized and startup information
* read before doing this.
*/
if (flagchk == 'r' && argv[0] == NULL) {
if (rcv_list(sp))
goto err;
if (screen_end(sp))
goto err;
goto done;
}
/*
* !!!
* Initialize the default ^D, ^U scrolling value here, after the
* user has had every opportunity to set the window option.
*
* It's historic practice that changing the value of the window
* option did not alter the default scrolling value, only giving
* a count to ^D/^U did that.
*/
sp->defscroll = (O_VAL(sp, O_WINDOW) + 1) / 2;
/*
* If we don't have a command-line option, switch into the right
* editor now, so that we position default files correctly, and
* so that any tags file file-already-locked messages are in the
* vi screen, not the ex screen.
*
* XXX
* If we have a command-line option, the error message can end
* up in the wrong place, but I think that the combination is
* unlikely.
*/
if (gp->c_option == NULL) {
F_CLR(sp, SC_EX | SC_VI);
F_SET(sp, LF_ISSET(SC_EX | SC_VI));
}
/* Open a tag file if specified. */
if (tag_f != NULL) {
CHAR2INT(sp, tag_f, strlen(tag_f) + 1, w, wlen);
if (ex_tag_first(sp, w))
goto err;
}
/*
* Append any remaining arguments as file names. Files are recovery
* files if -r specified. If the tag option or ex startup commands
* loaded a file, then any file arguments are going to come after it.
*/
if (*argv != NULL) {
if (sp->frp != NULL) {
/* Cheat -- we know we have an extra argv slot. */
MALLOC_NOMSG(sp,
*--argv, char *, strlen(sp->frp->name) + 1);
if (*argv == NULL) {
v_estr(gp->progname, errno, NULL);
goto err;
}
(void)strcpy(*argv, sp->frp->name);
}
sp->argv = sp->cargv = argv;
F_SET(sp, SC_ARGNOFREE);
if (flagchk == 'r')
F_SET(sp, SC_ARGRECOVER);
}
/*
* If the ex startup commands and or/the tag option haven't already
* created a file, create one. If no command-line files were given,
* use a temporary file.
*/
if (sp->frp == NULL) {
if (sp->argv == NULL) {
if ((frp = file_add(sp, NULL)) == NULL)
goto err;
} else {
if ((frp = file_add(sp, sp->argv[0])) == NULL)
goto err;
if (F_ISSET(sp, SC_ARGRECOVER))
F_SET(frp, FR_RECOVER);
}
if (file_init(sp, frp, NULL, 0))
goto err;
if (EXCMD_RUNNING(wp)) {
(void)ex_cmd(sp);
if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
if (screen_end(sp))
goto err;
goto done;
}
}
}
/*
* Check to see if we need to wait for ex. If SC_SCR_EX is set, ex
* was forced to initialize the screen during startup. We'd like to
* wait for a single character from the user, but we can't because
* we're not in raw mode. We can't switch to raw mode because the
* vi initialization will switch to xterm's alternate screen, causing
* us to lose the messages we're pausing to make sure the user read.
* So, wait for a complete line.
*/
if (F_ISSET(sp, SC_SCR_EX)) {
p = msg_cmsg(sp, CMSG_CONT_R, &len);
(void)write(STDOUT_FILENO, p, len);
for (;;) {
if (v_event_get(sp, &ev, 0, 0))
goto err;
if (ev.e_event == E_INTERRUPT ||
(ev.e_event == E_CHARACTER &&
(ev.e_value == K_CR || ev.e_value == K_NL)))
break;
(void)gp->scr_bell(sp);
}
}
/* Switch into the right editor, regardless. */
F_CLR(sp, SC_EX | SC_VI);
F_SET(sp, LF_ISSET(SC_EX | SC_VI) | SC_STATUS_CNT);
/*
* Main edit loop. Vi handles split screens itself, we only return
* here when switching editor modes or restarting the screen.
*/
while (sp != NULL)
if (F_ISSET(sp, SC_EX) ? ex(&sp) : vi(&sp))
goto err;
done: rval = 0;
if (0)
err: rval = 1;
return (rval);
}
/*
* v_obsolete --
* Convert historic arguments into something getopt(3) will like.
*/
static int
v_obsolete(char *name, 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 modifies the
* strings.
* Change "+" into "-c$".
* Change "+<anything else>" into "-c<anything else>".
* Change "-" into "-s"
* The c, T, t and w options take arguments so they can't be
* special arguments.
*
* Stop if we find "--" as an argument, the user may want to edit
* a file named "+foo".
*/
while (*++argv && strcmp(argv[0], "--"))
if (argv[0][0] == '+') {
if (argv[0][1] == '\0') {
MALLOC_NOMSG(NULL, argv[0], char *, 4);
if (argv[0] == NULL)
goto nomem;
(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)
goto nomem;
argv[0][0] = '-';
argv[0][1] = 'c';
(void)strcpy(argv[0] + 2, p + 1);
}
} else if (argv[0][0] == '-') {
if (argv[0][1] == '\0') {
MALLOC_NOMSG(NULL, argv[0], char *, 3);
if (argv[0] == NULL) {
nomem: v_estr(name, errno, NULL);
return (1);
}
(void)strcpy(argv[0], "-s");
} else
if ((argv[0][1] == 'c' || argv[0][1] == 'T' ||
argv[0][1] == 't' || argv[0][1] == 'w') &&
argv[0][2] == '\0')
++argv;
}
return (0);
}
#ifdef DEBUG
static void
attach(GS *gp)
{
int fd;
char ch;
if ((fd = open(_PATH_TTY, O_RDONLY, 0)) < 0) {
v_estr(gp->progname, errno, _PATH_TTY);
return;
}
(void)printf("process %lu waiting, enter <CR> to continue: ",
(u_long)getpid());
(void)fflush(stdout);
do {
if (read(fd, &ch, 1) != 1) {
(void)close(fd);
return;
}
} while (ch != '\n' && ch != '\r');
(void)close(fd);
}
#endif
static void
v_estr(const char *name, int eno, const char *msg)
{
(void)fprintf(stderr, "%s", name);
if (msg != NULL)
(void)fprintf(stderr, ": %s", msg);
if (eno)
(void)fprintf(stderr, ": %s", strerror(errno));
(void)fprintf(stderr, "\n");
}

263
dist/nvi/common/mark.c vendored Normal file
View File

@@ -0,0 +1,263 @@
/* $NetBSD: mark.c,v 1.3 2009/01/18 03:45:50 lukem Exp $ */
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1992, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: mark.c,v 10.15 2001/06/25 15:19:11 skimo Exp (Berkeley) Date: 2001/06/25 15:19:11";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <bitstring.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
static LMARK *mark_find __P((SCR *, 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 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.
*
* PUBLIC: int mark_init __P((SCR *, EXF *));
*/
int
mark_init(SCR *sp, EXF *ep)
{
/*
* !!!
* ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
*
* Set up the marks.
*/
LIST_INIT(&ep->marks);
return (0);
}
/*
* mark_end --
* Free up the marks.
*
* PUBLIC: int mark_end __P((SCR *, EXF *));
*/
int
mark_end(SCR *sp, EXF *ep)
{
LMARK *lmp;
/*
* !!!
* ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
*/
while ((lmp = ep->marks.lh_first) != NULL) {
LIST_REMOVE(lmp, q);
free(lmp);
}
return (0);
}
/*
* mark_get --
* Get the location referenced by a mark.
*
* PUBLIC: int mark_get __P((SCR *, ARG_CHAR_T, MARK *, mtype_t));
*/
int
mark_get(SCR *sp, ARG_CHAR_T key, MARK *mp, mtype_t mtype)
{
LMARK *lmp;
if (key == ABSMARK2)
key = ABSMARK1;
lmp = mark_find(sp, key);
if (lmp == NULL || (ARG_CHAR_T)lmp->name != key) {
msgq(sp, mtype, "017|Mark %s: not set", KEY_NAME(sp, key));
return (1);
}
if (F_ISSET(lmp, MARK_DELETED)) {
msgq(sp, mtype,
"018|Mark %s: the line was deleted", KEY_NAME(sp, key));
return (1);
}
/*
* !!!
* The absolute mark is initialized to lno 1/cno 0, and historically
* you could use it in an empty file. Make such a mark always work.
*/
if ((lmp->lno != 1 || lmp->cno != 0) && !db_exist(sp, lmp->lno)) {
msgq(sp, mtype,
"019|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.
*
* PUBLIC: int mark_set __P((SCR *, ARG_CHAR_T, MARK *, int));
*/
int
mark_set(SCR *sp, 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, key);
if (lmp == NULL || (ARG_CHAR_T)lmp->name != key) {
MALLOC_RET(sp, lmt, LMARK *, sizeof(LMARK));
if (lmp == NULL) {
LIST_INSERT_HEAD(&sp->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(SCR *sp, ARG_CHAR_T key)
{
LMARK *lmp, *lastlmp;
/*
* Return the requested mark or the slot immediately before
* where it should go.
*/
for (lastlmp = NULL, lmp = sp->ep->marks.lh_first;
lmp != NULL; lastlmp = lmp, lmp = lmp->q.le_next)
if ((ARG_CHAR_T)lmp->name >= key)
return ((ARG_CHAR_T)lmp->name == key ? lmp : lastlmp);
return (lastlmp);
}
/*
* mark_insdel --
* Update the marks based on an insertion or deletion.
*
* PUBLIC: int mark_insdel __P((SCR *, lnop_t, db_recno_t));
*/
int
mark_insdel(SCR *sp, lnop_t op, db_recno_t lno)
{
LMARK *lmp;
db_recno_t lline;
switch (op) {
case LINE_APPEND:
/* All insert/append operations are done as inserts. */
abort();
case LINE_DELETE:
for (lmp = sp->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, lmp);
} else
--lmp->lno;
}
break;
case LINE_INSERT:
/*
* XXX
* Very nasty special case. If the file was empty, then we're
* adding the first line, which is a replacement. So, we don't
* modify the marks. This is a hack to make:
*
* mz:r!echo foo<carriage-return>'z
*
* work, i.e. historically you could mark the "line" in an empty
* file and replace it, and continue to use the mark. Insane,
* well, yes, I know, but someone complained.
*
* Check for line #2 before going to the end of the file.
*/
if (!db_exist(sp, 2)) {
if (db_last(sp, &lline))
return (1);
if (lline == 1)
return (0);
}
for (lmp = sp->ep->marks.lh_first;
lmp != NULL; lmp = lmp->q.le_next)
if (lmp->lno >= lno)
++lmp->lno;
break;
case LINE_RESET:
break;
}
return (0);
}

45
dist/nvi/common/mark.h vendored Normal file
View File

@@ -0,0 +1,45 @@
/* $NetBSD: mark.h,v 1.1.1.2 2008/05/18 14:29:47 aymeric Exp $ */
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1992, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*
* Id: mark.h,v 10.5 2000/07/14 14:29:16 skimo Exp (Berkeley) Date: 2000/07/14 14:29:16
*/
/*
* 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 db_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. */
db_recno_t lno; /* Line number. */
size_t cno; /* Column number. */
};
struct _lmark {
LIST_ENTRY(_lmark) q; /* Linked list of marks. */
db_recno_t lno; /* Line number. */
size_t cno; /* Column number. */
/* XXXX Needed ? Can non ascii-chars be mark names ? */
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. */

207
dist/nvi/common/mem.h vendored Normal file
View File

@@ -0,0 +1,207 @@
/* $NetBSD: mem.h,v 1.3 2009/01/18 03:43:45 lukem Exp $ */
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*
* Id: mem.h,v 10.13 2002/01/05 23:13:37 skimo Exp (Berkeley) Date: 2002/01/05 23:13:37
*/
#if defined(HAVE_GCC) && !defined(__NetBSD__)
#define CHECK_TYPE(type, var) \
do { type L__lp __attribute__((__unused__)) = var; } while (/*CONSTCOND*/0);
#else
#define CHECK_TYPE(type, var)
#endif
/* Increase the size of a malloc'd buffer. Two versions, one that
* returns, one that jumps to an error label.
*/
#define BINC_GOTO(sp, type, lp, llen, nlen) { \
CHECK_TYPE(type *, lp) \
void *L__bincp; \
if ((nlen) > llen) { \
if ((L__bincp = binc(sp, lp, &(llen), nlen)) == NULL) \
goto alloc_err; \
/* \
* !!! \
* Possible pointer conversion. \
*/ \
lp = L__bincp; \
} \
}
#define BINC_GOTOC(sp, lp, llen, nlen) \
BINC_GOTO(sp, char, lp, llen, nlen)
#define BINC_GOTOW(sp, lp, llen, nlen) \
BINC_GOTO(sp, CHAR_T, lp, llen, (nlen) * sizeof(CHAR_T))
#define BINC_RET(sp, type, lp, llen, nlen) { \
CHECK_TYPE(type *, lp) \
void *L__bincp; \
if ((size_t)(nlen) > llen) { \
if ((L__bincp = binc(sp, lp, &(llen), nlen)) == NULL) \
return (1); \
/* \
* !!! \
* Possible pointer conversion. \
*/ \
lp = L__bincp; \
} \
}
#define BINC_RETC(sp, lp, llen, nlen) \
BINC_RET(sp, char, lp, llen, nlen)
#define BINC_RETW(sp, lp, llen, nlen) \
BINC_RET(sp, CHAR_T, lp, llen, (nlen) * sizeof(CHAR_T))
/*
* 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, type, bp, blen, nlen) { \
CHECK_TYPE(type *, bp) \
WIN *L__wp = (sp) == NULL ? NULL : (sp)->wp; \
if (L__wp == NULL || F_ISSET(L__wp, W_TMP_INUSE)) { \
bp = NULL; \
blen = 0; \
BINC_GOTO(sp, type, bp, blen, nlen); \
} else { \
BINC_GOTOC(sp, L__wp->tmp_bp, L__wp->tmp_blen, nlen); \
bp = (type *) L__wp->tmp_bp; \
blen = L__wp->tmp_blen; \
F_SET(L__wp, W_TMP_INUSE); \
} \
}
#define GET_SPACE_GOTOC(sp, bp, blen, nlen) \
GET_SPACE_GOTO(sp, char, bp, blen, nlen)
#define GET_SPACE_GOTOW(sp, bp, blen, nlen) \
GET_SPACE_GOTO(sp, CHAR_T, bp, blen, (nlen) * sizeof(CHAR_T))
#define GET_SPACE_RET(sp, type, bp, blen, nlen) { \
CHECK_TYPE(type *, bp) \
WIN *L__wp = (sp) == NULL ? NULL : (sp)->wp; \
if (L__wp == NULL || F_ISSET(L__wp, W_TMP_INUSE)) { \
bp = NULL; \
blen = 0; \
BINC_RET(sp, type, bp, blen, nlen); \
} else { \
BINC_RETC(sp, L__wp->tmp_bp, L__wp->tmp_blen, nlen); \
bp = (type *) L__wp->tmp_bp; \
blen = L__wp->tmp_blen; \
F_SET(L__wp, W_TMP_INUSE); \
} \
}
#define GET_SPACE_RETC(sp, bp, blen, nlen) \
GET_SPACE_RET(sp, char, bp, blen, nlen)
#define GET_SPACE_RETW(sp, bp, blen, nlen) \
GET_SPACE_RET(sp, CHAR_T, bp, blen, (nlen) * sizeof(CHAR_T))
/*
* 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, type, bp, blen, nlen) { \
WIN *L__wp = (sp) == NULL ? NULL : (sp)->wp; \
CHECK_TYPE(type *, bp) \
if (L__wp == NULL || bp == (type *)L__wp->tmp_bp) { \
F_CLR(L__wp, W_TMP_INUSE); \
BINC_GOTOC(sp, L__wp->tmp_bp, L__wp->tmp_blen, nlen); \
bp = (type *) L__wp->tmp_bp; \
blen = L__wp->tmp_blen; \
F_SET(L__wp, W_TMP_INUSE); \
} else \
BINC_GOTO(sp, type, bp, blen, nlen); \
}
#define ADD_SPACE_GOTOW(sp, bp, blen, nlen) \
ADD_SPACE_GOTO(sp, CHAR_T, bp, blen, (nlen) * sizeof(CHAR_T))
#define ADD_SPACE_RET(sp, type, bp, blen, nlen) { \
CHECK_TYPE(type *, bp) \
WIN *L__wp = (sp) == NULL ? NULL : (sp)->wp; \
if (L__wp == NULL || bp == (type *)L__wp->tmp_bp) { \
F_CLR(L__wp, W_TMP_INUSE); \
BINC_RETC(sp, L__wp->tmp_bp, L__wp->tmp_blen, nlen); \
bp = (type *) L__wp->tmp_bp; \
blen = L__wp->tmp_blen; \
F_SET(L__wp, W_TMP_INUSE); \
} else \
BINC_RET(sp, type, bp, blen, nlen); \
}
#define ADD_SPACE_RETW(sp, bp, blen, nlen) \
ADD_SPACE_RET(sp, CHAR_T, bp, blen, (nlen) * sizeof(CHAR_T))
/* Free a GET_SPACE returned buffer. */
#define FREE_SPACE(sp, bp, blen) { \
WIN *L__wp = (sp) == NULL ? NULL : (sp)->wp; \
if (L__wp != NULL && bp == L__wp->tmp_bp) \
F_CLR(L__wp, W_TMP_INUSE); \
else \
free(bp); \
}
#define FREE_SPACEW(sp, bp, blen) { \
CHECK_TYPE(CHAR_T *, bp) \
FREE_SPACE(sp, (char *)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(sp, p, cast, nmemb, size) { \
if ((p = (cast)calloc(nmemb, size)) == NULL) \
msgq(sp, M_SYSERR, NULL); \
}
#define CALLOC_GOTO(sp, p, cast, nmemb, size) { \
if ((p = (cast)calloc(nmemb, size)) == NULL) \
goto alloc_err; \
}
#define CALLOC_NOMSG(sp, p, cast, nmemb, size) { \
p = (cast)calloc(nmemb, size); \
}
#define CALLOC_RET(sp, p, cast, nmemb, size) { \
if ((p = (cast)calloc(nmemb, size)) == NULL) { \
msgq(sp, M_SYSERR, NULL); \
return (1); \
} \
}
#define MALLOC(sp, p, cast, size) { \
if ((p = (cast)malloc(size)) == NULL) \
msgq(sp, M_SYSERR, NULL); \
}
#define MALLOC_GOTO(sp, p, cast, size) { \
if ((p = (cast)malloc(size)) == NULL) \
goto alloc_err; \
}
#define MALLOC_NOMSG(sp, p, cast, size) { \
p = (cast)malloc(size); \
}
#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)))

929
dist/nvi/common/msg.c vendored Normal file
View File

@@ -0,0 +1,929 @@
/* $NetBSD: msg.c,v 1.5 2011/08/17 12:56:55 christos Exp $ */
/*-
* Copyright (c) 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1991, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: msg.c,v 10.61 2003/07/18 23:17:30 skimo Exp (Berkeley) Date: 2003/07/18 23:17:30";
#endif /* not lint */
#include <sys/param.h>
#include <sys/types.h> /* XXX: param.h may not have included types.h */
#include <sys/queue.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <bitstring.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifdef __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif
#include "common.h"
#include "dbinternal.h"
#include "../vi/vi.h"
/*
* msgq --
* Display a message.
*
* PUBLIC: void msgq __P((SCR *, mtype_t, const char *, ...));
*/
void
#ifdef __STDC__
msgq(SCR *sp, mtype_t mt, const char *fmt, ...)
#else
msgq(sp, mt, fmt, va_alist)
SCR *sp;
mtype_t mt;
const char *fmt;
va_dcl
#endif
{
#ifndef NL_ARGMAX
#define __NL_ARGMAX 20 /* Set to 9 by System V. */
struct {
const char *str; /* String pointer. */
size_t arg; /* Argument number. */
size_t prefix; /* Prefix string length. */
size_t skip; /* Skipped string length. */
size_t suffix; /* Suffix string length. */
} str[__NL_ARGMAX];
#endif
static int reenter; /* STATIC: Re-entrancy check. */
GS *gp;
WIN *wp = NULL;
size_t blen, len, mlen, nlen;
const char *p;
char *bp, *mp;
va_list ap;
#ifndef NL_ARGMAX
int ch;
char *rbp, *s_rbp;
const char *t, *u;
size_t cnt1, cnt2, soff;
#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 put the
* message out to stderr.
*/
if (sp == NULL) {
gp = NULL;
if (mt == M_BERR)
mt = M_ERR;
else if (mt == M_VINFO)
mt = M_INFO;
} else {
gp = sp->gp;
wp = sp->wp;
switch (mt) {
case M_BERR:
if (F_ISSET(sp, SC_VI) && !O_ISSET(sp, O_VERBOSE)) {
F_SET(gp, G_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, SC_EX_SILENT))
return;
break;
case M_ERR:
case M_SYSERR:
case M_DBERR:
break;
default:
abort();
}
}
/*
* It's possible to reenter msg when it allocates space. We're
* probably dead anyway, but there's no reason to drop core.
*
* XXX
* Yes, there's a race, but it should only be two instructions.
*/
if (reenter++)
return;
/* Get space for the message. */
nlen = 1024;
if (0) {
retry: FREE_SPACE(sp, bp, blen);
nlen *= 2;
}
bp = NULL;
blen = 0;
GET_SPACE_GOTOC(sp, bp, blen, nlen);
/*
* Error prefix.
*
* mp: pointer to the current next character to be written
* mlen: length of the already written characters
* blen: total length of the buffer
*/
#define REM (blen - mlen)
mp = bp;
mlen = 0;
if (mt == M_SYSERR || mt == M_DBERR) {
p = msg_cat(sp, "020|Error: ", &len);
if (REM < len)
goto retry;
memcpy(mp, p, len);
mp += len;
mlen += len;
}
/*
* If we're running an ex command that the user didn't enter, display
* the file name and line number prefix.
*/
if ((mt == M_ERR || mt == M_SYSERR) &&
sp != NULL && wp != NULL && wp->if_name != NULL) {
for (p = wp->if_name; *p != '\0'; ++p) {
len = snprintf(mp, REM, "%s", KEY_NAME(sp, *p));
mp += len;
if ((mlen += len) > blen)
goto retry;
}
len = snprintf(mp, REM, ", %d: ", wp->if_lno);
mp += len;
if ((mlen += len) > blen)
goto retry;
}
/* If nothing to format, we're done. */
if (fmt == NULL)
goto nofmt;
fmt = msg_cat(sp, fmt, NULL);
#ifndef NL_ARGMAX
/*
* Nvi should run on machines that don't support the numbered argument
* specifications (%[digit]*$). We do this by reformatting the string
* so that we can hand it to vsprintf(3) and it will use the arguments
* in the right order. When vsprintf returns, we put the string back
* into the right order. It's undefined, according to SVID III, to mix
* numbered argument specifications with the standard style arguments,
* so this should be safe.
*
* In addition, we also need a character that is known to not occur in
* any vi message, for separating the parts of the string. As callers
* of msgq are responsible for making sure that all the non-printable
* characters are formatted for printing before calling msgq, we use a
* random non-printable character selected at terminal initialization
* time. This code isn't fast by any means, but as messages should be
* relatively short and normally have only a few arguments, it won't be
* too bad. Regardless, nobody has come up with any other solution.
*
* The result of this loop is an array of pointers into the message
* string, with associated lengths and argument numbers. The array
* is in the "correct" order, and the arg field contains the argument
* order.
*/
for (p = fmt, soff = 0; soff < __NL_ARGMAX;) {
for (t = p; *p != '\0' && *p != '%'; ++p);
if (*p == '\0')
break;
++p;
if (!isdigit((unsigned char)*p)) {
if (*p == '%')
++p;
continue;
}
for (u = p; *++p != '\0' && isdigit((unsigned char)*p););
if (*p != '$')
continue;
/* Up to, and including the % character. */
str[soff].str = t;
str[soff].prefix = u - t;
/* Up to, and including the $ character. */
str[soff].arg = atoi(u);
str[soff].skip = (p - u) + 1;
if (str[soff].arg >= __NL_ARGMAX)
goto ret;
/* Up to, and including the conversion character. */
for (u = p; (ch = (unsigned char)*++p) != '\0';)
if (isalpha(ch) &&
strchr("diouxXfeEgGcspn", ch) != NULL)
break;
str[soff].suffix = p - u;
if (ch != '\0')
++p;
++soff;
}
/* If no magic strings, we're done. */
if (soff == 0)
goto format;
/* Get space for the reordered strings. */
if ((rbp = malloc(nlen)) == NULL)
goto ret;
s_rbp = rbp;
/*
* Reorder the strings into the message string based on argument
* order.
*
* !!!
* We ignore arguments that are out of order, i.e. if we don't find
* an argument, we continue. Assume (almost certainly incorrectly)
* that whoever created the string knew what they were doing.
*
* !!!
* Brute force "sort", but since we don't expect more than one or two
* arguments in a string, the setup cost of a fast sort will be more
* expensive than the loop.
*/
for (cnt1 = 1; cnt1 <= soff; ++cnt1)
for (cnt2 = 0; cnt2 < soff; ++cnt2)
if (cnt1 == str[cnt2].arg) {
memmove(s_rbp, str[cnt2].str, str[cnt2].prefix);
memmove(s_rbp + str[cnt2].prefix,
str[cnt2].str + str[cnt2].prefix +
str[cnt2].skip, str[cnt2].suffix);
s_rbp += str[cnt2].prefix + str[cnt2].suffix;
*s_rbp++ =
gp == NULL ? DEFAULT_NOPRINT : gp->noprint;
break;
}
*s_rbp = '\0';
fmt = rbp;
#endif
#ifndef NL_ARGMAX
format: /* Format the arguments into the string. */
#endif
#ifdef __STDC__
va_start(ap, fmt);
#else
va_start(ap);
#endif
len = vsnprintf(mp, REM, fmt, ap);
va_end(ap);
if (len >= nlen)
goto retry;
#ifndef NL_ARGMAX
if (soff == 0)
goto nofmt;
/*
* Go through the resulting string, and, for each separator character
* separated string, enter its new starting position and length in the
* array.
*/
for (p = t = mp, cnt1 = 1,
ch = gp == NULL ? DEFAULT_NOPRINT : gp->noprint; *p != '\0'; ++p)
if (*p == ch) {
for (cnt2 = 0; cnt2 < soff; ++cnt2)
if (str[cnt2].arg == cnt1)
break;
str[cnt2].str = t;
str[cnt2].prefix = p - t;
t = p + 1;
++cnt1;
}
/*
* Reorder the strings once again, putting them back into the
* message buffer.
*
* !!!
* Note, the length of the message gets decremented once for
* each substring, when we discard the separator character.
*/
for (s_rbp = rbp, cnt1 = 0; cnt1 < soff; ++cnt1) {
memmove(rbp, str[cnt1].str, str[cnt1].prefix);
rbp += str[cnt1].prefix;
--len;
}
memmove(mp, s_rbp, rbp - s_rbp);
/* Free the reordered string memory. */
free(s_rbp);
#endif
nofmt: mp += len;
if ((mlen += len) > blen)
goto retry;
if (mt == M_SYSERR) {
len = snprintf(mp, REM, ": %s", strerror(errno));
mp += len;
if ((mlen += len) > blen)
goto retry;
mt = M_ERR;
}
if (mt == M_DBERR) {
len = snprintf(mp, REM, ": %s", db_strerror(sp->db_error));
mp += len;
if ((mlen += len) > blen)
goto retry;
mt = M_ERR;
}
/* Add trailing newline. */
if ((mlen += 1) > blen)
goto retry;
*mp = '\n';
if (sp != NULL && sp->ep != NULL)
(void)ex_fflush(sp);
if (wp != NULL)
wp->scr_msg(sp, mt, bp, mlen);
else
(void)fprintf(stderr, "%.*s", (int)mlen, bp);
/* Cleanup. */
#ifndef NL_ARGMAX
ret:
#endif
FREE_SPACE(sp, bp, blen);
alloc_err:
reenter = 0;
}
/*
* msgq_str --
* Display a message with an embedded string.
*
* PUBLIC: void msgq_wstr __P((SCR *, mtype_t, const CHAR_T *, const char *));
*/
void
msgq_wstr(SCR *sp, mtype_t mtype, const CHAR_T *str, const char *fmt)
{
size_t nlen;
const char *nstr;
if (str == NULL) {
msgq(sp, mtype, "%s", fmt);
return;
}
INT2CHAR(sp, str, STRLEN(str) + 1, nstr, nlen);
msgq_str(sp, mtype, nstr, fmt);
}
/*
* msgq_str --
* Display a message with an embedded string.
*
* PUBLIC: void msgq_str __P((SCR *, mtype_t, char *, char *));
*/
void
msgq_str(SCR *sp, mtype_t mtype, const char *str, const char *fmt)
{
int nf, sv_errno;
char *p;
if (str == NULL) {
msgq(sp, mtype, "%s", fmt);
return;
}
sv_errno = errno;
p = msg_print(sp, str, &nf);
errno = sv_errno;
msgq(sp, mtype, fmt, p);
if (nf)
FREE_SPACE(sp, p, 0);
}
/*
* mod_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 wasn't true -- edit a large file and do "100d|1". We don't implement
* this semantic since it requires tracking 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.
*
* PUBLIC: void mod_rpt __P((SCR *));
*/
void
mod_rpt(SCR *sp)
{
static const char * const action[] = {
"293|added",
"294|changed",
"295|deleted",
"296|joined",
"297|moved",
"298|shifted",
"299|yanked",
};
static const char * const lines[] = {
"300|line",
"301|lines",
};
db_recno_t total;
u_long rptval;
int first;
size_t cnt, blen, len, tlen;
const char *t;
const char * const *ap;
char *bp, *p;
/* Change reports are turned off in batch mode. */
if (F_ISSET(sp, SC_EX_SILENT))
return;
/* Reset changing line number. */
sp->rptlchange = OOBLNO;
/*
* Don't build a message if not enough changed.
*
* !!!
* And now, a vi clone test. Historically, vi reported if the number
* of changed lines was > than the value, not >=, unless it was a yank
* command, which used >=. No lie. Furthermore, an action was never
* reported for a single line action. This is consistent for actions
* other than yank, but yank didn't report single line actions even if
* the report edit option was set to 1. In addition, setting report to
* 0 in the 4BSD historic vi was equivalent to setting it to 1, for an
* unknown reason (this bug was fixed in System III/V at some point).
* I got complaints, so nvi conforms to System III/V historic practice
* except that we report a yank of 1 line if report is set to 1.
*/
#define ARSIZE(a) sizeof(a) / sizeof (*a)
#define MAXNUM 25
rptval = O_VAL(sp, O_REPORT);
for (cnt = 0, total = 0; cnt < ARSIZE(action); ++cnt)
total += sp->rptlines[cnt];
if (total == 0)
return;
if (total <= rptval && sp->rptlines[L_YANKED] < rptval) {
for (cnt = 0; cnt < ARSIZE(action); ++cnt)
sp->rptlines[cnt] = 0;
return;
}
/* Build and display the message. */
GET_SPACE_GOTOC(sp, bp, blen, sizeof(action) * MAXNUM + 1);
for (p = bp, first = 1, tlen = 0,
ap = action, cnt = 0; cnt < ARSIZE(action); ++ap, ++cnt)
if (sp->rptlines[cnt] != 0) {
if (first)
first = 0;
else {
*p++ = ';';
*p++ = ' ';
tlen += 2;
}
len = snprintf(p, MAXNUM, "%lu ",
(unsigned long) sp->rptlines[cnt]);
p += len;
tlen += len;
t = msg_cat(sp,
lines[sp->rptlines[cnt] == 1 ? 0 : 1], &len);
memcpy(p, t, len);
p += len;
tlen += len;
*p++ = ' ';
++tlen;
t = msg_cat(sp, *ap, &len);
memcpy(p, t, len);
p += len;
tlen += len;
sp->rptlines[cnt] = 0;
}
/* Add trailing newline. */
*p = '\n';
++tlen;
(void)ex_fflush(sp);
sp->wp->scr_msg(sp, M_INFO, bp, tlen);
FREE_SPACE(sp, bp, blen);
alloc_err:
return;
#undef ARSIZE
#undef MAXNUM
}
/*
* msgq_status --
* Report on the file's status.
*
* PUBLIC: void msgq_status __P((SCR *, db_recno_t, u_int));
*/
void
msgq_status(SCR *sp, db_recno_t lno, u_int flags)
{
db_recno_t last;
size_t blen, len;
int cnt, needsep;
const char *t;
char **ap, *bp, *np, *p, *s;
/* Get sufficient memory. */
len = strlen(sp->frp->name);
GET_SPACE_GOTOC(sp, bp, blen, len * MAX_CHARACTER_COLUMNS + 128);
p = bp;
/* Copy in the filename. */
for (p = bp, t = sp->frp->name; *t != '\0'; ++t) {
len = KEY_LEN(sp, *t);
memcpy(p, KEY_NAME(sp, *t), len);
p += len;
}
np = p;
*p++ = ':';
*p++ = ' ';
/* Copy in the argument count. */
if (F_ISSET(sp, SC_STATUS_CNT) && sp->argv != NULL) {
for (cnt = 0, ap = sp->argv; *ap != NULL; ++ap, ++cnt);
if (cnt > 1) {
(void)sprintf(p,
msg_cat(sp, "317|%d files to edit", NULL), cnt);
p += strlen(p);
*p++ = ':';
*p++ = ' ';
}
F_CLR(sp, SC_STATUS_CNT);
}
/*
* 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]".
*/
needsep = 0;
if (F_ISSET(sp->frp, FR_NEWFILE)) {
F_CLR(sp->frp, FR_NEWFILE);
t = msg_cat(sp, "021|new file", &len);
memcpy(p, t, len);
p += len;
needsep = 1;
} else {
if (F_ISSET(sp->frp, FR_NAMECHANGE)) {
t = msg_cat(sp, "022|name changed", &len);
memcpy(p, t, len);
p += len;
needsep = 1;
}
if (needsep) {
*p++ = ',';
*p++ = ' ';
}
if (F_ISSET(sp->ep, F_MODIFIED))
t = msg_cat(sp, "023|modified", &len);
else
t = msg_cat(sp, "024|unmodified", &len);
memcpy(p, t, len);
p += len;
needsep = 1;
}
if (F_ISSET(sp->frp, FR_UNLOCKED)) {
if (needsep) {
*p++ = ',';
*p++ = ' ';
}
t = msg_cat(sp, "025|UNLOCKED", &len);
memcpy(p, t, len);
p += len;
needsep = 1;
}
if (O_ISSET(sp, O_READONLY)) {
if (needsep) {
*p++ = ',';
*p++ = ' ';
}
t = msg_cat(sp, "026|readonly", &len);
memcpy(p, t, len);
p += len;
needsep = 1;
}
if (needsep) {
*p++ = ':';
*p++ = ' ';
}
if (LF_ISSET(MSTAT_SHOWLAST)) {
if (db_last(sp, &last))
return;
if (last == 0) {
t = msg_cat(sp, "028|empty file", &len);
memcpy(p, t, len);
p += len;
} else {
t = msg_cat(sp, "027|line %lu of %lu [%ld%%]", &len);
(void)sprintf(p, t, lno, last, (lno * 100) / last);
p += strlen(p);
}
} else {
t = msg_cat(sp, "029|line %lu", &len);
(void)sprintf(p, t, lno);
p += strlen(p);
}
#ifdef DEBUG
(void)sprintf(p, " (pid %lu)", (u_long)getpid());
p += strlen(p);
#endif
*p++ = '\n';
len = p - bp;
/*
* There's a nasty problem with long path names. Cscope and tags files
* can result in long paths and vi will request a continuation key from
* the user as soon as it starts the screen. Unfortunately, the user
* has already typed ahead, and chaos results. If we assume that the
* characters in the filenames and informational messages only take a
* single screen column each, we can trim the filename.
*
* XXX
* Status lines get put up at fairly awkward times. For example, when
* you do a filter read (e.g., :read ! echo foo) in the top screen of a
* split screen, we have to repaint the status lines for all the screens
* below the top screen. We don't want users having to enter continue
* characters for those screens. Make it really hard to screw this up.
*/
s = bp;
if (LF_ISSET(MSTAT_TRUNCATE) && len > sp->cols) {
for (; s < np && (*s != '/' || (size_t)(p - s) > sp->cols - 3); ++s);
if (s == np) {
s = p - (sp->cols - 5);
*--s = ' ';
}
*--s = '.';
*--s = '.';
*--s = '.';
len = p - s;
}
/* Flush any waiting ex messages. */
(void)ex_fflush(sp);
sp->wp->scr_msg(sp, M_INFO, s, len);
FREE_SPACE(sp, bp, blen);
alloc_err:
return;
}
/*
* msg_open --
* Open the message catalogs.
*
* PUBLIC: int msg_open __P((SCR *, const char *));
*/
int
msg_open(SCR *sp, const char *file)
{
/*
* !!!
* Assume that the first file opened is the system default, and that
* all subsequent ones user defined. Only display error messages
* if we can't open the user defined ones -- it's useful to know if
* the system one wasn't there, but if nvi is being shipped with an
* installed system, the file will be there, if it's not, then the
* message will be repeated every time nvi is started up.
*/
static int first = 1;
DB *db;
DBT data, key;
db_recno_t msgno;
char buf[MAXPATHLEN];
const char *p, *t;
if ((p = strrchr(file, '/')) != NULL && p[1] == '\0' &&
(((t = getenv("LC_MESSAGES")) != NULL && t[0] != '\0') ||
((t = getenv("LANG")) != NULL && t[0] != '\0'))) {
(void)snprintf(buf, sizeof(buf), "%s%s", file, t);
p = buf;
} else
p = file;
if ((sp->db_error = db_create(&db, 0, 0)) != 0 ||
(sp->db_error = db->set_re_source(db, p)) != 0 ||
(sp->db_error = db_open(db, NULL, DB_RECNO, 0, 0)) != 0) {
if (first) {
first = 0;
return (1);
}
msgq_str(sp, M_DBERR, p, "%s");
return (1);
}
/*
* Test record 1 for the magic string. The msgq call is here so
* the message catalog build finds it.
*/
#define VMC "VI_MESSAGE_CATALOG"
memset(&key, 0, sizeof(key));
key.data = &msgno;
key.size = sizeof(db_recno_t);
memset(&data, 0, sizeof(data));
msgno = 1;
if ((sp->db_error = db->get(db, NULL, &key, &data, 0)) != 0 ||
data.size != sizeof(VMC) - 1 ||
memcmp(data.data, VMC, sizeof(VMC) - 1)) {
(void)db->close(db, DB_NOSYNC);
if (first) {
first = 0;
return (1);
}
msgq_str(sp, M_DBERR, p,
"030|The file %s is not a message catalog");
return (1);
}
first = 0;
if (sp->gp->msg != NULL)
(void)sp->gp->msg->close(sp->gp->msg, DB_NOSYNC);
sp->gp->msg = db;
return (0);
}
/*
* msg_close --
* Close the message catalogs.
*
* PUBLIC: void msg_close __P((GS *));
*/
void
msg_close(GS *gp)
{
if (gp->msg != NULL) {
(void)gp->msg->close(gp->msg, 0);
gp->msg = NULL;
}
}
/*
* msg_cont --
* Return common continuation messages.
*
* PUBLIC: const char *msg_cmsg __P((SCR *, cmsg_t, size_t *));
*/
const char *
msg_cmsg(SCR *sp, cmsg_t which, size_t *lenp)
{
switch (which) {
case CMSG_CONF:
return (msg_cat(sp, "268|confirm? [ynq]", lenp));
case CMSG_CONT:
return (msg_cat(sp, "269|Press any key to continue: ", lenp));
case CMSG_CONT_EX:
return (msg_cat(sp,
"270|Press any key to continue [: to enter more ex commands]: ",
lenp));
case CMSG_CONT_R:
return (msg_cat(sp, "161|Press Enter to continue: ", lenp));
case CMSG_CONT_S:
return (msg_cat(sp, "275| cont?", lenp));
case CMSG_CONT_Q:
return (msg_cat(sp,
"271|Press any key to continue [q to quit]: ", lenp));
default:
abort();
}
/* NOTREACHED */
}
/*
* msg_cat --
* Return a single message from the catalog, plus its length.
*
* !!!
* Only a single catalog message can be accessed at a time, if multiple
* ones are needed, they must be copied into local memory.
*
* PUBLIC: const char *msg_cat __P((SCR *, const char *, size_t *));
*/
const char *
msg_cat(SCR *sp, const char *str, size_t *lenp)
{
GS *gp;
DBT data, key;
db_recno_t msgno;
/*
* If it's not a catalog message, i.e. has doesn't have a leading
* number and '|' symbol, we're done.
*/
if (isdigit((unsigned char)str[0]) &&
isdigit((unsigned char)str[1]) && isdigit((unsigned char)str[2]) &&
str[3] == '|') {
memset(&key, 0, sizeof(key));
key.data = &msgno;
key.size = sizeof(db_recno_t);
memset(&data, 0, sizeof(data));
msgno = atoi(str);
/*
* XXX
* Really sleazy hack -- we put an extra character on the
* end of the format string, and then we change it to be
* the nul termination of the string. There ought to be
* a better way. Once we can allocate multiple temporary
* memory buffers, maybe we can use one of them instead.
*/
gp = sp == NULL ? NULL : sp->gp;
if (gp != NULL && gp->msg != NULL &&
gp->msg->get(gp->msg, NULL, &key, &data, 0) == 0 &&
data.size != 0) {
if (lenp != NULL)
*lenp = data.size - 1;
((char *)data.data)[data.size - 1] = '\0';
return (data.data);
}
str = &str[4];
}
if (lenp != NULL)
*lenp = strlen(str);
return (str);
}
/*
* msg_print --
* Return a printable version of a string, in allocated memory.
*
* PUBLIC: char *msg_print __P((SCR *, const char *, int *));
*/
char *
msg_print(SCR *sp, const char *s, int *needfree)
{
size_t blen, nlen;
const char *cp;
char *bp, *ep, *p;
unsigned char *t;
*needfree = 0;
for (cp = s; *cp != '\0'; ++cp)
if (!isprint((unsigned char)*cp))
break;
if (*cp == '\0')
return ((char *)__UNCONST(s)); /* SAFE: needfree set to 0. */
nlen = 0;
if (0) {
retry: if (sp == NULL)
free(bp);
else
FREE_SPACE(sp, bp, blen);
*needfree = 0;
}
nlen += 256;
if (sp == NULL) {
if ((bp = malloc(nlen)) == NULL)
goto alloc_err;
} else
GET_SPACE_GOTOC(sp, bp, blen, nlen);
if (0) {
alloc_err: return __UNCONST("");
}
*needfree = 1;
for (p = bp, ep = (bp + blen) - 1, cp = s; *cp != '\0' && p < ep; ++cp)
for (t = KEY_NAME(sp, *cp); *t != '\0' && p < ep; *p++ = *t++);
if (p == ep)
goto retry;
*p = '\0';
return (bp);
}

67
dist/nvi/common/msg.h vendored Normal file
View File

@@ -0,0 +1,67 @@
/* $NetBSD: msg.h,v 1.1.1.2 2008/05/18 14:29:48 aymeric Exp $ */
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*
* Id: msg.h,v 10.11 2000/04/21 21:26:19 skimo Exp (Berkeley) Date: 2000/04/21 21:26:19
*/
/*
* Common messages (continuation or confirmation).
*/
typedef enum {
CMSG_CONF, CMSG_CONT, CMSG_CONT_EX,
CMSG_CONT_R, CMSG_CONT_S, CMSG_CONT_Q } cmsg_t;
/*
* 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_NONE Display to the user, no reformatting, no nothing.
*
* 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.
*
* The underlying message display routines only need to know about M_NONE,
* M_ERR and M_INFO -- all the other message types are converted into one
* of them by the message routines.
*/
typedef enum {
M_NONE = 1, M_BERR, M_ERR, M_INFO, M_SYSERR, M_VINFO, M_DBERR } mtype_t;
/*
* There are major problems with error messages being generated by routines
* preparing the screen to display error messages. It's possible for the
* editor to generate messages before we have a screen in which to display
* them, or during the transition between ex (and vi startup) and a true vi.
* There's a queue in the global area to hold them.
*
* If SC_EX/SC_VI is set, that's the mode that the editor is in. If the flag
* S_SCREEN_READY is set, that means that the screen is prepared to display
* messages.
*/
typedef struct _msgh MSGH; /* MSGS list head structure. */
LIST_HEAD(_msgh, _msg);
struct _msg {
LIST_ENTRY(_msg) q; /* Linked list of messages. */
mtype_t mtype; /* Message type: M_NONE, M_ERR, M_INFO. */
char *buf; /* Message buffer. */
size_t len; /* Message length. */
};
/* Flags to msgq_status(). */
#define MSTAT_SHOWLAST 0x01 /* Show the line number of the last line. */
#define MSTAT_TRUNCATE 0x02 /* Truncate the file name if it's too long. */

108
dist/nvi/common/multibyte.h vendored Normal file
View File

@@ -0,0 +1,108 @@
/* $NetBSD: multibyte.h,v 1.7 2011/11/23 15:43:39 tnozaki Exp $ */
#ifndef MULTIBYTE_H
#define MULTIBYTE_H
/*
* 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.
*/
#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
#define ISDIGIT(c) ((c) >= '0' && (c) <= '9')
#define ISXDIGIT(c) (ISDIGIT(c) || \
((c) >= 'A' && (c) <= 'F') || ((c) >= 'a' && (c) <= 'f'))
#define ISALPHA(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z'))
#define ISALNUM(c) (ISALPHA(c) || ISDIGIT(c))
/*
* 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.
*
* If no integral type can hold a character, don't even try the port.
*/
#ifdef USE_WIDECHAR
#include <wchar.h>
#include <wctype.h>
typedef wchar_t RCHAR_T;
#define REOF WEOF
typedef wchar_t CHAR_T;
typedef wint_t ARG_CHAR_T;
typedef wint_t UCHAR_T;
#define STRLEN wcslen
#define STRTOL wcstol
#define STRTOUL wcstoul
#define SPRINTF swprintf
#define STRCMP wcscmp
#define STRPBRK wcspbrk
#define ISBLANK2 iswblank
#define ISCNTRL iswcntrl
#define ISGRAPH iswgraph
#define ISLOWER iswlower
#define ISPUNCT iswpunct
#define ISSPACE iswspace
#define ISUPPER iswupper
#define TOLOWER towlower
#define TOUPPER towupper
#define STRSET wmemset
#define STRCHR wcschr
#define L(ch) L ## ch
#define WS "%ls"
#define WVS "%*ls"
#define WC "%lc"
#else
#include <stdio.h>
typedef char RCHAR_T;
#define REOF EOF
typedef char CHAR_T;
typedef int ARG_CHAR_T;
typedef unsigned char UCHAR_T;
#define STRLEN strlen
#define STRTOL strtol
#define STRTOUL strtoul
#define SPRINTF snprintf
#define STRCMP strcmp
#define STRPBRK strpbrk
#define ISBLANK2 isblank
#define ISCNTRL iscntrl
#define ISGRAPH isgraph
#define ISLOWER islower
#define ISPUNCT ispunct
#define ISSPACE isspace
#define ISUPPER isupper
#define TOLOWER tolower
#define TOUPPER toupper
#define STRSET memset
#define STRCHR strchr
#define L(ch) ch
#define WS "%s"
#define WVS "%*s"
#define WC "%c"
#endif
#define MEMCMP(to, from, n) \
memcmp(to, from, (n) * sizeof(*(to)))
#define MEMMOVE(p, t, len) memmove(p, t, (len) * sizeof(*(p)))
#define MEMCPY(p, t, len) memcpy(p, t, (len) * sizeof(*(p)))
#define SIZE(w) (sizeof(w)/sizeof(*w))
#endif

58
dist/nvi/common/nothread.c vendored Normal file
View File

@@ -0,0 +1,58 @@
/* $NetBSD: nothread.c,v 1.1.1.2 2008/05/18 14:29:48 aymeric Exp $ */
/*-
* Copyright (c) 2000
* Sven Verdoolaege. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: nothread.c,v 1.4 2000/07/22 14:52:37 skimo Exp (Berkeley) Date: 2000/07/22 14:52:37";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <bitstring.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "../common/common.h"
static int vi_nothread_run __P((WIN *wp, void *(*fun)(void*), void *data));
static int vi_nothread_lock __P((WIN *, void **));
/*
* thread_init
*
* PUBLIC: void thread_init __P((GS *gp));
*/
void
thread_init(GS *gp)
{
gp->run = vi_nothread_run;
gp->lock_init = vi_nothread_lock;
gp->lock_end = vi_nothread_lock;
gp->lock_try = vi_nothread_lock;
gp->lock_unlock = vi_nothread_lock;
}
static int
vi_nothread_run(WIN *wp, void *(*fun)(void*), void *data)
{
fun(data);
return 0;
}
static int
vi_nothread_lock (WIN * wp, void **lp)
{
return 0;
}

21
dist/nvi/common/options.awk vendored Normal file
View File

@@ -0,0 +1,21 @@
# Id: options.awk,v 10.1 1995/06/08 19:00:01 bostic Exp (Berkeley) Date: 1995/06/08 19:00:01
/^\/\* O_[0-9A-Z_]*/ {
opt = $2
printf("#define %s %d\n", opt, cnt++)
ofs = FS
FS="\""
do getline
while ($1 != " {L(")
FS=ofs
opt_name = $2
if (opt_name < prev_name) {
printf "missorted %s: \"%s\" < \"%s\"\n", opt, opt_name, prev_name >"/dev/stderr"
exit 1
}
prev_name = opt_name
next
}
END {
printf("#define O_OPTIONCOUNT %d\n", cnt);
}

1190
dist/nvi/common/options.c vendored Normal file

File diff suppressed because it is too large Load Diff

104
dist/nvi/common/options.h vendored Normal file
View File

@@ -0,0 +1,104 @@
/* $NetBSD: options.h,v 1.3 2008/12/05 22:51:42 christos Exp $ */
/*-
* Copyright (c) 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1991, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*
* Id: options.h,v 10.20 2001/06/09 18:26:28 skimo Exp (Berkeley) Date: 2001/06/09 18:26:28
*/
/*
* Edit option information. Historically, if you set a boolean or numeric
* edit option value to its "default" value, it didn't show up in the :set
* display, i.e. it wasn't considered "changed". String edit options would
* show up as changed, regardless. We maintain a parallel set of values
* which are the default values and never consider an edit option changed
* if it was reset to the default value.
*
* Macros to retrieve boolean, integral and string option values, and to
* set, clear and test boolean option values. Some options (secure, lines,
* columns, terminal type) are global in scope, and are therefore stored
* in the global area. The offset in the global options array is stored
* in the screen's value field. This is set up when the options are first
* initialized.
*/
#define O_V(sp, o, fld) \
(F_ISSET(&(sp)->opts[(o)], OPT_GLOBAL) ? \
(sp)->gp->opts[(sp)->opts[(o)].o_cur.val].fld : \
(sp)->opts[(o)].fld)
/* Global option macros. */
#define OG_CLR(gp, o) ((gp)->opts[(o)].o_cur.val) = 0
#define OG_SET(gp, o) ((gp)->opts[(o)].o_cur.val) = 1
#define OG_STR(gp, o) ((gp)->opts[(o)].o_cur.str)
#define OG_VAL(gp, o) ((gp)->opts[(o)].o_cur.val)
#define OG_ISSET(gp, o) OG_VAL(gp, o)
#define OG_D_STR(gp, o) ((gp)->opts[(o)].o_def.str)
#define OG_D_VAL(gp, o) ((gp)->opts[(o)].o_def.val)
/*
* Flags to o_set(); need explicit OS_STR as can be setting the value to
* NULL.
*/
#define OS_DEF 0x01 /* Set the default value. */
#define OS_NOFREE 0x02 /* Don't free the old string. */
#define OS_STR 0x04 /* Set to string argument. */
#define OS_STRDUP 0x08 /* Copy then set to string argument. */
struct _option {
union {
u_long val; /* Value or boolean. */
const char *str; /* String. */
} o_cur;
#define O_CLR(sp, o) o_set(sp, o, 0, NULL, 0)
#define O_SET(sp, o) o_set(sp, o, 0, NULL, 1)
#define O_STR(sp, o) O_V(sp, o, o_cur.str)
#define O_VAL(sp, o) O_V(sp, o, o_cur.val)
#define O_ISSET(sp, o) O_VAL(sp, o)
union {
u_long val; /* Value or boolean. */
const char *str; /* String. */
} o_def;
#define O_D_CLR(sp, o) o_set(sp, o, OS_DEF, NULL, 0)
#define O_D_SET(sp, o) o_set(sp, o, OS_DEF, NULL, 1)
#define O_D_STR(sp, o) O_V(sp, o, o_def.str)
#define O_D_VAL(sp, o) O_V(sp, o, o_def.val)
#define O_D_ISSET(sp, o) O_D_VAL(sp, o)
#define OPT_GLOBAL 0x01 /* Option is global. */
#define OPT_SELECTED 0x02 /* Selected for display. */
u_int8_t flags;
};
/* List of option names, associated update functions and information. */
struct _optlist {
const CHAR_T *name; /* Name. */
/* Change function. */
int (*func) __P((SCR *, OPTION *, const char *, u_long *));
/* Type of object. */
enum { OPT_0BOOL, OPT_1BOOL, OPT_NUM, OPT_STR } type;
#define OPT_ADISP 0x001 /* Always display the option. */
#define OPT_ALWAYS 0x002 /* Always call the support function. */
#define OPT_NDISP 0x004 /* Never display the option. */
#define OPT_NOSAVE 0x008 /* Mkexrc command doesn't save. */
#define OPT_NOSET 0x010 /* Option may not be set. */
#define OPT_NOUNSET 0x020 /* Option may not be unset. */
#define OPT_NOZERO 0x040 /* Option may not be set to 0. */
#define OPT_PAIRS 0x080 /* String with even length */
u_int8_t flags;
};
/* Option argument to opts_dump(). */
enum optdisp { NO_DISPLAY, ALL_DISPLAY, CHANGED_DISPLAY, SELECT_DISPLAY };
/* Options array. */
extern OPTLIST const optlist[];
#include "options_def.h"

299
dist/nvi/common/options_f.c vendored Normal file
View File

@@ -0,0 +1,299 @@
/* $NetBSD: options_f.c,v 1.3 2008/12/05 22:51:42 christos Exp $ */
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: options_f.c,v 10.33 2001/06/25 15:19:11 skimo Exp (Berkeley) Date: 2001/06/25 15:19:11";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <bitstring.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "common.h"
/*
* PUBLIC: int f_altwerase __P((SCR *, OPTION *, const char *, u_long *));
*/
int
f_altwerase(SCR *sp, OPTION *op, const char *str, u_long *valp)
{
if (*valp)
O_CLR(sp, O_TTYWERASE);
return (0);
}
/*
* PUBLIC: int f_columns __P((SCR *, OPTION *, const char *, u_long *));
*/
int
f_columns(SCR *sp, OPTION *op, const char *str, u_long *valp)
{
/* Validate the number. */
if (*valp < MINIMUM_SCREEN_COLS) {
msgq(sp, M_ERR, "040|Screen columns too small, less than %d",
MINIMUM_SCREEN_COLS);
return (1);
}
/*
* !!!
* It's not uncommon for allocation of huge chunks of memory to cause
* core dumps on various systems. So, we prune out numbers that are
* "obviously" wrong. Vi will not work correctly if it has the wrong
* number of lines/columns for the screen, but at least we don't drop
* core.
*/
#define MAXIMUM_SCREEN_COLS 4000
if (*valp > MAXIMUM_SCREEN_COLS) {
msgq(sp, M_ERR, "041|Screen columns too large, greater than %d",
MAXIMUM_SCREEN_COLS);
return (1);
}
return (0);
}
/*
* PUBLIC: int f_lines __P((SCR *, OPTION *, const char *, u_long *));
*/
int
f_lines(SCR *sp, OPTION *op, const char *str, u_long *valp)
{
/* Validate the number. */
if (*valp < MINIMUM_SCREEN_ROWS) {
msgq(sp, M_ERR, "042|Screen lines too small, less than %d",
MINIMUM_SCREEN_ROWS);
return (1);
}
/*
* !!!
* It's not uncommon for allocation of huge chunks of memory to cause
* core dumps on various systems. So, we prune out numbers that are
* "obviously" wrong. Vi will not work correctly if it has the wrong
* number of lines/columns for the screen, but at least we don't drop
* core.
*/
#define MAXIMUM_SCREEN_ROWS 4000
if (*valp > MAXIMUM_SCREEN_ROWS) {
msgq(sp, M_ERR, "043|Screen lines too large, greater than %d",
MAXIMUM_SCREEN_ROWS);
return (1);
}
/*
* Set the value, and the related scroll value. If no window
* value set, set a new default window.
*/
o_set(sp, O_LINES, 0, NULL, *valp);
if (*valp == 1) {
sp->defscroll = 1;
if (O_VAL(sp, O_WINDOW) == O_D_VAL(sp, O_WINDOW) ||
O_VAL(sp, O_WINDOW) > *valp) {
o_set(sp, O_WINDOW, 0, NULL, 1);
o_set(sp, O_WINDOW, OS_DEF, NULL, 1);
}
} else {
sp->defscroll = (*valp - 1) / 2;
if (O_VAL(sp, O_WINDOW) == O_D_VAL(sp, O_WINDOW) ||
O_VAL(sp, O_WINDOW) > *valp) {
o_set(sp, O_WINDOW, 0, NULL, *valp - 1);
o_set(sp, O_WINDOW, OS_DEF, NULL, *valp - 1);
}
}
return (0);
}
/*
* PUBLIC: int f_lisp __P((SCR *, OPTION *, const char *, u_long *));
*/
int
f_lisp(SCR *sp, OPTION *op, const char *str, u_long *valp)
{
msgq(sp, M_ERR, "044|The lisp option is not implemented");
return (0);
}
/*
* PUBLIC: int f_msgcat __P((SCR *, OPTION *, const char *, u_long *));
*/
int
f_msgcat(SCR *sp, OPTION *op, const char *str, u_long *valp)
{
(void)msg_open(sp, str);
return (0);
}
/*
* PUBLIC: int f_print __P((SCR *, OPTION *, const char *, u_long *));
*/
int
f_print(SCR *sp, OPTION *op, const char *str, u_long *valp)
{
int offset = op - sp->opts;
/* Preset the value, needed for reinitialization of lookup table. */
if (offset == O_OCTAL) {
if (*valp)
O_SET(sp, offset);
else
O_CLR(sp, offset);
} else if (o_set(sp, offset, OS_STRDUP, str, 0))
return(1);
/* Reinitialize the key fast lookup table. */
v_key_ilookup(sp);
/* Reformat the screen. */
F_SET(sp, SC_SCR_REFORMAT);
return (0);
}
/*
* PUBLIC: int f_readonly __P((SCR *, OPTION *, const char *, u_long *));
*/
int
f_readonly(SCR *sp, OPTION *op, const char *str, u_long *valp)
{
/*
* !!!
* See the comment in exf.c.
*/
if (*valp)
F_SET(sp, SC_READONLY);
else
F_CLR(sp, SC_READONLY);
return (0);
}
/*
* PUBLIC: int f_recompile __P((SCR *, OPTION *, const char *, u_long *));
*/
int
f_recompile(SCR *sp, OPTION *op, const char *str, u_long *valp)
{
if (F_ISSET(sp, SC_RE_SEARCH)) {
regfree(&sp->re_c);
F_CLR(sp, SC_RE_SEARCH);
}
if (F_ISSET(sp, SC_RE_SUBST)) {
regfree(&sp->subre_c);
F_CLR(sp, SC_RE_SUBST);
}
return (0);
}
/*
* PUBLIC: int f_reformat __P((SCR *, OPTION *, const char *, u_long *));
*/
int
f_reformat(SCR *sp, OPTION *op, const char *str, u_long *valp)
{
F_SET(sp, SC_SCR_REFORMAT);
return (0);
}
/*
* PUBLIC: int f_ttywerase __P((SCR *, OPTION *, const char *, u_long *));
*/
int
f_ttywerase(SCR *sp, OPTION *op, const char *str, u_long *valp)
{
if (*valp)
O_CLR(sp, O_ALTWERASE);
return (0);
}
/*
* PUBLIC: int f_w300 __P((SCR *, OPTION *, const char *, u_long *));
*/
int
f_w300(SCR *sp, OPTION *op, const char *str, u_long *valp)
{
u_long v;
/* Historical behavior for w300 was < 1200. */
if (sp->gp->scr_baud(sp, &v))
return (1);
if (v >= 1200)
return (0);
return (f_window(sp, op, str, valp));
}
/*
* PUBLIC: int f_w1200 __P((SCR *, OPTION *, const char *, u_long *));
*/
int
f_w1200(SCR *sp, OPTION *op, const char *str, u_long *valp)
{
u_long v;
/* Historical behavior for w1200 was == 1200. */
if (sp->gp->scr_baud(sp, &v))
return (1);
if (v < 1200 || v > 4800)
return (0);
return (f_window(sp, op, str, valp));
}
/*
* PUBLIC: int f_w9600 __P((SCR *, OPTION *, const char *, u_long *));
*/
int
f_w9600(SCR *sp, OPTION *op, const char *str, u_long *valp)
{
u_long v;
/* Historical behavior for w9600 was > 1200. */
if (sp->gp->scr_baud(sp, &v))
return (1);
if (v <= 4800)
return (0);
return (f_window(sp, op, str, valp));
}
/*
* PUBLIC: int f_window __P((SCR *, OPTION *, const char *, u_long *));
*/
int
f_window(SCR *sp, OPTION *op, const char *str, u_long *valp)
{
if (*valp >= O_VAL(sp, O_LINES) - 1 &&
(*valp = O_VAL(sp, O_LINES) - 1) == 0)
*valp = 1;
return (0);
}
/*
* PUBLIC: int f_encoding __P((SCR *, OPTION *, const char *, u_long *));
*/
int
f_encoding(SCR *sp, OPTION *op, const char *str, u_long *valp)
{
int offset = op - sp->opts;
return conv_enc(sp, offset, str);
}

107
dist/nvi/common/pthread.c vendored Normal file
View File

@@ -0,0 +1,107 @@
/* $NetBSD: pthread.c,v 1.1.1.2 2008/05/18 14:29:50 aymeric Exp $ */
/*-
* Copyright (c) 2000
* Sven Verdoolaege. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: pthread.c,v 1.4 2000/07/22 14:52:37 skimo Exp (Berkeley) Date: 2000/07/22 14:52:37";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <bitstring.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "../common/common.h"
static int vi_pthread_run __P((WIN *wp, void *(*fun)(void*), void *data));
static int vi_pthread_lock_init __P((WIN *, void **));
static int vi_pthread_lock_end __P((WIN *, void **));
static int vi_pthread_lock_try __P((WIN *, void **));
static int vi_pthread_lock_unlock __P((WIN *, void **));
/*
* thread_init
*
* PUBLIC: void thread_init __P((GS *gp));
*/
void
thread_init(GS *gp)
{
gp->run = vi_pthread_run;
gp->lock_init = vi_pthread_lock_init;
gp->lock_end = vi_pthread_lock_end;
gp->lock_try = vi_pthread_lock_try;
gp->lock_unlock = vi_pthread_lock_unlock;
}
static int
vi_pthread_run(WIN *wp, void *(*fun)(void*), void *data)
{
pthread_t *t = malloc(sizeof(pthread_t));
pthread_create(t, NULL, fun, data);
return 0;
}
static int
vi_pthread_lock_init (WIN * wp, void **p)
{
pthread_mutex_t *mutex;
int rc;
MALLOC_RET(NULL, mutex, pthread_mutex_t *, sizeof(*mutex));
if (rc = pthread_mutex_init(mutex, NULL)) {
free(mutex);
*p = NULL;
return rc;
}
*p = mutex;
return 0;
}
static int
vi_pthread_lock_end (WIN * wp, void **p)
{
int rc;
pthread_mutex_t *mutex = (pthread_mutex_t *)*p;
if (rc = pthread_mutex_destroy(mutex))
return rc;
free(mutex);
*p = NULL;
return 0;
}
static int
vi_pthread_lock_try (WIN * wp, void **p)
{
printf("try %p\n", *p);
fflush(stdout);
return 0;
return pthread_mutex_trylock((pthread_mutex_t *)*p);
}
static int
vi_pthread_lock_unlock (WIN * wp, void **p)
{
printf("unlock %p\n", *p);
return 0;
return pthread_mutex_unlock((pthread_mutex_t *)*p);
}

230
dist/nvi/common/put.c vendored Normal file
View File

@@ -0,0 +1,230 @@
/* $NetBSD: put.c,v 1.3 2011/11/23 19:25:28 tnozaki Exp $ */
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1992, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: put.c,v 10.18 2001/06/25 15:19:11 skimo Exp (Berkeley) Date: 2001/06/25 15:19:11";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <bitstring.h>
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
/*
* put --
* Put text buffer contents into the file.
*
* PUBLIC: int put __P((SCR *, CB *, CHAR_T *, MARK *, MARK *, int));
*/
int
put(SCR *sp, CB *cbp, ARG_CHAR_T *namep, MARK *cp, MARK *rp, int append)
{
ARG_CHAR_T name;
TEXT *ltp, *tp;
db_recno_t lno;
size_t blen, clen, len;
int rval;
CHAR_T *bp, *t;
CHAR_T *p;
if (cbp == NULL) {
if (namep == NULL) {
cbp = sp->wp->dcbp;
if (cbp == NULL) {
msgq(sp, M_ERR,
"053|The default buffer is empty");
return (1);
}
} else {
name = *namep;
CBNAME(sp, cbp, name);
if (cbp == NULL) {
msgq(sp, M_ERR, "054|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 (db_last(sp, &lno))
return (1);
if (lno == 0) {
for (; tp != (void *)&cbp->textq;
++lno, ++sp->rptlines[L_ADDED], tp = tp->q.cqe_next)
if (db_append(sp, 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 (db_append(sp, 1, lno, tp->lb, tp->len))
return (1);
rp->cno = 0;
(void)nonblank(sp, 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 (db_get(sp, lno, DBG_FATAL, &p, &len))
return (1);
GET_SPACE_RETW(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) {
MEMCPYW(bp, p, clen);
p += clen;
t += clen;
}
/* First line from the CB. */
if (tp->len != 0) {
MEMCPYW(t, tp->lb, tp->len);
t += tp->len;
}
/* Calculate length left in the original line. */
clen = len == 0 ? 0 : len - (cp->cno + (append ? 1 : 0));
/*
* !!!
* In the historical 4BSD version of vi, character mode puts within
* a single line have two cursor behaviors: if the put is from the
* unnamed buffer, the cursor moves to the character inserted which
* appears last in the file. If the put is from a named buffer,
* the cursor moves to the character inserted which appears first
* in the file. In System III/V, it was changed at some point and
* the cursor always moves to the first character. In both versions
* of vi, character mode puts that cross line boundaries leave the
* cursor on the first character. Nvi implements the System III/V
* behavior, and expect POSIX.2 to do so as well.
*/
rp->lno = lno;
rp->cno = len == 0 ? 0 : sp->cno + (append && tp->len ? 1 : 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.
*/
if (tp->q.cqe_next == (void *)&cbp->textq) {
if (clen > 0) {
MEMCPYW(t, p, clen);
t += clen;
}
if (db_set(sp, lno, bp, t - bp))
goto err;
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_RETW(sp, bp, blen, ltp->len + clen);
t = bp + len;
/* Add in last part of the CB. */
MEMCPYW(t, ltp->lb, ltp->len);
if (clen)
MEMCPYW(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 (db_set(sp, lno, bp, t - bp))
goto err;
if (sp->rptlchange != lno) {
sp->rptlchange = lno;
++sp->rptlines[L_CHANGED];
}
/* 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 (db_append(sp, 1, lno, tp->lb, tp->len))
goto err;
if (db_append(sp, 1, lno, t, clen))
goto err;
++sp->rptlines[L_ADDED];
}
rval = 0;
if (0)
err: rval = 1;
FREE_SPACEW(sp, bp, blen);
return (rval);
}

859
dist/nvi/common/recover.c vendored Normal file
View File

@@ -0,0 +1,859 @@
/* $NetBSD: recover.c,v 1.3 2009/01/18 03:45:50 lukem Exp $ */
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: recover.c,v 10.31 2001/11/01 15:24:44 skimo Exp (Berkeley) Date: 2001/11/01 15:24:44";
#endif /* not lint */
#include <sys/param.h>
#include <sys/types.h> /* XXX: param.h may not have included types.h */
#include <sys/queue.h>
#include <sys/stat.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 <bitstring.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "common.h"
#include "pathnames.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 of 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 *, int, char *));
static int rcv_mktemp __P((SCR *, char *, const char *, int));
/*
* rcv_tmp --
* Build a file name that will be used as the recovery file.
*
* PUBLIC: int rcv_tmp __P((SCR *, EXF *, char *));
*/
int
rcv_tmp(SCR *sp, EXF *ep, char *name)
{
struct stat sb;
int fd;
char path[MAXPATHLEN];
const char *dp;
/*
* !!!
* ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
*
*
* 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.
*/
if (opts_empty(sp, O_RECDIR, 0))
goto err;
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. */
if (strchr(name, '\n')) {
msgq(sp, M_ERR,
"055|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,
"056|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.
*
* PUBLIC: int rcv_init __P((SCR *));
*/
int
rcv_init(SCR *sp)
{
EXF *ep;
db_recno_t lno;
ep = sp->ep;
/* 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, 0, NULL))
goto err;
/* Force a read of the entire file. */
if (db_last(sp, &lno))
goto err;
/* Turn on a busy message, and sync it to backing store. */
sp->gp->scr_busy(sp,
"057|Copying file for recovery...", BUSY_ON);
if (ep->db->sync(ep->db, 0)) {
msgq_str(sp, M_SYSERR, ep->rcv_path,
"058|Preservation failed: %s");
sp->gp->scr_busy(sp, NULL, BUSY_OFF);
goto err;
}
sp->gp->scr_busy(sp, NULL, BUSY_OFF);
}
/* 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);
err: msgq(sp, M_ERR,
"059|Modifications not recoverable if the session fails");
return (1);
}
/*
* 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
*
* PUBLIC: int rcv_sync __P((SCR *, u_int));
*/
int
rcv_sync(SCR *sp, u_int flags)
{
EXF *ep;
int fd, rval;
char buf[1024];
const char *dp;
/* Make sure that there's something to recover/sync. */
ep = sp->ep;
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, 0)) {
F_CLR(ep, F_RCV_ON | F_RCV_NORM);
msgq_str(sp, M_SYSERR,
ep->rcv_path, "060|File backup failed: %s");
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)) {
if (opts_empty(sp, O_RECDIR, 0))
goto err;
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 err;
sp->gp->scr_busy(sp,
"061|Copying file for recovery...", BUSY_ON);
if (rcv_copy(sp, fd, ep->rcv_path) ||
close(fd) || rcv_mailfile(sp, 1, buf)) {
(void)unlink(buf);
(void)close(fd);
rval = 1;
}
sp->gp->scr_busy(sp, NULL, BUSY_OFF);
}
if (0) {
err: rval = 1;
}
/* REQUEST: end the file session. */
if (LF_ISSET(RCV_ENDSESSION) && file_end(sp, NULL, 1))
rval = 1;
return (rval);
}
/*
* rcv_mailfile --
* Build the file to mail to the user.
*/
static int
rcv_mailfile(SCR *sp, int issync, char *cp_path)
{
EXF *ep;
GS *gp;
struct passwd *pw;
size_t len;
time_t now;
uid_t uid;
int fd;
char *p, *t, buf[4096], mpath[MAXPATHLEN];
const char *dp;
char *t1, *t2, *t3;
/*
* XXX
* MAXHOSTNAMELEN is in various places on various systems, including
* <netdb.h> and <sys/socket.h>. If not found, use a large default.
*/
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 1024
#endif
char host[MAXHOSTNAMELEN];
gp = sp->gp;
if ((pw = getpwuid(uid = getuid())) == NULL) {
msgq(sp, M_ERR,
"062|Information on user id %u not found", uid);
return (1);
}
if (opts_empty(sp, O_RECDIR, 0))
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.
*/
ep = sp->ep;
if (file_lock(sp, NULL, NULL, fd, 1) != LOCK_SUCCESS)
msgq(sp, M_SYSERR, "063|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 ((size_t)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%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 ", gp->progname, ":\n\n\t",
gp->progname, " -r ", t);
if (len > sizeof(buf) - 1) {
lerr: msgq(sp, M_ERR, "064|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)
goto werr;
}
if (issync) {
rcv_email(sp, mpath);
if (close(fd)) {
werr: msgq(sp, M_SYSERR, "065|Recovery file");
goto err;
}
}
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.
*
* PUBLIC: int rcv_list __P((SCR *));
*/
int
rcv_list(SCR *sp)
{
struct dirent *dp;
struct stat sb;
DIR *dirp;
FILE *fp;
int found;
char *p, *t;
const char *d;
char file[MAXPATHLEN], path[MAXPATHLEN];
/* Open the recovery directory for reading. */
if (opts_empty(sp, O_RECDIR, 0))
return (1);
d = O_STR(sp, O_RECDIR);
if (chdir(d) || (dirp = opendir(".")) == NULL) {
msgq_str(sp, M_SYSERR, d, "recdir: %s");
return (1);
}
/* Read the directory. */
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(sp, 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_str(sp, M_ERR, dp->d_name,
"066|%s: malformed recovery file");
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("%.24s: %s\n",
ctime(&sb.st_mtime), file + sizeof(VI_FHEADER) - 1);
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.
*
* PUBLIC: int rcv_read __P((SCR *, FREF *));
*/
int
rcv_read(SCR *sp, FREF *frp)
{
struct dirent *dp;
struct stat sb;
DIR *dirp;
EXF *ep;
time_t rec_mtime;
int fd, found, locked = 0, requested, sv_fd;
char *name, *p, *t, *recp, *pathp;
const char *rp;
char file[MAXPATHLEN], path[MAXPATHLEN], recpath[MAXPATHLEN];
if (opts_empty(sp, O_RECDIR, 0))
return (1);
rp = O_STR(sp, O_RECDIR);
if ((dirp = opendir(rp)) == NULL) {
msgq_str(sp, M_ERR, rp, "%s");
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", rp, 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(sp, 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_str(sp, M_ERR, recpath,
"067|%s: malformed recovery file");
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_SYSERR, NULL);
recp = p;
goto next;
}
if ((pathp = strdup(path)) == NULL) {
msgq(sp, M_SYSERR, NULL);
free(recp);
recp = p;
pathp = t;
goto next;
}
if (p != NULL) {
free(p);
free(t);
}
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_str(sp, M_INFO, name,
"068|No files named %s, readable by you, to recover");
return (1);
}
if (found) {
if (requested > 1)
msgq(sp, M_INFO,
"069|There are older versions of this file for you to recover");
if (found > requested)
msgq(sp, M_INFO,
"070|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(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_str(sp, M_SYSERR, fname, "%s");
return (1);
}
/*
* rcv_gets --
* Fgets(3) for a file descriptor.
*/
static char *
rcv_gets(char *buf, size_t len, int fd)
{
int 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(SCR *sp, char *path, const char *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_str(sp, M_SYSERR, dname, "%s");
else
(void)chmod(path, perms);
return (fd);
}
/*
* rcv_email --
* Send email.
*/
static void
rcv_email(SCR *sp, char *fname)
{
struct stat sb;
char buf[MAXPATHLEN * 2 + 20];
if (_PATH_SENDMAIL[0] != '/' || stat(_PATH_SENDMAIL, &sb))
msgq_str(sp, M_SYSERR,
_PATH_SENDMAIL, "071|not sending email: %s");
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);
}
}

238
dist/nvi/common/screen.c vendored Normal file
View File

@@ -0,0 +1,238 @@
/* $NetBSD: screen.c,v 1.1.1.2 2008/05/18 14:29:51 aymeric Exp $ */
/*-
* Copyright (c) 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: screen.c,v 10.22 2001/06/25 15:19:12 skimo Exp (Berkeley) Date: 2001/06/25 15:19:12";
#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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "common.h"
#include "../vi/vi.h"
/*
* screen_init --
* Do the default initialization of an SCR structure.
*
* PUBLIC: int screen_init __P((GS *, SCR *, SCR **));
*/
int
screen_init(GS *gp, SCR *orig, SCR **spp)
{
SCR *sp;
size_t len;
*spp = NULL;
CALLOC_RET(orig, sp, SCR *, 1, sizeof(SCR));
*spp = sp;
/* INITIALIZED AT SCREEN CREATE. */
sp->id = ++gp->id;
sp->refcnt = 1;
sp->gp = gp; /* All ref the GS structure. */
sp->ccnt = 2; /* Anything > 1 */
/*
* XXX
* sp->defscroll is initialized by the opts_init() code because
* we don't have the option information yet.
*/
CIRCLEQ_INIT(&sp->tiq);
/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
if (orig == NULL) {
sp->searchdir = NOTSET;
} else {
sp->wp = orig->wp;
/* Alternate file name. */
if (orig->alt_name != NULL &&
(sp->alt_name = strdup(orig->alt_name)) == NULL)
goto mem;
/* Last executed at buffer. */
if (F_ISSET(orig, SC_AT_SET)) {
F_SET(sp, SC_AT_SET);
sp->at_lbuf = orig->at_lbuf;
}
/* Retain searching/substitution information. */
sp->searchdir = orig->searchdir == NOTSET ? NOTSET : FORWARD;
if (orig->re != NULL && (sp->re =
v_wstrdup(sp, orig->re, orig->re_len)) == NULL)
goto mem;
sp->re_len = orig->re_len;
if (orig->subre != NULL && (sp->subre =
v_wstrdup(sp, orig->subre, orig->subre_len)) == NULL)
goto mem;
sp->subre_len = orig->subre_len;
if (orig->repl != NULL && (sp->repl =
v_wstrdup(sp, orig->repl, orig->repl_len)) == NULL)
goto mem;
sp->repl_len = 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) {
mem: msgq(orig, M_SYSERR, NULL);
goto err;
}
sp->newl_len = orig->newl_len;
sp->newl_cnt = orig->newl_cnt;
memcpy(sp->newl, orig->newl, len);
}
if (opts_copy(orig, sp))
goto err;
F_SET(sp, F_ISSET(orig, SC_EX | SC_VI));
}
if (ex_screen_copy(orig, sp)) /* Ex. */
goto err;
if (v_screen_copy(orig, sp)) /* Vi. */
goto err;
sp->cl_private = 0; /* XXX */
conv_init(orig, sp); /* XXX */
*spp = sp;
return (0);
err: screen_end(sp);
return (1);
}
/*
* screen_end --
* Release a screen, no matter what had (and had not) been
* initialized.
*
* PUBLIC: int screen_end __P((SCR *));
*/
int
screen_end(SCR *sp)
{
int rval;
/* If multiply referenced, just decrement the count and return. */
if (--sp->refcnt != 0)
return (0);
/*
* Remove the screen from the displayed queue.
*
* If a created screen failed during initialization, it may not
* be linked into the chain.
*/
if (sp->q.cqe_next != NULL)
CIRCLEQ_REMOVE(&sp->wp->scrq, sp, q);
/* The screen is no longer real. */
F_CLR(sp, SC_SCR_EX | SC_SCR_VI);
rval = 0;
#ifdef HAVE_PERL_INTERP
if (perl_screen_end(sp)) /* End perl. */
rval = 1;
#endif
if (v_screen_end(sp)) /* End vi. */
rval = 1;
if (ex_screen_end(sp)) /* End ex. */
rval = 1;
/* Free file names. */
{ char **ap;
if (!F_ISSET(sp, SC_ARGNOFREE) && sp->argv != NULL) {
for (ap = sp->argv; *ap != NULL; ++ap)
free(*ap);
free(sp->argv);
}
}
/* Free any text input. */
if (sp->tiq.cqh_first != NULL)
text_lfree(&sp->tiq);
/* Free alternate file name. */
if (sp->alt_name != NULL)
free(sp->alt_name);
/* Free up search information. */
if (sp->re != NULL)
free(sp->re);
if (F_ISSET(sp, SC_RE_SEARCH))
regfree(&sp->re_c);
if (sp->subre != NULL)
free(sp->subre);
if (F_ISSET(sp, SC_RE_SUBST))
regfree(&sp->subre_c);
if (sp->repl != NULL)
free(sp->repl);
if (sp->newl != NULL)
free(sp->newl);
/* Free all the options */
opts_free(sp);
/* Free the screen itself. */
free(sp);
return (rval);
}
/*
* screen_next --
* Return the next screen in the queue.
*
* PUBLIC: SCR *screen_next __P((SCR *));
*/
SCR *
screen_next(SCR *sp)
{
GS *gp;
WIN *wp;
SCR *next;
/* Try the display queue, without returning the current screen. */
gp = sp->gp;
wp = sp->wp;
for (next = wp->scrq.cqh_first;
next != (void *)&wp->scrq; next = next->q.cqe_next)
if (next != sp)
break;
if (next != (void *)&wp->scrq)
return (next);
/* Try the hidden queue; if found, move screen to the display queue. */
if (gp->hq.cqh_first != (void *)&gp->hq) {
next = gp->hq.cqh_first;
CIRCLEQ_REMOVE(&gp->hq, next, q);
CIRCLEQ_INSERT_HEAD(&wp->scrq, next, q);
next->wp = sp->wp;
return (next);
}
return (NULL);
}

283
dist/nvi/common/screen.h vendored Normal file
View File

@@ -0,0 +1,283 @@
/* $NetBSD: screen.h,v 1.2 2011/11/23 19:25:28 tnozaki Exp $ */
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1992, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*
* Id: screen.h,v 10.49 2002/03/02 23:47:02 skimo Exp (Berkeley) Date: 2002/03/02 23:47:02
*/
/*
* There are minimum values that vi has to have to display a screen. The row
* minimum is fixed at 1 (the svi code can share a line between the text line
* and the colon command/message line). 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 simpler to have a fixed value and not
* worry about it.
*
* XXX
* MINIMUM_SCREEN_COLS is almost certainly wrong.
*/
#define MINIMUM_SCREEN_ROWS 1
#define MINIMUM_SCREEN_COLS 20
/*
* WIN --
* A list of screens that are displayed as a whole.
*/
struct _win {
CIRCLEQ_ENTRY(_win) q; /* Windows. */
CIRCLEQ_HEAD(_scrh, _scr) scrq; /* Screens */
GS *gp; /* Pointer to global area. */
SCR *ccl_sp; /* Colon command-line screen. */
void *perl_private; /* Perl interpreter. */
void *ip_private; /* IP support private area. */
void *th_private; /* Threading support private area. */
/*
* Ex command structures (EXCMD). Defined here because ex commands
* exist outside of any particular screen or file.
*/
#define EXCMD_RUNNING(wp) ((wp)->ecq.lh_first->clen != 0)
LIST_HEAD(_excmdh, _excmd) ecq; /* Ex command linked list. */
EXCMD excmd; /* Default ex command structure. */
char *if_name; /* Current associated file. */
db_recno_t if_lno; /* Current associated line number. */
EVENT *i_event; /* Array of input events. */
size_t i_nelem; /* Number of array elements. */
size_t i_cnt; /* Count of events. */
size_t i_next; /* Offset of next event. */
CB *dcbp; /* Default cut buffer pointer. */
CB dcb_store; /* Default cut buffer storage. */
LIST_HEAD(_cuth, _cb) cutq; /* Linked list of cut buffers. */
/* For now, can be either char or CHAR_T buffer */
char *tmp_bp; /* Temporary buffer. */
size_t tmp_blen; /* Temporary buffer size. */
char *l_lp; /* Log buffer. */
size_t l_len; /* Log buffer length. */
CONVWIN cw;
/* Flags. */
#define W_TMP_INUSE 0x0001 /* Temporary buffer in use. */
u_int32_t flags;
/* Message or ex output. */
void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
};
/*
* 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. */
CIRCLEQ_ENTRY(_scr) eq; /* Screens. */
int id; /* Screen id #. */
int refcnt; /* Reference count. */
WIN *wp; /* Pointer to window. */
GS *gp; /* Pointer to global area. */
SCR *nextdisp; /* Next display screen. */
SCR *ccl_parent; /* Colon command-line parent screen. */
EXF *ep; /* Screen's current EXF structure. */
CHAR_T *c_lp; /* Cached line. */
size_t c_len; /* Cached line length. */
/* May move out again once we use DB
* to cache internal representation
*/
size_t c_blen; /* Cached line buffer length. */
db_recno_t c_lno; /* Cached line number. */
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 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. */
size_t coff; /* 0-N: screen col offset in display. */
size_t roff; /* 0-N: screen row offset in display. */
/* Cursor's: */
db_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. */
#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_SHIFT 5 /* Shift lines. */
#define L_YANKED 6 /* Yanked lines. */
db_recno_t rptlchange; /* Ex/vi: last L_CHANGED lno. */
db_recno_t rptlines[L_YANKED + 1];/* Ex/vi: lines changed by last op. */
TEXTH tiq; /* Ex/vi: text input queue. */
SCRIPT *script; /* Vi: script mode information .*/
db_recno_t defscroll; /* Vi: ^D, ^U scroll information. */
/* Display character. */
u_char cname[MAX_CHARACTER_COLUMNS + 1];
size_t clen; /* Length of display character. */
enum { /* Vi editor mode. */
SM_APPEND = 0, SM_CHANGE, SM_COMMAND, SM_INSERT,
SM_REPLACE } showmode;
void *ex_private; /* Ex private area. */
void *vi_private; /* Vi private area. */
void *perl_private; /* Perl private area. */
void *cl_private; /* Curses private area. */
CONV conv;
struct _log_state state; /* State during log traversal. */
/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
char *alt_name; /* Ex/vi: alternate file name. */
ARG_CHAR_T at_lbuf; /* Ex/vi: Last executed at buffer. */
/* Ex/vi: re_compile flags. */
#define RE_WSTART L("[[:<:]]") /* Ex/vi: not-in-word search pattern. */
#define RE_WSTOP L("[[:>:]]")
#define RE_WSTART_LEN (sizeof(RE_WSTART)/sizeof(CHAR_T)-1)
#define RE_WSTOP_LEN (sizeof(RE_WSTOP)/sizeof(CHAR_T)-1)
/* Ex/vi: flags to search routines. */
#define SEARCH_CSCOPE 0x000001 /* Search for a cscope pattern. */
#define SEARCH_CSEARCH 0x000002 /* Compile search replacement. */
#define SEARCH_CSUBST 0x000004 /* Compile substitute replacement. */
#define SEARCH_EOL 0x000008 /* Offset past EOL is okay. */
#define SEARCH_EXTEND 0x000010 /* Extended RE. */
#define SEARCH_FIRST 0x000020 /* Search from the first line. */
#define SEARCH_IC 0x000040 /* Ignore case. */
#define SEARCH_ICL 0x000080 /* Ignore case. */
#define SEARCH_INCR 0x000100 /* Search incrementally. */
#define SEARCH_LITERAL 0x000200 /* Literal string. */
#define SEARCH_MSG 0x000400 /* Display search messages. */
#define SEARCH_NOOPT 0x000800 /* Ignore edit options. */
#define SEARCH_PARSE 0x001000 /* Parse the search pattern. */
#define SEARCH_SET 0x002000 /* Set search direction. */
#define SEARCH_TAG 0x004000 /* Search for a tag pattern. */
#define SEARCH_WMSG 0x008000 /* Display search-wrapped messages. */
#define SEARCH_WRAP 0x010000 /* Wrap past sof/eof. */
/* Ex/vi: RE information. */
dir_t searchdir; /* Last file search direction. */
regex_t re_c; /* Search RE: compiled form. */
CHAR_T *re; /* Search RE: uncompiled form. */
size_t re_len; /* Search RE: uncompiled length. */
regex_t subre_c; /* Substitute RE: compiled form. */
CHAR_T *subre; /* Substitute RE: uncompiled form. */
size_t subre_len; /* Substitute RE: uncompiled length). */
CHAR_T *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_int8_t c_suffix; /* Edcompatible 'c' suffix value. */
u_int8_t g_suffix; /* Edcompatible 'g' suffix value. */
OPTION opts[O_OPTIONCOUNT]; /* Ex/vi: Options. */
/*
* Screen flags.
*
* Editor screens.
*/
#define SC_EX 0x00000001 /* Ex editor. */
#define SC_VI 0x00000002 /* Vi editor. */
/*
* Screen formatting flags, first major, then minor.
*
* SC_SCR_EX
* Ex screen, i.e. cooked mode.
* SC_SCR_VI
* Vi screen, i.e. raw mode.
* SC_SCR_EXWROTE
* The editor had to write on the screen behind curses' back, and we can't
* let curses change anything until the user agrees, e.g. entering the
* commands :!utility followed by :set. We have to switch back into the
* vi "editor" to read the user's command input, but we can't touch the
* rest of the screen because it's known to be wrong.
* SC_SCR_REFORMAT
* The expected presentation of the lines on the screen have changed,
* requiring that the intended screen lines be recalculated. Implies
* SC_SCR_REDRAW.
* SC_SCR_REDRAW
* The screen doesn't correctly represent the file; repaint it. Note,
* setting SC_SCR_REDRAW in the current window causes *all* windows to
* be repainted.
* SC_SCR_CENTER
* If the current line isn't already on the screen, center it.
* SC_SCR_TOP
* If the current line isn't already on the screen, put it at the to@.
*/
#define SC_SCR_EX 0x00000004 /* Screen is in ex mode. */
#define SC_SCR_VI 0x00000008 /* Screen is in vi mode. */
#define SC_SCR_EXWROTE 0x00000010 /* Ex overwrite: see comment above. */
#define SC_SCR_REFORMAT 0x00000020 /* Reformat (refresh). */
#define SC_SCR_REDRAW 0x00000040 /* Refresh. */
#define SC_SCR_CENTER 0x00000080 /* Center the line if not visible. */
#define SC_SCR_TOP 0x00000100 /* Top the line if not visible. */
/* Screen/file changes. */
#define SC_EXIT 0x00000200 /* Exiting (not forced). */
#define SC_EXIT_FORCE 0x00000400 /* Exiting (forced). */
#define SC_FSWITCH 0x00000800 /* Switch underlying files. */
#define SC_SSWITCH 0x00001000 /* Switch screens. */
#define SC_ARGNOFREE 0x00002000 /* Argument list wasn't allocated. */
#define SC_ARGRECOVER 0x00004000 /* Argument list is recovery files. */
#define SC_AT_SET 0x00008000 /* Last at buffer set. */
#define SC_COMEDIT 0x00010000 /* Colon command-line edit window. */
#define SC_EX_GLOBAL 0x00020000 /* Ex: executing a global command. */
#define SC_EX_SILENT 0x00040000 /* Ex: batch script. */
#define SC_EX_WAIT_NO 0x00080000 /* Ex: don't wait for the user. */
#define SC_EX_WAIT_YES 0x00100000 /* Ex: do wait for the user. */
#define SC_READONLY 0x00200000 /* Persistent readonly state. */
#define SC_RE_SEARCH 0x00400000 /* Search RE has been compiled. */
#define SC_RE_SUBST 0x00800000 /* Substitute RE has been compiled. */
#define SC_SCRIPT 0x01000000 /* Shell script window. */
#define SC_STATUS 0x02000000 /* Welcome message. */
#define SC_STATUS_CNT 0x04000000 /* Welcome message plus file count. */
#define SC_TINPUT 0x08000000 /* Doing text input. */
#define SC_TINPUT_INFO 0x10000000 /* Doing text input on info line. */
#define SC_CONV_ERROR 0x20000000 /* Met with a conversion error. */
u_int32_t flags;
int db_error; /* Return code from db function. */
};

482
dist/nvi/common/search.c vendored Normal file
View File

@@ -0,0 +1,482 @@
/* $NetBSD: search.c,v 1.3 2009/01/22 10:33:58 lukem Exp $ */
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1992, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: search.c,v 10.31 2001/06/25 15:19:12 skimo Exp (Berkeley) Date: 2001/06/25 15:19:12";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <bitstring.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "common.h"
typedef enum { S_EMPTY, S_EOF, S_NOPREV, S_NOTFOUND, S_SOF, S_WRAP } smsg_t;
static void search_msg __P((SCR *, smsg_t));
static int search_init __P((SCR *, dir_t, CHAR_T *, size_t, CHAR_T **, u_int));
/*
* search_init --
* Set up a search.
*/
static int
search_init(SCR *sp, dir_t dir, CHAR_T *ptrn, size_t plen, CHAR_T **epp, u_int flags)
{
db_recno_t lno;
int delim;
CHAR_T *p, *t;
/* If the file is empty, it's a fast search. */
if (sp->lno <= 1) {
if (db_last(sp, &lno))
return (1);
if (lno == 0) {
if (LF_ISSET(SEARCH_MSG))
search_msg(sp, S_EMPTY);
return (1);
}
}
if (LF_ISSET(SEARCH_PARSE)) { /* Parse the string. */
/*
* Use the saved pattern if no pattern specified, or if only
* one or two delimiter characters specified.
*
* !!!
* Historically, only the pattern itself was saved, vi didn't
* preserve addressing or delta information.
*/
if (ptrn == NULL)
goto prev;
if (plen == 1) {
if (epp != NULL)
*epp = ptrn + 1;
goto prev;
}
if (ptrn[0] == ptrn[1]) {
if (epp != NULL)
*epp = ptrn + 2;
/* Complain if we don't have a previous pattern. */
prev: if (sp->re == NULL) {
search_msg(sp, S_NOPREV);
return (1);
}
/* Re-compile the search pattern if necessary. */
if (!F_ISSET(sp, SC_RE_SEARCH) && re_compile(sp,
sp->re, sp->re_len, NULL, NULL, &sp->re_c,
SEARCH_CSEARCH | SEARCH_MSG))
return (1);
/* Set the search direction. */
if (LF_ISSET(SEARCH_SET))
sp->searchdir = dir;
return (0);
}
/*
* Set the delimiter, and move forward to the terminating
* delimiter, handling escaped delimiters.
*
* QUOTING NOTE:
* Only discard an escape character if it escapes a delimiter.
*/
for (delim = *ptrn, p = t = ++ptrn;; *t++ = *p++) {
if (--plen == 0 || p[0] == delim) {
if (plen != 0)
++p;
break;
}
if (plen > 1 && p[0] == '\\' && p[1] == delim) {
++p;
--plen;
}
}
if (epp != NULL)
*epp = p;
plen = t - ptrn;
}
/* Compile the RE. */
if (re_compile(sp, ptrn, plen, &sp->re, &sp->re_len, &sp->re_c,
SEARCH_CSEARCH | LF_ISSET(SEARCH_CSCOPE | SEARCH_IC |
SEARCH_LITERAL | SEARCH_MSG | SEARCH_TAG)))
return (1);
/* Set the search direction. */
if (LF_ISSET(SEARCH_SET))
sp->searchdir = dir;
return (0);
}
/*
* f_search --
* Do a forward search.
*
* PUBLIC: int f_search __P((SCR *,
* PUBLIC: MARK *, MARK *, CHAR_T *, size_t, CHAR_T **, u_int));
*/
int
f_search(SCR *sp, MARK *fm, MARK *rm, CHAR_T *ptrn, size_t plen, CHAR_T **eptrn, u_int flags)
{
busy_t btype;
db_recno_t lno;
regmatch_t match[1];
size_t coff, len;
int cnt, eval, rval, wrapped;
CHAR_T *l;
if (search_init(sp, FORWARD, ptrn, plen, eptrn, flags))
return (1);
/* Figure out if we're going to wrap. */
if (!LF_ISSET(SEARCH_NOOPT) && O_ISSET(sp, O_WRAPSCAN))
LF_SET(SEARCH_WRAP);
if (LF_ISSET(SEARCH_FIRST)) {
lno = 1;
coff = 0;
} else {
if (db_get(sp, fm->lno, DBG_FATAL, &l, &len))
return (1);
lno = fm->lno;
/*
* If doing incremental search, start searching at the previous
* column, so that we search a minimal distance and still match
* special patterns, e.g., \< for beginning of a word.
*
* Otherwise, 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 the
* "-t foo" didn't work if the 'f' was the first character in
* the file.
*/
if (LF_ISSET(SEARCH_INCR)) {
if ((coff = fm->cno) != 0)
--coff;
} else if (fm->cno + 1 >= len) {
coff = 0;
lno = fm->lno + 1;
if (db_get(sp, lno, 0, &l, &len)) {
if (!LF_ISSET(SEARCH_WRAP)) {
if (LF_ISSET(SEARCH_MSG))
search_msg(sp, S_EOF);
return (1);
}
lno = 1;
}
} else
coff = fm->cno + 1;
}
btype = BUSY_ON;
for (cnt = INTERRUPT_CHECK, rval = 1, wrapped = 0;; ++lno, coff = 0) {
if (cnt-- == 0) {
if (INTERRUPTED(sp))
break;
if (LF_ISSET(SEARCH_MSG)) {
search_busy(sp, btype);
btype = BUSY_UPDATE;
}
cnt = INTERRUPT_CHECK;
}
if ((wrapped && lno > fm->lno) ||
db_get(sp, lno, 0, &l, &len)) {
if (wrapped) {
if (LF_ISSET(SEARCH_MSG))
search_msg(sp, S_NOTFOUND);
break;
}
if (!LF_ISSET(SEARCH_WRAP)) {
if (LF_ISSET(SEARCH_MSG))
search_msg(sp, S_EOF);
break;
}
lno = 0;
wrapped = 1;
continue;
}
/* If already at EOL, just keep going. */
if (len != 0 && coff == len)
continue;
/* Set the termination. */
match[0].rm_so = coff;
match[0].rm_eo = len;
#if defined(DEBUG) && 0
vtrace(sp, "F search: %lu from %u to %u\n",
lno, coff, len != 0 ? len - 1 : len);
#endif
/* Search the line. */
eval = regexec(&sp->re_c, l, 1, match,
(match[0].rm_so == 0 ? 0 : REG_NOTBOL) | REG_STARTEND);
if (eval == REG_NOMATCH)
continue;
if (eval != 0) {
if (LF_ISSET(SEARCH_MSG))
re_error(sp, eval, &sp->re_c);
else
(void)sp->gp->scr_bell(sp);
break;
}
/* Warn if the search wrapped. */
if (wrapped && LF_ISSET(SEARCH_WMSG))
search_msg(sp, S_WRAP);
#if defined(DEBUG) && 0
vtrace(sp, "F search: %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 (e.g. 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 != 0 ? len - 1 : 0;
rval = 0;
break;
}
if (LF_ISSET(SEARCH_MSG))
search_busy(sp, BUSY_OFF);
return (rval);
}
/*
* b_search --
* Do a backward search.
*
* PUBLIC: int b_search __P((SCR *,
* PUBLIC: MARK *, MARK *, CHAR_T *, size_t, CHAR_T **, u_int));
*/
int
b_search(SCR *sp, MARK *fm, MARK *rm, CHAR_T *ptrn, size_t plen, CHAR_T **eptrn, u_int flags)
{
busy_t btype;
db_recno_t lno;
regmatch_t match[1];
size_t coff, last, len;
int cnt, eval, rval, wrapped;
CHAR_T *l;
if (search_init(sp, BACKWARD, ptrn, plen, eptrn, flags))
return (1);
/* Figure out if we're going to wrap. */
if (!LF_ISSET(SEARCH_NOOPT) && O_ISSET(sp, O_WRAPSCAN))
LF_SET(SEARCH_WRAP);
/*
* If doing incremental search, set the "starting" position past the
* current column, so that we search a minimal distance and still
* match special patterns, e.g., \> for the end of a word. This is
* safe when the cursor is at the end of a line because we only use
* it for comparison with the location of the match.
*
* Otherwise, start searching immediately before the cursor. If in
* the first column, start search on the previous line.
*/
if (LF_ISSET(SEARCH_INCR)) {
lno = fm->lno;
coff = fm->cno + 1;
} else {
if (fm->cno == 0) {
if (fm->lno == 1 && !LF_ISSET(SEARCH_WRAP)) {
if (LF_ISSET(SEARCH_MSG))
search_msg(sp, S_SOF);
return (1);
}
lno = fm->lno - 1;
} else
lno = fm->lno;
coff = fm->cno;
}
btype = BUSY_ON;
for (cnt = INTERRUPT_CHECK, rval = 1, wrapped = 0;; --lno, coff = 0) {
if (cnt-- == 0) {
if (INTERRUPTED(sp))
break;
if (LF_ISSET(SEARCH_MSG)) {
search_busy(sp, btype);
btype = BUSY_UPDATE;
}
cnt = INTERRUPT_CHECK;
}
if ((wrapped && lno < fm->lno) || lno == 0) {
if (wrapped) {
if (LF_ISSET(SEARCH_MSG))
search_msg(sp, S_NOTFOUND);
break;
}
if (!LF_ISSET(SEARCH_WRAP)) {
if (LF_ISSET(SEARCH_MSG))
search_msg(sp, S_SOF);
break;
}
if (db_last(sp, &lno))
break;
if (lno == 0) {
if (LF_ISSET(SEARCH_MSG))
search_msg(sp, S_EMPTY);
break;
}
++lno;
wrapped = 1;
continue;
}
if (db_get(sp, lno, 0, &l, &len))
break;
/* Set the termination. */
match[0].rm_so = 0;
match[0].rm_eo = len;
#if defined(DEBUG) && 0
vtrace(sp,
"B search: %lu from 0 to %qu\n", lno, match[0].rm_eo);
#endif
/* Search the line. */
eval = regexec(&sp->re_c, l, 1, match,
((size_t)match[0].rm_eo == len ? 0 : REG_NOTEOL) | REG_STARTEND);
if (eval == REG_NOMATCH)
continue;
if (eval != 0) {
if (LF_ISSET(SEARCH_MSG))
re_error(sp, eval, &sp->re_c);
else
(void)sp->gp->scr_bell(sp);
break;
}
/* Check for a match starting past the cursor. */
if (coff != 0 && (size_t)match[0].rm_so >= coff)
continue;
/* Warn if the search wrapped. */
if (wrapped && LF_ISSET(SEARCH_WMSG))
search_msg(sp, S_WRAP);
#if defined(DEBUG) && 0
vtrace(sp, "B 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 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 ((size_t)match[0].rm_so >= len)
break;
match[0].rm_eo = len;
eval = regexec(&sp->re_c, l, 1, match,
(match[0].rm_so == 0 ? 0 : REG_NOTBOL) |
REG_STARTEND);
if (eval == REG_NOMATCH)
break;
if (eval != 0) {
if (LF_ISSET(SEARCH_MSG))
re_error(sp, eval, &sp->re_c);
else
(void)sp->gp->scr_bell(sp);
goto err;
}
if (coff && (size_t)match[0].rm_so >= coff)
break;
}
rm->lno = lno;
/* See comment in f_search(). */
if (!LF_ISSET(SEARCH_EOL) && last >= len)
rm->cno = len != 0 ? len - 1 : 0;
else
rm->cno = last;
rval = 0;
break;
}
err: if (LF_ISSET(SEARCH_MSG))
search_busy(sp, BUSY_OFF);
return (rval);
}
/*
* search_msg --
* Display one of the search messages.
*/
static void
search_msg(SCR *sp, smsg_t msg)
{
switch (msg) {
case S_EMPTY:
msgq(sp, M_ERR, "072|File empty; nothing to search");
break;
case S_EOF:
msgq(sp, M_ERR,
"073|Reached end-of-file without finding the pattern");
break;
case S_NOPREV:
msgq(sp, M_ERR, "074|No previous search pattern");
break;
case S_NOTFOUND:
msgq(sp, M_ERR, "075|Pattern not found");
break;
case S_SOF:
msgq(sp, M_ERR,
"076|Reached top-of-file without finding the pattern");
break;
case S_WRAP:
msgq(sp, M_ERR, "077|Search wrapped");
break;
default:
abort();
}
}
/*
* search_busy --
* Put up the busy searching message.
*
* PUBLIC: void search_busy __P((SCR *, busy_t));
*/
void
search_busy(SCR *sp, busy_t btype)
{
sp->gp->scr_busy(sp, "078|Searching...", btype);
}

369
dist/nvi/common/seq.c vendored Normal file
View File

@@ -0,0 +1,369 @@
/* $NetBSD: seq.c,v 1.3 2011/03/21 14:53:02 tnozaki Exp $ */
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1992, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: seq.c,v 10.15 2001/06/25 15:19:12 skimo Exp (Berkeley) Date: 2001/06/25 15:19:12";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <bitstring.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
/*
* seq_set --
* Internal version to enter a sequence.
*
* PUBLIC: int seq_set __P((SCR *, CHAR_T *,
* PUBLIC: size_t, CHAR_T *, size_t, CHAR_T *, size_t, seq_t, int));
*/
int
seq_set(SCR *sp, CHAR_T *name, size_t nlen, CHAR_T *input, size_t ilen, CHAR_T *output, size_t olen, seq_t stype, int flags)
{
CHAR_T *p;
SEQ *lastqp, *qp;
int sv_errno;
/*
* An input string must always be present. The output string
* can be NULL, when set internally, that's how we throw away
* input.
*
* Just replace the output field if the string already set.
*/
if ((qp =
seq_find(sp, &lastqp, NULL, input, ilen, stype, NULL)) != NULL) {
if (LF_ISSET(SEQ_NOOVERWRITE))
return (0);
if (output == NULL || olen == 0) {
p = NULL;
olen = 0;
} else if ((p = v_wstrdup(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_wstrdup(sp, name, nlen)) == NULL) {
sv_errno = errno;
goto mem2;
}
qp->nlen = nlen;
/* Input. */
if ((qp->input = v_wstrdup(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_wstrdup(sp, output, olen)) == NULL) {
sv_errno = errno;
free(qp->input);
mem3: if (qp->name != NULL)
free(qp->name);
mem2: free(qp);
mem1: errno = sv_errno;
msgq(sp, M_SYSERR, NULL);
return (1);
}
qp->olen = olen;
/* Type, flags. */
qp->stype = stype;
qp->flags = flags;
/* Link into the chain. */
if (lastqp == NULL) {
LIST_INSERT_HEAD(&sp->gp->seqq, qp, q);
} else {
LIST_INSERT_AFTER(lastqp, qp, q);
}
/* Set the fast lookup bit. */
if ((qp->input[0] & ~MAX_BIT_SEQ) == 0)
bit_set(sp->gp->seqb, qp->input[0]);
return (0);
}
/*
* seq_delete --
* Delete a sequence.
*
* PUBLIC: int seq_delete __P((SCR *, CHAR_T *, size_t, seq_t));
*/
int
seq_delete(SCR *sp, CHAR_T *input, size_t ilen, seq_t stype)
{
SEQ *qp;
if ((qp = seq_find(sp, NULL, NULL, input, ilen, stype, NULL)) == NULL)
return (1);
return (seq_mdel(qp));
}
/*
* seq_mdel --
* Delete a map entry, without lookup.
*
* PUBLIC: int seq_mdel __P((SEQ *));
*/
int
seq_mdel(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);
return (0);
}
/*
* seq_find --
* Search the sequence list for a match to a buffer, if ispartial
* isn't NULL, partial matches count.
*
* PUBLIC: SEQ *seq_find
* PUBLIC: __P((SCR *, SEQ **, EVENT *, CHAR_T *, size_t, seq_t, int *));
*/
SEQ *
seq_find(SCR *sp, SEQ **lastqp, EVENT *e_input, CHAR_T *c_input, size_t ilen, seq_t 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, and then
* a real comparison.
*/
if (e_input == NULL) {
if (qp->input[0] > c_input[0])
break;
if (qp->input[0] < c_input[0] ||
qp->stype != stype || F_ISSET(qp, SEQ_FUNCMAP))
continue;
diff = MEMCMP(qp->input, c_input, MIN(qp->ilen, ilen));
} else {
if (qp->input[0] > e_input->e_c)
break;
if (qp->input[0] < e_input->e_c ||
qp->stype != stype || F_ISSET(qp, SEQ_FUNCMAP))
continue;
diff =
e_memcmp(qp->input, e_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_close --
* Discard all sequences.
*
* PUBLIC: void seq_close __P((GS *));
*/
void
seq_close(GS *gp)
{
SEQ *qp;
while ((qp = gp->seqq.lh_first) != NULL) {
if (qp->name != NULL)
free(qp->name);
if (qp->input != NULL)
free(qp->input);
if (qp->output != NULL)
free(qp->output);
LIST_REMOVE(qp, q);
free(qp);
}
}
/*
* seq_dump --
* Display the sequence entries of a specified type.
*
* PUBLIC: int seq_dump __P((SCR *, seq_t, int));
*/
int
seq_dump(SCR *sp, seq_t stype, int isname)
{
CHAR_T *p;
GS *gp;
SEQ *qp;
int cnt, len, olen;
cnt = 0;
gp = sp->gp;
for (qp = 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_puts(sp, (char *)KEY_NAME(sp, *p));
for (len = STANDARD_TAB - len % STANDARD_TAB; len > 0;)
len -= ex_puts(sp, " ");
if (qp->output != NULL)
for (p = qp->output,
olen = qp->olen, len = 0; olen > 0; --olen, ++p)
len += ex_puts(sp, (char *)KEY_NAME(sp, *p));
else
len = 0;
if (isname && qp->name != NULL) {
for (len = STANDARD_TAB - len % STANDARD_TAB; len > 0;)
len -= ex_puts(sp, " ");
for (p = qp->name,
olen = qp->nlen; olen > 0; --olen, ++p)
(void)ex_puts(sp, (char *)KEY_NAME(sp, *p));
}
(void)ex_puts(sp, "\n");
}
return (cnt);
}
/*
* seq_save --
* Save the sequence entries to a file.
*
* PUBLIC: int seq_save __P((SCR *, FILE *, char *, seq_t));
*/
int
seq_save(SCR *sp, FILE *fp, const char *prefix, seq_t stype)
{
CHAR_T *p;
SEQ *qp;
size_t olen;
ARG_CHAR_T ch;
/* Write a sequence command for all keys the user defined. */
for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next) {
if (stype != qp->stype || !F_ISSET(qp, SEQ_USERDEF))
continue;
if (prefix)
(void)fprintf(fp, "%s", prefix);
for (p = qp->input, olen = qp->ilen; olen > 0; --olen) {
ch = (UCHAR_T)*p++;
if (ch == CH_LITERAL || ch == '|' ||
ISBLANK(ch) || KEY_VAL(sp, ch) == K_NL)
(void)putc(CH_LITERAL, fp);
(void)fprintf(fp, WC, ch);
}
(void)putc(' ', fp);
if (qp->output != NULL)
for (p = qp->output,
olen = qp->olen; olen > 0; --olen) {
ch = (UCHAR_T)*p++;
if (ch == CH_LITERAL || ch == '|' ||
KEY_VAL(sp, ch) == K_NL)
(void)putc(CH_LITERAL, fp);
(void)fprintf(fp, WC, ch);
}
(void)putc('\n', fp);
}
return (0);
}
/*
* e_memcmp --
* Compare a string of EVENT's to a string of CHAR_T's.
*
* PUBLIC: int e_memcmp __P((CHAR_T *, EVENT *, size_t));
*/
int
e_memcmp(CHAR_T *p1, EVENT *ep, size_t n)
{
if (n != 0) {
do {
if (*p1++ != ep->e_c)
return (*--p1 - ep->e_c);
++ep;
} while (--n != 0);
}
return (0);
}

46
dist/nvi/common/seq.h vendored Normal file
View File

@@ -0,0 +1,46 @@
/* $NetBSD: seq.h,v 1.1.1.2 2008/05/18 14:29:52 aymeric Exp $ */
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1992, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*
* Id: seq.h,v 10.3 1996/03/06 19:51:03 bostic Exp (Berkeley) Date: 1996/03/06 19:51:03
*/
/*
* 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.
*/
struct _seq {
LIST_ENTRY(_seq) q; /* Linked list of all sequences. */
seq_t stype; /* Sequence type. */
CHAR_T *name; /* Sequence name (if any). */
size_t nlen; /* Name length. */
CHAR_T *input; /* Sequence input keys. */
size_t ilen; /* Input keys length. */
CHAR_T *output; /* Sequence output keys. */
size_t olen; /* Output keys length. */
#define SEQ_FUNCMAP 0x01 /* If unresolved function key.*/
#define SEQ_NOOVERWRITE 0x02 /* Don't replace existing entry. */
#define SEQ_SCREEN 0x04 /* If screen specific. */
#define SEQ_USERDEF 0x08 /* If user defined. */
u_int8_t flags;
};

93
dist/nvi/common/trace.c vendored Normal file
View File

@@ -0,0 +1,93 @@
/* $NetBSD: trace.c,v 1.1.1.2 2008/05/18 14:29:52 aymeric Exp $ */
/*-
* Copyright (c) 1996
* Rob Zimmermann. All rights reserved.
* Copyright (c) 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: trace.c,v 8.4 1997/08/03 15:04:23 bostic Exp (Berkeley) Date: 1997/08/03 15:04:23";
#endif /* not lint */
#include <sys/queue.h>
#include <bitstring.h>
#include <stdio.h>
#ifdef __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif
#include "common.h"
#ifdef TRACE
static FILE *tfp;
/*
* vtrace_end --
* End tracing.
*
* PUBLIC: void vtrace_end __P((void));
*/
void
vtrace_end()
{
if (tfp != NULL && tfp != stderr)
(void)fclose(tfp);
}
/*
* vtrace_init --
* Initialize tracing.
*
* PUBLIC: void vtrace_init __P((char *));
*/
void
vtrace_init(name)
char *name;
{
if (name == NULL || (tfp = fopen(name, "w")) == NULL)
tfp = stderr;
vtrace("\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\nTRACE\n");
}
/*
* vtrace --
* Debugging trace routine.
*
* PUBLIC: void vtrace __P((const char *, ...));
*/
void
#ifdef __STDC__
vtrace(const char *fmt, ...)
#else
vtrace(fmt, va_alist)
char *fmt;
va_dcl
#endif
{
va_list ap;
if (tfp == NULL)
vtrace_init(NULL);
#ifdef __STDC__
va_start(ap, fmt);
#else
va_start(ap);
#endif
(void)vfprintf(tfp, fmt, ap);
va_end(ap);
(void)fflush(tfp);
}
#endif

196
dist/nvi/common/util.c vendored Normal file
View File

@@ -0,0 +1,196 @@
/* $NetBSD: util.c,v 1.4 2011/03/21 14:53:02 tnozaki Exp $ */
/*-
* Copyright (c) 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1991, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "Id: util.c,v 10.22 2001/06/25 15:19:12 skimo Exp (Berkeley) Date: 2001/06/25 15:19:12";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <bitstring.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "common.h"
/*
* binc --
* Increase the size of a buffer.
*
* PUBLIC: void *binc __P((SCR *, void *, size_t *, size_t));
*/
void *
binc(SCR *sp, void *bp, size_t *bsizep, size_t min)
/* sp MAY BE NULL!!! */
{
size_t csize;
/* If already larger than the minimum, just return. */
if (min && *bsizep >= min)
return (bp);
csize = *bsizep + MAX(min, 256);
REALLOC(sp, bp, void *, csize);
if (bp == NULL) {
/*
* Theoretically, realloc is supposed to leave any already
* held memory alone if it can't get more. Don't trust it.
*/
*bsizep = 0;
return (NULL);
}
/*
* Memory is guaranteed to be zero-filled, various parts of
* nvi depend on this.
*/
memset((char *)bp + *bsizep, 0, csize - *bsizep);
*bsizep = csize;
return (bp);
}
/*
* nonblank --
* Set the column number of the first non-blank character
* including or after the starting column. On error, set
* the column to 0, it's safest.
*
* PUBLIC: int nonblank __P((SCR *, db_recno_t, size_t *));
*/
int
nonblank(SCR *sp, db_recno_t lno, size_t *cnop)
{
CHAR_T *p;
size_t cnt, len, off;
int isempty;
/* Default. */
off = *cnop;
*cnop = 0;
/* Get the line, succeeding in an empty file. */
if (db_eget(sp, lno, &p, &len, &isempty))
return (!isempty);
/* Set the offset. */
if (len == 0 || off >= len)
return (0);
for (cnt = off, p = &p[off],
len -= off; len && ISBLANK((UCHAR_T)*p); ++cnt, ++p, --len);
/* Set the return. */
*cnop = len ? cnt : cnt - 1;
return (0);
}
/*
* tail --
* Return tail of a path.
*
* PUBLIC: char *tail __P((char *));
*/
const char *
tail(const char *path)
{
const char *p;
if ((p = strrchr(path, '/')) == NULL)
return (path);
return (p + 1);
}
/*
* v_strdup --
* Strdup for wide character strings with an associated length.
*
* PUBLIC: char *v_strdup __P((SCR *, const char *, size_t));
*/
char *
v_strdup(SCR *sp, const char *str, size_t len)
{
char *copy;
MALLOC(sp, copy, char *, (len + 1));
if (copy == NULL)
return (NULL);
memcpy(copy, str, len);
copy[len] = '\0';
return (copy);
}
/*
* v_strdup --
* Strdup for wide character strings with an associated length.
*
* PUBLIC: CHAR_T *v_wstrdup __P((SCR *, const CHAR_T *, size_t));
*/
CHAR_T *
v_wstrdup(SCR *sp, const CHAR_T *str, size_t len)
{
CHAR_T *copy;
MALLOC(sp, copy, CHAR_T *, (len + 1) * sizeof(CHAR_T));
if (copy == NULL)
return (NULL);
MEMCPYW(copy, str, len);
copy[len] = '\0';
return (copy);
}
/*
* nget_uslong --
* Get an unsigned long, checking for overflow.
*
* PUBLIC: enum nresult nget_uslong __P((SCR *, u_long *, const CHAR_T *, CHAR_T **, int));
*/
enum nresult
nget_uslong(SCR *sp, u_long *valp, const CHAR_T *p, CHAR_T **endp, int base)
{
errno = 0;
*valp = STRTOUL(p, (RCHAR_T **)endp, base);
if (errno == 0)
return (NUM_OK);
if (errno == ERANGE && *valp == ULONG_MAX)
return (NUM_OVER);
return (NUM_ERR);
}
/*
* nget_slong --
* Convert a signed long, checking for overflow and underflow.
*
* PUBLIC: enum nresult nget_slong __P((SCR *, long *, const CHAR_T *, CHAR_T **, int));
*/
enum nresult
nget_slong(SCR *sp, long int *valp, const CHAR_T *p, CHAR_T **endp, int base)
{
errno = 0;
*valp = STRTOL(p, (RCHAR_T **)endp, base);
if (errno == 0)
return (NUM_OK);
if (errno == ERANGE) {
if (*valp == LONG_MAX)
return (NUM_OVER);
if (*valp == LONG_MIN)
return (NUM_UNDER);
}
return (NUM_ERR);
}

59
dist/nvi/common/util.h vendored Normal file
View File

@@ -0,0 +1,59 @@
/* $NetBSD: util.h,v 1.2 2009/01/18 03:43:45 lukem Exp $ */
/*-
* Copyright (c) 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*
* Id: util.h,v 10.5 1996/03/16 14:42:47 bostic Exp (Berkeley) Date: 1996/03/16 14:42:47
*/
/* Macros to init/set/clear/test flags. */
#define FL_INIT(l, f) (l) = (f) /* Specific flags location. */
#define FL_SET(l, f) ((l) |= (f))
#define FL_CLR(l, f) ((l) &= ~(f))
#define FL_ISSET(l, f) ((l) & (f))
#define LF_INIT(f) FL_INIT(flags, f) /* Local variable flags. */
#define LF_SET(f) FL_SET(flags, f)
#define LF_CLR(f) FL_CLR(flags, f)
#define LF_ISSET(f) FL_ISSET(flags, f)
#define F_INIT(p, f) FL_INIT((p)->flags, f) /* Structure element flags. */
#define F_SET(p, f) FL_SET((p)->flags, f)
#define F_CLR(p, f) FL_CLR((p)->flags, f)
#define F_ISSET(p, f) FL_ISSET((p)->flags, f)
/* Offset to next column of stop size, e.g. tab offsets. */
#define COL_OFF(c, stop) ((stop) - ((c) % (stop)))
/* Busy message types. */
typedef enum { B_NONE, B_OFF, B_READ, B_RECOVER, B_SEARCH, B_WRITE } bmsg_t;
/*
* Number handling defines and protoypes.
*
* NNFITS: test for addition of two negative numbers under a limit
* NPFITS: test for addition of two positive numbers under a limit
* NADD_SLONG: test for addition of two signed longs
* NADD_USLONG: test for addition of two unsigned longs
*/
enum nresult { NUM_ERR, NUM_OK, NUM_OVER, NUM_UNDER };
#define NNFITS(min, cur, add) \
(((long)(min)) - (cur) <= (add))
#define NPFITS(max, cur, add) \
(((unsigned long)(max)) - (cur) >= (add))
#define NADD_SLONG(sp, v1, v2) \
((v1) < 0 ? \
((v2) < 0 && \
NNFITS(LONG_MIN, (v1), (v2))) ? NUM_UNDER : NUM_OK : \
(v1) > 0 ? \
(v2) > 0 && \
NPFITS(LONG_MAX, (unsigned long)(v1), (unsigned long)(v2)) ? \
NUM_OK : NUM_OVER : \
NUM_OK)
#define NADD_USLONG(sp, v1, v2) \
(NPFITS(ULONG_MAX, (v1), (v2)) ? NUM_OK : NUM_OVER)

38
dist/nvi/common/util2.c vendored Normal file
View File

@@ -0,0 +1,38 @@
/* $NetBSD: util2.c,v 1.1.1.2 2008/05/18 14:29:52 aymeric Exp $ */
#include "config.h"
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "multibyte.h"
int
ucs2utf8(const CHAR_T *src, size_t len, char *dst)
{
int i, j;
for (i = 0, j = 0; i < len; ++i) {
if (src[i] < 0x80)
dst[j++] = src[i];
else if (src[i] < 0x800) {
dst[j++] = (src[i] >> 6) | 0xc0;
dst[j++] = (src[i] & 0x3f) | 0x80;
} else {
dst[j++] = (src[i] >> 12) | 0xe0;
dst[j++] = ((src[i] >> 6) & 0x3f) | 0x80;
dst[j++] = (src[i] & 0x3f) | 0x80;
}
}
return j;
}

37
dist/nvi/common/vi.src vendored Normal file
View File

@@ -0,0 +1,37 @@
PREFIX vi
INCLUDE #include <sys/types.h>
INCLUDE #include <sys/queue.h>
INCLUDE #include <sys/stat.h>
INCLUDE #include <bitstring.h>
INCLUDE #include <errno.h>
INCLUDE #include <fcntl.h>
INCLUDE #include <limits.h>
INCLUDE #include <stdio.h>
INCLUDE #include <stdlib.h>
INCLUDE #include <string.h>
INCLUDE #include "common.h"
INCLUDE #include <db_int.h>
INCLUDE #include "db_page.h"
INCLUDE #include "db_am.h"
INCLUDE #include "rep.h"
INCLUDE #include "txn.h"
BEGIN marker 200
ARG opcode u_int32_t lu
END
BEGIN cursor 201
ARG opcode u_int32_t lu
ARG lno db_recno_t lu
ARG cno size_t d
END
BEGIN mark 202
POINTER lmp LMARK * %
END
BEGIN change 203
ARG opcode u_int32_t lu
ARG lno db_recno_t lu
END

810
dist/nvi/common/vi_auto.c vendored Normal file
View File

@@ -0,0 +1,810 @@
/* $NetBSD: vi_auto.c,v 1.1.1.2 2008/05/18 14:29:53 aymeric Exp $ */
/* Do not edit: automatically built by gen_rec.awk. */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <bitstring.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
#include <db_int.h>
#include "db_page.h"
#include "db_am.h"
#include "rep.h"
#include "txn.h"
/*
* PUBLIC: #ifdef USE_DB4_LOGGING
*/
/*
* PUBLIC: int __vi_marker_log __P((DB_ENV *, DB_TXN *, DB_LSN *, u_int32_t,
* PUBLIC: u_int32_t));
*/
int
__vi_marker_log(dbenv, txnid, ret_lsnp, flags,
opcode)
DB_ENV *dbenv;
DB_TXN *txnid;
DB_LSN *ret_lsnp;
u_int32_t flags;
u_int32_t opcode;
{
DBT logrec;
DB_LSN *lsnp, null_lsn;
u_int32_t uinttmp;
u_int32_t rectype, txn_num;
int ret;
u_int8_t *bp;
rectype = DB_vi_marker;
if (txnid != NULL &&
TAILQ_FIRST(&txnid->kids) != NULL &&
(ret = __txn_activekids(dbenv, rectype, txnid)) != 0)
return (ret);
txn_num = txnid == NULL ? 0 : txnid->txnid;
if (txnid == NULL) {
ZERO_LSN(null_lsn);
lsnp = &null_lsn;
} else
lsnp = &txnid->last_lsn;
logrec.size = sizeof(rectype) + sizeof(txn_num) + sizeof(DB_LSN)
+ sizeof(u_int32_t);
if ((ret = __os_malloc(dbenv, logrec.size, &logrec.data)) != 0)
return (ret);
bp = logrec.data;
memcpy(bp, &rectype, sizeof(rectype));
bp += sizeof(rectype);
memcpy(bp, &txn_num, sizeof(txn_num));
bp += sizeof(txn_num);
memcpy(bp, lsnp, sizeof(DB_LSN));
bp += sizeof(DB_LSN);
uinttmp = (u_int32_t)opcode;
memcpy(bp, &uinttmp, sizeof(uinttmp));
bp += sizeof(uinttmp);
DB_ASSERT((u_int32_t)(bp - (u_int8_t *)logrec.data) == logrec.size);
ret = dbenv->log_put(dbenv, ret_lsnp, (DBT *)&logrec, flags);
if (txnid != NULL && ret == 0)
txnid->last_lsn = *ret_lsnp;
#ifdef LOG_DIAGNOSTIC
if (ret != 0)
(void)__vi_marker_print(dbenv,
(DBT *)&logrec, ret_lsnp, NULL, NULL);
#endif
__os_free(dbenv, logrec.data, logrec.size);
return (ret);
}
/*
* PUBLIC: int __vi_marker_getpgnos __P((DB_ENV *, DBT *, DB_LSN *, db_recops,
* PUBLIC: void *));
*/
int
__vi_marker_getpgnos(dbenv, rec, lsnp, notused1, summary)
DB_ENV *dbenv;
DBT *rec;
DB_LSN *lsnp;
db_recops notused1;
void *summary;
{
TXN_RECS *t;
int ret;
COMPQUIET(rec, NULL);
COMPQUIET(notused1, DB_TXN_ABORT);
t = (TXN_RECS *)summary;
if ((ret = __rep_check_alloc(dbenv, t, 1)) != 0)
return (ret);
t->array[t->npages].flags = LSN_PAGE_NOLOCK;
t->array[t->npages].lsn = *lsnp;
t->array[t->npages].fid = DB_LOGFILEID_INVALID;
memset(&t->array[t->npages].pgdesc, 0,
sizeof(t->array[t->npages].pgdesc));
t->npages++;
return (0);
}
/*
* PUBLIC: int __vi_marker_print __P((DB_ENV *, DBT *, DB_LSN *, db_recops,
* PUBLIC: void *));
*/
int
__vi_marker_print(dbenv, dbtp, lsnp, notused2, notused3)
DB_ENV *dbenv;
DBT *dbtp;
DB_LSN *lsnp;
db_recops notused2;
void *notused3;
{
__vi_marker_args *argp;
int ret;
notused2 = DB_TXN_ABORT;
notused3 = NULL;
if ((ret = __vi_marker_read(dbenv, dbtp->data, &argp)) != 0)
return (ret);
(void)printf(
"[%lu][%lu]vi_marker: rec: %lu txnid %lx prevlsn [%lu][%lu]\n",
(u_long)lsnp->file,
(u_long)lsnp->offset,
(u_long)argp->type,
(u_long)argp->txnid->txnid,
(u_long)argp->prev_lsn.file,
(u_long)argp->prev_lsn.offset);
(void)printf("\topcode: %lu\n", (u_long)argp->opcode);
(void)printf("\n");
__os_free(dbenv, argp, 0);
return (0);
}
/*
* PUBLIC: int __vi_marker_read __P((DB_ENV *, void *, __vi_marker_args **));
*/
int
__vi_marker_read(dbenv, recbuf, argpp)
DB_ENV *dbenv;
void *recbuf;
__vi_marker_args **argpp;
{
__vi_marker_args *argp;
int ret;
u_int32_t uinttmp;
u_int8_t *bp;
ret = __os_malloc(dbenv, sizeof(__vi_marker_args) +
sizeof(DB_TXN), &argp);
if (ret != 0)
return (ret);
argp->txnid = (DB_TXN *)&argp[1];
bp = recbuf;
memcpy(&argp->type, bp, sizeof(argp->type));
bp += sizeof(argp->type);
memcpy(&argp->txnid->txnid, bp, sizeof(argp->txnid->txnid));
bp += sizeof(argp->txnid->txnid);
memcpy(&argp->prev_lsn, bp, sizeof(DB_LSN));
bp += sizeof(DB_LSN);
memcpy(&uinttmp, bp, sizeof(uinttmp));
argp->opcode = (u_int32_t)uinttmp;
bp += sizeof(uinttmp);
*argpp = argp;
return (0);
}
/*
* PUBLIC: int __vi_cursor_log __P((DB_ENV *, DB_TXN *, DB_LSN *, u_int32_t,
* PUBLIC: u_int32_t, db_recno_t, size_t));
*/
int
__vi_cursor_log(dbenv, txnid, ret_lsnp, flags,
opcode, lno, cno)
DB_ENV *dbenv;
DB_TXN *txnid;
DB_LSN *ret_lsnp;
u_int32_t flags;
u_int32_t opcode;
db_recno_t lno;
size_t cno;
{
DBT logrec;
DB_LSN *lsnp, null_lsn;
u_int32_t uinttmp;
u_int32_t rectype, txn_num;
int ret;
u_int8_t *bp;
rectype = DB_vi_cursor;
if (txnid != NULL &&
TAILQ_FIRST(&txnid->kids) != NULL &&
(ret = __txn_activekids(dbenv, rectype, txnid)) != 0)
return (ret);
txn_num = txnid == NULL ? 0 : txnid->txnid;
if (txnid == NULL) {
ZERO_LSN(null_lsn);
lsnp = &null_lsn;
} else
lsnp = &txnid->last_lsn;
logrec.size = sizeof(rectype) + sizeof(txn_num) + sizeof(DB_LSN)
+ sizeof(u_int32_t)
+ sizeof(u_int32_t)
+ sizeof(u_int32_t);
if ((ret = __os_malloc(dbenv, logrec.size, &logrec.data)) != 0)
return (ret);
bp = logrec.data;
memcpy(bp, &rectype, sizeof(rectype));
bp += sizeof(rectype);
memcpy(bp, &txn_num, sizeof(txn_num));
bp += sizeof(txn_num);
memcpy(bp, lsnp, sizeof(DB_LSN));
bp += sizeof(DB_LSN);
uinttmp = (u_int32_t)opcode;
memcpy(bp, &uinttmp, sizeof(uinttmp));
bp += sizeof(uinttmp);
uinttmp = (u_int32_t)lno;
memcpy(bp, &uinttmp, sizeof(uinttmp));
bp += sizeof(uinttmp);
uinttmp = (u_int32_t)cno;
memcpy(bp, &uinttmp, sizeof(uinttmp));
bp += sizeof(uinttmp);
DB_ASSERT((u_int32_t)(bp - (u_int8_t *)logrec.data) == logrec.size);
ret = dbenv->log_put(dbenv, ret_lsnp, (DBT *)&logrec, flags);
if (txnid != NULL && ret == 0)
txnid->last_lsn = *ret_lsnp;
#ifdef LOG_DIAGNOSTIC
if (ret != 0)
(void)__vi_cursor_print(dbenv,
(DBT *)&logrec, ret_lsnp, NULL, NULL);
#endif
__os_free(dbenv, logrec.data, logrec.size);
return (ret);
}
/*
* PUBLIC: int __vi_cursor_getpgnos __P((DB_ENV *, DBT *, DB_LSN *, db_recops,
* PUBLIC: void *));
*/
int
__vi_cursor_getpgnos(dbenv, rec, lsnp, notused1, summary)
DB_ENV *dbenv;
DBT *rec;
DB_LSN *lsnp;
db_recops notused1;
void *summary;
{
TXN_RECS *t;
int ret;
COMPQUIET(rec, NULL);
COMPQUIET(notused1, DB_TXN_ABORT);
t = (TXN_RECS *)summary;
if ((ret = __rep_check_alloc(dbenv, t, 1)) != 0)
return (ret);
t->array[t->npages].flags = LSN_PAGE_NOLOCK;
t->array[t->npages].lsn = *lsnp;
t->array[t->npages].fid = DB_LOGFILEID_INVALID;
memset(&t->array[t->npages].pgdesc, 0,
sizeof(t->array[t->npages].pgdesc));
t->npages++;
return (0);
}
/*
* PUBLIC: int __vi_cursor_print __P((DB_ENV *, DBT *, DB_LSN *, db_recops,
* PUBLIC: void *));
*/
int
__vi_cursor_print(dbenv, dbtp, lsnp, notused2, notused3)
DB_ENV *dbenv;
DBT *dbtp;
DB_LSN *lsnp;
db_recops notused2;
void *notused3;
{
__vi_cursor_args *argp;
int ret;
notused2 = DB_TXN_ABORT;
notused3 = NULL;
if ((ret = __vi_cursor_read(dbenv, dbtp->data, &argp)) != 0)
return (ret);
(void)printf(
"[%lu][%lu]vi_cursor: rec: %lu txnid %lx prevlsn [%lu][%lu]\n",
(u_long)lsnp->file,
(u_long)lsnp->offset,
(u_long)argp->type,
(u_long)argp->txnid->txnid,
(u_long)argp->prev_lsn.file,
(u_long)argp->prev_lsn.offset);
(void)printf("\topcode: %lu\n", (u_long)argp->opcode);
(void)printf("\tlno: %lu\n", (u_long)argp->lno);
(void)printf("\tcno: %d\n", argp->cno);
(void)printf("\n");
__os_free(dbenv, argp, 0);
return (0);
}
/*
* PUBLIC: int __vi_cursor_read __P((DB_ENV *, void *, __vi_cursor_args **));
*/
int
__vi_cursor_read(dbenv, recbuf, argpp)
DB_ENV *dbenv;
void *recbuf;
__vi_cursor_args **argpp;
{
__vi_cursor_args *argp;
int ret;
u_int32_t uinttmp;
u_int8_t *bp;
ret = __os_malloc(dbenv, sizeof(__vi_cursor_args) +
sizeof(DB_TXN), &argp);
if (ret != 0)
return (ret);
argp->txnid = (DB_TXN *)&argp[1];
bp = recbuf;
memcpy(&argp->type, bp, sizeof(argp->type));
bp += sizeof(argp->type);
memcpy(&argp->txnid->txnid, bp, sizeof(argp->txnid->txnid));
bp += sizeof(argp->txnid->txnid);
memcpy(&argp->prev_lsn, bp, sizeof(DB_LSN));
bp += sizeof(DB_LSN);
memcpy(&uinttmp, bp, sizeof(uinttmp));
argp->opcode = (u_int32_t)uinttmp;
bp += sizeof(uinttmp);
memcpy(&uinttmp, bp, sizeof(uinttmp));
argp->lno = (db_recno_t)uinttmp;
bp += sizeof(uinttmp);
memcpy(&uinttmp, bp, sizeof(uinttmp));
argp->cno = (size_t)uinttmp;
bp += sizeof(uinttmp);
*argpp = argp;
return (0);
}
/*
* PUBLIC: int __vi_mark_log __P((DB_ENV *, DB_TXN *, DB_LSN *, u_int32_t,
* PUBLIC: LMARK *));
*/
int
__vi_mark_log(dbenv, txnid, ret_lsnp, flags,
lmp)
DB_ENV *dbenv;
DB_TXN *txnid;
DB_LSN *ret_lsnp;
u_int32_t flags;
LMARK * lmp;
{
DBT logrec;
DB_LSN *lsnp, null_lsn;
u_int32_t rectype, txn_num;
int ret;
u_int8_t *bp;
rectype = DB_vi_mark;
if (txnid != NULL &&
TAILQ_FIRST(&txnid->kids) != NULL &&
(ret = __txn_activekids(dbenv, rectype, txnid)) != 0)
return (ret);
txn_num = txnid == NULL ? 0 : txnid->txnid;
if (txnid == NULL) {
ZERO_LSN(null_lsn);
lsnp = &null_lsn;
} else
lsnp = &txnid->last_lsn;
logrec.size = sizeof(rectype) + sizeof(txn_num) + sizeof(DB_LSN)
+ sizeof(*lmp);
if ((ret = __os_malloc(dbenv, logrec.size, &logrec.data)) != 0)
return (ret);
bp = logrec.data;
memcpy(bp, &rectype, sizeof(rectype));
bp += sizeof(rectype);
memcpy(bp, &txn_num, sizeof(txn_num));
bp += sizeof(txn_num);
memcpy(bp, lsnp, sizeof(DB_LSN));
bp += sizeof(DB_LSN);
if (lmp != NULL)
memcpy(bp, lmp, sizeof(*lmp));
else
memset(bp, 0, sizeof(*lmp));
bp += sizeof(*lmp);
DB_ASSERT((u_int32_t)(bp - (u_int8_t *)logrec.data) == logrec.size);
ret = dbenv->log_put(dbenv, ret_lsnp, (DBT *)&logrec, flags);
if (txnid != NULL && ret == 0)
txnid->last_lsn = *ret_lsnp;
#ifdef LOG_DIAGNOSTIC
if (ret != 0)
(void)__vi_mark_print(dbenv,
(DBT *)&logrec, ret_lsnp, NULL, NULL);
#endif
__os_free(dbenv, logrec.data, logrec.size);
return (ret);
}
/*
* PUBLIC: int __vi_mark_getpgnos __P((DB_ENV *, DBT *, DB_LSN *, db_recops,
* PUBLIC: void *));
*/
int
__vi_mark_getpgnos(dbenv, rec, lsnp, notused1, summary)
DB_ENV *dbenv;
DBT *rec;
DB_LSN *lsnp;
db_recops notused1;
void *summary;
{
TXN_RECS *t;
int ret;
COMPQUIET(rec, NULL);
COMPQUIET(notused1, DB_TXN_ABORT);
t = (TXN_RECS *)summary;
if ((ret = __rep_check_alloc(dbenv, t, 1)) != 0)
return (ret);
t->array[t->npages].flags = LSN_PAGE_NOLOCK;
t->array[t->npages].lsn = *lsnp;
t->array[t->npages].fid = DB_LOGFILEID_INVALID;
memset(&t->array[t->npages].pgdesc, 0,
sizeof(t->array[t->npages].pgdesc));
t->npages++;
return (0);
}
/*
* PUBLIC: int __vi_mark_print __P((DB_ENV *, DBT *, DB_LSN *, db_recops,
* PUBLIC: void *));
*/
int
__vi_mark_print(dbenv, dbtp, lsnp, notused2, notused3)
DB_ENV *dbenv;
DBT *dbtp;
DB_LSN *lsnp;
db_recops notused2;
void *notused3;
{
__vi_mark_args *argp;
int ret;
notused2 = DB_TXN_ABORT;
notused3 = NULL;
if ((ret = __vi_mark_read(dbenv, dbtp->data, &argp)) != 0)
return (ret);
(void)printf(
"[%lu][%lu]vi_mark: rec: %lu txnid %lx prevlsn [%lu][%lu]\n",
(u_long)lsnp->file,
(u_long)lsnp->offset,
(u_long)argp->type,
(u_long)argp->txnid->txnid,
(u_long)argp->prev_lsn.file,
(u_long)argp->prev_lsn.offset);
(void)printf("\tlmp: %%\n", argp->lmp);
(void)printf("\n");
__os_free(dbenv, argp, 0);
return (0);
}
/*
* PUBLIC: int __vi_mark_read __P((DB_ENV *, void *, __vi_mark_args **));
*/
int
__vi_mark_read(dbenv, recbuf, argpp)
DB_ENV *dbenv;
void *recbuf;
__vi_mark_args **argpp;
{
__vi_mark_args *argp;
int ret;
u_int8_t *bp;
ret = __os_malloc(dbenv, sizeof(__vi_mark_args) +
sizeof(DB_TXN), &argp);
if (ret != 0)
return (ret);
argp->txnid = (DB_TXN *)&argp[1];
bp = recbuf;
memcpy(&argp->type, bp, sizeof(argp->type));
bp += sizeof(argp->type);
memcpy(&argp->txnid->txnid, bp, sizeof(argp->txnid->txnid));
bp += sizeof(argp->txnid->txnid);
memcpy(&argp->prev_lsn, bp, sizeof(DB_LSN));
bp += sizeof(DB_LSN);
memcpy(&argp->lmp, bp, sizeof(argp->lmp));
bp += sizeof(argp->lmp);
*argpp = argp;
return (0);
}
/*
* PUBLIC: int __vi_change_log __P((DB_ENV *, DB_TXN *, DB_LSN *, u_int32_t,
* PUBLIC: u_int32_t, db_recno_t));
*/
int
__vi_change_log(dbenv, txnid, ret_lsnp, flags,
opcode, lno)
DB_ENV *dbenv;
DB_TXN *txnid;
DB_LSN *ret_lsnp;
u_int32_t flags;
u_int32_t opcode;
db_recno_t lno;
{
DBT logrec;
DB_LSN *lsnp, null_lsn;
u_int32_t uinttmp;
u_int32_t rectype, txn_num;
int ret;
u_int8_t *bp;
rectype = DB_vi_change;
if (txnid != NULL &&
TAILQ_FIRST(&txnid->kids) != NULL &&
(ret = __txn_activekids(dbenv, rectype, txnid)) != 0)
return (ret);
txn_num = txnid == NULL ? 0 : txnid->txnid;
if (txnid == NULL) {
ZERO_LSN(null_lsn);
lsnp = &null_lsn;
} else
lsnp = &txnid->last_lsn;
logrec.size = sizeof(rectype) + sizeof(txn_num) + sizeof(DB_LSN)
+ sizeof(u_int32_t)
+ sizeof(u_int32_t);
if ((ret = __os_malloc(dbenv, logrec.size, &logrec.data)) != 0)
return (ret);
bp = logrec.data;
memcpy(bp, &rectype, sizeof(rectype));
bp += sizeof(rectype);
memcpy(bp, &txn_num, sizeof(txn_num));
bp += sizeof(txn_num);
memcpy(bp, lsnp, sizeof(DB_LSN));
bp += sizeof(DB_LSN);
uinttmp = (u_int32_t)opcode;
memcpy(bp, &uinttmp, sizeof(uinttmp));
bp += sizeof(uinttmp);
uinttmp = (u_int32_t)lno;
memcpy(bp, &uinttmp, sizeof(uinttmp));
bp += sizeof(uinttmp);
DB_ASSERT((u_int32_t)(bp - (u_int8_t *)logrec.data) == logrec.size);
ret = dbenv->log_put(dbenv, ret_lsnp, (DBT *)&logrec, flags);
if (txnid != NULL && ret == 0)
txnid->last_lsn = *ret_lsnp;
#ifdef LOG_DIAGNOSTIC
if (ret != 0)
(void)__vi_change_print(dbenv,
(DBT *)&logrec, ret_lsnp, NULL, NULL);
#endif
__os_free(dbenv, logrec.data, logrec.size);
return (ret);
}
/*
* PUBLIC: int __vi_change_getpgnos __P((DB_ENV *, DBT *, DB_LSN *, db_recops,
* PUBLIC: void *));
*/
int
__vi_change_getpgnos(dbenv, rec, lsnp, notused1, summary)
DB_ENV *dbenv;
DBT *rec;
DB_LSN *lsnp;
db_recops notused1;
void *summary;
{
TXN_RECS *t;
int ret;
COMPQUIET(rec, NULL);
COMPQUIET(notused1, DB_TXN_ABORT);
t = (TXN_RECS *)summary;
if ((ret = __rep_check_alloc(dbenv, t, 1)) != 0)
return (ret);
t->array[t->npages].flags = LSN_PAGE_NOLOCK;
t->array[t->npages].lsn = *lsnp;
t->array[t->npages].fid = DB_LOGFILEID_INVALID;
memset(&t->array[t->npages].pgdesc, 0,
sizeof(t->array[t->npages].pgdesc));
t->npages++;
return (0);
}
/*
* PUBLIC: int __vi_change_print __P((DB_ENV *, DBT *, DB_LSN *, db_recops,
* PUBLIC: void *));
*/
int
__vi_change_print(dbenv, dbtp, lsnp, notused2, notused3)
DB_ENV *dbenv;
DBT *dbtp;
DB_LSN *lsnp;
db_recops notused2;
void *notused3;
{
__vi_change_args *argp;
int ret;
notused2 = DB_TXN_ABORT;
notused3 = NULL;
if ((ret = __vi_change_read(dbenv, dbtp->data, &argp)) != 0)
return (ret);
(void)printf(
"[%lu][%lu]vi_change: rec: %lu txnid %lx prevlsn [%lu][%lu]\n",
(u_long)lsnp->file,
(u_long)lsnp->offset,
(u_long)argp->type,
(u_long)argp->txnid->txnid,
(u_long)argp->prev_lsn.file,
(u_long)argp->prev_lsn.offset);
(void)printf("\topcode: %lu\n", (u_long)argp->opcode);
(void)printf("\tlno: %lu\n", (u_long)argp->lno);
(void)printf("\n");
__os_free(dbenv, argp, 0);
return (0);
}
/*
* PUBLIC: int __vi_change_read __P((DB_ENV *, void *, __vi_change_args **));
*/
int
__vi_change_read(dbenv, recbuf, argpp)
DB_ENV *dbenv;
void *recbuf;
__vi_change_args **argpp;
{
__vi_change_args *argp;
int ret;
u_int32_t uinttmp;
u_int8_t *bp;
ret = __os_malloc(dbenv, sizeof(__vi_change_args) +
sizeof(DB_TXN), &argp);
if (ret != 0)
return (ret);
argp->txnid = (DB_TXN *)&argp[1];
bp = recbuf;
memcpy(&argp->type, bp, sizeof(argp->type));
bp += sizeof(argp->type);
memcpy(&argp->txnid->txnid, bp, sizeof(argp->txnid->txnid));
bp += sizeof(argp->txnid->txnid);
memcpy(&argp->prev_lsn, bp, sizeof(DB_LSN));
bp += sizeof(DB_LSN);
memcpy(&uinttmp, bp, sizeof(uinttmp));
argp->opcode = (u_int32_t)uinttmp;
bp += sizeof(uinttmp);
memcpy(&uinttmp, bp, sizeof(uinttmp));
argp->lno = (db_recno_t)uinttmp;
bp += sizeof(uinttmp);
*argpp = argp;
return (0);
}
/*
* PUBLIC: int __vi_init_print __P((DB_ENV *, int (***)(DB_ENV *, DBT *,
* PUBLIC: DB_LSN *, db_recops, void *), size_t *));
*/
int
__vi_init_print(dbenv, dtabp, dtabsizep)
DB_ENV *dbenv;
int (***dtabp)__P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
size_t *dtabsizep;
{
int ret;
if ((ret = __db_add_recovery(dbenv, dtabp, dtabsizep,
__vi_marker_print, DB_vi_marker)) != 0)
return (ret);
if ((ret = __db_add_recovery(dbenv, dtabp, dtabsizep,
__vi_cursor_print, DB_vi_cursor)) != 0)
return (ret);
if ((ret = __db_add_recovery(dbenv, dtabp, dtabsizep,
__vi_mark_print, DB_vi_mark)) != 0)
return (ret);
if ((ret = __db_add_recovery(dbenv, dtabp, dtabsizep,
__vi_change_print, DB_vi_change)) != 0)
return (ret);
return (0);
}
/*
* PUBLIC: int __vi_init_getpgnos __P((DB_ENV *, int (***)(DB_ENV *, DBT *,
* PUBLIC: DB_LSN *, db_recops, void *), size_t *));
*/
int
__vi_init_getpgnos(dbenv, dtabp, dtabsizep)
DB_ENV *dbenv;
int (***dtabp)__P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
size_t *dtabsizep;
{
int ret;
if ((ret = __db_add_recovery(dbenv, dtabp, dtabsizep,
__vi_marker_getpgnos, DB_vi_marker)) != 0)
return (ret);
if ((ret = __db_add_recovery(dbenv, dtabp, dtabsizep,
__vi_cursor_getpgnos, DB_vi_cursor)) != 0)
return (ret);
if ((ret = __db_add_recovery(dbenv, dtabp, dtabsizep,
__vi_mark_getpgnos, DB_vi_mark)) != 0)
return (ret);
if ((ret = __db_add_recovery(dbenv, dtabp, dtabsizep,
__vi_change_getpgnos, DB_vi_change)) != 0)
return (ret);
return (0);
}
/*
* PUBLIC: int __vi_init_recover __P((DB_ENV *));
*/
int
__vi_init_recover(dbenv)
DB_ENV *dbenv;
{
int ret;
if ((ret = __db_add_recovery(dbenv, &dbenv->dtab, &dbenv->dtab_size,
__vi_marker_recover, DB_vi_marker)) != 0)
return (ret);
if ((ret = __db_add_recovery(dbenv, &dbenv->dtab, &dbenv->dtab_size,
__vi_cursor_recover, DB_vi_cursor)) != 0)
return (ret);
if ((ret = __db_add_recovery(dbenv, &dbenv->dtab, &dbenv->dtab_size,
__vi_mark_recover, DB_vi_mark)) != 0)
return (ret);
if ((ret = __db_add_recovery(dbenv, &dbenv->dtab, &dbenv->dtab_size,
__vi_change_recover, DB_vi_change)) != 0)
return (ret);
return (0);
}
/*
* PUBLIC: #endif
*/

42
dist/nvi/common/vi_auto.h vendored Normal file
View File

@@ -0,0 +1,42 @@
/* $NetBSD: vi_auto.h,v 1.1.1.2 2008/05/18 14:29:53 aymeric Exp $ */
/* Do not edit: automatically built by gen_rec.awk. */
#ifndef vi_AUTO_H
#define vi_AUTO_H
#define DB_vi_marker 200
typedef struct _vi_marker_args {
u_int32_t type;
DB_TXN *txnid;
DB_LSN prev_lsn;
u_int32_t opcode;
} __vi_marker_args;
#define DB_vi_cursor 201
typedef struct _vi_cursor_args {
u_int32_t type;
DB_TXN *txnid;
DB_LSN prev_lsn;
u_int32_t opcode;
db_recno_t lno;
size_t cno;
} __vi_cursor_args;
#define DB_vi_mark 202
typedef struct _vi_mark_args {
u_int32_t type;
DB_TXN *txnid;
DB_LSN prev_lsn;
LMARK lmp;
} __vi_mark_args;
#define DB_vi_change 203
typedef struct _vi_change_args {
u_int32_t type;
DB_TXN *txnid;
DB_LSN prev_lsn;
u_int32_t opcode;
db_recno_t lno;
} __vi_change_args;
#endif

323
dist/nvi/common/vi_rec.c vendored Normal file
View File

@@ -0,0 +1,323 @@
/* $NetBSD: vi_rec.c,v 1.1.1.2 2008/05/18 14:29:53 aymeric Exp $ */
#include "db_config.h"
#ifndef NO_SYSTEM_INCLUDES
#include <sys/types.h>
#include <string.h>
#endif
#include "common.h"
#include "db_int.h"
#include "db_page.h"
#include <log.h>
#include "hash.h"
#include "btree.h"
#define LOG_CURSOR_HIT -1000
/*
* PUBLIC: #ifdef USE_DB4_LOGGING
*/
/*
* __vi_marker_recover --
* Recovery function for marker.
*
* PUBLIC: int __vi_marker_recover
* PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
*/
int
__vi_marker_recover(dbenv, dbtp, lsnp, op, info)
DB_ENV *dbenv;
DBT *dbtp;
DB_LSN *lsnp;
db_recops op;
void *info;
{
__vi_marker_args *argp;
int ret;
REC_PRINT(__vi_marker_print);
REC_NOOP_INTRO(__vi_marker_read);
*lsnp = argp->prev_lsn;
ret = 0;
REC_NOOP_CLOSE;
}
/*
* __vi_cursor_recover --
* Recovery function for cursor.
*
* PUBLIC: int __vi_cursor_recover
* PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
*/
int
__vi_cursor_recover(dbenv, dbtp, lsnp, op, info)
DB_ENV *dbenv;
DBT *dbtp;
DB_LSN *lsnp;
db_recops op;
void *info;
{
__vi_cursor_args *argp;
int ret;
SCR *sp;
REC_PRINT(__vi_cursor_print);
REC_NOOP_INTRO(__vi_cursor_read);
sp = (SCR *)dbenv->app_private;
*lsnp = argp->prev_lsn;
if (sp->state.undo == UNDO_SETLINE) {
/* Why the check for ep->l_cur ? (copied from log.c)
*/
ret = (argp->lno != sp->lno ||
(argp->opcode == LOG_CURSOR_INIT && sp->ep->l_cur == 1))
? LOG_CURSOR_HIT : 0;
}
else {
ret = argp->opcode ==
(DB_UNDO(op) ? LOG_CURSOR_INIT : LOG_CURSOR_END)
? LOG_CURSOR_HIT : 0;
if (ret) {
sp->state.pos.lno = argp->lno;
sp->state.pos.cno = argp->cno;
}
}
REC_NOOP_CLOSE;
}
/*
* __vi_mark_recover --
* Recovery function for mark.
*
* PUBLIC: int __vi_mark_recover
* PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
*/
int
__vi_mark_recover(dbenv, dbtp, lsnp, op, info)
DB_ENV *dbenv;
DBT *dbtp;
DB_LSN *lsnp;
db_recops op;
void *info;
{
__vi_mark_args *argp;
int ret;
MARK m;
SCR *sp;
REC_PRINT(__vi_mark_print);
REC_NOOP_INTRO(__vi_mark_read);
sp = (SCR *)dbenv->app_private;
*lsnp = argp->prev_lsn;
m.lno = argp->lmp.lno;
m.cno = argp->lmp.cno;
ret = mark_set(sp, argp->lmp.name, &m, 0);
REC_NOOP_CLOSE;
}
/*
* __vi_change_recover --
* Recovery function for change.
*
* PUBLIC: int __vi_change_recover
* PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
*/
int
__vi_change_recover(dbenv, dbtp, lsnp, op, info)
DB_ENV *dbenv;
DBT *dbtp;
DB_LSN *lsnp;
db_recops op;
void *info;
{
__vi_change_args *argp;
int ret;
SCR *sp;
REC_PRINT(__vi_change_print);
REC_NOOP_INTRO(__vi_change_read);
ret = 0;
sp = (SCR *)dbenv->app_private;
if (DB_UNDO(op) != (argp->opcode & 1))
switch (argp->opcode) {
case LOG_LINE_RESET_B:
case LOG_LINE_RESET_F:
ret = line_insdel(sp, LINE_RESET, argp->lno);
update_cache(sp, LINE_RESET, argp->lno);
ret = scr_update(sp, argp->lno, LINE_RESET, 1) || ret;
break;
case LOG_LINE_APPEND_B:
case LOG_LINE_DELETE_F:
ret = line_insdel(sp, LINE_DELETE, argp->lno);
update_cache(sp, LINE_DELETE, argp->lno);
ret = scr_update(sp, argp->lno, LINE_DELETE, 1) || ret;
break;
case LOG_LINE_DELETE_B:
case LOG_LINE_APPEND_F:
ret = line_insdel(sp, LINE_INSERT, argp->lno);
update_cache(sp, LINE_INSERT, argp->lno);
ret = scr_update(sp, argp->lno, LINE_INSERT, 1) || ret;
break;
}
*lsnp = argp->prev_lsn;
REC_NOOP_CLOSE;
}
/*
*
* PUBLIC: int __vi_log_truncate __P((EXF *ep));
*/
int
__vi_log_truncate(EXF *ep)
{
DB_LSN ckplsn;
ZERO_LSN(ckplsn);
return __log_vtruncate(ep->env, &ep->lsn_cur, &ckplsn);
/*return __log_vtruncate(ep->env, &ep->lsn_cur, &ep->lsn_first);*/
}
/*
*
* PUBLIC: int __vi_log_dispatch __P((DB_ENV *dbenv, DBT *data, DB_LSN *lsn, db_recops ops));
*/
int
__vi_log_dispatch(DB_ENV *dbenv, DBT *data, DB_LSN *lsn, db_recops ops)
{
u_int32_t rectype;
char s[100];
memcpy(&rectype, data->data, sizeof(rectype));
snprintf(s,100,"%d\n", rectype);
return dbenv->dtab[rectype](dbenv, data, lsn, ops, NULL);
}
static int
vi_log_get(SCR *sp, DB_LOGC *logc, DBT *data, u_int32_t which)
{
size_t nlen;
EXF *ep;
ep = sp->ep;
nlen = 1024;
retry:
BINC_GOTO(sp, sp->wp->l_lp, sp->wp->l_len, nlen);
memset(data, 0, sizeof(*data));
data->data = sp->wp->l_lp;
data->ulen = sp->wp->l_len;
data->flags = DB_DBT_USERMEM;
switch ((sp->db_error = logc->get(logc, &ep->lsn_cur, data, which))) {
case ENOMEM:
nlen = data->size;
goto retry;
default:
alloc_err:
msgq(sp, M_DBERR, "logc->get");
F_SET(ep, F_NOLOG);
return (1);
case 0:
;
}
return 0;
}
/*
*
* PUBLIC: int __vi_log_traverse __P((SCR *sp, undo_t undo, MARK *));
*/
int
__vi_log_traverse(SCR *sp, undo_t undo, MARK *rp)
{
DB_LOGC *logc;
DBT data;
EXF *ep;
int ret;
DB_LSN lsn;
u_int32_t which;
db_recops ops;
ep = sp->ep;
F_SET(ep, F_NOLOG); /* Turn off logging. */
sp->state.undo = undo;
ep->env->app_private = sp;
if ((sp->db_error = ep->env->log_cursor(ep->env, &logc, 0))
!= 0) {
msgq(sp, M_DBERR, "env->log_cursor");
return (1);
}
if (vi_log_get(sp, logc, &data, DB_SET))
return 1;
if (undo == UNDO_FORWARD) {
ops = DB_TXN_FORWARD_ROLL;
which = DB_NEXT;
if (vi_log_get(sp, logc, &data, DB_NEXT))
return 1;
} else {
ops = DB_TXN_BACKWARD_ROLL;
which = DB_PREV;
}
for (;;) {
MEMCPY(&lsn, &ep->lsn_cur, 1);
ret = __vi_log_dispatch(ep->env, &data, &lsn, ops);
if (ret != 0) {
if (ret == LOG_CURSOR_HIT)
break;
}
if (vi_log_get(sp, logc, &data, which))
return 1;
if (undo == UNDO_SETLINE &&
log_compare(&ep->lsn_cur, &ep->lsn_first) <= 0) {
/* Move to previous record without dispatching. */
undo = UNDO_BACKWARD;
break;
}
}
if (undo == UNDO_BACKWARD)
if (vi_log_get(sp, logc, &data, DB_PREV))
return 1;
logc->close(logc, 0);
ep->env->app_private = NULL;
MEMMOVE(rp, &sp->state.pos, 1);
F_CLR(ep, F_NOLOG);
return 0;
}
int
vi_db_init_recover(DB_ENV *dbenv)
{
int ret;
if ((ret = __db_init_recover(dbenv)) != 0)
return (ret);
if ((ret = __bam_init_recover(dbenv)) != 0)
return (ret);
return 0;
}
/*
* PUBLIC: #endif
*/

473
dist/nvi/dist/ExtUtils/Embed.pm vendored Normal file
View File

@@ -0,0 +1,473 @@
# Id: Embed.pm,v 10.2 1996/09/19 09:12:51 bostic Exp
require 5.002;
package ExtUtils::Embed;
require Exporter;
require FileHandle;
use Config;
use Getopt::Std;
#Only when we need them
#require ExtUtils::MakeMaker;
#require ExtUtils::Liblist;
use vars qw(@ISA @EXPORT $VERSION
@Extensions $Verbose $lib_ext
$opt_o $opt_s
);
use strict;
$VERSION = sprintf("%d.%02d", qRevision: 10.2 =~ /(\d+)\.(\d+)/);
#for the namespace change
$Devel::embed::VERSION = "99.99";
sub Version { $VERSION; }
@ISA = qw(Exporter);
@EXPORT = qw(&xsinit &ldopts
&ccopts &ccflags &ccdlflags &perl_inc
&xsi_header &xsi_protos &xsi_body);
#let's have Miniperl borrow from us instead
#require ExtUtils::Miniperl;
#*canon = \&ExtUtils::Miniperl::canon;
$Verbose = 0;
$lib_ext = $Config{lib_ext} || '.a';
sub xsinit {
my($file, $std, $mods) = @_;
my($fh,@mods,%seen);
$file ||= "perlxsi.c";
if (@_) {
@mods = @$mods if $mods;
}
else {
getopts('o:s:');
$file = $opt_o if defined $opt_o;
$std = $opt_s if defined $opt_s;
@mods = @ARGV;
}
$std = 1 unless scalar @mods;
if ($file eq "STDOUT") {
$fh = \*STDOUT;
}
else {
$fh = new FileHandle "> $file";
}
push(@mods, static_ext()) if defined $std;
@mods = grep(!$seen{$_}++, @mods);
print $fh &xsi_header();
print $fh "EXTERN_C void xs_init _((void));\n\n";
print $fh &xsi_protos(@mods);
print $fh "\nEXTERN_C void\nxs_init()\n{\n";
print $fh &xsi_body(@mods);
print $fh "}\n";
}
sub xsi_header {
return <<EOF;
#ifdef __cplusplus
extern "C" {
#endif
#include <EXTERN.h>
#include <perl.h>
#ifdef __cplusplus
}
# ifndef EXTERN_C
# define EXTERN_C extern "C"
# endif
#else
# ifndef EXTERN_C
# define EXTERN_C extern
# endif
#endif
EOF
}
sub xsi_protos {
my(@exts) = @_;
my(@retval,%seen);
foreach $_ (@exts){
my($pname) = canon('/', $_);
my($mname, $cname);
($mname = $pname) =~ s!/!::!g;
($cname = $pname) =~ s!/!__!g;
my($ccode) = "EXTERN_C void boot_${cname} _((CV* cv));\n";
next if $seen{$ccode}++;
push(@retval, $ccode);
}
return join '', @retval;
}
sub xsi_body {
my(@exts) = @_;
my($pname,@retval,%seen);
my($dl) = canon('/','DynaLoader');
push(@retval, "\tdXSUB_SYS;\n") if $] > 5.002;
push(@retval, "\tchar *file = __FILE__;\n\n");
foreach $_ (@exts){
my($pname) = canon('/', $_);
my($mname, $cname, $ccode);
($mname = $pname) =~ s!/!::!g;
($cname = $pname) =~ s!/!__!g;
if ($pname eq $dl){
# Must NOT install 'DynaLoader::boot_DynaLoader' as 'bootstrap'!
# boot_DynaLoader is called directly in DynaLoader.pm
$ccode = "\t/* DynaLoader is a special case */\n\tnewXS(\"${mname}::boot_${cname}\", boot_${cname}, file);\n";
push(@retval, $ccode) unless $seen{$ccode}++;
} else {
$ccode = "\tnewXS(\"${mname}::bootstrap\", boot_${cname}, file);\n";
push(@retval, $ccode) unless $seen{$ccode}++;
}
}
return join '', @retval;
}
sub static_ext {
unless (scalar @Extensions) {
@Extensions = sort split /\s+/, $Config{static_ext};
unshift @Extensions, qw(DynaLoader);
}
@Extensions;
}
sub ldopts {
require ExtUtils::MakeMaker;
require ExtUtils::Liblist;
my($std,$mods,$link_args,$path) = @_;
my(@mods,@link_args,@argv);
my($dllib,$config_libs,@potential_libs,@path);
local($") = ' ' unless $" eq ' ';
my $MM = bless {} => 'MY';
if (scalar @_) {
@link_args = @$link_args if $link_args;
@mods = @$mods if $mods;
}
else {
@argv = @ARGV;
#hmm
while($_ = shift @argv) {
/^-std$/ && do { $std = 1; next; };
/^--$/ && do { @link_args = @argv; last; };
/^-I(.*)/ && do { $path = $1 || shift @argv; next; };
push(@mods, $_);
}
}
$std = 1 unless scalar @link_args;
@path = $path ? split(/:/, $path) : @INC;
push(@potential_libs, @link_args) if scalar @link_args;
push(@potential_libs, $Config{libs}) if defined $std;
push(@mods, static_ext()) if $std;
my($mod,@ns,$root,$sub,$extra,$archive,@archives);
print STDERR "Searching (@path) for archives\n" if $Verbose;
foreach $mod (@mods) {
@ns = split('::', $mod);
$sub = $ns[-1];
$root = $MM->catdir(@ns);
print STDERR "searching for '$sub${lib_ext}'\n" if $Verbose;
foreach (@path) {
next unless -e ($archive = $MM->catdir($_,"auto",$root,"$sub$lib_ext"));
push @archives, $archive;
if(-e ($extra = $MM->catdir($_,"auto",$root,"extralibs.ld"))) {
local(*FH);
if(open(FH, $extra)) {
my($libs) = <FH>; chomp $libs;
push @potential_libs, split /\s+/, $libs;
}
else {
warn "Couldn't open '$extra'";
}
}
last;
}
}
#print STDERR "\@potential_libs = @potential_libs\n";
my($extralibs, $bsloadlibs, $ldloadlibs, $ld_run_path) =
$MM->ext(join ' ',
$MM->catdir("-L$Config{archlib}", "CORE"), " -lperl",
@potential_libs);
my $ld_or_bs = $bsloadlibs || $ldloadlibs;
print STDERR "bs: $bsloadlibs ** ld: $ldloadlibs" if $Verbose;
my $linkage = "$Config{ldflags} @archives $ld_or_bs";
print STDERR "ldopts: '$linkage'\n" if $Verbose;
return $linkage if scalar @_;
print "$linkage\n";
}
sub ccflags {
print " $Config{ccflags} ";
}
sub ccdlflags {
print " $Config{ccdlflags} ";
}
sub perl_inc {
print " -I$Config{archlib}/CORE ";
}
sub ccopts {
ccflags;
ccdlflags;
perl_inc;
}
sub canon {
my($as, @ext) = @_;
foreach(@ext) {
# might be X::Y or lib/auto/X/Y/Y.a
next if s!::!/!g;
s:^(lib|ext)/(auto/)?::;
s:/\w+\.\w+$::;
}
grep(s:/:$as:, @ext) if ($as ne '/');
@ext;
}
__END__
=head1 NAME
ExtUtils::Embed - Utilities for embedding Perl in C/C++ applications
=head1 SYNOPSIS
perl -MExtUtils::Embed -e xsinit
perl -MExtUtils::Embed -e ldopts
=head1 DESCRIPTION
ExtUtils::Embed provides utility functions for embedding a Perl interpreter
and extensions in your C/C++ applications.
Typically, an application B<Makefile> will invoke ExtUtils::Embed
functions while building your application.
=head1 @EXPORT
ExtUtils::Embed exports the following functions:
L<xsinit()>, L<ldopts()>, L<ccopts()>, L<perl_inc()>, L<ccflags()>,
L<ccdlflags()>, L<xsi_header()>, L<xsi_protos()>, L<xsi_body()>
=head1 FUNCTIONS
=item xsinit()
Generate C/C++ code for the XS intializer function.
When invoked as C<`perl -MExtUtils::Embed -e xsinit --`>
the following options are recognized:
B<-o> <output filename> (Defaults to B<perlxsi.c>)
B<-o STDOUT> will print to STDOUT.
B<-std> (Write code for extensions that are linked with the current Perl.)
Any additional arguments are expected to be names of modules
to generate code for.
When invoked with parameters the following are accepted and optional:
C<xsinit($filename,$std,[@modules])>
Where,
B<$filename> is equivalent to the B<-o> option.
B<$std> is boolean, equivalent to the B<-std> option.
B<[@modules]> is an array ref, same as additional arguments mentioned above.
=item Examples
perl -MExtUtils::Embed -e xsinit -- -o xsinit.c Socket
This will generate code with an B<xs_init> function that glues the perl B<Socket::bootstrap> function
to the C B<boot_Socket> function and writes it to a file named "xsinit.c".
Note that B<DynaLoader> is a special case where it must call B<boot_DynaLoader> directly.
perl -MExtUtils::Embed -e xsinit
This will generate code for linking with B<DynaLoader> and
each static extension found in B<$Config{static_ext}>.
The code is written to the default file name B<perlxsi.c>.
perl -MExtUtils::Embed -e xsinit -- -o xsinit.c -std DBI DBD::Oracle
Here, code is written for all the currently linked extensions along with code
for B<DBI> and B<DBD::Oracle>.
If you have a working B<DynaLoader> then there is rarely any need to statically link in any
other extensions.
=item ldopts()
Output arguments for linking the Perl library and extensions to your
application.
When invoked as C<`perl -MExtUtils::Embed -e ldopts --`>
the following options are recognized:
B<-std>
Output arguments for linking the Perl library and any extensions linked
with the current Perl.
B<-I> <path1:path2>
Search path for ModuleName.a archives.
Default path is B<@INC>.
Library archives are expected to be found as
B</some/path/auto/ModuleName/ModuleName.a>
For example, when looking for B<Socket.a> relative to a search path,
we should find B<auto/Socket/Socket.a>
When looking for B<DBD::Oracle> relative to a search path,
we should find B<auto/DBD/Oracle/Oracle.a>
Keep in mind, you can always supply B</my/own/path/ModuleName.a>
as an additional linker argument.
B<--> <list of linker args>
Additional linker arguments to be considered.
Any additional arguments found before the B<--> token
are expected to be names of modules to generate code for.
When invoked with parameters the following are accepted and optional:
C<ldopts($std,[@modules],[@link_args],$path)>
Where,
B<$std> is boolean, equivalent to the B<-std> option.
B<[@modules]> is equivalent to additional arguments found before the B<--> token.
B<[@link_args]> is equivalent to arguments found after the B<--> token.
B<$path> is equivalent to the B<-I> option.
In addition, when ldopts is called with parameters, it will return the argument string
rather than print it to STDOUT.
=item Examples
perl -MExtUtils::Embed -e ldopts
This will print arguments for linking with B<libperl.a>, B<DynaLoader> and
extensions found in B<$Config{static_ext}>. This includes libraries
found in B<$Config{libs}> and the first ModuleName.a library
for each extension that is found by searching B<@INC> or the path
specifed by the B<-I> option.
In addition, when ModuleName.a is found, additional linker arguments
are picked up from the B<extralibs.ld> file in the same directory.
perl -MExtUtils::Embed -e ldopts -- -std Socket
This will do the same as the above example, along with printing additional arguments for linking with the B<Socket> extension.
perl -MExtUtils::Embed -e ldopts -- DynaLoader
This will print arguments for linking with just the B<DynaLoader> extension
and B<libperl.a>.
perl -MExtUtils::Embed -e ldopts -- -std Msql -- -L/usr/msql/lib -lmsql
Any arguments after the second '--' token are additional linker
arguments that will be examined for potential conflict. If there is no
conflict, the additional arguments will be part of the output.
=item perl_inc()
For including perl header files this function simply prints:
-I $Config{archlib}/CORE
So, rather than having to say:
perl -MConfig -e 'print "-I $Config{archlib}/CORE"'
Just say:
perl -MExtUtils::Embed -e perl_inc
=item ccflags(), ccdlflags()
These functions simply print $Config{ccflags} and $Config{ccdlflags}
=item ccopts()
This function combines perl_inc(), ccflags() and ccdlflags() into one.
=item xsi_header()
This function simply returns a string defining the same B<EXTERN_C> macro as
B<perlmain.c> along with #including B<perl.h> and B<EXTERN.h>.
=item xsi_protos(@modules)
This function returns a string of B<boot_$ModuleName> prototypes for each @modules.
=item xsi_body(@modules)
This function returns a string of calls to B<newXS()> that glue the module B<bootstrap>
function to B<boot_ModuleName> for each @modules.
B<xsinit()> uses the xsi_* functions to generate most of it's code.
=head1 EXAMPLES
For examples on how to use B<ExtUtils::Embed> for building C/C++ applications
with embedded perl, see the eg/ directory and the I<perlembed> man page.
=head1 SEE ALSO
the I<perlembed> man page
=head1 AUTHOR
Doug MacEachern <dougm@osf.org>
Based on ideas from Tim Bunce <Tim.Bunce@ig.co.uk> and
B<minimod.pl> by Andreas Koenig <k@anna.in-berlin.de> and Tim Bunce.
=cut

Some files were not shown because too many files have changed in this diff Show More