Imported Upstream version 050325 upstream upstream/050325
authorMaciej Wereski <m.wereski@partner.samsung.com>
Thu, 8 Aug 2013 12:32:30 +0000 (14:32 +0200)
committerMaciej Wereski <m.wereski@partner.samsung.com>
Thu, 8 Aug 2013 12:32:30 +0000 (14:32 +0200)
79 files changed:
Changes [new file with mode: 0644]
LICENSE [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
catd/en_US [new file with mode: 0644]
config.h [new file with mode: 0644]
ex.1 [new file with mode: 0644]
ex.c [new file with mode: 0644]
ex.h [new file with mode: 0644]
ex.spec [new file with mode: 0644]
ex_addr.c [new file with mode: 0644]
ex_argv.h [new file with mode: 0644]
ex_cmds.c [new file with mode: 0644]
ex_cmds2.c [new file with mode: 0644]
ex_cmdsub.c [new file with mode: 0644]
ex_data.c [new file with mode: 0644]
ex_extern.c [new file with mode: 0644]
ex_get.c [new file with mode: 0644]
ex_io.c [new file with mode: 0644]
ex_proto.h [new file with mode: 0644]
ex_put.c [new file with mode: 0644]
ex_re.c [new file with mode: 0644]
ex_re.h [new file with mode: 0644]
ex_set.c [new file with mode: 0644]
ex_subr.c [new file with mode: 0644]
ex_tagio.c [new file with mode: 0644]
ex_temp.c [new file with mode: 0644]
ex_temp.h [new file with mode: 0644]
ex_tty.c [new file with mode: 0644]
ex_tty.h [new file with mode: 0644]
ex_tune.h [new file with mode: 0644]
ex_unix.c [new file with mode: 0644]
ex_v.c [new file with mode: 0644]
ex_vadj.c [new file with mode: 0644]
ex_vars.h [new file with mode: 0644]
ex_version.c [new file with mode: 0644]
ex_vget.c [new file with mode: 0644]
ex_vis.h [new file with mode: 0644]
ex_vmain.c [new file with mode: 0644]
ex_voper.c [new file with mode: 0644]
ex_vops.c [new file with mode: 0644]
ex_vops2.c [new file with mode: 0644]
ex_vops3.c [new file with mode: 0644]
ex_vput.c [new file with mode: 0644]
ex_vwind.c [new file with mode: 0644]
expreserve.c [new file with mode: 0644]
exrecover.c [new file with mode: 0644]
libterm/Makefile [new file with mode: 0644]
libterm/libterm.h [new file with mode: 0644]
libterm/termcap.c [new file with mode: 0644]
libterm/tgoto.c [new file with mode: 0644]
libterm/tputs.c [new file with mode: 0644]
libuxre/COPYING.LGPL [new file with mode: 0644]
libuxre/Makefile [new file with mode: 0644]
libuxre/NOTES [new file with mode: 0644]
libuxre/_collelem.c [new file with mode: 0644]
libuxre/_collmult.c [new file with mode: 0644]
libuxre/bracket.c [new file with mode: 0644]
libuxre/colldata.h [new file with mode: 0644]
libuxre/onefile.c [new file with mode: 0644]
libuxre/re.h [new file with mode: 0644]
libuxre/regcomp.c [new file with mode: 0644]
libuxre/regdfa.c [new file with mode: 0644]
libuxre/regdfa.h [new file with mode: 0644]
libuxre/regerror.c [new file with mode: 0644]
libuxre/regex.h [new file with mode: 0644]
libuxre/regexec.c [new file with mode: 0644]
libuxre/regfree.c [new file with mode: 0644]
libuxre/regnfa.c [new file with mode: 0644]
libuxre/regparse.c [new file with mode: 0644]
libuxre/stubs.c [new file with mode: 0644]
libuxre/wcharm.h [new file with mode: 0644]
makeoptions [new file with mode: 0644]
malloc.c [new file with mode: 0644]
mapmalloc.c [new file with mode: 0644]
printf.c [new file with mode: 0644]
regexp.h [new file with mode: 0644]
vi.1 [new file with mode: 0644]

diff --git a/Changes b/Changes
new file mode 100644 (file)
index 0000000..15c8851
--- /dev/null
+++ b/Changes
@@ -0,0 +1,241 @@
+Release 3/25/05
+* vi no longer dies with a segmentation fault if a line does not fit on the
+  screen after an insertion.
+* The 'p' command now works correctly if the buffer contains a partial line
+  with multibyte characters.
+* Traditional regular expressions sometimes failed to operate correctly
+  since the last release.
+
+Release 2/25/05
+* Traditional regular expressions can now be used with multibyte characters.
+* When the 'ignorecase' option is toggled, saved regular expressions are now
+  updated accordingly. (P)
+* If a line began with a tabulator and another tabulator was inserted with
+  the cursor located on the first tabulator, the display was not updated
+  appropriately since the last release (Bugreport by Matthew Fischer). (P)
+* Fixed a segmentation fault that occured in multibyte locales when operation
+  was continued after vi had been stopped by ^Z, with the cursor positioned
+  in a line longer than the terminal width as the last line of the current
+  screen.
+* Made multicolumn characters work in lines longer than the screen width in
+  visual mode (Bugreport by Matthew Fischer).
+* Made it work for Big5 locales (Patches by Matthew Fischer).
+* Fixed a problem with the 'r' command in EUC-JP and Big5 locales (Bugreport
+  by Matthew Fischer).
+* The insertion of multicolumn characters and tab characters in multibyte
+  locales now works with terminals that have the 'ic' but no 'im' termcap
+  capability (Bugreport by Matthew Fischer).
+* The argument to the -w option is correctly recognized now.
+* If the SHELL environment variable is set to the empty string, it is now
+  ignored.
+* A non-null exit status is now returned if a file could not be opened, if
+  an invalid address is given for a command, or if a tag is not found.
+* If the match for a substitution is of length zero, a line overflow is
+  now avoided when compiled with the 'UNIX(R) Regular Expression Library'.
+* When moving left while the cursor is positioned over a multicolumn
+  character at the end of the line, the bell is rung now (Bugreport by
+  Matthew Fischer).
+* When moving up or down to a row with different column arrangement while
+  the cursor is positioned over a multicolumn character, the leftmost
+  character above the original position is chosen in the new row.
+* If both the -t and the -c option are given, the -t option is now
+  processed first, i.e. the command is executed at the position where
+  the tag was found.
+* The -w option now also sets the scroll size for the 'z' command.
+* When the name of a nonexisting file is given with the 'edit' ex command,
+  the command now succeeds and prints a "[New file]" message.
+* If standard output is not a terminal, no '\r' is written at the end of
+  printed lines anymore.
+* The 'source' ex command now works if command input comes from a pipe or
+  regular file.
+* Ex does not exit on errors immediately anymore if standard input is not
+  a terminal but a pipe or regular file.
+* The 'substitute' ex command can now be abbreviated as 'sub', 'subst' etc.
+* A new version of mapmalloc.c that is derived from Unix 7th Edition code
+  has been introduced.
+* If the 'next!' ex command is given and the autowrite option is set, the
+  current file is not written anymore.
+
+Release 1/19/05
+* The last release erroneously made 'X' work like 'x' in visual mode. It now
+  deletes the character before the cursor again as documented (Bugreport by
+  Matthew Fischer). (P)
+* When a multicolumn character was replaced by another multicolumn character
+  in insert mode, the display was not updated appropriately with terminals
+  other than xterm.
+* Various rendering errors happened with multicolumn characters if they
+  started at an even column (counting from 1 upwards).
+* When a multicolumn character was inserted and then replaced, the visual
+  screen representation was sometimes not updated accordingly.
+* Undoing the replacement of a multicolumn character by a singlecolumn
+  character twice made the singlecolumn character invisible.
+* The 'cw' visual command with a multibyte character as last character of
+  the affected word located at the end of the line left garbage bytes past
+  the end of the new text.
+* Visual 'U' followed by 'u' lead to garbage on the screen when multibyte
+  characters were present on the changed line.
+* The position of '$' when changing text was fixed for cases where the first
+  changed character had multiple columns but the last one had not.
+* The handling of multicolumn characters was fixed for terminals without the
+  IM (insert mode) capability. It is unlikely that such terminals actually
+  exist, but vi will use the corresponding code for open mode if a termcap
+  entry is not available.
+* When an illegal multibyte sequence is entered in vi insert mode, no garbage
+  bytes are generated anymore when the insert mode is left.
+* The '\u', '\l', '\U', and '\L' substitution sequences work with multibyte
+  characters now.
+* Handle character case conversions with the '~' vi command correctly if the
+  length of the converted multibyte sequence is smaller than the original one.
+* Multibyte sequences that correspond to an unprintable character are now
+  printed as multiple octal escape sequences.
+* Pressing the ^U (kill) character in insert mode with a multibyte character
+  at the beginning of an insertion at the start of a line sometimes positioned
+  the cursor at weird locations since the last revision.
+* Fixed an old vi bug: If a vi command that yanked or deleted part of a line
+  was followed by an ex command that also yanked or deleted some text, a
+  following 'p' vi command pasted the text affected by the former vi command.
+  It now pastes the text of the last yank or delete even if that was an ex
+  command.
+* Some build fixes for diet libc have been made.
+
+Release 12/2/04
+* Support for multibyte character locales was added.
+* The code has been converted to ANSI C, and support for pre-POSIX systems has
+  been dropped.
+* When the end of the current line consists of blank characters and the 'w'
+  visual command is given at this point, vi now advances to the start of the
+  next line instead of ringing the bell. This is compatible with SVR4 vi and
+  seems to be what POSIX specifies.
+* If the replacement part of a substitute command consists of a single '%',
+  as in ':s/foo/%/', the replacement part of the previous substitution is
+  used. This is compatible with SVR4 vi and is specified by POSIX.
+* Fixed a number of possible heap overflows, e.g. because of too long tag
+  strings.
+
+Release 6/5/04
+* Some changes were made to the Makefile to support RPM builds. In particular,
+  the meaning of the DESTDIR and PREFIX variables was changed.
+* An insufficient size of a variable caused the window size set to 8 on
+  FreeBSD if the terminal baud rate was 38400.
+
+Release 1/3/04
+* Changes to the included libterm only: Made multiple tc= capabilities in
+  a termcap entry work; recognize tc= also if it is not the last capability
+  in an entry (Bugreport by Andrew Minter).
+
+Release 9/3/03
+* The code did not check st_blksize from stat(2) at other points besides
+  the one fixed in the last release.
+* The keyboard input character with code 255 ("y in ISO-8859-1) was
+  misinterpreted as end-of-file indicator.
+
+Release 8/27/03
+* Compile fixes for AIX and HP-UX (Mike Jetzer).
+* Delete temporary file when preserving was successful after receiving
+  SIGHUP or SIGTERM (Fix taken from 4.3BSD Reno).
+* Set MAILRC to /dev/null in expreserve to avoid reading the user's
+  mail initialization file.
+* Optionally use Caldera's 'UNIX(R) Regular Expression Library' to
+  get POSIX.2 compatible REs.
+* Don't refuse to quit with 'No write since last change' if a line of a
+  newly read input file is too long but no changes were made by the user.
+* The POSIX_2 preprocessor define has been removed. The behavior previously
+  enabled by this variable is now the default (except as noted below).
+* Backslash inside RE bracket expresssions (as in [\]]) is now recognized
+  as an escape character by default. This is not POSIX.2 compliant, but is
+  compatible with historic vi behavior, is demanded by the some traditional
+  standards such as the System V Interface definition, and is compatible
+  with vim. To get POSIX.2 compliant behavior here, define NO_BE_BACKSLASH.
+* The input buffer did overflow with large values of st_blksize from stat(2).
+
+Release 4/3/02
+* Option "showmode" has no effect on hardcopy or glass terminals (P).
+* Fixed undo for :r! command.
+* Interrupt character is not misinterpreted as DEL on insertion (P).
+* Fixed interaction of <count>~ vi command with abbreviations and macros (P).
+* Avoid horizontal scrolling of showmode string on wraparound to last line (P).
+* No showmode string is printed when executing a macro.
+* Recovery listing fixed not to mix characters from long filenames in
+  /var/preserve with /var/tmp listing (P).
+* Catch SIGXFSZ (file size limit exceeded).
+
+Release 3/3/02
+* Separated terse/noterse messages for RE errors (P).
+* The expreserve and exrecover commands work again (P).
+* Passing beyond EOL in <count>~ vi command is not an error (P).
+* Fixed segmentation violation on mdjc'd and related bugs (Sven Mascheck).
+* Marks remain on lines changed in an undo operation.
+* Close mapmalloc file descriptor for /dev/zero on exec() (P).
+* Added -L and -V command line options as in SVr4 ex.
+* POSIX.2: Processing EXINIT does not depend on stdin being a terminal.
+* POSIX.2: No newline is appended to an empty file in visual mode.
+* Fixed segmentation violation on :f^V^M^M and similar commands.
+* Mapmalloc extended to allocate and release multiple memory pools.
+* Exrecover lists /var/tmp in addition to /var/preserve.
+* Have multiple attempts to create named buffer temporary file.
+* Size limit for temporary file names removed.
+
+Release 2/17/02
+* POSIX.2: Backslash has no special meaning inside RE bracket expressions.
+* RE cleanup; make it compile without POSIX_2 defined (P).
+* Fixed $(CC) to "$(CC)" for libterm compilation (Felix von Leitner) (P).
+* Support for LC_MESSAGES using catgets() added.
+* Renamed POSIX define to POSIX_1.
+* Renamed UNIX98 define to POSIX_2.
+* POSIX.2: Fixed a conflict between -t tag and wrapscan option.
+* POSIX.2: Take the initial value for the vi scroll command from scroll var.
+* <count>~ vi command fixed to work with repeat and control chars in text (P).
+* Fixed recursion on :ab abbreviations (Bugreport by Matthias Kopfermann).
+* Fixed undo for :<addr>r command in visual mode.
+* Made modelines compatible to SVr4 ex: The option name is "modelines" or
+  "ml" now; a space or tab preceeding "ex:" or "vi:" is not required.
+* Use O_EXCL and O_NOFOLLOW when creating temporary files, if possible.
+
+Release 1/26/02
+* Use mmap() for memory allocation if possible.
+* POSIX.2: Added RE interval expressions \{m,n\} (taken from V7 expr).
+* POSIX.2: Added backreferences \1 ... \9.
+* Print one-character mode strings if both "showmode" and "terse" are set.
+* Added the "flash" variable.
+* POSIX.2: "~" visual command accepts a count.
+* License notices added.
+
+Release 5/21/01
+* If compiled using BIT8, all characters except ASCII NUL can now be handled.
+* Support larger files if LARGEF is defined.
+
+Release 09/23/00
+* POSIX locale support for 8bit character sets is enabled by -DBIT8.
+* Regex code is 8bit clean.
+* Smaller fixes for SVR4 systems.
+* POSIX termios support including job control.
+* POSIX sigaction support.
+* Check for ELF executables and compressed files.
+* Extended the pattern for temporary files from 5 to 10 digits.
+* ANSI C stdarg function calling added, Linux/ia64 needs them.
+* Reintegrated the UCVISUAL routines from 4.4BSD ex.
+* Only use the "ic" termcap sequence if "im" is empty.
+* POSIX.2: Command line options added and revised.
+* POSIX.2: Added a "showmode" option for novices.
+* POSIX.2: Ex respects the environment variables COLUMNS and LINES.
+* POSIX.2: Added an "exrc" option like on POSIX.2 ex; arbitrary .exrc
+  files are ignored unless it is set in EXINIT or ~/.exrc.
+* POSIX.2: If .exrc files are world or group writeable, they are ignored
+  unless "sourceany" is set.
+* Ex now provides malloc routines. They are necessary since C library
+  calls like setlocale could otherwise break the sbrk calls in ex. As
+  an additional benefit, linking against ncurses is now possible.
+
+Release 31/05/00
+* String extraction using mkstr and xstr is not longer be done.
+* An ANSI C preprocessor may be used.
+* Changes of symbol names due to collisions on newer systems.
+* Fixed a null pointer reference in ex_tty.c.
+* Included the 2.11BSD termcap in a subdirectory. Ex could use any
+  termcap library, however, that does not use malloc().
+* Support of eight bit characters excluding the range 0200 to 0237 is
+  enabled with -DISO8859_1. It does not include the regular expression code,
+  but otherwise works well in practice with the ISO-8859-1 character set.
+
+Fixes for problems that were introduced in this port are marked (P), unless
+they only affect newly introduced parts (such as multibyte support).
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..6133cec
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,69 @@
+This code contains changes by
+     Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+
+Conditions 1, 2, and 4 and the no-warranty notice below apply
+to these changes.
+
+Copyright (c) 1980, 1993
+       The Regents of the University of California.  All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software
+   must display the following acknowledgement:
+       This product includes software developed by the University of
+       California, Berkeley and its contributors.
+4. Neither the name of the University nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+
+Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+  Redistributions of source code and documentation must retain the
+   above copyright notice, this list of conditions and the following
+   disclaimer.
+  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.
+  All advertising materials mentioning features or use of this software
+   must display the following acknowledgement:
+     This product includes software developed or owned by Caldera
+     International, Inc.
+  Neither the name of Caldera International, Inc. nor the names of
+   other contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..b3e01ea
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,392 @@
+#
+# This code contains changes by
+#      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+#
+# Conditions 1, 2, and 4 and the no-warranty notice below apply
+# to these changes.
+#
+#
+# Copyright (c) 1980, 1993
+#      The Regents of the University of California.  All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+#    must display the following acknowledgement:
+#      This product includes software developed by the University of
+#      California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+#    may be used to endorse or promote products derived from this software
+#    without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+# Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#   Redistributions of source code and documentation must retain the
+#    above copyright notice, this list of conditions and the following
+#    disclaimer.
+#   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.
+#   All advertising materials mentioning features or use of this software
+#    must display the following acknowledgement:
+#      This product includes software developed or owned by Caldera
+#      International, Inc.
+#   Neither the name of Caldera International, Inc. nor the names of
+#    other contributors may be used to endorse or promote products
+#    derived from this software without specific prior written permission.
+#
+# USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+# INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+#
+#      from Makefile   7.13.1.3 (2.11BSD GTE) 1996/10/23
+#
+#      @(#)Makefile    1.50 (gritter) 2/20/05
+#
+
+#
+# Destinations for installation. $(PRESERVEDIR) is used for recovery files.
+# It will get mode 1777.
+#
+PREFIX         = /usr/local
+BINDIR         = $(PREFIX)/bin
+LIBEXECDIR     = $(PREFIX)/libexec
+MANDIR         = $(PREFIX)/share/man
+PRESERVEDIR    = /var/preserve
+
+#
+# DESTDIR is prepended to the installation paths. It is mostly useful
+# for package building and should be left empty otherwise.
+#
+DESTDIR                =
+
+#
+# A BSD-like install program. GNU install will fit well here, too.
+#
+INSTALL                = /usr/ucb/install
+
+#
+# Compiler and linker flags.
+#
+# On HP-UX, add -D_INCLUDE__STDC_A1_SOURCE to CPPFLAGS.
+#
+#CFLAGS        =
+#CPPFLAGS =
+#LDFLAGS       =
+#LDADD =
+
+#
+# All of the following settings are quite recommended, so you should only
+# change them if it becomes really necessary.
+#
+# Remove -DMB to build without multibyte character support. This will be
+#     necessary if the compiler complains about a missing wchar.h, wctype.h,
+#     mbtowc(), mbrtowc(), btowc(), wcwidth() etc. You will likely also have
+#     to use the old regular expression code in these cases (see below).
+# Remove -DBIT8 to get a 7bit-only ex. This setting is mostly provided for
+#     testing the internationalization code against the older version and
+#     should not normally be removed.
+# Add -DNO_BE_BACKSLASH to make backslash a regular character inside RE
+#     bracket expressions. This is required for POSIX conformance but
+#     conflicts with historical practice for ex.
+#
+# Some historic comments:
+#
+# Ex is very large - this version will not fit on PDP-11's without overlay
+# software.  Things that can be turned off to save
+# space include LISPCODE (-l flag, showmatch and lisp options), CHDIR (the 
+# previously undocumented chdir command.)
+#
+# If your system expands tabs to 4 spaces you should -DTABS=4 below
+#
+FEATURES       = -DLISPCODE -DCHDIR -DFASTTAG -DUCVISUAL -DMB -DBIT8
+
+#
+# This disables the LC_CTYPE locale settings and assumes all characters
+# upwards from octal 0240 are printable ones, like in ISO 8859-1. It
+# should only be used if the system's locales are broken. It cannot be
+# used in combination with -DMB.
+#
+#CHARSET       = -DISO8859_1
+
+#
+# If you want LC_MESSAGES support using catgets(), define this. To make
+# any practical use of it, you also have to create a message catalogue,
+# put it in a proper location and change UNKNOWN to its name. See your
+# system's documentation on "gencat" or on language support in general
+# for further information. This setting is not interesting at the current
+# time unless you want to contribute internationalized message catalogues.
+#
+#LANGMSG               = -DLANGMSG -DCATNAME='"UNKNOWN"'
+
+#
+# For POSIX regular expressions, e.g. the star applied to subexpressions
+# as in \(ab\)* and localized regular expressions like [:class:], [.c.],
+# and [=c=], you need Caldera's 'UNIX(R) Regular Expression Library' or
+# the included derivative of it.
+#
+# Comment out the three following lines if you do not have it or if it
+# does not compile; it needs some advanced multibyte character support
+# (wchar.h, wctype.h, btowc() etc.) which is not provided by older
+# compilation environments.
+#
+REINC  = -I./libuxre -DUXRE
+RELIB  = -L./libuxre -luxre
+RETGT  = uxre
+
+#
+# VMUNIX should be correct for any modern Unix.
+#
+# Historic comments:
+#
+# VMUNIX makes ex considerably larger, raising 
+# many limits and improving speed and simplicity of maintenance.  It is 
+# suitable only for a VAX or other large machine, and then probably only in 
+# a paged system.
+#
+# Don't define VFORK unless your system has the VFORK system call,
+# which is like fork but the two processes have only one data space until the
+# child execs. This speeds up ex by saving the memory copy.
+#
+#      Note by Gunnar Ritter: vfork() is unreliable and one of the worst
+#      hacks in Unix history. Do not define VFORK unless you have a
+#      special good reason for that.
+#
+OSTYPE = -DVMUNIX
+
+#
+# On VMUNIX systems, ex can normally edit files up to 32 MB of size. LARGEF
+# raises this limit to around 1 GB, but ex will consume much more of core
+# and temp file space then.
+#
+#LARGEF        = -DLARGEF
+
+# 
+# The next setting is a crucial one since it determines the way ex
+# gets its knowledge about the terminal's capabilities. If you get
+# weird results, try using another library here. Uncomment exactly
+# one entry.
+#
+# On System V, the terminfo library may be more accurate than the termcap
+# file. To use it, link against the curses library.
+#
+#TERMLIB       = curses
+#
+# You may also get terminfo access by using the ncurses library.
+#
+#TERMLIB       = ncurses
+#
+# The preferred choice for ex on Linux distributions, other systems that
+# provide a good termcap file, or when setting the TERMCAP environment
+# variable is deemed sufficient, is the included 2.11BSD termcap library.
+#
+TERMLIB        = termlib
+
+#
+# Since ex uses sbrk() internally, a conflict with the libc's version of
+# malloc() must be avoided. There are two ways to work around this problem.
+# The first is to allocate a static pool for all malloc purposes. This will
+# work on any kind of system.
+#
+#MALLOC=malloc.o
+#
+# If mmap() can be used to allocate anonymous memory, this is the preferred
+# choice as it allows to grow memory dynamically as it is needed. This will
+# usually work unless you are compiling for a vector machine or another
+# unusual enviroment.
+MALLOC=mapmalloc.o
+
+###############################################################################
+#                                                                             #
+# That's it. You do not normally need to change anything below here.          #
+#                                                                             #
+###############################################################################
+
+#WARN  = -Wall -Wno-parentheses -Werror
+
+STRIP = -s
+RECOVER        = -DEXRECOVER=\"$(LIBEXECDIR)/exrecover\" \
+                       -DEXPRESERVE=\"$(LIBEXECDIR)/expreserve\"
+CCFLAGS        = $(CFLAGS) $(WARN) $(CPPFLAGS) $(FEATURES) $(CHARSET) $(OSTYPE) \
+               $(LARGEF) $(RECOVER) $(LANGMSG) $(REINC) $(RPMCFLAGS)
+TLIB   = libterm/libtermlib.a
+INCLUDE        = /usr/include
+OBJS   = ex.o ex_addr.o ex_cmds.o ex_cmds2.o ex_cmdsub.o \
+               ex_data.o ex_extern.o ex_get.o ex_io.o ex_put.o ex_re.o \
+               ex_set.o ex_subr.o ex_tagio.o ex_temp.o ex_tty.o ex_unix.o \
+               ex_v.o ex_vadj.o ex_vget.o ex_vmain.o ex_voper.o \
+               ex_vops.o ex_vops2.o ex_vops3.o ex_vput.o ex_vwind.o \
+               printf.o ex_version.o $(MALLOC)
+HDRS   = ex.h ex_argv.h ex_re.h ex_temp.h ex_tty.h ex_tune.h ex_vars.h \
+               ex_vis.h libterm/libterm.h
+SRC1   = ex.c ex_addr.c ex_cmds.c ex_cmds2.c ex_cmdsub.c
+SRC2   = ex_data.c ex_get.c ex_io.c ex_put.c ex_re.c
+SRC3   = ex_set.c ex_subr.c ex_tagio.c ex_temp.c ex_tty.c ex_unix.c
+SRC4   = ex_v.c ex_vadj.c ex_vget.c ex_vmain.c ex_voper.c
+SRC5   = ex_vops.c ex_vops2.c ex_vops3.c ex_vput.c ex_vwind.c
+SRC6   = printf.c expreserve.c exrecover.c ex_version.c
+SRC7   = mapmalloc.c malloc.c
+
+.SUFFIXES: .o .c
+.c.o: ; $(CC) $(CCFLAGS) -c $<
+
+all: $(RETGT) exrecover expreserve ex
+
+ex: $(TLIB) $(OBJS)
+       $(CC) -o ex $(LDFLAGS) $(OBJS) $(LDADD) -Llibterm -l$(TERMLIB) $(RELIB)
+       size ex
+
+$(TLIB): libterm/termcap.c libterm/tgoto.c libterm/tputs.c libterm/libterm.h
+       @cd libterm && $(MAKE) CC="$(CC)" \
+               COPT="$(CFLAGS) $(WARN) $(CPPFLAGS) $(OSTYPE)"
+
+exrecover: exrecover.o $(MALLOC)
+       $(CC) -o exrecover $(LDFLAGS) exrecover.o $(MALLOC) $(LDADD)
+
+expreserve: expreserve.o
+       $(CC) -o expreserve $(LDFLAGS) expreserve.o $(LDADD)
+
+ex_vars.h: ex_data.c
+       sh makeoptions $(CCFLAGS)
+
+uxre:
+       @cd libuxre && $(MAKE) CC="$(CC)" \
+               COPT="$(CFLAGS) $(WARN) $(CPPFLAGS) $(OSTYPE)"
+
+clean:
+       @cd libterm && $(MAKE) clean
+       @test ! -d libuxre || (cd libuxre && $(MAKE) clean)
+#      If we dont have ex we cant make it so don't rm ex_vars.h
+       -rm -f ex exrecover expreserve *.o x*.[cs] core errs trace
+
+mrproper: clean
+       -rm -f log
+
+# install in standard place
+
+install-man:
+       test -d $(DESTDIR)$(PREFIX) || mkdir -p $(DESTDIR)$(PREFIX)
+       test -d $(DESTDIR)$(MANDIR) || mkdir -p $(DESTDIR)$(MANDIR)
+       test -d $(DESTDIR)$(MANDIR)/man1 || mkdir -p $(DESTDIR)$(MANDIR)/man1
+       rm -f $(DESTDIR)$(MANDIR)/man1/ex.1 $(DESTDIR)$(MANDIR)/man1/edit.1 \
+               $(DESTDIR)$(MANDIR)/man1/vedit.1 \
+               $(DESTDIR)$(MANDIR)/man1/vi.1 \
+               $(DESTDIR)$(MANDIR)/man1/view.1
+       $(INSTALL) -c -m 644 ex.1 $(DESTDIR)$(MANDIR)/man1/ex.1
+       $(INSTALL) -c -m 644 vi.1 $(DESTDIR)$(MANDIR)/man1/vi.1
+       ln -s ex.1 $(DESTDIR)$(MANDIR)/man1/edit.1
+       ln -s vi.1 $(DESTDIR)$(MANDIR)/man1/vedit.1
+       ln -s vi.1 $(DESTDIR)$(MANDIR)/man1/view.1
+
+install: all install-man
+       rm -f $(DESTDIR)$(BINDIR)/ex $(DESTDIR)$(BINDIR)/edit \
+               $(DESTDIR)$(BINDIR)/vedit $(DESTDIR)$(BINDIR)/vi \
+               $(DESTDIR)$(BINDIR)/view
+       test -d $(DESTDIR)$(BINDIR) || mkdir -p $(DESTDIR)$(BINDIR)
+# special provisions for sticky install
+       if test -f $(DESTDIR)$(BINDIR)/ex; \
+       then    test -f $(DESTDIR)$(BINDIR)/ex.old.$$$$ && exit 1; \
+               chmod 755 $(DESTDIR)$(BINDIR)/ex; \
+               echo q | $(DESTDIR)$(BINDIR)/ex; \
+               mv $(DESTDIR)$(BINDIR)/ex $(DESTDIR)$(BINDIR)/ex.old.$$$$; \
+               rm -f $(DESTDIR)$(BINDIR)/ex.old.$$$$; \
+       fi
+       $(INSTALL) -c $(STRIP) -m 1755 ex $(DESTDIR)$(BINDIR)/ex
+       test -d $(DESTDIR)$(LIBEXECDIR) || mkdir -p $(DESTDIR)$(LIBEXECDIR)
+       $(INSTALL) -c $(STRIP) exrecover $(DESTDIR)$(LIBEXECDIR)/exrecover
+       $(INSTALL) -c $(STRIP) expreserve $(DESTDIR)$(LIBEXECDIR)/expreserve
+       ln -s ex $(DESTDIR)$(BINDIR)/edit
+       ln -s ex $(DESTDIR)$(BINDIR)/vedit
+       ln -s ex $(DESTDIR)$(BINDIR)/vi
+       ln -s ex $(DESTDIR)$(BINDIR)/view
+       test -d $(DESTDIR)$(PRESERVEDIR) || mkdir -p $(DESTDIR)$(PRESERVEDIR)
+       chmod 1777 $(DESTDIR)$(PRESERVEDIR)
+
+ex.o: config.h ex_argv.h ex.h ex_proto.h ex_temp.h ex_tty.h ex_tune.h
+ex.o: ex_vars.h libterm/libterm.h
+ex_addr.o: config.h ex.h ex_proto.h ex_re.h ex_tune.h ex_vars.h
+ex_cmds.o: config.h ex_argv.h ex.h ex_proto.h ex_temp.h ex_tty.h ex_tune.h
+ex_cmds.o: ex_vars.h ex_vis.h libterm/libterm.h
+ex_cmds2.o: config.h ex_argv.h ex.h ex_proto.h ex_temp.h ex_tty.h ex_tune.h
+ex_cmds2.o: ex_vars.h ex_vis.h libterm/libterm.h
+ex_cmdsub.o: config.h ex_argv.h ex.h ex_proto.h ex_temp.h ex_tty.h ex_tune.h
+ex_cmdsub.o: ex_vars.h ex_vis.h libterm/libterm.h
+ex_data.o: config.h ex.h ex_proto.h ex_tty.h ex_tune.h ex_vars.h
+ex_data.o: libterm/libterm.h
+ex_extern.o: config.h ex_argv.h ex.h ex_proto.h ex_re.h ex_temp.h ex_tty.h
+ex_extern.o: ex_tune.h ex_vars.h ex_vis.h libterm/libterm.h
+ex_get.o: config.h ex.h ex_proto.h ex_tty.h ex_tune.h ex_vars.h
+ex_get.o: libterm/libterm.h
+ex_io.o: config.h ex_argv.h ex.h ex_proto.h ex_temp.h ex_tty.h ex_tune.h
+ex_io.o: ex_vars.h ex_vis.h libterm/libterm.h
+ex_put.o: config.h ex.h ex_proto.h ex_tty.h ex_tune.h ex_vars.h ex_vis.h
+ex_put.o: libterm/libterm.h
+ex_re.o: config.h ex.h ex_proto.h ex_re.h ex_tune.h ex_vars.h
+ex_set.o: config.h ex.h ex_proto.h ex_temp.h ex_tty.h ex_tune.h ex_vars.h
+ex_set.o: libterm/libterm.h
+ex_subr.o: config.h ex.h ex_proto.h ex_re.h ex_tty.h ex_tune.h ex_vars.h
+ex_subr.o: ex_vis.h libterm/libterm.h
+ex_tagio.o: config.h ex.h ex_proto.h ex_tune.h ex_vars.h
+ex_temp.o: config.h ex.h ex_proto.h ex_temp.h ex_tty.h ex_tune.h ex_vars.h
+ex_temp.o: ex_vis.h libterm/libterm.h
+ex_tty.o: config.h ex.h ex_proto.h ex_tty.h ex_tune.h ex_vars.h
+ex_tty.o: libterm/libterm.h
+ex_unix.o: config.h ex.h ex_proto.h ex_temp.h ex_tty.h ex_tune.h ex_vars.h
+ex_unix.o: ex_vis.h libterm/libterm.h
+ex_v.o: config.h ex.h ex_proto.h ex_re.h ex_tty.h ex_tune.h ex_vars.h ex_vis.h
+ex_v.o: libterm/libterm.h
+ex_vadj.o: config.h ex.h ex_proto.h ex_tty.h ex_tune.h ex_vars.h ex_vis.h
+ex_vadj.o: libterm/libterm.h
+ex_vget.o: config.h ex.h ex_proto.h ex_tty.h ex_tune.h ex_vars.h ex_vis.h
+ex_vget.o: libterm/libterm.h
+ex_vmain.o: config.h ex.h ex_proto.h ex_tty.h ex_tune.h ex_vars.h ex_vis.h
+ex_vmain.o: libterm/libterm.h
+ex_voper.o: config.h ex.h ex_proto.h ex_re.h ex_tty.h ex_tune.h ex_vars.h
+ex_voper.o: ex_vis.h libterm/libterm.h
+ex_vops.o: config.h ex.h ex_proto.h ex_tty.h ex_tune.h ex_vars.h ex_vis.h
+ex_vops.o: libterm/libterm.h
+ex_vops2.o: config.h ex.h ex_proto.h ex_tty.h ex_tune.h ex_vars.h ex_vis.h
+ex_vops2.o: libterm/libterm.h
+ex_vops3.o: config.h ex.h ex_proto.h ex_tty.h ex_tune.h ex_vars.h ex_vis.h
+ex_vops3.o: libterm/libterm.h
+ex_vput.o: config.h ex.h ex_proto.h ex_tty.h ex_tune.h ex_vars.h ex_vis.h
+ex_vput.o: libterm/libterm.h
+ex_vwind.o: config.h ex.h ex_proto.h ex_tty.h ex_tune.h ex_vars.h ex_vis.h
+ex_vwind.o: libterm/libterm.h
+expreserve.o: config.h
+exrecover.o: config.h ex.h ex_proto.h ex_temp.h ex_tty.h ex_tune.h ex_vars.h
+exrecover.o: libterm/libterm.h
+malloc.o: config.h
+mapmalloc.o: config.h
+printf.o: config.h
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..4d0e83d
--- /dev/null
+++ b/README
@@ -0,0 +1,145 @@
+Welcome to the ex/vi port!
+==========================
+
+This implementation is derived from ex/vi 3.7 of 6/7/85 and the BSD
+termcap library, originally from the 2.11BSD distribution. All of them
+were changed to compile and run on newer POSIX compatible Unix systems.
+Support for international character sets was added, including support
+for multibyte locales (based on UTF-8 or East Asian encodings), and some
+changes were made to get closer to the POSIX.2 guidelines for ex and
+vi. Some issues that were clearly bugs and not features have also been
+resolved; see the Changes file for details.
+
+New releases are announced on Freshmeat. If you want to get
+notified by email on each release, use their subscription service at
+<http://freshmeat.net/projects/vi/>.
+
+The project homepage is currently at <http://ex-vi.sourceforge.net>.
+
+
+How to build
+============
+
+First look at the Makefile and change the settings there to match your
+build environment. Explanations are provided directly in this file.
+
+You can tune the sizes of some internal buffers by editing config.h. In
+particular, you will have to raise the size of the 'TUBE' constants if
+you wish to use really large-sized terminals.
+
+Then type 'make' and 'make install'.
+
+It is possible to build a RPM file directly from the source distribution
+by executing
+
+        rpmbuild -tb ex-<version>.tar.bz2
+
+Note that the RPM spec installs the binary in /usr/5bin by default to
+avoid conflicts with vendor files in /usr/bin. The default locations
+match those of the Heirloom Toolchest <http://heirloom.sourceforge.net>.
+
+The following systems have been reported to compile this code:
+
+Linux               Kernel 2.0 and above; libc4, libc5, glibc 2.2 and above,
+                    diet libc, uClibc
+Sun Solaris         2.5.1 and above
+Caldera Open UNIX   8.0.0
+SCO UnixWare        7.1.1, 7.0.1, 2.1.2
+HP HP-UX            B.11.23, B.11.11, B.11.00, B.10.20
+HP Tru64 UNIX       4.0G, 5.1B
+IBM AIX             5.1, 4.3
+NEC SUPER-UX        10.2
+NEC UX/4800         Release11.5 Rev.A
+Control Data EP/IX  2.2.1AA
+FreeBSD             3.1, 4.5, 5.x
+NetBSD              1.6, 2.0
+
+Reports about other Unix systems are welcome, whether successful or not
+(in the latter case add a detailed description). This port of vi is only
+aimed at Unix, though, so I am not interested about results from running
+this software on Windows etc.
+
+Prerequisites for ports to other systems are:
+
+- The system must provide an ANSI C-89 compiler and POSIX.1-1990 functions.
+
+- The system must provide an sbrk() call to increase the memory heap size.
+  If only a fake sbrk() call is provided that works by pre-allocating
+  several MB, vi will probably work too.
+
+- The system library must allow replacement of malloc() and printf() by the
+  versions provided by vi. For malloc(), it also must make its own internal
+  memory requests using the vi malloc(). Otherwise, vi will likely die with
+  a segmentation fault because the storage allocated by sbrk() interferes
+  with usual Unix library implementations of malloc().
+
+The last two requirements could probably be eliminated with some effort, but
+it would not result in any real improvements for usual the Unix platforms vi
+is targeted at, so it has not be done yet.
+
+
+Terminal capabilities
+=====================
+
+vi normally uses the termcap library to gather information about the
+capabilities of the terminal it is using. A BSD-derived termcap library
+is included with the vi distribution, and is usually the preferred choice.
+On some platforms, though, either no /etc/termcap file exists, or the file
+lacks up-to-date entries. In these cases, two workarounds are possible.
+First, vi can be linked against libcurses, libncurses, or libtermcap, if
+these provide access to a proper terminal information database. Second, it
+is possible to use the included termcap library with a TERMCAP environment
+variable that contains a complete termcap entry. Most terminals in current
+use provide a superset of DEC VT102 capabilities, so the following will
+normally work:
+
+TERMCAP="vt102|$TERM|dec vt102:"'\
+       :do=^J:co#80:li#24:cl=50\E[;H\E[2J:\
+       :le=^H:bs:cm=5\E[%i%d;%dH:nd=2\E[C:up=2\E[A:\
+       :ce=3\E[K:cd=50\E[J:so=2\E[7m:se=2\E[m:us=2\E[4m:ue=2\E[m:\
+       :md=2\E[1m:mr=2\E[7m:mb=2\E[5m:me=2\E[m:is=\E[1;24r\E[24;1H:\
+       :rs=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h:ks=\E[?1h\E=:ke=\E[?1l\E>:\
+       :ku=\EOA:kd=\EOB:kr=\EOC:kl=\EOD:kb=^H:\
+       :ho=\E[H:k1=\EOP:k2=\EOQ:k3=\EOR:k4=\EOS:pt:sr=5\EM:vt#3:\
+       :sc=\E7:rc=\E8:cs=\E[%i%d;%dr:vs=\E[?7l:ve=\E[?7h:\
+       :mi:al=\E[L:dc=\E[P:dl=\E[M:ei=\E[4l:im=\E[4h:'
+export TERMCAP
+
+
+Multibyte locale support
+========================
+
+Support for multibyte locales has been added to vi. It requires a number of
+functions that, while specified in XPG6, are not present on all systems that
+provide basic multibyte support. In particular, vi needs wcwidth() to
+determine the visual width of a character, and mbrtowc() to detect when a
+byte sequence that is entered at the terminal has been completed.
+
+The multibyte code is known to work on the following systems:
+
+Linux           glibc 2.2.2 and later
+Sun Solaris     9 and later
+HP HP-UX        B.11.11 and later
+FreeBSD         5.3
+NetBSD          2.0
+
+It has been tested on xterm patch #192, rxvt-unicode 4.2, mlterm 2.9.1, and
+xiterm 0.5.
+
+Successful operation is known for the following encodings: UTF-8, EUC-JP,
+EUC-KR, Big5, Big5-HKSCS, GB 2312, GBK. vi does not support locking-shift
+encodings like those that use ISO 2022 escape sequences. It also requires
+that the first byte of any multibyte character has the highest bit set.
+This excludes 7-bit encodings like UTF-7, and encodings whose sequences
+start with ASCII characters like TCVN 5712.
+
+To use UTF-8 locales in ex mode, the terminal should be put in 'stty iutf8'
+mode on Linux if it does not perform this automatically. Otherwise, typing
+the erase key once after entering a multibyte character will result in an
+incomplete byte sequence.
+
+
+Gunnar Ritter                                           2/20/05
+Freiburg i. Br.
+Germany
+<Gunnar.Ritter@pluto.uni-freiburg.de>
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..aaf0564
--- /dev/null
+++ b/TODO
@@ -0,0 +1,12 @@
+TODO list for ex
+
+- Some support for UTF-8 combining characters should probably be added.
+
+- Since the POSIX standard developers did not include a method to
+    determine whether something is a valid collation symbol or an
+    equivalence class, and since there is no access to the basic
+    collation sequence, LC_COLLATE locales are completely ignored.
+
+- SVr4 ex probably has some silent features that this one should have too.
+
+Gunnar Ritter                                          2/19/05
diff --git a/catd/en_US b/catd/en_US
new file mode 100644 (file)
index 0000000..7888f2c
--- /dev/null
@@ -0,0 +1,309 @@
+$ Message catalogue for ex/vi
+$ Sccsid @(#)en_US     1.4 (gritter) 3/18/03
+$quote "
+$set 1
+1 "Usage: %s [- | -s] [-l] [-L] [-R] [-r [file]] [-t tag]\n\
+       [-v] [-V] [-w size] [+cmd | -c cmd] file...\n"
+2 "%s: option requires an argument -- %c\n"
+3 "%s: illegal option -- %c\n"
+4 "Trace create error\n"
+5 "Unknown option %s\n"
+6 "Addr1 > addr2|First address exceeds second"
+7 "Bad count|Nonzero count required"
+8 "No address allowed@on this command"
+9 "Badly formed address"
+10 "No match to BOTTOM|Address search hit BOTTOM without matching pattern"
+11 "No match to TOP|Address search hit TOP without matching pattern"
+12 "Fail|Pattern not found"
+13 "Marks are ' and a-z"
+14 "Undefined mark@referenced"
+15 "Negative address@- first buffer line is 1"
+16 "Not that many lines@in buffer"
+17 "Offset out-of-bounds|Offset after command too large"
+18 "Home directory unknown"
+19 "Mark what?|%s requires following letter"
+20 "Bad mark|Mark must specify a letter"
+21 "Preserve failed!"
+22 "File preserved."
+23 "No write@since last change (:rewind! overrides)"
+24 "Old tty driver|Not using new tty driver/shell"
+25 "Bad register"
+26 "At EOF|At end-of-file"
+27 "What?|Unknown command character '%c'"
+28 "Extra chars|Extra characters at end of command"
+29 " [Warning - %s is incomplete]"
+30 "%d files@to edit"
+31 "No more files@to edit"
+$quote
+32 Extra chars|Extra characters at end of "%s" command
+$quote "
+33 "%d more file"
+34 "%s@to edit"
+35 "No write@since last change (:%s! overrides)"
+36 "What?|%s: No such command from open/visual"
+37 "What?|%s: Not an editor command"
+38 "[Hit return to continue] "
+39 "Out of memory@- too many lines in file"
+40 "Line overflow|Result line of join would be too long"
+41 "That move would do nothing!"
+42 "Move to a moved line"
+43 "%s where?|%s requires a trailing address"
+44 "Cannot put inside global/macro"
+45 "Line too long|Result line after shift would be too long"
+46 "Bad tag|Give one tag per line"
+47 "No previous tag"
+48 "%s: Bad tags file entry"
+49 "No write@since last change (:tag! overrides)"
+50 "No tags file"
+51 "%s: No such tag@in tags file"
+52 "Can't yank inside global/macro"
+53 "\nAt EOF"
+54 "At EOF"
+55 "Hit BOTTOM"
+56 "Hit TOP"
+57 "Nothing to undo"
+58 "Nothing changed|Last undoable command didn't change anything"
+59 "Can't undo in global@commands"
+60 "Missing lhs"
+61 "Missing rhs"
+62 "Missing rhs"
+63 "No tail recursion"
+64 "Too dangerous to map that"
+65 "No tail recursion"
+66 "Missing lhs"
+67 "Not mapped|That macro wasn't mapped"
+68 "Too many macros"
+69 "Too much macro text"
+70 "^H discarded\n"
+71 "Input line too long"
+72 "No file|No current filename"
+73 " [Read only]"
+74 " [Not edited]"
+75 " [Modified]"
+76 "No file "
+77 " line %d of %d --%ld%%--"
+78 "Pattern too long"
+79 "Argument buffer overflow"
+80 "No alternate filename@to substitute for #"
+81 "No current filename@to substitute for %%"
+82 "Can't make pipe to glob"
+83 "Can't fork to do glob"
+84 "Arg list too long"
+85 "Arg list too long"
+86 "No match"
+87 "Missing filename"
+88 "Ambiguous|Too many file names"
+89 "Filename too long"
+90 " [New file]"
+91 " Block special file"
+92 " Teletype"
+93 " Character special file"
+94 " Directory"
+95 " Socket"
+96 " Named pipe"
+97 " Executable"
+98 " Compressed Data"
+99 " ELF object"
+100 " Archive"
+101 " Non-ascii file"
+102 " [Read only]"
+103 " %d/%d"
+104 " %d line%s, %d character%s"
+105 " ("
+106 "%d null"
+107 ", "
+108 "%d non-ASCII"
+109 "Write forms are 'w' and 'w>>'"
+110 "No file|No current filename"
+$quote #
+111 # File exists| File exists - use "w! %s" to overwrite#
+$quote "
+112 " File is read only"
+113 " File is read only"
+$quote #
+114 # Use "w!" to write partial buffer#
+$quote "
+115 " [New file]"
+116 " [Existing file]"
+117 " [Incomplete last line]"
+118 " Line too long"
+119 "Too many nested sources"
+120 "Open and visual must be used interactively"
+121 "Global within global@not allowed"
+122 "Global needs re|Missing regular expression for global"
+123 "Global command too long"
+124 "substitution loop"
+125 "Fail|Substitute pattern match failed"
+126 "Substitute needs re|Missing regular expression for substitute"
+127 "No previous re|No previous regular expression"
+128 "No previous substitute re|No previous substitute to repeat"
+129 "Replacement pattern too long@- limit 256 characters"
+130 "Line overflow@in substitute"
+131 "%d subs|%d substitutions"
+132 " on %d lines"
+133 "Regular expressions cannot be delimited by letters or digits"
+134 "No previous scan re|No previous scanning regular expression"
+135 "No previous substitute re|No previous substitute regular expression"
+136 "Badly formed re|Regular expression \\ must be followed by / or ?"
+137 "No previous re|No previous regular expression"
+138 "Missing closing delimiter@for regular expression"
+139 "Re too complex|Regular expression too complicated"
+140 "Unmatched \\(|More \\('s than \\)'s in regular expression"
+141 "Awash in \\('s!|Too many \\('d subexressions in a regular expression"
+142 "Extra \\)|More \\)'s than \\('s in regular expression"
+143 "Bad number|Bad number in regular expression"
+144 "Range endpoint too large|Range endpoint too large in regular expression"
+145 "More than 2 numbers given in \\{~\\}"
+146 "} expected after \\"
+147 "First number exceeds second in \\{~\\}"
+$quote
+148 "\\digit" out of range
+$quote "
+149 "Replacement pattern contains &@- cannot use in re"
+150 "Replacement pattern contains \\d@- cannot use in re"
+151 "Illegal *|Can't * a \\( ... \\) in regular expression")
+152 "Illegal *|Can't * a \\n in regular expression"
+153 "Bad character class|Empty character class '[]' or '[^]' cannot match"
+154 "Missing ]"
+155 "No newlines in re's|Can't escape newlines into regular expressions"
+156 "Bad \\n|\\n in regular expression with n greater than the number of \\('s"
+157 "Badly formed re|Missing closing delimiter for regular expression"
+158 "Re internal error"
+159 "%s: No such option@- 'set all' gives all option values"
+160 "Option %s is not a toggle"
+161 "Missing =@in assignment to option %s"
+162 "Digits required@after ="
+163 "String too long@in option assignment"
+164 "Can't change type of terminal from within open/visual"
+165 "%s%s"
+166 ""
+167 "no"
+168 "%s=%d"
+169 "%s=%s"
+170 "%d lines"
+171 " %c%s"
+172 "Nonzero address required@on this command"
+173 "No lines@in the buffer"
+174 "more "
+175 "fewer "
+176 ""
+177 "%d %slines@in file after %s"
+178 ""
+179 "s"
+180 "Out of memory@saving lines for undo - try using ed"
+181 "emt trap, _ovno is %d @ - try again"
+182 "\nInterrupt"
+183 " Tmp file too large"
+184 " Tmp file too large"
+185 " Tmp file too large"
+186 "Out of register space (ugh)"
+187 "Nothing in register %c"
+188 "Can't put partial line inside macro"
+189 "Nothing in register %c"
+190 "Register too long@to fit in memory"
+191 "%s: Unknown terminal type"
+192 "Incomplete shell escape command@- use 'shell' to get a shell"
+193 "Command too long"
+194 "No previous command@to substitute for !"
+195 "No alternate filename@to substitute for #"
+196 "No filename@to substitute for %%"
+197 "[No write]|[No write since last change]"
+198 "No previous command@to repeat"
+199 "Can't make pipe for filter"
+200 "No more processes"
+201 "No %s!\n"
+202 "Can't make pipe"
+203 "No more processes"
+204 " Can't make pipe for recovery"
+205 " Can't fork to execute recovery"
+206 " No recovery routine"
+207 "Fail|Pattern not found on addressed line"
+208 "Can't use open/visual unless open option is set"
+209 "Recursive open/visual not allowed"
+210 "[Using open mode]"
+211 "Visual needs addressible cursor or upline capability"
+212 Can't use visual on a terminal which overstrikes"
+213 "Visual requires clear screen capability"
+214 "Visual requires scrolling"
+215 "Screen too large for internal buffer"
+216 "Don't know enough about your terminal to use %s"
+217 "Terminal too wide"
+218 "Screen too large"
+219 "Internal error: vscroll"
+220 "No lines in buffer"
+221 "Internal error: vredraw"
+222 "Input read error"
+223 "%d %sline"
+224 "Macro too long@ - maybe recursive?"
+225 "Infinite macro loop"
+226 "Q gets ex command mode, :q leaves vi"
+227 "               "
+228 "AAPPEND MODE"
+229 "CCHANGE MODE"
+230 "OOPEN MODE"
+231 "RREPLACE MODE"
+232 "rREPLACE 1 CHAR"
+233 "IINSERT MODE"
+234 "Infinite macro loop"
+235 "Line too long"
+236 "Line too long"
+237 "Internal error: vclreol"
+238 "Internal error: vgoto"
+239 "Line too long for open"
+240 "Line too long"
+241 "No memory pool"
+242 "Memory pool exhausted"
+243 "failed to memory map anonymous area"
+244 "failed to open /dev/zero"
+245 "failed to memory map /dev/zero"
+246 "chunk of memory already in free list"
+247 "out of memory"
+248 "(null pointer)"
+249 "y"
+$ exrecover
+$set 2
+1 " Wrong number of arguments to exrecover"
+2 " [Dated: %s"
+3 ", newest of %d saved]"
+4 "]"
+5 " Not enough core for lines"
+6 "No files saved.\n"
+7 "On %s at "
+$quote #
+8 # saved %d lines of file "%s"\n#
+$quote "
+9 " File not found"
+10 " [Lost line(s):"
+11 " %d"
+12 "-%d"
+13 " [Lost line(s):"
+14 " %d"
+15 "-%d"
+16 "]"
+17 " Tmp file too large"
+$ expreserve
+$set 3
+1 "NOT super user\n"
+2 "the system went down"
+3 "the editor was killed"
+4 "Subject: editor saved ``LOST''\n"
+5 "You were editing a file without a name\n"
+6 "at <%s> on the machine ``%s'' when %s.\n"
+$quote #
+7 #Since the file had no name, it has been named "LOST".\n#
+$quote "
+8 "Subject: editor saved ``%s''\n"
+$quote #
+9 #You were editing the file "%s"\n#
+$quote "
+10 "at <%s> on the machine ``%s''\n"
+11 "when %s.\n"
+12 "\nYou can retrieve most of your changes to this file\n"
+$quote #
+13 #using the "recover" command of the editor.\n#
+14 #An easy way to do this is to give the command "vi -r %s".\n#
+15 #This method also works using "ex" and "edit".\n#
+$quote "
+16 "Buffer format error\t"
+17 "Buffer read error"
+18 "Can't find a name\t"
diff --git a/config.h b/config.h
new file mode 100644 (file)
index 0000000..23f7779
--- /dev/null
+++ b/config.h
@@ -0,0 +1,146 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+ *
+ *     @(#)config.h    1.12 (gritter) 2/19/05
+ */
+
+/*
+ * Configurable settings for the ex editor.
+ */
+
+/*
+ * Maximum screen size in visual mode.
+ *
+ * Because the routine "alloca" is not portable, TUBESIZE
+ * bytes are allocated on the stack each time you go into visual
+ * and then never freed by the system.  Thus if you have no terminals
+ * which are larger than 24 * 80 you may well want to make TUBESIZE
+ * smaller.  TUBECOLS should stay at 160 at least since this defines
+ * the maximum length of opening on hardcopies and allows two lines
+ * of open on terminals like adm3's (glass tty's) where it switches
+ * to pseudo hardcopy mode when a line gets longer than 80 characters.
+ */
+#ifndef VMUNIX
+#define TUBELINES       70      /* Number of screen lines for visual */
+#define TUBECOLS        160     /* Number of screen columns for visual */
+#define TUBESIZE        6000    /* Maximum screen size for visual */
+#else  /* VMUNIX */
+#define TUBELINES       100
+#define TUBECOLS        160
+#define TUBESIZE        16000
+#endif /* VMUNIX */
+
+/*
+ * Various buffer sizes.
+ */
+#ifndef        VMUNIX
+#define        ESIZE           128     /* Regular expression buffer size */
+#define        RHSSIZE         256     /* Size of rhs of substitute */
+#define        TAGSIZE         128     /* Tag length */
+#define        ONMSZ           64      /* Option name size */
+#else  /* VMUNIX */
+#define        ESIZE           1024
+#define        RHSSIZE         512
+#define        TAGSIZE         256
+#define        ONMSZ           256
+#endif /* VMUNIX */
+
+/*
+ * The following types are usually predefined on modern platforms; it
+ * is only necessary to define them manually if compilation errors occur.
+ */
+
+/*
+ * The intptr_t type was introduced by SUSv2 and C99. It is a signed
+ * integer type capable of holding pointers:
+ *
+ *       sizeof(intptr_t) == sizeof(void *).
+ *
+ * Type  Environment Typical systems
+ * int   IP16        PDP11, 80286
+ * int   ILP32       Most VAX, M68k, IA32, SPARC
+ * long  LP32        Some IA32 and M68k
+ * long  LP64        64 bit mode of IA64, SPARC v9, and Alpha
+ *
+ * The argument to the sbrk() system call has this type.
+ */
+#ifdef notdef
+typedef int    intptr_t;
+#endif
+
+/*
+ * The ssize_t type should be the same as the return type of read()
+ * and write().
+ */
+#ifdef notdef
+typedef        int     ssize_t;
+#endif
diff --git a/ex.1 b/ex.1
new file mode 100644 (file)
index 0000000..cb95ff5
--- /dev/null
+++ b/ex.1
@@ -0,0 +1,2045 @@
+.\"
+.\" This code contains changes by
+.\"      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+.\"
+.\" Conditions 1, 2, and 4 and the no-warranty notice below apply
+.\" to these changes.
+.\"
+.\"
+.\" Copyright (c) 1980, 1993
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"
+.\" Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"   Redistributions of source code and documentation must retain the
+.\"    above copyright notice, this list of conditions and the following
+.\"    disclaimer.
+.\"   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.
+.\"   All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"      This product includes software developed or owned by Caldera
+.\"      International, Inc.
+.\"   Neither the name of Caldera International, Inc. nor the names of
+.\"    other contributors may be used to endorse or promote products
+.\"    derived from this software without specific prior written permission.
+.\"
+.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+.\" INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+.\"
+.\"    from ex.1       6.4.1 (2.11BSD) 1996/10/21
+.\"
+.\"    Sccsid @(#)ex.1 1.44 (gritter) 12/1/04
+.\"
+.ie \n(.g==1 \{\
+.ds lq \(lq
+.ds rq \(rq
+.\}
+.el \{\
+.ds lq ``
+.ds rq ''
+.\}
+.TH EX 1 "12/1/04" "Ancient Unix Ports" "User Commands"
+.SH NAME
+ex, edit \- text editor
+.SH SYNOPSIS
+.HP
+.ad l
+\fBex\fR [\fB\-c\fI\ command\fR|\fB+\fIcommand\fR]
+[\fB\-r\fR\ [\fIfilename\fR]] [\fB\-s\fR|\fB\-\fR]
+[\fB\-t\fI\ tagstring\fR] [\fB\-w\fI\ size\fR]
+[\fB\-lLRvV\fR] [\fIfile\fR ...]
+.HP
+.ad l
+\fBedit\fR [\fB\-c\fI\ command\fR|\fB+\fIcommand\fR]
+[\fB\-r\fR\ [\fIfilename\fR]] [\fB\-s\fR|\fB\-\fR]
+[\fB\-t\fI\ tagstring\fR] [\fB\-w\fI\ size\fR]
+[\fB\-lLRvV\fR] [\fIfile\fR ...]
+.br
+.ad b
+.SH DESCRIPTION
+.I Ex
+is the root of a family of editors:
+.I edit,
+.I ex
+and
+.I vi.
+.I Ex
+is a superset of
+.I ed,
+with the most notable extension being a display editing facility.
+Display based editing on
+.SM CRT
+terminals is the focus of
+.IR vi .
+.PP
+For those who have not used
+.I ed,
+or for casual users, the editor
+.I edit
+may be convenient.
+It avoids some of the complexities of
+.I ex
+used mostly by systems programmers and persons very familiar with
+.I ed.
+.PP
+The following options are accepted:
+.TP
+\fB\-c\fP\fI\ command\fP or \fB+\fP\fIcommand\fP
+Execute
+.I command
+when editing begins.
+.TP
+.B \-l
+Start in a special mode useful for the
+.I Lisp
+programming language.
+.TP
+\fB\-r\fI\ [filename]\fR or \fB\-L\fR
+When no argument is supplied with this option,
+all files to be recovered are listed
+and the editor exits immediately.
+If a
+.I filename
+is specified,
+the corresponding temporary file is opened in recovery mode.
+.TP
+.B \-R
+Files are opened read-only when this option is given.
+.TP
+.BR \-s \ or\  \-
+Script mode;
+all feedback for interactive editing is disabled.
+.SM EXINIT
+and
+.I .exrc
+files are not processed.
+.TP
+.BI \-t \ tagstring
+Read the
+.I tags
+file,
+then choose the file and position specified by
+.I tagstring
+for editing.
+.TP
+.B \-v
+Start in visual mode even if called as
+.IR ex .
+.TP
+.B \-V
+Echo command input to standard error,
+unless it originates from a terminal.
+.TP
+.BI \-w \ size
+Specify the size of the editing window for visual mode.
+.\"    from ex.rm       8.1 (Berkeley) 6/8/93
+.SS "File manipulation"
+.I Ex
+is normally editing the contents of a single file,
+whose name is recorded in the
+.I current
+file name.
+.I Ex
+performs all editing actions in a buffer
+(actually a temporary file)
+into which the text of the file is initially read.
+Changes made to the buffer have no effect on the file being
+edited unless and until the buffer contents are written out to the
+file with a
+.I write
+command.
+After the buffer contents are written,
+the previous contents of the written file are no longer accessible.
+When a file is edited,
+its name becomes the current file name,
+and its contents are read into the buffer.
+.PP
+The current file is almost always considered to be
+.I edited.
+This means that the contents of the buffer are logically
+connected with the current file name,
+so that writing the current buffer contents onto that file,
+even if it exists,
+is a reasonable action.
+If the current file is not 
+.I edited
+then
+.I ex
+will not normally write on it if it already exists.
+.PP
+For saving blocks of text while editing, and especially when editing
+more than one file,
+.I ex
+has a group of named buffers.
+These are similar to the normal buffer, except that only a limited number
+of operations are available on them.
+The buffers have names
+.I a
+through
+.I z.
+.SS "Exceptional Conditions"
+.PP
+When errors occur
+.I ex
+(optionally) rings the terminal bell and, in any case, prints an error
+diagnostic.  If the primary input is from a file, editor processing
+will terminate.  If an interrupt signal is received,
+.I ex
+prints \*(lqInterrupt\*(rq and returns to its command level.  If the primary
+input is a file, then
+.I ex
+will exit when this occurs.
+.PP
+If a hangup signal is received and the buffer has been modified since
+it was last written out, or if the system crashes, either the editor
+(in the first case) or the system (after it reboots in the second) will
+attempt to preserve the buffer.  The next time the user logs in he should be
+able to recover the work he was doing, losing at most a few lines of
+changes from the last point before the hangup or editor crash.  To
+recover a file one can use the
+.B \-r
+option.  If one was editing the file
+.I resume,
+then he should change
+to the directory where he were when the crash occurred, giving the command
+.RS
+.sp
+\fBex \-r\fP\fI resume\fP
+.sp
+.RE
+After checking that the retrieved file is indeed ok, he can
+.I write
+it over the previous contents of that file.
+.PP
+The user will normally get mail from the system telling him when a file has
+been saved after a crash.  The command
+.RS
+.sp
+\fBex\fP \-\fBr\fP
+.sp
+.RE 
+will print a list of the files which have been saved for the user. 
+.\"(In the case of a hangup,
+.\"the file will not appear in the list,
+.\"although it can be recovered.)
+.SS "Editing modes"
+.PP
+.I Ex
+has five distinct modes.  The primary mode is
+.I command
+mode.  Commands are entered in command mode when a `:' prompt is
+present, and are executed each time a complete line is sent.  In
+.I "text input"
+mode
+.I ex
+gathers input lines and places them in the file.  The
+.I append,
+.I insert,
+and
+.I change
+commands use text input mode.
+No prompt is printed when in text input mode.
+This mode is left by typing a `.' alone at the beginning of a line, and
+.I command
+mode resumes.
+.PP
+The last three modes are
+.I open
+and
+.I visual
+modes, entered by the commands of the same name, and, within open and
+visual modes
+.I "text insertion"
+mode.
+.I Open
+and
+.I visual
+modes allow local editing operations to be performed on the text in the
+file.  The
+.I open
+command displays one line at a time on any terminal while
+.I visual
+works on 
+.SM CRT
+terminals with random positioning cursors, using the
+screen as a (single) window for file editing changes.
+These modes are described (only) in
+.I "An Introduction to Display Editing with Vi."
+.SS "Command structure"
+.PP
+Most command names are English words,
+and initial prefixes of the words are acceptable abbreviations.
+The ambiguity of abbreviations is resolved in favor of the more commonly
+used commands.
+.PP
+Most commands accept prefix addresses specifying the lines in the file
+upon which they are to have effect.
+The forms of these addresses will be discussed below.
+A number of commands also may take a trailing
+.I count
+specifying the number of lines to be involved in the command.
+Thus the command \*(lq10p\*(rq will print the tenth line in the buffer while
+\*(lqdelete 5\*(rq will delete five lines from the buffer,
+starting with the current line.
+.PP
+Some commands take other information or parameters,
+this information always being given after the command name.
+.PP
+A number of commands have two distinct variants.
+The variant form of the command is invoked by placing an
+`!' immediately after the command name. 
+Some of the default variants may be controlled by options;
+in this case, the `!' serves to toggle the default.
+.PP
+The characters `#', `p' and `l' may be placed after many commands
+(A `p' or `l' must be preceded by a blank or tab
+except in the single special case `dp').
+In this case, the command abbreviated by these characters
+is executed after the command completes.
+Since
+.I ex
+normally prints the new current line after each change, `p' is rarely necessary.
+Any number of `+' or `\-' characters may also be given with these flags.
+If they appear, the specified offset is applied to the current line
+value before the printing command is executed.
+.PP
+It is possible to give editor commands which are ignored.
+This is useful when making complex editor scripts
+for which comments are desired.
+The comment character is the double quote: ".
+Any command line beginning with " is ignored.
+Comments beginning with " may also be placed at the ends
+of commands, except in cases where they could be confused as part
+of text (shell escapes and the substitute and map commands).
+.PP
+More than one command may be placed on a line by separating each pair
+of commands by a `|' character.
+However the
+.I global
+commands,
+comments,
+and the shell escape `!'
+must be the last command on a line, as they are not terminated by a `|'.
+.SS "Command addressing"
+.IP \fB.\fR 20
+The current line.
+Most commands leave the current line as the last line which they affect.
+The default address for most commands is the current line,
+thus `\fB.\fR' is rarely used alone as an address.
+.IP \fIn\fR 20
+The \fIn\fRth line in the editor's buffer, lines being numbered
+sequentially from 1.
+.IP \fB$\fR 20
+The last line in the buffer.
+.IP \fB%\fR 20
+An abbreviation for \*(lq1,$\*(rq, the entire buffer.
+.IP \fI+n\fR\ \fI\-n\fR 20
+An offset relative to the current buffer line.
+The forms `.+3' `+3' and `+++' are all equivalent;
+if the current line is line 100 they all address line 103.
+.IP \fB/\fIpat\fR\fB/\fR\ \fB?\fIpat\fR\fB?\fR 20
+Scan forward and backward respectively for a line containing \fIpat\fR, a
+regular expression (as defined below).  The scans normally wrap around the end
+of the buffer.
+If all that is desired is to print the next line containing \fIpat\fR, then
+the trailing \fB/\fR or \fB?\fR may be omitted.
+If \fIpat\fP is omitted or explicitly empty, then the last
+regular expression specified is located.
+The forms \fB\e/\fP and \fB\e?\fP scan
+using the last regular expression used in a scan; after a substitute
+\fB//\fP and \fB??\fP would scan using the substitute's regular expression.
+.IP \fB\(aa\(aa\fP\ \fB\(aa\fP\fIx\fP 20
+Before each non-relative motion of the current line `\fB.\fP',
+the previous current line is marked with a tag, subsequently referred to as
+`\(aa\(aa'.
+This makes it easy to refer or return to this previous context.
+Marks may also be established by the
+.I mark
+command, using single lower case letters
+.I x
+and the marked lines referred to as
+`\(aa\fIx\fR'.
+.PP
+Addresses to commands consist of a series of addressing primitives,
+separated by `,' or `;'.
+Such address lists are evaluated left-to-right.
+When addresses are separated by `;' the current line `\fB.\fR'
+is set to the value of the previous addressing expression
+before the next address is interpreted.
+If more addresses are given than the command requires,
+then all but the last one or two are ignored.
+If the command takes two addresses, the first addressed line must
+precede the second in the buffer.
+.PP
+Null address specifications are permitted in a list of addresses,
+the default in this case is the current line `.';
+thus `,100' is equivalent to `\fB.\fR,100'.
+It is an error to give a prefix address to a command which expects none.
+.SS "Command descriptions"
+.PP
+The following form is a prototype for all
+.I ex
+commands:
+.RS
+.sp
+\fIaddress\fR \fBcommand\fR \fI! parameters count flags\fR
+.sp
+.RE
+All parts are optional; the degenerate case is the empty command which prints
+the next line in the file.  For sanity with use from within
+.I visual
+mode,
+.I ex
+ignores a \*(lq:\*(rq preceding any command.
+.PP
+In the following command descriptions, the
+default addresses are shown in parentheses,
+which are
+.I not,
+however,
+part of the command.
+.TP
+\fBabbreviate\fR \fIword rhs\fP        abbr: \fBab\fP
+Add the named abbreviation to the current list.
+When in input mode in visual, if
+.I word
+is typed as a complete word, it will be changed to
+.I rhs .
+.LP
+( \fB.\fR ) \fBappend\fR       abbr: \fBa\fR
+.br
+\fItext\fR
+.br
+\&\fB.\fR
+.RS
+Reads the input text and places it after the specified line.
+After the command, `\fB.\fR'
+addresses the last line input or the
+specified line if no lines were input.
+If address `0' is given,
+text is placed at the beginning of the buffer.
+.RE
+.LP
+\fBa!\fR
+.br
+\fItext\fR
+.br
+\&\fB.\fR
+.RS
+The variant flag to
+.I append
+toggles the setting for the
+.I autoindent
+option during the input of
+.I text.
+.RE
+.TP
+\fBargs\fR
+The members of the argument list are printed, with the current argument
+delimited by `[' and `]'.
+.TP
+\fBcd\fR \fIdirectory\fR
+The
+.I cd
+command is a synonym for
+.I chdir.
+.LP
+( \fB.\fP , \fB.\fP ) \fBchange\fP \fIcount\fP abbr: \fBc\fP
+.br
+\fItext\fP
+.br
+\&\fB.\fP
+.RS
+Replaces the specified lines with the input \fItext\fP.
+The current line becomes the last line input;
+if no lines were input it is left as for a
+\fIdelete\fP.
+.RE
+.LP
+\fBc!\fP
+.br
+\fItext\fP
+.br
+\&\fB.\fP
+.RS
+The variant toggles
+.I autoindent
+during the
+.I change.
+.RE
+.TP
+\fBchdir\fR \fIdirectory\fR
+The specified \fIdirectory\fR becomes the current directory.
+If no directory is specified, the current value of the
+.I home
+option is used as the target directory.
+After a
+.I chdir
+the current file is not considered to have been
+edited so that write restrictions on pre-existing files apply.
+.TP
+( \fB.\fP , \fB.\fP )\|\fBcopy\fP \fIaddr\fP \fIflags\fP       abbr: \fBco\fP
+A \fIcopy\fP
+of the specified lines is placed after
+.I addr,
+which may be `0'.
+The current line
+`\fB.\fR'
+addresses the last line of the copy.
+The command
+.I t
+is a synonym for
+.I copy.
+.TP
+( \fB.\fR , \fB.\fR )\|\fBdelete\fR \fIbuffer\fR \fIcount\fR \fIflags\fR       abbr: \fBd\fR
+Removes the specified lines from the buffer.
+The line after the last line deleted becomes the current line;
+if the lines deleted were originally at the end,
+the new last line becomes the current line.
+If a named
+.I buffer
+is specified by giving a letter,
+then the specified lines are saved in that buffer,
+or appended to it if an upper case letter is used.
+.LP
+\fBedit\fR \fIfile\fR  abbr: \fBe\fR
+.br
+\fBex\fR \fIfile\fR
+.RS
+Used to begin an editing session on a new file.
+The editor
+first checks to see if the buffer has been modified since the last
+.I write
+command was issued.
+If it has been,
+a warning is issued and the
+command is aborted.
+The
+command otherwise deletes the entire contents of the editor buffer,
+makes the named file the current file and prints the new filename.
+After insuring that this file is sensible
+(i.e., that it is not a binary file such as a directory,
+a block or character special file other than
+.I /dev/tty,
+a terminal,
+or a binary or executable file),
+the editor reads the file into its buffer.
+.PP
+If the read of the file completes without error,
+the number of lines and characters read is typed.
+Any null characters in the file are discarded.
+If none of these errors occurred, the file is considered
+.I edited.
+If the last line of the input file is missing the trailing
+newline character, it will be supplied and a complaint will be issued.
+This command leaves the current line `\fB.\fR' at the last line read.
+If executed from within
+.I open
+or
+.I visual,
+the current line is initially the first line of the file.
+.RE
+.TP
+\fBe!\fR \fIfile\fR
+The variant form suppresses the complaint about modifications having
+been made and not written from the editor buffer, thus
+discarding all changes which have been made before editing the new file.
+.TP
+\fBe\fR \fB+\fIn\fR \fIfile\fR
+Causes the editor to begin at line
+.I n
+rather than at the last line;
+\fIn\fR may also be an editor command containing no spaces,
+e.g.: \*(lq+/pat\*(rq.
+.TP
+\fBfile\fR     abbr: \fBf\fR
+Prints the current file name,
+whether it has been `[Modified]' since the last
+.I write 
+command,
+whether it is
+.I "read only" ,
+the current line,
+the number of lines in the buffer,
+and the percentage of the way through the buffer of the current line.
+In the rare case that the current file is `[Not edited]' this is
+noted also; in this case one has to use the form \fBw!\fR to write to
+the file, since the editor is not sure that a \fBwrite\fR will not
+destroy a file unrelated to the current contents of the buffer.
+.TP
+\fBfile\fR \fIfile\fR
+The current file name is changed to
+.I file
+which is considered 
+`[Not edited]'.
+.TP
+( 1 , $ ) \fBglobal\fR /\fIpat\|\fR/ \fIcmds\fR        abbr: \fBg\fR
+First marks each line among those specified which matches
+the given regular expression.
+Then the given command list is executed with `\fB.\fR' initially
+set to each marked line.
+.IP
+The command list consists of the remaining commands on the current
+input line and may continue to multiple lines by ending all but the
+last such line with a `\e'.
+If
+.I cmds
+(and possibly the trailing \fB/\fR delimiter) is omitted, each line matching
+.I pat
+is printed.
+.I Append,
+.I insert,
+and
+.I change
+commands and associated input are permitted;
+the `\fB.\fR' terminating input may be omitted if it would be on the
+last line of the command list.
+.I Open
+and
+.I visual
+commands are permitted in the command list and take input from the terminal.
+.IP
+The
+.I global
+command itself may not appear in
+.I cmds.
+The
+.I undo
+command is also not permitted there,
+as
+.I undo
+instead can be used to reverse the entire
+.I global
+command.
+The options
+.I autoprint
+and
+.I autoindent
+are inhibited during a
+.I global,
+(and possibly the trailing \fB/\fR delimiter) and the value of the
+.I report
+option is temporarily infinite,
+in deference to a \fIreport\fR for the entire global.
+Finally, the context mark `\'\'' is set to the value of
+`.' before the global command begins and is not changed during a global
+command,
+except perhaps by an
+.I open
+or
+.I visual
+within the
+.I global.
+.TP
+\fBg!\fR \fB/\fIpat\fB/\fR \fIcmds\fR  abbr: \fBv\fR
+The variant form of \fIglobal\fR runs \fIcmds\fR at each line not matching
+\fIpat\fR.
+.LP
+( \fB.\fR )\|\fBinsert\fR      abbr: \fBi\fR
+.br
+\fItext\fR
+.br
+\&\fB.\fR
+.RS
+Places the given text before the specified line.
+The current line is left at the last line input;
+if there were none input it is left at the line before the addressed line.
+This command differs from
+.I append
+only in the placement of text.
+.RE
+.LP
+\fBi!\fR
+.br
+\fItext\fR
+.br
+\&\fB.\fR
+.RS
+The variant toggles
+.I autoindent
+during the
+.I insert.
+.RE
+.TP
+( \fB.\fR , \fB.\fR+1 ) \fBjoin\fR \fIcount\fR \fIflags\fR     abbr: \fBj\fR
+Places the text from a specified range of lines
+together on one line.
+White space is adjusted at each junction to provide at least
+one blank character, two if there was a `\fB.\fR' at the end of the line,
+or none if the first following character is a `)'.
+If there is already white space at the end of the line,
+then the white space at the start of the next line will be discarded.
+.TP
+\fBj!\fR
+The variant causes a simpler
+.I join
+with no white space processing; the characters in the lines are simply
+concatenated.
+.TP
+( \fB.\fR ) \fBk\fR \fIx\fR
+The
+.I k
+command is a synonym for
+.I mark.
+It does not require a blank or tab before the following letter.
+.TP
+( \fB.\fR , \fB.\fR ) \fBlist\fR \fIcount\fR \fIflags\fR
+Prints the specified lines in a more unambiguous way:
+tabs are printed as `^I'
+and the end of each line is marked with a trailing `$'.
+The current line is left at the last line printed.
+.TP
+\fBmap\fR[\fB!\fR] \fIlhs\fR \fIrhs\fR
+The
+.I map
+command is used to define macros for use in
+.I visual
+command mode.
+.I Lhs
+should be a single character, or the sequence \*(lq#n\*(rq, for n a digit,
+referring to function key \fIn\fR.  When this character or function key
+is typed in
+.I visual
+mode, it will be as though the corresponding \fIrhs\fR had been typed.
+On terminals without function keys, the user can type \*(lq#n\*(rq.
+If the `\fB!\fP' character follows the command name,
+the mapping is interpreted in input mode.
+See section 6.9 of the \*(lqIntroduction to Display Editing with Vi\*(rq
+for more details.
+.TP
+( \fB.\fR ) \fBmark\fR \fIx\fR
+Gives the specified line mark
+.I x,
+a single lower case letter.
+The
+.I x
+must be preceded by a blank or a tab.
+The addressing form `\'x' then addresses this line.
+The current line is not affected by this command.
+.TP
+( \fB.\fR , \fB.\fR ) \fBmove\fR \fIaddr\fR    abbr: \fBm\fR
+The
+.I move
+command repositions the specified lines to be after
+.I addr .
+The first of the moved lines becomes the current line.
+.TP
+\fBnext\fR     abbr: \fBn\fR
+The next file from the command line argument list is edited.
+.TP
+\fBn!\fR
+The variant suppresses warnings about the modifications to the buffer not
+having been written out, discarding (irretrievably) any changes which may
+have been made.
+.LP
+\fBn\fR \fIfilelist\fR
+.br
+\fBn\fR \fB+\fIcommand\fR \fIfilelist\fR
+.RS
+The specified
+.I filelist
+is expanded and the resulting list replaces the
+current argument list;
+the first file in the new list is then edited.
+If
+.I command
+is given (it must contain no spaces), then it is executed after editing the first such file.
+.RE
+.TP
+( \fB.\fR , \fB.\fR ) \fBnumber\fR \fIcount\fR \fIflags\fR     abbr: \fB#\fR or \fBnu\fR
+Prints each specified line preceded by its buffer line
+number.
+The current line is left at the last line printed.
+.LP
+( \fB.\fR ) \fBopen\fR \fIflags\fR     abbr: \fBo\fR
+.br
+( \fB.\fR ) \fBopen\fR /\fIpat\|\fR/ \fIflags\fR
+.RS
+Enters intraline editing \fIopen\fR mode at each addressed line.
+If
+.I pat
+is given,
+then the cursor will be placed initially at the beginning of the
+string matched by the pattern.
+To exit this mode use Q.
+See
+.I "An Introduction to Display Editing with Vi"
+for more details.
+.RE
+.TP
+\fBpreserve\fR
+The current editor buffer is saved as though the system had just crashed.
+This command is for use only in emergencies when a
+.I write
+command has resulted in an error.
+.TP
+( \fB.\fR , \fB.\fR )\|\fBprint\fR \fIcount\fR abbr: \fBp\fR or \fBP\fR
+Prints the specified lines
+with non-printing characters printed as control characters `^\fIx\fR\|';
+delete (octal 177) is represented as `^?'.
+The current line is left at the last line printed.
+.TP
+( \fB.\fR )\|\fBput\fR \fIbuffer\fR    abbr: \fBpu\fR
+Puts back
+previously
+.I deleted
+or
+.I yanked
+lines.
+Normally used with
+.I delete
+to effect movement of lines,
+or with
+.I yank
+to effect duplication of lines.
+If no
+.I buffer
+is specified, then the last
+.I deleted
+or
+.I yanked
+text is restored.
+But no modifying commands may intervene between the
+.I delete
+or
+.I yank
+and the
+.I put,
+nor may lines be moved between files without using a named buffer.
+By using a named buffer, text may be restored that was saved there at any
+previous time.
+.TP
+\fBquit\fR     abbr: \fBq\fR
+Causes 
+.I ex
+to terminate.
+No automatic write of the editor buffer to a file is performed.
+However,
+.I ex
+issues a warning message if the file has changed
+since the last
+.I write
+command was issued, and does not
+.I quit.
+\fIEx\fR
+will also issue a diagnostic if there are more files in the argument
+list.
+.FE
+Normally, the user will wish to save his changes, and he
+should give a \fIwrite\fR command;
+if he wishes to discard them, he should the \fBq!\fR command variant.
+.TP
+\fBq!\fR
+Quits from the editor, discarding changes to the buffer without complaint.
+.TP
+( \fB.\fR ) \fBread\fR \fIfile\fR      abbr: \fBr\fR
+Places a copy of the text of the given file in the
+editing buffer after the specified line.
+If no 
+.I file
+is given the current file name is used.
+The current file name is not changed unless there is none in which
+case
+.I file
+becomes the current name.
+The sensibility restrictions for the 
+.I edit
+command apply here also.
+If the file buffer is empty and there is no current name then
+.I ex
+treats this as an
+.I edit
+command.
+.IP
+Address `0' is legal for this command and causes the file to be read at
+the beginning of the buffer.
+Statistics are given as for the 
+.I edit
+command when the 
+.I read
+successfully terminates.
+After a
+.I read
+the current line is the last line read.
+Within
+.I open
+and
+.I visual
+the current line is set to the first line read rather than the last.
+.TP
+( \fB.\fR ) \fBread\fR  \fB!\fR\fIcommand\fR
+Reads the output of the command
+.I command
+into the buffer after the specified line.
+This is not a variant form of the command, rather a read
+specifying a
+.I command
+rather than a 
+.I filename;
+a blank or tab before the \fB!\fR is mandatory.
+.TP
+\fBrecover \fIfile\fR
+Recovers
+.I file
+from the system save area.
+Used after a accidental hangup of the phone
+or a system crash or
+.I preserve
+command.
+Except when
+.I preserve
+is used, the user will be notified by mail when a file is saved.
+.TP
+\fBrewind\fR   abbr: \fBrew\fR
+The argument list is rewound, and the first file in the list is edited.
+.TP
+\fBrew!\fR
+Rewinds the argument list discarding any changes made to the current buffer.
+.TP
+\fBset\fR \fIparameter\fR
+With no arguments, prints those options whose values have been
+changed from their defaults;
+with parameter
+.I all
+it prints all of the option values.
+.IP
+Giving an option name followed by a `?'
+causes the current value of that option to be printed.
+The `?' is unnecessary unless the option is Boolean valued.
+Boolean options are given values either by the form
+`set \fIoption\fR' to turn them on or
+`set no\fIoption\fR' to turn them off;
+string and numeric options are assigned via the form
+`set \fIoption\fR=value'.
+.IP
+More than one parameter may be given to 
+.I set \|;
+they are interpreted left-to-right.
+.IP
+A list of options can be found below.
+.TP
+\fBshell\fR    abbr: \fBsh\fR
+A new shell is created.
+When it terminates, editing resumes.
+.TP
+\fBsource\fR \fIfile\fR        abbr: \fBso\fR
+Reads and executes commands from the specified file.
+.I Source
+commands may be nested.
+.LP
+.ad l
+(\ \fB.\fR\ ,\ \fB.\fR\ )\ \fBsubstitute\fR\ /\fIpat\fR\|/\fIrepl\fR\|/\ \fIoptions\fR\ \fIcount\fR\ \fIflags\fR
+.RS
+abbr: \fBs\fR
+.br
+.ad b
+On each specified line, the first instance of pattern
+.I pat
+is replaced by replacement pattern
+.I repl.
+If the
+.I global
+indicator option character `g'
+appears, then all instances are substituted;
+if the
+.I confirm
+indication character `c' appears,
+then before each substitution the line to be substituted
+is typed with the string to be substituted marked
+with `^' characters.
+By typing an `y' one can cause the substitution to be performed,
+any other input causes no change to take place.
+After a
+.I substitute
+the current line is the last line substituted.
+.PP
+Lines may be split by substituting
+new-line characters into them.
+The newline in
+.I repl
+must be escaped by preceding it with a `\e'.
+Other metacharacters available in
+.I pat
+and
+.I repl
+are described below.
+.RE
+.TP
+.B stop
+Suspends the editor, returning control to the top level shell.
+If
+.I autowrite
+is set and there are unsaved changes,
+a write is done first unless the form
+.B stop !
+is used.
+This commands is only available where supported by the teletype driver,
+shell and operating system.
+.TP
+( \fB.\fR , \fB.\fR ) \fBsubstitute\fR \fIoptions\fR \fIcount\fR \fIflags\fR   abbr: \fBs\fR
+If
+.I pat
+and
+.I repl
+are omitted, then the last substitution is repeated.
+This is a synonym for the
+.B &
+command.
+.TP
+( \fB.\fR , \fB.\fR ) \fBt\fR \fIaddr\fR \fIflags\fR
+The
+.I t
+command is a synonym for 
+.I copy .
+.TP
+\fBta\fR \fItag\fR
+The focus of editing switches to the location of
+.I tag,
+switching to a different line in the current file where it is defined,
+or if necessary to another file.
+.IP
+The tags file is normally created by a program such as
+.I ctags,
+and consists of a number of lines with three fields separated by blanks
+or tabs.  The first field gives the name of the tag,
+the second the name of the file where the tag resides, and the third
+gives an addressing form which can be used by the editor to find the tag;
+this field is usually a contextual scan using `/\fIpat\fR/' to be immune
+to minor changes in the file.  Such scans are always performed as if
+.I nomagic
+was set.
+.IP
+The tag names in the tags file must be sorted alphabetically.
+.TP
+\fBunabbreviate\fR \fIword\fP  abbr: \fBuna\fP
+Delete
+.I word
+from the list of abbreviations.
+.TP
+\fBundo\fR     abbr: \fBu\fR
+Reverses the changes made in the buffer by the last
+buffer editing command.
+Note that
+.I global
+commands are considered a single command for the purpose of 
+.I undo
+(as are
+.I open
+and
+.I visual.)
+Also, the commands
+.I write
+and
+.I edit
+which interact with the
+file system cannot be undone.
+.I Undo
+is its own inverse.
+.IP
+.I Undo
+always marks the previous value of the current line `\fB.\fR'
+as `\'\''.
+After an
+.I undo
+the current line is the first line restored
+or the line before the first line deleted if no lines were restored.
+For commands with more global effect
+such as
+.I global
+and
+.I visual
+the current line regains it's pre-command value after an
+.I undo.
+.TP
+\fBunmap\fR[\fB!\fR] \fIlhs\fR
+The macro expansion associated by
+.I map
+for
+.I lhs
+is removed.
+.TP
+( 1 , $ ) \fBv\fR /\fIpat\fR\|/ \fIcmds\fR
+A synonym for the
+.I global
+command variant \fBg!\fR, running the specified \fIcmds\fR on each
+line which does not match \fIpat\fR.
+.TP
+\fBversion\fR  abbr: \fBve\fR
+Prints the current version number of the editor
+as well as the date the editor was last changed.
+.TP
+( \fB.\fR ) \fBvisual\fR \fItype\fR \fIcount\fR \fIflags\fR    abbr: \fBvi\fR
+Enters visual mode at the specified line.
+.I Type
+is optional and may be `\-' , `^' or `\fB.\fR'
+as in the
+.I z
+command to specify the placement of the specified line on the screen.
+By default, if
+.I type
+is omitted, the specified line is placed as the first on the screen.
+A
+.I count
+specifies an initial window size; the default is the value of the option
+.I window.
+See the document
+.I "An Introduction to Display Editing with Vi"
+for more details.
+To exit this mode, type Q.
+.LP
+\fBvisual\fP file
+.br
+\fBvisual\fP +\fIn\fP file
+.RS
+From visual mode,
+this command is the same as edit.
+.RE
+.TP
+( 1 , $ ) \fBwrite\fR \fIfile\fR       abbr: \fBw\fR
+Writes changes made back to \fIfile\fR, printing the number of lines and
+characters written.
+Normally \fIfile\fR is omitted and the text goes back where it came from.
+If a \fIfile\fR is specified, then text will be written to that file.
+If the file does not exist it is created.
+The current file name is changed only if there is no current file
+name; the current line is never changed.
+.IP
+If an error occurs while writing the current and
+.I edited
+file, the editor
+considers that there has been \*(lqNo write since last change\*(rq
+even if the buffer had not previously been modified.
+.TP
+( 1 , $ ) \fBwrite>>\fR \fIfile\fR     abbr: \fBw>>\fR
+Writes the buffer contents at the end of
+an existing file.
+.IP
+.TP
+\fBw!\fR \fIname\fR
+Overrides the checking of the normal \fIwrite\fR command,
+and will write to any file which the system permits.
+.TP
+( 1 , $ ) \fBw\fR  \fB!\fR\fIcommand\fR
+Writes the specified lines into 
+.I command.
+Note the difference between \fBw!\fR which overrides checks and
+\fBw\ \ !\fR which writes to a command.
+.TP
+\fBwq\fR \fIname\fR
+Like a \fIwrite\fR and then a \fIquit\fR command.
+.TP
+\fBwq!\fR \fIname\fR
+The variant overrides checking on the sensibility of the
+.I write
+command, as \fBw!\fR does.
+.TP
+\fBxit\fP \fIname\fR
+If any changes have been made
+and not written to any file,
+writes the buffer out.
+Then, in any case, quits.
+.TP
+( \fB.\fR , \fB.\fR )\|\fByank\fR \fIbuffer\fR \fIcount\fR     abbr: \fBya\fR
+Places the specified lines in the named
+.I buffer,
+for later retrieval via
+.I put.
+If no buffer name is specified, the lines go to a more volatile place;
+see the \fIput\fR command description.
+.TP
+( \fB.+1\fR ) \fBz\fR \fIcount\fR
+Print the next \fIcount\fR lines, default \fIwindow\fR.
+.TP
+( \fB.\fR ) \fBz\fR \fItype\fR \fIcount\fR
+Prints a window of text with the specified line at the top.
+If \fItype\fR is `\-' the line is placed at the bottom; a `\fB.\fR' causes
+the line to be placed in the center.
+A count gives the number of lines to be displayed rather than
+double the number specified by the \fIscroll\fR option.
+On a \s-1CRT\s0 the screen is cleared before display begins unless a
+count which is less than the screen size is given.
+The current line is left at the last line printed.
+Forms `z=' and `z^' also exist; `z=' places the current line in the
+center, surrounds it with lines of `\-' characters and leaves the current
+line at this line.  The form `z^' prints the window before `z\-'
+would.  The characters `+', `^' and `\-' may be repeated for cumulative
+effect.
+.TP
+\fB!\fR \fIcommand\fR\fR
+The remainder of the line after the `!' character is sent to a shell
+to be executed.
+Within the text of
+.I command
+the characters 
+`%' and `#' are expanded as in filenames and the character
+`!' is replaced with the text of the previous command.
+Thus, in particular,
+`!!' repeats the last such shell escape.
+If any such expansion is performed, the expanded line will be echoed.
+The current line is unchanged by this command.
+.IP
+If there has been \*(lq[No\ write]\*(rq of the buffer contents since the last
+change to the editing buffer, then a diagnostic will be printed
+before the command is executed as a warning.
+A single `!' is printed when the command completes.
+.TP
+( \fIaddr\fR , \fIaddr\fR ) \fB!\fR \fIcommand\fR\fR
+Takes the specified address range and supplies it as
+standard input to
+.I command;
+the resulting output then replaces the input lines.
+.TP
+( $ ) \fB=\fR
+Prints the line number of the
+addressed line.
+The current line is unchanged.
+.LP
+( \fB.\fR , \fB.\fR ) \fB>\fR \fIcount\fR \fIflags\fR
+.br
+( \fB.\fR , \fB.\fR ) \fB<\fR \fIcount\fR \fIflags\fR
+.RS
+Perform intelligent shifting on the specified lines;
+\fB<\fR shifts left and \fB>\fR shift right.
+The quantity of shift is determined by the
+.I shiftwidth
+option and the repetition of the specification character.
+Only white space (blanks and tabs) is shifted;
+no non-white characters are discarded in a left-shift.
+The current line becomes the last line which changed due to the
+shifting.
+.RE
+.TP
+\fB^D\fR
+An end-of-file from a terminal input scrolls through the file.
+The
+.I scroll
+option specifies the size of the scroll, normally a half screen of text.
+.LP
+( \fB.\fR+1 , \fB.\fR+1 )
+.br
+( \fB.\fR+1 , \fB.\fR+1 ) |
+.RS
+An address alone causes the addressed lines to be printed.
+A blank line prints the next line in the file.
+.RE
+.TP
+( \fB.\fR , \fB.\fR ) \fB&\fR \fIoptions\fR \fIcount\fR \fIflags\fR
+Repeats the previous
+.I substitute
+command.
+.TP
+( \fB.\fR , \fB.\fR ) \fB\s+2~\s0\fR \fIoptions\fR \fIcount\fR \fIflags\fR
+Replaces the previous regular expression with the previous
+replacement pattern from a substitution.
+.SS "Regular expressions"
+.PP
+A regular expression specifies a set of strings of characters.
+A member of this set of strings is said to be
+.I matched
+by the regular expression.
+.I Ex
+remembers two previous regular expressions:
+the previous regular expression used in a
+.I substitute
+command
+and the previous regular expression used elsewhere
+(referred to as the previous \fIscanning\fR regular expression.)
+The previous regular expression
+can always be referred to by a null \fIre\fR, e.g. `//' or `??'.
+.PP
+The following basic constructs are used to construct
+.I magic
+mode regular expressions.
+.IP \fIchar\fR 15
+An ordinary character matches itself.
+The characters `\fB^\fR' at the beginning of a line,
+`\fB$\fR' at the end of line, 
+`\fB*\fR' as any character other than the first,
+`\fB.\fR', `\fB\e\fR', `\fB[\fR',
+and `\s+2\fB~\fR\s0' are not ordinary characters and
+must be escaped (preceded) by `\fB\e\fR' to be treated as such.
+.IP \fB^\fR
+At the beginning of a pattern
+forces the match to succeed only at the beginning of a line.
+.IP \fB$\fR
+At the end of a regular expression forces the match to
+succeed only at the end of the line.
+.IP \&\fB.\fR
+Matches any single character except
+the new-line character.
+.IP \fB\e<\fR 
+Forces the match
+to occur only at the beginning of a \*(lqvariable\*(rq or \*(lqword\*(rq;
+that is, either at the beginning of a line, or just before
+a letter, digit, or underline and after a character not one of
+these.
+.IP \fB\e>\fR
+Similar to `\e<', but matching the end of a \*(lqvariable\*(rq
+or \*(lqword\*(rq, i.e. either the end of the line or before character
+which is neither a letter, nor a digit, nor the underline character.
+.IP \fB[\fIstring\fR\fB]\fR
+Matches any (single) character in the class defined by
+.I string.
+Most characters in
+.I string
+define themselves.
+.br
+\ \ A pair of characters separated by `\fB\-\fR' in
+.I string
+defines the set of characters collating between the specified lower and upper
+bounds, thus `[a\-z]' as a regular expression matches
+any (single)
+.SM ASCII
+lower-case letter.
+.br
+\ \ If the sequence `\fB[:\fIclass\fB:]\fR' appears in
+.IR string ,
+where class is one of
+.RB ` alnum ',
+.RB ` alpha ',
+.RB ` blank ',
+.RB ` cntrl ',
+.RB ` digit ',
+.RB ` graph ',
+.RB ` lower ',
+.RB ` print ',
+.RB ` punct ',
+.RB ` space ',
+.RB ` upper ',
+.RB ` xdigit ',
+or a locale-specific character class,
+all characters that belong to the given class are matched.
+Thus `[[:lower:]]' matches any lower-case letter,
+possibly including characters beyond the scope of
+.SM ASCII.
+.br
+\ \ If the first character of
+.I string
+is an `\fB^\fR' then the construct
+matches those characters which it otherwise would not;
+thus `[^a\-z]' matches anything but an
+.SM ASCII
+lower-case letter
+(and of course a newline).
+.br
+\ \ Backslash `\e' is interpreted as an escape character.
+To place a `\e' character in
+.IR string ,
+write it twice: `\e\e';
+to place any of the characters
+`^', `[', or `\-' in
+.IR string ,
+you escape them with a preceding `\e'.
+.br
+\ \ Characters also lose their special meaning by position:
+`^' is an ordinary character unless immediately
+following the initial `[',
+`]' is an ordinary character if immediately
+following the initial `[' (or `^', if present),
+and `\-' is an ordinary character if placed immediately
+behind `[' or `^', or before ']'.
+.PP
+The concatenation of two regular expressions matches the leftmost and
+then longest string
+which can be divided with the first piece matching the first regular
+expression and the second piece matching the second.
+.PP
+A regular expression may be enclosed between the sequences
+`\fB\e(\fR' and `\fB\e)\fR',
+which matches whatever the enclosed expression matches.
+.PP
+Any of the (single character matching) regular expressions mentioned above
+or a regular expression surrounded by `\e(' and '\e)'
+may be followed by the character `\fB*\fR' to form a regular expression 
+which matches any number of adjacent occurrences (including 0) of characters
+matched by the regular expression it follows.
+.PP
+A single character regular expression
+or a regular expression surrounded by `\e(' and '\e)'
+followed by `\fB\e{\fIm\fB,\fIn\fB\e}\fR'
+matches a sequence of \fIm\fP through \fIn\fP occurences, inclusive,
+of the single character expression.
+The values of \fIm\fP and \fIn\fP
+must be non-negative and smaller than 255.
+The form `\fB\e{\fIm\fB\e}\fR' matches exactly \fIm\fP occurences,
+`\fB\e{\fIm\fB,\e}\fR' matches at least \fIm\fP occurences.
+.PP
+The character `\s+2\fB~\fR\s0' may be used in a regular expression,
+and matches the text which defined the replacement part
+of the last
+.I substitute
+command.
+.PP
+The sequence `\fB\e\fIn\fR' matches the text that was matched by the
+\fIn\fR-th regular subexpression enclosed between `\e(' and `\e)'
+earlier in the expression.
+.SS "Substitute replacement patterns"
+.PP
+The basic metacharacters for the replacement pattern are
+`\fB&\fR', `\fB~\fR', and `\fB#\fR'; the first two of them are
+given as `\fB\e&\fR' and `\fB\e~\fR' when
+.I nomagic
+is set.
+Each instance of `\fB&\fR' is replaced by the characters
+which the regular expression matched.
+The metacharacter `\fB~\fR' stands, in the replacement pattern,
+for the defining text of the previous replacement pattern.
+If the entire replacement pattern is `\fB#\fR',
+the defining text of the previous replacement pattern is used.
+.PP
+Other metasequences possible in the replacement pattern
+are always introduced by the escaping character `\fB\e\fR'.
+The sequence `\fB\e\fIn\fR' is replaced by the text matched
+by the \fIn\fR-th regular subexpression enclosed between
+`\e(' and `\e)'.
+When nested, parenthesized subexpressions are present,
+\fIn\fR is determined by counting occurrences of `\e(' starting from the left.
+The sequences `\fB\eu\fR' and `\fB\el\fR'
+cause the immediately following character in
+the replacement to be converted to upper- or lower-case respectively
+if this character is a letter.
+The sequences `\fB\eU\fR' and `\fB\eL\fR'
+turn such conversion on,
+either until `\fB\eE\fR' or `\fB\ee\fR' is encountered,
+or until the end of the replacement pattern.
+.SS "Option descriptions"
+.PP
+.TP
+\fBautoindent\fR, \fBai\fR     default: noai
+Can be used to ease the preparation of structured program text.
+At the beginning of each
+.I append ,
+.I change
+or
+.I insert
+command
+or when a new line is
+.I opened
+or created by an
+.I append ,
+.I change ,
+.I insert ,
+or
+.I substitute
+operation within
+.I open
+or
+.I visual
+mode,
+.I ex
+looks at the line being appended after,
+the first line changed
+or the line inserted before and calculates the amount of white space
+at the start of the line.
+It then aligns the cursor at the level of indentation so determined.
+.IP
+If the user then types lines of text in,
+they will continue to be justified at the displayed indenting level.
+If more white space is typed at the beginning of a line,
+the following line will start aligned with the first non-white character
+of the previous line.
+To back the cursor up to the preceding tab stop one can hit
+\fB^D\fR.
+The tab stops going backwards are defined at multiples of the
+.I shiftwidth
+option.
+The user
+.I cannot
+backspace over the indent,
+except by sending an end-of-file with a \fB^D\fR.
+.IP
+Specially processed in this mode is a line with no characters added
+to it, which turns into a completely blank line (the white
+space provided for the
+.I autoindent
+is discarded.)
+Also specially processed in this mode are lines beginning with
+an `^' and immediately followed by a \fB^D\fR.
+This causes the input to be repositioned at the beginning of the line,
+but retaining the previous indent for the next line.
+Similarly, a `0' followed by a \fB^D\fR
+repositions at the beginning but without
+retaining the previous indent.
+.IP
+.I Autoindent
+doesn't happen in
+.I global
+commands or when the input is not a terminal.
+.TP
+\fBautoprint\fR, \fBap\fR      default: ap
+Causes the current line to be printed after each
+.I delete ,
+.I copy ,
+.I join ,
+.I move ,
+.I substitute ,
+.I t ,
+.I undo
+or
+shift command.
+This has the same effect as supplying a trailing `p'
+to each such command.
+.I Autoprint
+is suppressed in globals,
+and only applies to the last of many commands on a line.
+.TP
+\fBautowrite\fR, \fBaw\fR      default: noaw
+Causes the contents of the buffer to be written to the current file
+if the user has modified it and gives a
+.I next,
+.I rewind,
+.I stop,
+.I tag,
+or
+.I !
+command, or a \fB^^\fR (switch files) or \fB^]\fR (tag goto) command
+in
+.I visual.
+Note, that the
+.I edit
+and
+.I ex
+commands do
+.B not
+autowrite.
+In each case, there is an equivalent way of switching when autowrite
+is set to avoid the
+.I autowrite
+(\fIedit\fR
+for
+.I next ,
+.I rewind!
+for .I rewind ,
+.I stop!
+for
+.I stop ,
+.I tag!
+for
+.I tag ,
+.I shell
+for
+.I ! ,
+and
+\fB:e\ #\fR and a \fB:ta!\fR command from within
+.I visual).
+.TP
+\fBbeautify\fR, \fBbf\fR       default: nobeautify
+Causes all control characters except tab, newline and form-feed
+to be discarded from the input.
+A complaint is registered the first time a
+backspace character is discarded.
+.I Beautify
+does not apply to command input.
+.TP
+\fBdirectory\fR, \fBdir\fR     default: dir=/tmp
+Specifies the directory in which
+.I ex
+places its buffer file.
+If this directory in not
+writable, then the editor will exit abruptly when it fails to be
+able to create its buffer there.
+.TP
+\fBedcompatible\fR     default: noedcompatible
+Causes the presence of absence of
+.B g
+and
+.B c
+suffixes on substitute commands to be remembered, and to be toggled
+by repeating the suffices.  The suffix
+.B r
+makes the substitution be as in the
+.I ~
+command, instead of like
+.I &.
+.TP
+\fBerrorbells\fR, \fBeb\fR     default: noeb
+Error messages are preceded by a bell.
+Bell ringing in
+.I open
+and
+.I visual
+on errors is not suppressed by setting
+.I noeb.
+If possible the editor always places the error message in a standout mode of the
+terminal (such as inverse video) instead of ringing the bell.
+.TP
+\fBexrc\fR                     default: noexrc
+If set, the current directory is searched for a
+.I .exrc
+file on startup.
+If this file is found,
+its content is treated as
+.I ex
+commands and executed immediately after the contents of
+.I $HOME/.exrc
+on startup.
+.TP
+\fBflash\fR, \fBfl\fR  default: flash
+If the terminal provides the \*(lqvisual bell\*(rq capability,
+ex will use it instead of the audible bell if
+.I flash
+is set.
+.TP
+\fBhardtabs\fR, \fBht\fR       default: ht=8
+Gives the boundaries on which terminal hardware tabs are set (or
+on which the system expands tabs).
+.TP
+\fBignorecase\fR, \fBic\fR     default: noic
+All upper case characters in the text are mapped to lower case in regular
+expression matching.
+In addition, all upper case characters in regular expressions are mapped
+to lower case except in character class specifications.
+.TP
+\fBlisp\fR     default: nolisp
+\fIAutoindent\fR indents appropriately for
+.I lisp
+code, and the \fB( ) { } [[\fR and \fB]]\fR commands in
+.I open
+and
+.I visual
+are modified to have meaning for \fIlisp\fR.
+.TP
+\fBlist\fR     default: nolist
+All printed lines will be displayed (more) unambiguously,
+showing tabs and end-of-lines as in the
+.I list
+command.
+.TP
+\fBmagic\fR    default: magic for \fIex\fR and \fIvi\fR, \fINomagic\fR for \fIedit\fR.
+If
+.I nomagic
+is set, the number of regular expression metacharacters is greatly reduced,
+with only `^' and `$' having special effects.
+In addition the metacharacters
+`~'
+and
+`&'
+of the replacement pattern are treated as normal characters.
+All the normal metacharacters may be made
+.I magic
+when
+.I nomagic
+is set by preceding them with a `\e'.
+.TP
+\fBmesg\fR     default: mesg
+Causes write permission to be turned off to the terminal
+while the user is in visual mode, if
+.I nomesg
+is set.
+.TP
+\fBmodelines, ml\fR    default: nomodelines
+If
+.I modelines
+is set, then the first 5 lines and the last five lines of the file
+will be checked for ex command lines and the comands issued.
+To be recognized as a command line, the line must have the string
+.B ex:
+or
+.B vi:
+in it.
+.\" preceeded by a tab or a space.
+This string may be anywhere in the line and anything after the 
+.I :
+is interpeted as editor commands.  This option defaults to off because
+of unexpected behavior when editting files such as
+.I /etc/passwd.
+.TP
+\fBnumber, nu\fR       default: nonumber
+Causes all output lines to be printed with their
+line numbers.
+In addition each input line will be prompted for by supplying the line number
+it will have.
+.TP
+\fBopen\fR     default: open
+If \fInoopen\fR, the commands
+.I open
+and
+.I visual
+are not permitted.
+.\"This is set for
+.\".I edit
+.\"to prevent confusion resulting from accidental entry to 
+.\"open or visual mode.
+.TP
+\fBoptimize, opt\fR    default: optimize
+Throughput of text is expedited by setting the terminal
+to not do automatic carriage returns
+when printing more than one (logical) line of output,
+greatly speeding output on terminals without addressable
+cursors when text with leading white space is printed.
+.TP
+\fBparagraphs,\ para\fR        default: para=IPLPPPQPP\0LIbp
+Specifies the paragraphs for the \fB{\fR and \fB}\fR operations in
+.I open
+and 
+.I visual.
+The pairs of characters in the option's value are the names
+of the macros which start paragraphs.
+.TP
+\fBprompt\fR   default: prompt
+Command mode input is prompted for with a `:'.
+.TP
+\fBredraw\fR   default: noredraw
+The editor simulates (using great amounts of output), an intelligent
+terminal on a dumb terminal (e.g. during insertions in
+.I visual
+the characters to the right of the cursor position are refreshed
+as each input character is typed.)
+Useful only at very high speed.
+.TP
+\fBremap\fP    default: remap
+If on, macros are repeatedly tried until they are unchanged.
+For example, if
+.B o
+is mapped to
+.B O ,
+and
+.B O
+is mapped to
+.B I ,
+then if
+.I remap
+is set,
+.B o
+will map to
+.B I ,
+but if
+.I noremap
+is set, it will map to
+.B O .
+.TP
+\fBreport\fR   default: report=5, 2 for \fIedit\fR.
+Specifies a threshold for feedback from commands.
+Any command which modifies more than the specified number of lines
+will provide feedback as to the scope of its changes.
+For commands such as
+.I global ,
+.I open ,
+.I undo ,
+and
+.I visual
+which have potentially more far reaching scope,
+the net change in the number of lines in the buffer is
+presented at the end of the command, subject to this same threshold.
+Thus notification is suppressed during a
+.I global
+command on the individual commands performed.
+.TP
+\fBscroll\fR   default: scroll=\(12 window
+Determines the number of logical lines scrolled when an end-of-file
+is received from a terminal input in command mode,
+and the number of lines printed by a command mode
+.I z
+command (double the value of
+.I scroll ).
+.TP
+\fBsections\fR default: sections=SHNHH\0HU
+Specifies the section macros for the \fB[[\fR and \fB]]\fR operations
+in
+.I open
+and
+.I visual.
+The pairs of characters in the options's value are the names
+of the macros which start paragraphs.
+.TP
+\fBshell\fR, \fBsh\fR  default: sh=/bin/sh
+Gives the path name of the shell forked for 
+the shell escape command `!', and by the
+.I shell
+command.
+The default is taken from SHELL in the environment, if present.
+.TP
+\fBshiftwidth\fR, \fBsw\fR     default: sw=8
+Gives the width a software tab stop,
+used in reverse tabbing with \fB^D\fR when using
+.I autoindent
+to append text,
+and by the shift commands.
+.TP
+\fBshowmatch, sm\fR    default: nosm
+In
+.I open
+and
+.I visual
+mode, when a \fB)\fR or \fB}\fR is typed, move the cursor to the matching
+\fB(\fR or \fB{\fR for one second if this matching character is on the
+screen.  Extremely useful with
+.I lisp.
+.TP
+\fBshowmode, smd\fR    default: nosmd
+In
+.I visual
+mode, show a description of the current editing mode
+in the window's lower right corner.
+.TP
+\fBslowopen, slow\fR   terminal dependent
+Affects the display algorithm used in
+.I visual
+mode, holding off display updating during input of new text to improve
+throughput when the terminal in use is both slow and unintelligent.
+See
+.I "An Introduction to Display Editing with Vi"
+for more details.
+.TP
+\fBtabstop,\ ts\fR     default: ts=8
+The editor expands tabs in the input file to be on
+.I tabstop
+boundaries for the purposes of display.
+.TP
+\fBtaglength,\ tl\fR   default: tl=0
+Tags are not significant beyond this many characters.
+A value of zero (the default) means that all characters are significant.
+.TP
+\fBtags\fR     default: tags=tags /usr/lib/tags
+A path of files to be used as tag files for the
+.I tag
+command.
+A requested tag is searched for in the specified files, sequentially.
+By default, files called
+.B tags
+are searched for in the current directory and in /usr/lib
+(a master file for the entire system).
+.TP
+\fBterm\fR     from environment TERM
+The terminal type of the output device.
+.TP
+\fBterse\fR    default: noterse
+Shorter error diagnostics are produced for the experienced user.
+.TP
+\fBwarn\fR     default: warn
+Warn if there has been `[No write since last change]' before a `!'
+command escape.
+.TP
+\fBwindow\fR   default: window=speed dependent
+The number of lines in a text window in the
+.I visual
+command.
+The default is 8 at slow speeds (600 baud or less),
+16 at medium speed (1200 baud),
+and the full screen (minus one line) at higher speeds.
+.TP
+\fBw300,\ w1200,\ w9600\fR
+These are not true options but set
+.B window
+only if the speed is slow (300), medium (1200), or high (9600),
+respectively.
+They are suitable for an EXINIT
+and make it easy to change the 8/16/full screen rule.
+.TP
+\fBwrapscan\fR, \fBws\fR       default: ws
+Searches using the regular expressions in addressing
+will wrap around past the end of the file.
+.TP
+\fBwrapmargin\fR, \fBwm\fR     default: wm=0
+Defines a margin for automatic wrapover of text during input in
+.I open
+and
+.I visual
+modes.  See
+.I "An Introduction to Text Editing with Vi"
+for details.
+.TP
+\fBwriteany\fR, \fBwa\fR       default: nowa
+.IP
+Inhibit the checks normally made before
+.I write
+commands, allowing a write to any file which the system protection
+mechanism will allow.
+.SH "ENVIRONMENT VARIABLES"
+.PP
+The following environment variables affect the behaviour of ex:
+.TP
+.B COLUMNS
+Overrides the system-supplied number of terminal columns.
+.TP
+.B EXINIT
+Contains commands to execute at editor startup.
+If this variable is present, the
+.I .exrc
+file in the user's home directory is ignored.
+.TP
+.B HOME
+Used to locate the editor startup file.
+.TP
+.BR LANG ", " LC_ALL
+See
+.IR locale (7).
+.TP
+.B LC_CTYPE
+Determines the mapping of bytes to characters,
+types of characters,
+case conversion
+and composition of character classes in regular expressions.
+.TP
+.B LC_MESSAGES
+Sets the language used for diagnostic and informal messages.
+.TP
+.B LINES
+Overrides the system-supplied number of terminal lines.
+.TP
+.B NLSPATH
+See
+.IR catopen (3).
+.TP
+.B SHELL
+The program file used to execute external commands.
+.TP
+.B TERM
+Determines the terminal type.
+.SH FILES
+.TP
+.B /usr/libexec/expreserve
+preserve command
+.TP
+.B /usr/libexec/exrecover
+recover command
+.TP
+.B /etc/termcap
+describes capabilities of terminals
+.TP
+.B $HOME/.exrc
+editor startup file
+.TP
+.B /var/tmp/Ex\fInnnnnnnnnn\fP
+editor temporary
+.TP
+.B /var/tmp/Rx\fInnnnnnnnnn\fP
+named buffer temporary
+.TP
+.B /var/preserve
+preservation directory
+.SH DOCUMENTATION
+The document
+.I "Edit: A tutorial"
+(USD:14) provides a comprehensive introduction to
+.I edit
+assuming no previous knowledge of computers or the 
+.SM UNIX
+system.
+.PP
+The
+.I "Ex Reference Manual \(en Version 3.7"
+(USD:16)
+is a comprehensive and complete manual for the command mode features
+of
+.I ex.
+.\"but one cannot learn to use the editor by reading it.
+The
+.SM \fIUSAGE\fP
+section of this page is taken from the manual.
+For an introduction to
+more advanced forms of editing using the command mode of
+.I ex
+see the editing documents written by Brian Kernighan for the editor
+.I ed;
+the material in the introductory and advanced documents works also with
+.I ex.
+.PP
+.I "An Introduction to Display Editing with Vi"
+(USD:15)
+introduces the display editor
+.I vi
+and provides reference material on
+.I vi.
+(This reference now forms the
+.IR vi (1)
+manual page).
+In addition, the
+.I "Vi Quick Reference"
+card summarizes the commands
+of
+.I vi
+in a useful, functional way, and is useful with the
+.I Introduction.
+.SH SEE ALSO
+awk(1),
+ed(1),
+grep(1),
+sed(1),
+grep(1),
+vi(1),
+catopen(3),
+termcap(5),
+environ(7),
+locale(7),
+regex(7)
+.SH AUTHOR
+Originally written by William Joy.
+.PP
+Mark Horton has maintained the editor since version 2.7, adding macros,
+support for many unusual terminals,
+and other features such as word abbreviation mode.
+.PP
+This version incorporates changes by Gunnar Ritter.
+.SH NOTES
+.I Undo
+never clears the buffer modified condition.
+.PP
+The
+.I z
+command prints a number of logical rather than physical lines.
+More than a screen full of output may result if long lines are present.
+.PP
+File input/output errors don't print a name if the command line \fB`\-'\fR
+option is used.
+.\".PP
+.\"There is no easy way to do a single scan ignoring case.
+.PP
+The editor does not warn if text is placed in named buffers and not used
+before exiting the editor.
+.PP
+Null (00) characters are converted to 0200 characters
+when reading input files,
+and cannot appear in resultant files.
+.PP
+LC_COLLATE locales are ignored;
+collating symbols `[.c.]'
+and equivalence classes `[=c=]'
+in bracket expressions are recognized but useless
+since `c' is restricted to a single character
+and is the only character matched;
+range expressions `[a\-m]' are always evaluated in byte order.
diff --git a/ex.c b/ex.c
new file mode 100644 (file)
index 0000000..7461788
--- /dev/null
+++ b/ex.c
@@ -0,0 +1,678 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+char *copyright =
+"@(#) Copyright (c) 1980 Regents of the University of California.\n\
+ All rights reserved.\n";
+
+static char sccsid[] = "@(#)ex.c       1.36 (gritter) 2/13/05";
+#endif /* DOSCCS */
+#endif /* !lint */
+
+/* from ex.c   7.5.1.1 (Berkeley) 8/12/86 */
+
+#include "ex.h"
+#include "ex_argv.h"
+#include "ex_temp.h"
+#include "ex_tty.h"
+
+#ifdef TRACE
+char   tttrace[]       = { '/','d','e','v','/','t','t','y','x','x',0 };
+#endif
+
+/*
+ * The code for ex is divided as follows:
+ *
+ * ex.c                        Entry point and routines handling interrupt, hangup
+ *                     signals; initialization code.
+ *
+ * ex_addr.c           Address parsing routines for command mode decoding.
+ *                     Routines to set and check address ranges on commands.
+ *
+ * ex_cmds.c           Command mode command decoding.
+ *
+ * ex_cmds2.c          Subroutines for command decoding and processing of
+ *                     file names in the argument list.  Routines to print
+ *                     messages and reset state when errors occur.
+ *
+ * ex_cmdsub.c         Subroutines which implement command mode functions
+ *                     such as append, delete, join.
+ *
+ * ex_data.c           Initialization of options.
+ *
+ * ex_get.c            Command mode input routines.
+ *
+ * ex_io.c             General input/output processing: file i/o, unix
+ *                     escapes, filtering, source commands, preserving
+ *                     and recovering.
+ *
+ * ex_put.c            Terminal driving and optimizing routines for low-level
+ *                     output (cursor-positioning); output line formatting
+ *                     routines.
+ *
+ * ex_re.c             Global commands, substitute, regular expression
+ *                     compilation and execution.
+ *
+ * ex_set.c            The set command.
+ *
+ * ex_subr.c           Loads of miscellaneous subroutines.
+ *
+ * ex_temp.c           Editor buffer routines for main buffer and also
+ *                     for named buffers (Q registers if you will.)
+ *
+ * ex_tty.c            Terminal dependent initializations from termcap
+ *                     data base, grabbing of tty modes (at beginning
+ *                     and after escapes).
+ *
+ * ex_unix.c           Routines for the ! command and its variations.
+ *
+ * ex_v*.c             Visual/open mode routines... see ex_v.c for a
+ *                     guide to the overall organization.
+ */
+
+static char *progname;
+
+void 
+erropen(void)
+{
+       close(1);
+       dup(2);
+}
+
+void 
+usage(void)
+{
+       printf(catgets(catd, 1, 1, "\
+Usage: %s [- | -s] [-l] [-L] [-R] [-r [file]] [-t tag]\n\
+       [-v] [-V] [-w size] [+cmd | -c cmd] file...\n"),
+               progname);
+       flush();
+       exitex(1);
+}
+
+void
+needarg(int c)
+{
+       erropen();
+       printf(catgets(catd, 1, 2,
+               "%s: option requires an argument -- %c\n"), progname, c);
+       usage();
+}
+
+void
+invopt(int c)
+{
+       erropen();
+       printf(catgets(catd, 1, 3, "%s: illegal option -- %c\n"), progname, c);
+       usage();
+}
+
+/*
+ * Return last component of unix path name p.
+ */
+char *
+tailpath(register char *p)
+{
+       register char *r;
+
+       for (r=p; *p; p++)
+               if (*p == '/')
+                       r = p+1;
+       return(r);
+}
+
+/*
+ * Check ownership of file.  Return nonzero if it exists and is owned by the
+ * user or the option sourceany is used
+ */
+int 
+iownit(char *file)
+{
+       struct stat sb;
+
+       if (*file == '.' && value(EXRC) == 0)
+               return 0;
+       if (stat(file, &sb))
+               return 0;
+       if (value(SOURCEANY))
+               return 1;
+       if (sb.st_uid != getuid())
+               return 0;
+       if (sb.st_mode & (S_IWOTH | S_IWGRP))
+               return 0;
+       return 1;
+}
+
+shand 
+setsig(int signum, shand handler)
+{
+       struct sigaction nact, oact;
+
+       nact.sa_handler = handler;
+       sigemptyset(&nact.sa_mask);
+       nact.sa_flags = 0;
+       if (signum == SIGALRM) {
+#ifdef SA_INTERRUPT
+               nact.sa_flags |= SA_INTERRUPT;
+#endif
+       /*EMPTY*/ ;
+       } else {
+#ifdef SA_RESTART
+               nact.sa_flags |= SA_RESTART;
+#endif
+       /*EMPTY*/ ;
+       }
+       if (sigaction(signum, &nact, &oact) != 0)
+               return SIG_ERR;
+       return oact.sa_handler;
+}
+
+/*
+ * Initialization, before editing a new file.
+ * Main thing here is to get a new buffer (in fileinit),
+ * rest is peripheral state resetting.
+ */
+void 
+init(void)
+{
+       register int i;
+
+       fileinit();
+       dot = zero = truedol = unddol = dol = fendcore;
+       one = zero+1;
+       undkind = UNDNONE;
+       chng = 0;
+       edited = 0;
+       for (i = 0; i <= 'z'-'a'+1; i++)
+               names[i] = 1;
+       anymarks = 0;
+}
+
+/*
+ * Main procedure.  Process arguments and then
+ * transfer control to the main command processing loop
+ * in the routine commands.  We are entered as either "ex", "edit", "vi"
+ * or "view" and the distinction is made here.  Actually, we are "vi" if
+ * there is a 'v' in our name, "view" is there is a 'w', and "edit" if
+ * there is a 'd' in our name.  For edit we just diddle options;
+ * for vi we actually force an early visual command.
+ */
+int 
+main(register int ac, register char *av[])
+{
+#ifndef VMUNIX
+       char *erpath = EXSTRINGS;
+#endif
+       char *cp = NULL;
+       register int c;
+       bool ivis;
+       bool fast = 0;
+#ifdef TRACE
+       register char *tracef;
+#endif
+
+       CLOBBGRD(ivis);
+       CLOBBGRD(fast);
+       CLOBBGRD(cp);
+
+       /*
+        * Initialize the built-in memory allocator.
+        */
+#ifdef VMUNIX
+       poolsbrk(0);
+#endif
+
+       /*
+        * Immediately grab the tty modes so that we wont
+        * get messed up if an interrupt comes in quickly.
+        */
+       gTTY(1);
+       normf = tty;
+       ppid = getpid();
+       /*
+        * Defend against d's, v's, w's, and a's in directories of
+        * path leading to our true name.
+        */
+       av[0] = tailpath(av[0]);
+
+       /*
+        * Figure out how we were invoked: ex, edit, vi, view.
+        */
+       ivis = any('v', av[0]); /* "vi" */
+       if (any('w', av[0]))    /* "view" */
+               value(READONLY) = 1;
+       if (any('d', av[0])) {  /* "edit" */
+               value(SHOWMODE) = 1;
+               /*
+                * I do not understand why novices should not
+                * switch to visual mode. So they can now. gritter
+                */
+               /*value(OPEN) = 0;*/
+               value(REPORT) = 1;
+               value(MAGIC) = 0;
+       }
+
+#ifndef VMUNIX
+       /*
+        * For debugging take files out of . if name is a.out.
+        */
+       if (av[0][0] == 'a')
+               erpath = tailpath(erpath);
+#endif /* !VMUNIX */
+
+       progname = av[0];
+       /*
+        * Open the error message file.
+        */
+       draino();
+#ifndef VMUNIX
+       erfile = open(erpath+4, O_RDONLY);
+       if (erfile < 0) {
+               erfile = open(erpath, O_RDONLY);
+       }
+#endif /* !VMUNIX */
+       pstop();
+
+       /*
+        * Initialize interrupt handling.
+        */
+       oldhup = signal(SIGHUP, SIG_IGN);
+       if (oldhup == SIG_DFL)
+               signal(SIGHUP, onhup);
+       oldquit = signal(SIGQUIT, SIG_IGN);
+#ifdef SIGXFSZ
+       oldxfsz = signal(SIGXFSZ, SIG_IGN);
+#endif
+       ruptible = signal(SIGINT, SIG_IGN) == SIG_DFL;
+       if (signal(SIGTERM, SIG_IGN) == SIG_DFL)
+               signal(SIGTERM, onhup);
+#ifdef SIGEMT
+       if (signal(SIGEMT, SIG_IGN) == SIG_DFL)
+               signal(SIGEMT, onemt);
+#endif
+
+#ifdef BIT8
+#ifndef        ISO8859_1
+       setlocale(LC_CTYPE, "");
+#endif
+#endif
+
+#ifdef MB_CUR_MAX
+       mb_cur_max = MB_CUR_MAX;
+#else
+       mb_cur_max = 1;
+#endif
+
+#ifdef MB
+       TRIM = mb_cur_max > 1 ? 0x6fffffff : 0xff;
+       QUOTE = mb_cur_max > 1 ? 0x10000000 : 0x100;
+#endif
+
+#ifdef LANGMSG
+       setlocale(LC_MESSAGES, "");
+       catd = catopen(CATNAME, NL_CAT_LOCALE);
+#endif
+
+       /*
+        * Process flag arguments.
+        */
+       ac--, av++;
+       while (ac) {
+               if (av[0][0] == '+') {
+                       firstpat = &av[0][1];
+                       if (*firstpat == '\0')
+                               needarg('+');
+               } else if (av[0][0] == '-') {
+arggroup:
+               c = av[0][1];
+               if (c == 0
+                       || c == 's'
+                               ) {
+                       hush = 1;
+                       value(AUTOPRINT) = 0;
+                       fast++;
+               } else switch (c) {
+
+               case '-':
+                       if (av[0][2])
+                               invopt('-');
+                       ac--, av++;
+                       goto argend;
+
+               case 'R':
+                       value(READONLY) = 1;
+                       break;
+
+#ifdef TRACE
+               case 'T':
+                       if (av[0][2] == 0)
+                               tracef = "trace";
+                       else {
+                               tracef = tttrace;
+                               tracef[8] = av[0][2];
+                               if (tracef[8])
+                                       tracef[9] = av[0][3];
+                               else
+                                       tracef[9] = 0;
+                       }
+                       trace = fopen(tracef, "w");
+#define tracbuf NULL
+                       if (trace == NULL)
+                               printf(catgets(catd, 1, 4,
+                                               "Trace create error\n"));
+                       else
+                               setbuf(trace, tracbuf);
+                       break;
+
+#endif /* TRACE */
+
+               case 'c':
+                       if (av[0][2] == '\0' && (av[1] == NULL
+                                       || *av[1] == '-' || *av[1] == '+'))
+                               needarg('c');
+                       if (av[0][2]) {
+                               firstpat = &av[0][2];
+                       } else {
+                               firstpat = av[1];
+                               ac--, av++;
+                       }
+                       break;
+
+               case 'e':
+                       ivis = 0;
+                       break;
+
+#ifdef LISPCODE
+               case 'l':
+                       value(LISP) = 1;
+                       value(SHOWMATCH) = 1;
+                       break;
+#endif
+
+               case 'L':
+               case 'r':
+                       recov++;
+                       break;
+
+               case 't':
+                       if (av[0][2]) {
+                               tflag = 1;
+                               safecp(lasttag, av[0], sizeof lasttag,
+                                               "argument to -t too long");
+                       } else if (ac > 1 && av[1][0] != '-' &&
+                                       av[1][0] != '+') {
+                               ac--, av++;
+                               tflag = 1;
+                               safecp(lasttag, av[0], sizeof lasttag,
+                                               "argument to -t too long");
+                       } else
+                               needarg('t');
+                       break;
+
+               case 'v':
+                       ivis = 1;
+                       break;
+
+               case 'V':
+                       verbose = 1;
+                       break;
+
+               case 'w':
+                       if (av[0][2])
+                               cp = &av[0][2];
+                       else if (ac > 1 && av[1][0] != '-' && av[1][0] != '+') {
+                               cp = av[1];
+                               ac--, av++;
+                       } else
+                               needarg('w');
+                       defwind = atoi(cp);
+                       break;
+
+               default:
+                       invopt(c);
+               }
+               if (c && c != 'c' && c != 't' && c != 'w' && av[0][2]) {
+                       av[0]++;
+                       goto arggroup;
+               }
+           } else
+                   break;
+               ac--, av++;
+       }
+argend:
+
+       cntrlhm = catgets(catd, 1, 70, "^H discarded\n");
+       /*
+        * Initialize end of core pointers.
+        * Normally we avoid breaking back to fendcore after each
+        * file since this can be expensive (much core-core copying).
+        * If your system can scatter load processes you could do
+        * this as ed does, saving a little core, but it will probably
+        * not often make much difference.
+        */
+       fendcore = (line *) sbrk(0);
+       endcore = fendcore - 2;
+
+#ifdef SIGTSTP
+       if (!hush && signal(SIGTSTP, SIG_IGN) == SIG_DFL)
+               signal(SIGTSTP, onsusp), dosusp++;
+#endif /* SIGTSTP */
+
+       /*
+        * If we are doing a recover and no filename
+        * was given, then execute an exrecover command with
+        * the -r option to type out the list of saved file names.
+        * Otherwise set the remembered file name to the first argument
+        * file name so the "recover" initial command will find it.
+        */
+       if (recov) {
+               if (ac == 0) {
+                       ppid = 0;
+                       setrupt();
+                       execl(EXRECOVER, "exrecover", "-r", (char *)0);
+                       filioerr(EXRECOVER);
+                       exitex(1);
+               }
+               safecp(savedfile, *av++, sizeof savedfile, "Filename too long");
+               ac--;
+       }
+
+       /*
+        * Initialize the argument list.
+        */
+       argv0 = av;
+       argc0 = ac;
+       args0 = av[0];
+       erewind();
+
+       /*
+        * Initialize a temporary file (buffer) and
+        * set up terminal environment.  Read user startup commands.
+        */
+       if (setexit() == 0) {
+               setrupt();
+               intty = isatty(0);
+               value(PROMPT) = intty;
+               if ((cp = getenv("SHELL")) != NULL && *cp != '\0')
+                       safecp(shell, cp, sizeof shell, "$SHELL too long");
+               if (fast || !intty)
+                       setterm("dumb");
+               else {
+                       gettmode();
+                       if ((cp = getenv("TERM")) != 0 && *cp) {
+                               setterm(cp);
+                       }
+               }
+       }
+       if (setexit() == 0) {
+               /*
+                * This is necessary because 'if (setexit() == 0 && !fast)'
+                * is rejected on the Cray.
+                */
+               if (fast)
+                       goto skip;
+               if ((globp = getenv("EXINIT")) && *globp)
+                       commands(1,1);
+               else {
+                       globp = 0;
+                       if ((cp = getenv("HOME")) != 0 && *cp) {
+                               safecat(safecp(genbuf, cp, sizeof genbuf,
+                                                       "$HOME too long"),
+                                               "/.exrc", sizeof genbuf,
+                                               "$HOME too long");
+                               if (iownit(genbuf))
+                                       source(genbuf, 1);
+                       }
+               }
+               /*
+                * Allow local .exrc too.  This loses if . is $HOME,
+                * but nobody should notice unless they do stupid things
+                * like putting a version command in .exrc.  Besides,
+                * they should be using EXINIT, not .exrc, right?
+                *
+                * This may not be done anymore. GR
+                */
+               /*
+                * The getcwd() function is not present on very
+                * old Unix systems. So if this fails, comment out
+                * the following three lines or supply code e.g. from
+                * the `pwd' utility.
+                */
+               if (cp == NULL || *cp == '\0'
+                               || getcwd(genbuf, MAXBSIZE) == NULL
+                               || strcmp(cp, genbuf) != 0)
+
+               if (iownit(".exrc"))
+                       source(".exrc", 1);
+       }
+skip:  init(); /* moved after prev 2 chunks to fix directory option */
+
+       /*
+        * Initial processing.  Handle tag, recover, and file argument
+        * implied next commands.  If going in as 'vi', then don't do
+        * anything, just set initev so we will do it later (from within
+        * visual).
+        */
+       if (setexit() == 0) {
+               if (recov)
+                       globp = "recover";
+               else if (tflag)
+                       globp = ivis ? "tag" : "tag|p";
+               else if (argc)
+                       globp = "next";
+               if (ivis)
+                       initev = globp;
+               else if (globp) {
+                       inglobal = 1;
+                       commands(1, 1);
+                       inglobal = 0;
+               }
+       }
+
+       /*
+        * Vi command... go into visual.
+        * Strange... everything in vi usually happens
+        * before we ever "start".
+        */
+       if (ivis) {
+               /*
+                * Don't have to be upward compatible with stupidity
+                * of starting editing at line $.
+                */
+               if (dol > zero)
+                       dot = one;
+               globp = "visual";
+               if (setexit() == 0)
+                       commands(1, 1);
+       }
+
+       /*
+        * Clear out trash in state accumulated by startup,
+        * and then do the main command loop for a normal edit.
+        * If you quit out of a 'vi' command by doing Q or ^\,
+        * you also fall through to here.
+        */
+       seenprompt = 1;
+       ungetchar(0);
+       globp = 0;
+       initev = 0;
+       setlastchar('\n');
+       setexit();
+       commands(0, 0);
+       cleanup(1);
+       exitex(0);
+       /*NOTREACHED*/
+       return 0;
+}
diff --git a/ex.h b/ex.h
new file mode 100644 (file)
index 0000000..51ae267
--- /dev/null
+++ b/ex.h
@@ -0,0 +1,583 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+ *
+ *     from ex.h       7.7.1.1 (Berkeley) 8/12/86
+ *
+ *     @(#)ex.h        1.53 (gritter) 2/17/05
+ */
+
+/*
+ * Ex version 3 (see exact version in ex_version.c).
+ *
+ * Mark Horton, UC Berkeley
+ * Bill Joy, UC Berkeley
+ * November 1979
+ *
+ * Changes by Gunnar Ritter, Freiburg i. Br., Germany
+ * May 2000
+ *
+ * This file contains most of the declarations common to a large number
+ * of routines.  The file ex_vis.h contains declarations
+ * which are used only inside the screen editor.
+ * The file config.h contains parameters which can be diddled per installation.
+ * The file ex_tune.h contains parameters which should be changed by
+ * maintainers only.
+ *
+ * The declarations relating to the argument list, regular expressions,
+ * the temporary file data structure used by the editor
+ * and the data describing terminals are each fairly substantial and
+ * are kept in the files ex_{argv,re,temp,tty}.h which
+ * we #include separately.
+ *
+ * If you are going to dig into ex, you should look at the outline of the
+ * distribution of the code into files at the beginning of ex.c and ex_v.c.
+ * Code which is similar to that of ed is lightly or undocumented in spots
+ * (e.g. the regular expression code).  Newer code (e.g. open and visual)
+ * is much more carefully documented, and still rough in spots.
+ *
+ * Please forward bug reports to
+ *
+ *     Mark Horton
+ *     Computer Science Division, EECS
+ *     EVANS HALL
+ *     U.C. Berkeley 94704
+ *     (415) 642-4948
+ *     (415) 642-1024 (dept. office)
+ *
+ * or to csvax.mark@berkeley on the ARPA-net.  I would particularly like to hear
+ * of additional terminal descriptions you add to the termcap data base.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <setjmp.h>
+
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef BIT8
+#ifndef        ISO8859_1
+#include <locale.h>
+#endif
+#endif
+
+#ifdef MB
+#include <wchar.h>
+#include <wctype.h>
+#endif
+
+#include <termios.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#ifndef        TIOCGWINSZ
+#include <sys/ioctl.h>
+#endif
+
+#include "config.h"
+
+typedef        void    (*shand)(int);
+#ifdef signal
+#undef signal
+#endif
+#define        signal(a, b)    setsig((a), (b))
+
+/*
+ * Avoid clobbering of automatic variables with an ANSI C compiler.
+ */
+#define                CLOBBGRD(a)     (void)(&(a));
+
+#ifndef        MB_LEN_MAX
+#define        MB_LEN_MAX      1
+#endif
+
+/*
+ * Feature dependency checks.
+ */
+#ifdef ISO8859_1
+#ifndef        BIT8
+#define        BIT8
+#endif
+#endif
+
+#ifndef        LISPCODE
+#define        LISPCODE
+#endif
+#ifndef        CHDIR
+#define        CHDIR
+#endif
+
+#ifndef var
+#define var    extern
+#endif
+
+#ifndef        VMUNIX
+typedef        short   line;
+#else
+typedef        int     line;
+#endif
+
+typedef        short   bool;
+
+#ifdef LARGEF
+typedef        off_t   bloc;
+#else
+typedef        short   bloc;
+#endif
+
+#ifdef VMUNIX
+#ifdef LARGEF
+typedef        off_t   bbloc;
+#else
+typedef        int     bbloc;
+#endif
+#else
+typedef        short   bbloc;
+#endif
+
+/*
+ * The editor does not normally use the standard i/o library.  Because
+ * we expect the editor to be a heavily used program and because it
+ * does a substantial amount of input/output processing it is appropriate
+ * for it to call low level read/write primitives directly.  In fact,
+ * when debugging the editor we use the standard i/o library.  In any
+ * case the editor needs a printf which prints through "putchar" ala the
+ * old version 6 printf.  Thus we normally steal a copy of the "printf.c"
+ * and "strout" code from the standard i/o library and mung it for our
+ * purposes to avoid dragging in the stdio library headers, etc if we
+ * are not debugging.  Such a modified printf exists in "printf.c" here.
+ */
+#ifdef TRACE
+#      include <stdio.h>
+       var     FILE    *trace;
+       var     bool    trubble;
+       var     bool    techoin;
+       var     char    tracbuf[BUFSIZ];
+#      undef   putchar
+#      undef   getchar
+
+#else  /* !TRACE */
+
+#ifndef        BUFSIZ
+#ifdef LINE_MAX
+#define        BUFSIZ  LINE_MAX        /* POSIX line size */
+#else  /* !LINE_MAX */
+#ifdef VMUNIX
+#define        BUFSIZ  1024
+#else  /* !VMUNIX */
+#ifdef u370
+#define        BUFSIZ  4096
+#else  /* !u370 */
+#define        BUFSIZ  512
+#endif /* !u370 */
+#endif
+#endif /* !VMUNIX */
+#endif /* !LINE_MAX */
+
+#ifdef NULL
+#undef NULL
+#endif
+#ifdef EOF
+#undef EOF
+#endif
+#ifdef printf
+#undef printf
+#endif
+#ifdef vprintf
+#undef vprintf
+#endif
+#ifdef getchar
+#undef getchar
+#endif
+#ifdef putchar
+#undef putchar
+#endif
+
+#define        NULL    0
+#define        EOF     -1
+
+#endif /* !TRACE */
+
+typedef        sigjmp_buf      JMP_BUF;
+#define        SETJMP(a)       sigsetjmp(a, 1)
+#define        LONGJMP(a, b)   siglongjmp(a, b)
+
+#ifndef        MAXBSIZE
+#define        MAXBSIZE        8192    /* Same as in 4.2BSD */
+#endif
+
+#include "ex_tune.h"
+#include "ex_vars.h"
+
+/*
+ * Options in the editor are referred to usually by "value(name)" where
+ * name is all uppercase, i.e. "value(PROMPT)".  This is actually a macro
+ * which expands to a fixed field in a static structure and so generates
+ * very little code.  The offsets for the option names in the structure
+ * are generated automagically from the structure initializing them in
+ * ex_data.c... see the shell script "makeoptions".
+ */
+struct option {
+       char    *oname;
+       char    *oabbrev;
+       short   otype;          /* Types -- see below */
+       short   odefault;       /* Default value */
+       short   ovalue;         /* Current value */
+       char    *osvalue;
+};
+
+#define        ONOFF   0
+#define        NUMERIC 1
+#define        STRING  2               /* SHELL or DIRECTORY */
+#define        OTERM   3
+
+#define        value(a)        options[a].ovalue
+#define        svalue(a)       options[a].osvalue
+
+extern  struct option options[NOPTS + 1];
+
+/*
+ * Character constants and bits
+ *
+ * The editor uses the QUOTE bit as a flag to pass on with characters
+ * e.g. to the putchar routine.  The editor never uses a simple char variable.
+ * Only arrays of and pointers to characters are used and parameters and
+ * registers are never declared character.
+ */
+#ifdef CTRL
+#undef CTRL
+#endif
+#define        CTRL(c) ((c) & 037)
+#define        NL      CTRL('j')
+#define        CR      CTRL('m')
+#define        DELETE  0177            /* See also ATTN, QUIT in ex_tune.h */
+#define        ESCAPE  033
+
+/*
+ * BIT8 and MB routines by Gunnar Ritter 2000, 2004.
+ *
+ * -DISO8859_1 enables all characters >= 0240 regardless of
+ *  LC_CTYPE.
+ */
+#define        INVBIT          0x20000000
+#define        MULTICOL        0x40000000
+
+#if defined (MB)
+
+/*
+ * This type is used to represent a single character cell.
+ */
+typedef int    cell;
+var    int     TRIM;
+var    int     QUOTE;
+#define        printable(c)    (((c)&INVBIT) == 0 && \
+               (mb_cur_max > 1 ? iswprint((c)&TRIM) : isprint((c)&TRIM)))
+#define        ext(c)          (((c) & 0177) == 0)
+
+#elif defined (BIT8)
+
+typedef        short   cell;
+#define        QUOTE           0400
+#define        TRIM            0377
+#ifndef        ISO8859_1
+#define        printable(c)    isprint((c)&TRIM)
+#else  /* ISO8859_1 */
+#define        printable(c)    (((c) & 0140) && (c) != DELETE)
+#endif /* ISO8859_1 */
+
+#else  /* !BIT8 */
+
+typedef        char    cell;
+#define        QUOTE           0200
+#define        TRIM            0177
+
+#endif /* !BIT8 */
+
+/*
+ * Miscellaneous random variables used in more than one place
+ */
+var    bool    aiflag;         /* Append/change/insert with autoindent */
+var    bool    anymarks;       /* We have used '[a-z] */
+var    int     bsize;          /* Block size for disk i/o */
+var    int     chng;           /* Warn "No write" */
+var    char    *Command;
+var    short   defwind;        /* -w# change default window size */
+var    int     dirtcnt;        /* When >= MAXDIRT, should sync temporary */
+var    bool    dosusp;         /* Do SIGTSTP in visual when ^Z typed */
+var    bool    edited;         /* Current file is [Edited] */
+var    line    *endcore;       /* Last available core location */
+extern bool    endline;        /* Last cmd mode command ended with \n */
+#ifndef VMUNIX
+var    short   erfile;         /* Error message file unit */
+#endif
+var    line    *fendcore;      /* First address in line pointer space */
+var    char    file[FNSIZE];   /* Working file name */
+var    bool    fixedzero;      /* zero file size was fixed (for visual) */
+var    char    genbuf[MAXBSIZE]; /* Working buffer when manipulating linebuf */
+var    bool    hush;           /* Command line option - was given, hush up! */
+var    char    *globp;         /* (Untyped) input string to command mode */
+var    bool    holdcm;         /* Don't cursor address */
+var    bool    inappend;       /* in ex command append mode */
+var    bool    inglobal;       /* Inside g//... or v//... */
+var    char    *initev;        /* Initial : escape for visual */
+var    bool    inopen;         /* Inside open or visual */
+var    char    *input;         /* Current position in cmd line input buffer */
+var    bool    intty;          /* Input is a tty */
+var    short   io;             /* General i/o unit (auto-closed on error!) */
+extern int     lastc;          /* Last character ret'd from cmd input */
+var    bool    laste;          /* Last command was an "e" (or "rec") */
+var    char    lastmac;        /* Last macro called for ** */
+var    char    lasttag[TAGSIZE];       /* Last argument to a tag command */
+var    char    *linebp;        /* Used in substituting in \n */
+var    char    linebuf[LBSIZE];        /* The primary line buffer */
+var    bool    listf;          /* Command should run in list mode */
+var    line    names['z'-'a'+2];       /* Mark registers a-z,' */
+var    int     notecnt;        /* Count for notify (to visual from cmd) */
+var    bool    numberf;        /* Command should run in number mode */
+var    char    obuf[BUFSIZ];   /* Buffer for tty output */
+var    shand   oldhup;         /* Previous SIGHUP handler */
+var    shand   oldquit;        /* Previous SIGQUIT handler */
+#ifdef SIGXFSZ
+var    shand   oldxfsz;        /* Previous SIGXFSZ handler */
+#endif
+var    short   oprompt;        /* Saved during source */
+extern unsigned short  ospeed;         /* Output speed (from gtty) */
+var    int     otchng;         /* Backup tchng to find changes in macros */
+var    int     peekc;          /* Peek ahead character (cmd mode input) */
+var    char    *pkill[2];      /* Trim for put with ragged (LISP) delete */
+var    bool    pfast;          /* Have stty -nl'ed to go faster */
+var    pid_t   pid;            /* Process id of child */
+var    pid_t   ppid;           /* Process id of parent (e.g. main ex proc) */
+var    JMP_BUF resetlab;       /* For error throws to top level (cmd mode) */
+var    pid_t   rpid;           /* Pid returned from wait() */
+var    bool    recov;          /* A `n' command is executed as `recov' */
+var    bool    ruptible;       /* Interruptible is normal state */
+var    bool    seenprompt;     /* 1 if have gotten user input */
+var    bool    shudclob;       /* Have a prompt to clobber (e.g. on ^D) */
+var    int     status;         /* Status returned from wait() */
+var    int     tchng;          /* If nonzero, then [Modified] */
+extern int     tfile;          /* Temporary file unit */
+var    bool    tflag;          /* -t option given on command line */
+var    bool    vcatch;         /* Want to catch an error (open/visual) */
+var    bool    verbose;        /* -V option; print command input to stderr */
+var    JMP_BUF vreslab;        /* For error throws to a visual catch */
+var    bool    writing;        /* 1 if in middle of a file write */
+var    int     xchng;          /* Suppresses multiple "No writes" in !cmd */
+var    int     failed;         /* exit with a non-zero status */
+var    int     exitoneof;      /* exit command loop on EOF */
+
+/*
+ * Macros
+ */
+#define        CP(a, b)        (ignore(movestr(a, b)))
+                       /*
+                        * FIXUNDO: do we want to mung undo vars?
+                        * Usually yes unless in a macro or global.
+                        */
+#define FIXUNDO                (inopen >= 0 && (inopen || !inglobal))
+#define ckaw()         {if (chng && value(AUTOWRITE)) wop(0);}
+#define        copy(a,b,c)     Copy((char *) (a), (char *) (b), (c))
+#define        eq(a, b)        ((a) && (b) && strcmp(a, b) == 0)
+#define        getexit(a)      copy(a, resetlab, sizeof (JMP_BUF))
+#define        lastchar()      lastc
+#define        outchar(c)      (*Outchar)(c)
+#define        pastwh()        (ignore(skipwh()))
+#define        pline(no)       (*Pline)(no)
+#define        reset()         LONGJMP(resetlab,1)
+#define        resexit(a)      copy(resetlab, a, sizeof (JMP_BUF))
+#define        setexit()       SETJMP(resetlab)
+#define        setlastchar(c)  lastc = c
+#define        ungetchar(c)    peekc = c
+
+#define        CATCH           vcatch = 1; if (SETJMP(vreslab) == 0) {
+#define        ONERR           } else { vcatch = 0;
+#define        ENDCATCH        } vcatch = 0;
+
+/*
+ * Environment like memory
+ */
+var    char    altfile[FNSIZE];        /* Alternate file name */
+extern char    direct[ONMSZ];          /* Temp file goes here */
+extern char    shell[ONMSZ];           /* Copied to be settable */
+extern char    ttylongname[ONMSZ];     /* A long and pretty name */
+var    char    uxb[UXBSIZE + 2];       /* Last !command for !! */
+
+/*
+ * The editor data structure for accessing the current file consists
+ * of an incore array of pointers into the temporary file tfile.
+ * Each pointer is 15 bits (the low bit is used by global) and is
+ * padded with zeroes to make an index into the temp file where the
+ * actual text of the line is stored.
+ *
+ * To effect undo, copies of affected lines are saved after the last
+ * line considered to be in the buffer, between dol and unddol.
+ * During an open or visual, which uses the command mode undo between
+ * dol and unddol, a copy of the entire, pre-command buffer state
+ * is saved between unddol and truedol.
+ */
+var    line    *addr1;                 /* First addressed line in a command */
+var    line    *addr2;                 /* Second addressed line */
+var    line    *dol;                   /* Last line in buffer */
+var    line    *dot;                   /* Current line */
+var    line    *one;                   /* First line */
+var    line    *truedol;               /* End of all lines, including saves */
+var    line    *unddol;                /* End of undo saved lines */
+var    line    *zero;                  /* Points to empty slot before one */
+
+/*
+ * Undo information
+ *
+ * For most commands we save lines changed by salting them away between
+ * dol and unddol before they are changed (i.e. we save the descriptors
+ * into the temp file tfile which is never garbage collected).  The
+ * lines put here go back after unddel, and to complete the undo
+ * we delete the lines [undap1,undap2).
+ *
+ * Undoing a move is much easier and we treat this as a special case.
+ * Similarly undoing a "put" is a special case for although there
+ * are lines saved between dol and unddol we don't stick these back
+ * into the buffer.
+ */
+var    short   undkind;
+
+var    line    *unddel;        /* Saved deleted lines go after here */
+var    line    *undap1;        /* Beginning of new lines */
+var    line    *undap2;        /* New lines end before undap2 */
+var    line    *undadot;       /* If we saved all lines, dot reverts here */
+
+#define        UNDCHANGE       0
+#define        UNDMOVE         1
+#define        UNDALL          2
+#define        UNDNONE         3
+#define        UNDPUT          4
+
+extern int     (*Outchar)(int);
+extern void    (*Pline)(int);
+extern int     (*Putchar)(int);
+
+#define        NOSTR   (char *) 0
+#define        NOLINE  (line *) 0
+
+#define        ignore(a)       a
+#define        ignorf(a)       a
+
+#ifdef LANGMSG
+#include <nl_types.h>
+var    nl_catd catd;
+#else  /* !LANGMSG */
+#define        catgets(a, b, c, d)     (d)
+#endif /* !LANGMSG */
+var    char    *cntrlhm;
+
+#include "ex_proto.h"
+
+var    int     mb_cur_max;
+#ifdef MB
+#define        nextc(c, s, n)  (mb_cur_max > 1 && *(s) & 0200 ? \
+                       ((n) = mbtowi(&(c), (s), mb_cur_max), \
+                       (n) = ((n) > 0 ? (n) : (n) < 0 ? (c=WEOF, 1) : 1)) :\
+               ((c) = *(s) & 0377, (n) = 1))
+#define        colsc(c)        (mb_cur_max > 1 && ((c)&0177) != (c) ? wcwidth(c) : 1)
+#define        skipleft(l, p)  (mb_cur_max > 1 && ((p)[0]&0200 || \
+                               (p)>(l) && (p)[-1]&0200) ? wskipleft(l, p) : -1)
+#define        skipright(l, p) (mb_cur_max > 1 && (p)>=(l) && (p)[0]&0200 ? \
+                               wskipright(l, p) : 1)
+#define        samechar(cp, c) (mb_cur_max > 1 && *(cp)&0200 ? wsamechar(cp, c) : \
+                               (*(cp)&0377) == c)
+#define        xisdigit(c)     (mb_cur_max > 1 ? iswdigit(c) : isdigit(c))
+#define        xisalpha(c)     (mb_cur_max > 1 ? iswalpha(c) : isalpha(c))
+#define        xisalnum(c)     (mb_cur_max > 1 ? iswalnum(c) : isalnum(c))
+#define        xisspace(c)     (mb_cur_max > 1 ? iswspace(c) : isspace(c))
+#define        xisupper(c)     (mb_cur_max > 1 ? iswupper(c) : isupper(c))
+#define        xislower(c)     (mb_cur_max > 1 ? iswlower(c) : islower(c))
+#define        xtolower(c)     (mb_cur_max > 1 ? towlower(c) : tolower(c))
+#define        xtoupper(c)     (mb_cur_max > 1 ? towupper(c) : toupper(c))
+#else  /* !MB */
+#define        nextc(c, s, n)  ((c) = *(s) & 0377, (n) = 1)
+#define        colsc(c)        (1)
+#define        skipleft(l, p)  (-1)
+#define        skipright(l, p) (1)
+#define        samechar(cp, c) (*(cp)&0377 == c)
+#define        xisdigit(c)     isdigit(c)
+#define        xisalpha(c)     isalpha(c)
+#define        xisalnum(c)     isalnum(c)
+#define        xisspace(c)     isspace(c)
+#define        xisupper(c)     isupper(c)
+#define        xislower(c)     islower(c)
+#define        xtolower(c)     tolower(c)
+#define        xtoupper(c)     toupper(c)
+#endif /* !MB */
diff --git a/ex.spec b/ex.spec
new file mode 100644 (file)
index 0000000..8879956
--- /dev/null
+++ b/ex.spec
@@ -0,0 +1,58 @@
+#
+# Sccsid @(#)ex.spec   1.7 (gritter) 1/22/05
+#
+Summary: A port of the traditional ex/vi editors
+Name: ex
+Version: 050325
+Release: 1
+License: BSD
+Source: %{name}-%{version}.tar.bz2
+Group: System Environment/Base
+Vendor: Gunnar Ritter <Gunnar.Ritter@pluto.uni-freiburg.de>
+URL: <http://ex-vi.sourceforge.net>
+BuildRoot: %{_tmppath}/%{name}-root
+
+Requires: /etc/termcap
+
+# prefix applies to bindir, libexecdir, and mandir.
+%define        prefix          /usr
+%define        bindir          %{prefix}/5bin
+%define        libexecdir      %{prefix}/5lib
+%define        mandir          %{prefix}/share/man/5man
+
+%define        preservedir     /var/preserve
+
+# install command
+%define        ucbinstall      install
+
+%define        cflags          -Os -fomit-frame-pointer
+
+%define        makeflags       PREFIX=%{prefix} BINDIR=%{bindir} LIBEXECDIR=%{libexecdir} MANDIR=%{mandir} PRESERVEDIR=%{preservedir} INSTALL=%{ucbinstall} RPMCFLAGS="%{cflags}"
+
+%description
+This is a port of the traditional ex and vi editor implementation as
+found on 2BSD and 4BSD. It was enhanced to support most of the additions
+in System V and POSIX.2, and international character sets like UTF-8 and
+many East Asian encodings.
+
+%prep
+rm -rf %{buildroot}
+%setup
+
+%build
+make %{makeflags}
+
+%install
+make DESTDIR=%{buildroot} %{makeflags} install
+
+%clean
+cd ..; rm -rf %{_builddir}/%{name}-%{version}
+rm -rf %{buildroot}
+
+%files
+%defattr(-,root,root)
+%doc Changes LICENSE README TODO
+%{bindir}/*
+%{libexecdir}/*
+%{mandir}/man1/*
+%{preservedir}
diff --git a/ex_addr.c b/ex_addr.c
new file mode 100644 (file)
index 0000000..ffd8db8
--- /dev/null
+++ b/ex_addr.c
@@ -0,0 +1,406 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_addr.c  1.10 (gritter) 2/17/05";
+#endif
+#endif /* not lint */
+
+/* from ex_addr.c      7.3 (Berkeley) 6/7/85 */
+
+#include "ex.h"
+#include "ex_re.h"
+
+/*
+ * Routines for address parsing and assignment and checking of address bounds
+ * in command mode.  The routine address is called from ex_cmds.c
+ * to parse each component of a command (terminated by , ; or the beginning
+ * of the command itself.  It is also called by the scanning routine
+ * in ex_voperate.c from within open/visual.
+ *
+ * Other routines here manipulate the externals addr1 and addr2.
+ * These are the first and last lines for the current command.
+ *
+ * The variable bigmove remembers whether a non-local glitch of . was
+ * involved in an address expression, so we can set the previous context
+ * mark '' when such a motion occurs.
+ */
+
+static bool bigmove;
+
+/*
+ * Set up addr1 and addr2 for commands whose default address is dot.
+ */
+void 
+setdot(void)
+{
+
+       setdot1();
+       if (bigmove)
+               markDOT();
+}
+
+/*
+ * Call setdot1 to set up default addresses without ever
+ * setting the previous context mark.
+ */
+void 
+setdot1(void)
+{
+
+       if (addr2 == 0)
+               addr1 = addr2 = dot;
+       if (addr1 > addr2) {
+               notempty();
+               failed = 1;
+               error(catgets(catd, 1, 6,
+                       "Addr1 > addr2|First address exceeds second"));
+       }
+}
+
+/*
+ * Ex allows you to say
+ *     delete 5
+ * to delete 5 lines, etc.
+ * Such nonsense is implemented by setcount.
+ */
+void 
+setcount(void)
+{
+       register int cnt;
+
+       pastwh();
+       if (!isdigit(peekchar())) {
+               setdot();
+               return;
+       }
+       addr1 = addr2;
+       setdot();
+       cnt = getnum();
+       if (cnt <= 0)
+               error(catgets(catd, 1, 7, "Bad count|Nonzero count required"));
+       addr2 += cnt - 1;
+       if (addr2 > dol)
+               addr2 = dol;
+       nonzero();
+}
+
+/*
+ * Parse a number out of the command input stream.
+ */
+int 
+getnum(void)
+{
+       register int cnt;
+
+       for (cnt = 0; isdigit(peekcd());)
+               cnt = cnt * 10 + getchar() - '0';
+       return (cnt);
+}
+
+/*
+ * Set the default addresses for commands which use the whole
+ * buffer as default, notably write.
+ */
+void 
+setall(void)
+{
+
+       if (addr2 == 0) {
+               addr1 = one;
+               addr2 = dol;
+               if (dol == zero) {
+                       dot = zero;
+                       return;
+               }
+       }
+       /*
+        * Don't want to set previous context mark so use setdot1().
+        */
+       setdot1();
+}
+
+/*
+ * No address allowed on, e.g. the file command.
+ */
+void 
+setnoaddr(void)
+{
+
+       if (addr2 != 0) {
+               failed = 1;
+               error(catgets(catd, 1, 8,
+                               "No address allowed@on this command"));
+       }
+}
+
+/*
+ * Parse an address.
+ * Just about any sequence of address characters is legal.
+ *
+ * If you are tricky you can use this routine and the = command
+ * to do simple addition and subtraction of cardinals less
+ * than the number of lines in the file.
+ */
+line *
+address(char *in_line)
+{
+       register line *addr;
+       register int offset, c;
+       short lastsign;
+
+       bigmove = 0;
+       lastsign = 0;
+       offset = 0;
+       addr = 0;
+       for (;;) {
+               if (isdigit(peekcd())) {
+                       if (addr == 0) {
+                               addr = zero;
+                               bigmove = 1;
+                       }
+                       loc1 = 0;
+                       addr += offset;
+                       offset = getnum();
+                       if (lastsign >= 0)
+                               addr += offset;
+                       else
+                               addr -= offset;
+                       lastsign = 0;
+                       offset = 0;
+               }
+               switch (c = getcd()) {
+
+               case '?':
+               case '/':
+               case '$':
+               case '\'':
+               case '\\':
+                       bigmove++;
+               case '.':
+                       if (addr || offset)
+                               error(catgets(catd, 1, 9,
+                                               "Badly formed address"));
+               }
+               offset += lastsign;
+               lastsign = 0;
+               switch (c) {
+
+               case ' ':
+               case '\t':
+                       continue;
+
+               case '+':
+                       lastsign = 1;
+                       if (addr == 0)
+                               addr = dot;
+                       continue;
+
+               case '^':
+               case '-':
+                       lastsign = -1;
+                       if (addr == 0)
+                               addr = dot;
+                       continue;
+
+               case '\\':
+               case '?':
+               case '/':
+                       c = compile(c, 1);
+                       notempty();
+                       savere(&scanre);
+                       addr = dot;
+                       if (in_line && execute(0, dot)) {
+                               if (c == '/') {
+                                       while (loc1 <= in_line) {
+                                               if (loc1 == loc2)
+                                                       loc2++;
+                                               if (!execute(1, NULL))
+                                                       goto nope;
+                                       }
+                                       break;
+                               } else if (loc1 < in_line) {
+                                       char *last;
+doques:
+
+                                       do {
+                                               last = loc1;
+                                               if (loc1 == loc2)
+                                                       loc2++;
+                                               if (!execute(1, NULL))
+                                                       break;
+                                       } while (loc1 < in_line);
+                                       loc1 = last;
+                                       break;
+                               }
+                       }
+nope:
+                       for (;;) {
+                               if (c == '/') {
+                                       addr++;
+                                       if (addr > dol) {
+                                               if (value(WRAPSCAN) == 0)
+error(catgets(catd, 1, 10, "No match to BOTTOM|Address search hit BOTTOM without matching pattern"));
+                                               addr = zero;
+                                       }
+                               } else {
+                                       addr--;
+                                       if (addr < zero) {
+                                               if (value(WRAPSCAN) == 0)
+error(catgets(catd, 1, 11, "No match to TOP|Address search hit TOP without matching pattern"));
+                                               addr = dol;
+                                       }
+                               }
+                               if (execute(0, addr)) {
+                                       if (in_line && c == '?') {
+                                               in_line = &linebuf[LBSIZE];
+                                               goto doques;
+                                       }
+                                       break;
+                               }
+                               if (addr == dot)
+                                       error(catgets(catd, 1, 12,
+                                               "Fail|Pattern not found"));
+                       }
+                       continue;
+
+               case '$':
+                       addr = dol;
+                       continue;
+
+               case '.':
+                       addr = dot;
+                       continue;
+
+               case '\'':
+                       c = markreg(getchar());
+                       if (c == 0)
+                               error(catgets(catd, 1, 13,
+                                               "Marks are ' and a-z"));
+                       addr = getmark(c);
+                       if (addr == 0)
+                               error(catgets(catd, 1, 14,
+                                               "Undefined mark@referenced"));
+                       break;
+
+               default:
+                       ungetchar(c);
+                       if (offset) {
+                               if (addr == 0)
+                                       addr = dot;
+                               addr += offset;
+                               loc1 = 0;
+                       }
+                       if (addr == 0) {
+                               bigmove = 0;
+                               return (0);
+                       }
+                       if (addr != zero)
+                               notempty();
+                       addr += lastsign;
+                       if (addr < zero) {
+                               failed = 1;
+                               error(catgets(catd, 1, 15,
+                               "Negative address@- first buffer line is 1"));
+                       }
+                       if (addr > dol) {
+                               failed = 1;
+                               error(catgets(catd, 1, 16,
+                                       "Not that many lines@in buffer"));
+                       }
+                       return (addr);
+               }
+       }
+}
+
+/*
+ * Abbreviations to make code smaller
+ * Left over from squashing ex version 1.1 into
+ * 11/34's and 11/40's.
+ */
+void 
+setCNL(void)
+{
+
+       setcount();
+       newline();
+}
+
+void 
+setNAEOL(void)
+{
+
+       setnoaddr();
+       eol();
+}
diff --git a/ex_argv.h b/ex_argv.h
new file mode 100644 (file)
index 0000000..04a64ad
--- /dev/null
+++ b/ex_argv.h
@@ -0,0 +1,105 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+ *
+ *     from ex_argv.h  7.3 (Berkeley) 5/31/85
+ *
+ *     @(#)ex_argv.h   1.8 (gritter) 11/23/04
+ */
+
+/*
+ * The current implementation of the argument list is poor,
+ * using an argv even for internally done "next" commands.
+ * It is not hard to see that this is restrictive and a waste of
+ * space.  The statically allocated glob structure could be replaced
+ * by a dynamically allocated argument area space.
+ */
+var char       **argv;
+var char       **argv0;
+var char       *args;
+var char       *args0;
+var short      argc;
+var short      argc0;
+var short      morargc;                /* Used with "More files to edit..." */
+
+var int                firstln;                /* From +lineno */
+var char       *firstpat;              /* From +/pat   */
+
+/* Yech... */
+struct glob {
+       short   argc;                   /* Index of current file in argv */
+       short   argc0;                  /* Number of arguments in argv */
+       char    *argv[NARGS + 1];       /* WHAT A WASTE! */
+       char    argspac[NCARGS + sizeof (int)];
+};
+var struct     glob frob;
+
+extern void    gglob(struct glob *);
diff --git a/ex_cmds.c b/ex_cmds.c
new file mode 100644 (file)
index 0000000..c23a1e2
--- /dev/null
+++ b/ex_cmds.c
@@ -0,0 +1,988 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_cmds.c  1.22 (gritter) 2/18/05";
+#endif
+#endif
+
+/* from ex_cmds.c      7.10.1 (2.11BSD) 1996/11/23 */
+
+#include "ex.h"
+#include "ex_argv.h"
+#include "ex_temp.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+
+bool   pflag, nflag;
+int    poffset;
+
+#define        nochng()        lchng = chng
+
+/*
+ * Main loop for command mode command decoding.
+ * A few commands are executed here, but main function
+ * is to strip command addresses, do a little address oriented
+ * processing and call command routines to do the real work.
+ */
+void 
+commands(int noprompt, int _exitoneof)
+{
+       register line *addr;
+       register int c;
+       register int lchng;
+       int given;
+       int seensemi;
+       int cnt;
+       bool hadpr = 0;
+
+       resetflav();
+       nochng();
+       for (;;) {
+               exitoneof = _exitoneof;
+               /*
+                * If dot at last command
+                * ended up at zero, advance to one if there is a such.
+                */
+               if (dot <= zero) {
+                       dot = zero;
+                       if (dol > zero)
+                               dot = one;
+               }
+               shudclob = 0;
+
+               /*
+                * If autoprint or trailing print flags,
+                * print the line at the specified offset
+                * before the next command.
+                */
+               if (pflag ||
+                   lchng != chng && value(AUTOPRINT) && !inglobal && !inopen && endline) {
+                       pflag = 0;
+                       nochng();
+                       if (dol != zero) {
+                               addr1 = addr2 = dot + poffset;
+                               if (addr1 < one || addr1 > dol)
+error(catgets(catd, 1, 17,
+               "Offset out-of-bounds|Offset after command too large"));
+                               setdot1();
+                               goto print;
+                       }
+               }
+               nochng();
+
+               /*
+                * Print prompt if appropriate.
+                * If not in global flush output first to prevent
+                * going into pfast mode unreasonably.
+                */
+               if (inglobal == 0) {
+                       flush();
+                       if (!hush && value(PROMPT) && !globp && !noprompt && endline) {
+                               putchar(':');
+                               hadpr = 1;
+                       }
+                       TSYNC();
+               }
+
+               /*
+                * Gobble up the address.
+                * Degenerate addresses yield ".".
+                */
+               addr2 = 0;
+               given = seensemi = 0;
+               do {
+                       addr1 = addr2;
+                       addr = address(0);
+                       c = getcd();
+                       if (addr == 0)
+                               if (c == ',')
+                                       addr = dot;
+                               else if (addr1 != 0) {
+                                       addr2 = dot;
+                                       break;
+                               } else
+                                       break;
+                       addr2 = addr;
+                       given++;
+                       if (c == ';') {
+                               c = ',';
+                               dot = addr;
+                               seensemi = 1;
+                       }
+               } while (c == ',');
+               if (c == '%') {
+                       /* %: same as 1,$ */
+                       addr1 = one;
+                       addr2 = dol;
+                       given = 2;
+                       c = getchar();
+               }
+               if (addr1 == 0)
+                       addr1 = addr2;
+               if (c == ':')
+                       c = getchar();
+
+               /*
+                * Set command name for special character commands.
+                */
+               tailspec(c);
+
+               /*
+                * If called via : escape from open or visual, limit
+                * the set of available commands here to save work below.
+                */
+               if (inopen) {
+                       if (c=='\n' || c=='\r' || c==CTRL('d') || c==EOF) {
+                               if (addr2)
+                                       dot = addr2;
+                               if (c == EOF)
+                                       return;
+                               continue;
+                       }
+                       if (any(c, "o"))
+notinvis:
+                               tailprim(Command, 1, 1);
+               }
+/* choice: */
+               switch (c) {
+
+               case 'a':
+
+                       switch(peekchar()) {
+                       case 'b':
+/* abbreviate */
+                               tail("abbreviate");
+                               setnoaddr();
+                               mapcmd(0, 1);
+                               anyabbrs = 1;
+                               continue;
+                       case 'r':
+/* args */
+                               tail("args");
+                               setnoaddr();
+                               eol();
+                               pargs();
+                               continue;
+                       }
+
+/* append */
+                       if (inopen)
+                               goto notinvis;
+                       tail("append");
+                       setdot();
+                       aiflag = exclam();
+                       newline();
+                       vmacchng(0);
+                       deletenone();
+                       setin(addr2);
+                       inappend = 1;
+                       ignore(append(gettty, addr2));
+                       inappend = 0;
+                       nochng();
+                       continue;
+
+               case 'c':
+                       switch (peekchar()) {
+
+/* copy */
+                       case 'o':
+                               tail("copy");
+                               vmacchng(0);
+                               move();
+                               continue;
+
+#ifdef CHDIR
+/* cd */
+                       case 'd':
+                               tail("cd");
+                               goto changdir;
+
+/* chdir */
+                       case 'h':
+                               ignchar();
+                               if (peekchar() == 'd') {
+                                       register char *p;
+                                       tail2of("chdir");
+changdir:
+                                       if (savedfile[0] == '/' || !value(WARN))
+                                               ignore(exclam());
+                                       else
+                                               ignore(quickly());
+                                       if (skipend()) {
+                                               p = getenv("HOME");
+                                               if (p == NULL)
+                                                       error(catgets(catd, 1,
+                                               18, "Home directory unknown"));
+                                       } else
+                                               getone(), p = file;
+                                       eol();
+                                       if (chdir(p) < 0)
+                                               filioerr(p);
+                                       if (savedfile[0] != '/')
+                                               edited = 0;
+                                       continue;
+                               }
+                               if (inopen)
+                                       tailprim("change", 2, 1);
+                               tail2of("change");
+                               break;
+
+#endif
+                       default:
+                               if (inopen)
+                                       goto notinvis;
+                               tail("change");
+                               break;
+                       }
+/* change */
+                       aiflag = exclam();
+                       setCNL();
+                       vmacchng(0);
+                       setin(addr1);
+                       delete(0);
+                       inappend = 1;
+                       ignore(append(gettty, addr1 - 1));
+                       inappend = 0;
+                       nochng();
+                       continue;
+
+/* delete */
+               case 'd':
+                       /*
+                        * Caution: dp and dl have special meaning already.
+                        */
+                       tail("delete");
+                       c = cmdreg();
+                       setCNL();
+                       vmacchng(0);
+                       if (c)
+                               YANKreg(c);
+                       delete(0);
+                       appendnone();
+                       vkillDEL();
+                       continue;
+
+/* edit */
+/* ex */
+               case 'e':
+                       tail(peekchar() == 'x' ? "ex" : "edit");
+editcmd:
+                       if (!exclam() && chng)
+                               c = 'E';
+                       filename(c);
+                       if (c == 'E') {
+                               ungetchar(lastchar());
+                               ignore(quickly());
+                       }
+                       setnoaddr();
+doecmd:
+                       init();
+                       addr2 = zero;
+                       laste++;
+                       synced();
+                       rop(c);
+#ifdef INCORB
+                       tlaste();
+#endif
+                       laste = 0;
+                       synced();
+                       nochng();
+                       continue;
+
+/* file */
+               case 'f':
+                       tail("file");
+                       setnoaddr();
+                       filename(c);
+                       noonl();
+/*
+                       synctmp();
+*/
+                       continue;
+
+/* global */
+               case 'g':
+                       tail("global");
+                       global(!exclam());
+                       nochng();
+                       continue;
+
+/* insert */
+               case 'i':
+                       if (inopen)
+                               goto notinvis;
+                       tail("insert");
+                       setdot();
+                       nonzero();
+                       aiflag = exclam();
+                       newline();
+                       vmacchng(0);
+                       deletenone();
+                       setin(addr2);
+                       inappend = 1;
+                       ignore(append(gettty, addr2 - 1));
+                       inappend = 0;
+                       if (dot == zero && dol > zero)
+                               dot = one;
+                       nochng();
+                       continue;
+
+/* join */
+               case 'j':
+                       tail("join");
+                       c = exclam();
+                       setcount();
+                       nonzero();
+                       newline();
+                       vmacchng(0);
+                       if (given < 2 && addr2 != dol)
+                               addr2++;
+                       join(c);
+                       continue;
+
+/* k */
+               case 'k':
+casek:
+                       pastwh();
+                       c = getchar();
+                       if (endcmd(c))
+                               serror(catgets(catd, 1, 19,
+               "Mark what?|%s requires following letter"), Command);
+                       newline();
+                       if (!islower(c))
+                               error(catgets(catd, 1, 20,
+                               "Bad mark|Mark must specify a letter"));
+                       setdot();
+                       nonzero();
+                       names[c - 'a'] = *addr2 &~ 01;
+                       anymarks = 1;
+                       continue;
+
+/* list */
+               case 'l':
+                       tail("list");
+                       setCNL();
+                       ignorf(setlist(1));
+                       pflag = 0;
+                       goto print;
+
+               case 'm':
+                       if (peekchar() == 'a') {
+                               ignchar();
+                               if (peekchar() == 'p') {
+/* map */
+                                       tail2of("map");
+                                       setnoaddr();
+                                       mapcmd(0, 0);
+                                       continue;
+                               }
+/* mark */
+                               tail2of("mark");
+                               goto casek;
+                       }
+/* move */
+                       tail("move");
+                       vmacchng(0);
+                       move();
+                       continue;
+
+               case 'n':
+                       if (peekchar() == 'u') {
+                               tail("number");
+                               goto numberit;
+                       }
+/* next */
+                       tail("next");
+                       setnoaddr();
+                       if (!exclam())
+                               ckaw();
+                       ignore(quickly());
+                       if (getargs())
+                               makargs();
+                       next();
+                       c = 'e';
+                       filename(c);
+                       if (recov)
+                               goto recovnext;
+                       goto doecmd;
+
+/* open */
+               case 'o':
+                       tail("open");
+                       oop();
+                       pflag = 0;
+                       nochng();
+                       continue;
+
+               case 'p':
+               case 'P':
+                       switch (peekchar()) {
+
+/* put */
+                       case 'u':
+                               tail("put");
+                               setdot();
+                               c = cmdreg();
+                               eol();
+                               vmacchng(0);
+                               if (c)
+                                       putreg(c);
+                               else
+                                       put(0);
+                               continue;
+
+                       case 'r':
+                               ignchar();
+                               if (peekchar() == 'e') {
+/* preserve */
+                                       tail2of("preserve");
+                                       eol();
+                                       if (preserve() == 0)
+                                               error(catgets(catd, 1, 21,
+                                                       "Preserve failed!"));
+                                       else
+                                               error(catgets(catd, 1, 22,
+                                                       "File preserved."));
+                               }
+                               tail2of("print");
+                               break;
+
+                       default:
+                               tail("print");
+                               break;
+                       }
+/* print */
+                       setCNL();
+                       pflag = 0;
+print:
+                       nonzero();
+                       if (CL && span() > TLINES) {
+                               flush1();
+                               vclear();
+                       }
+                       plines(addr1, addr2, 1);
+                       continue;
+
+/* quit */
+               case 'q':
+                       tail("quit");
+                       setnoaddr();
+                       c = quickly();
+                       eol();
+                       if (!c)
+quit:
+                               nomore();
+                       if (inopen) {
+                               vgoto(WECHO, 0);
+                               if (!ateopr())
+                                       vnfl();
+                               else {
+                                       tostop();
+                               }
+                               flush();
+                               setty(normf);
+                       }
+                       cleanup(1);
+                       exitex(0);
+
+               case 'r':
+                       if (peekchar() == 'e') {
+                               ignchar();
+                               switch (peekchar()) {
+
+/* rewind */
+                               case 'w':
+                                       tail2of("rewind");
+                                       setnoaddr();
+                                       if (!exclam()) {
+                                               ckaw();
+                                               if (chng && dol > zero)
+                                                       error(catgets(catd,
+               1, 23, "No write@since last change (:rewind! overrides)"));
+                                       }
+                                       eol();
+                                       erewind();
+                                       next();
+                                       c = 'e';
+                                       ungetchar(lastchar());
+                                       filename(c);
+                                       goto doecmd;
+
+/* recover */
+                               case 'c':
+                                       tail2of("recover");
+                                       setnoaddr();
+                                       c = 'e';
+                                       if (!exclam() && chng)
+                                               c = 'E';
+                                       filename(c);
+                                       if (c == 'E') {
+                                               ungetchar(lastchar());
+                                               ignore(quickly());
+                                       }
+recovnext:
+                                       init();
+                                       addr2 = zero;
+                                       laste++;
+                                       synced();
+                                       recover();
+                                       rop2();
+                                       revocer();
+                                       if (status == 0)
+                                               rop3(c);
+                                       if (dol != zero)
+                                               change();
+#ifdef INCORB
+                                       tlaste();
+#endif
+                                       laste = 0;
+                                       nochng();
+                                       continue;
+                               }
+                               tail2of("read");
+                       } else
+                               tail("read");
+/* read */
+                       if (savedfile[0] == 0 && dol == zero)
+                               c = 'e';
+                       pastwh();
+                       vmacchng(0);
+                       if (peekchar() == '!') {
+                               setdot();
+                               ignchar();
+                               unix0(0);
+                               filter(0);
+                               continue;
+                       }
+                       filename(c);
+                       rop(c);
+                       nochng();
+                       if (inopen && endline && addr1 > zero && addr1 < dol)
+                               dot = addr1 + 1;
+                       continue;
+
+               case 's':
+                       switch (peekchar()) {
+                       /*
+                        * Caution: 2nd char cannot be c, g, or r
+                        * because these have meaning to substitute.
+                        */
+
+/* set */
+                       case 'e':
+                               tail("set");
+                               setnoaddr();
+                               set();
+                               continue;
+
+/* shell */
+                       case 'h':
+                               tail("shell");
+                               setNAEOL();
+                               vnfl();
+                               putpad(TE);
+                               flush();
+                               unixwt(1, unixex("-i", (char *) 0, 0, 0));
+                               vcontin(0);
+                               continue;
+
+/* source */
+                       case 'o':
+#ifdef notdef
+                               if (inopen)
+                                       goto notinvis;
+#endif
+                               tail("source");
+                               setnoaddr();
+                               getone();
+                               eol();
+                               source(file, 0);
+                               continue;
+#ifdef SIGTSTP
+/* stop, suspend */
+                       case 't':
+                               tail("stop");
+                               goto suspend;
+                       case 'u':
+                               getchar();
+                               if (peekchar() == 'b') {
+                                       tail2of("substitute");
+                                       goto substitute;
+                               }
+                               tail2of("suspend");
+suspend:
+                               if (!ldisc)
+                                       error(catgets(catd, 1, 24,
+                       "Old tty driver|Not using new tty driver/shell"));
+                               c = exclam();
+                               eol();
+                               if (!c)
+                                       ckaw();
+#ifdef SIGTSTP
+                               onsusp(SIGTSTP);
+#endif
+                               continue;
+#endif
+
+                       }
+                       /* fall into ... */
+
+/* & */
+/* ~ */
+/* substitute */
+               case '&':
+               case '~':
+                       Command = "substitute";
+                       if (c == 's')
+                               tail(Command);
+substitute:
+                       vmacchng(0);
+                       if (!substitute(c))
+                               pflag = 0;
+                       continue;
+
+/* t */
+               case 't':
+                       if (peekchar() == 'a') {
+                               tail("tag");
+                               tagfind(exclam());
+                               if (!inopen)
+                                       lchng = chng - 1;
+                               else
+                                       nochng();
+                               continue;
+                       }
+                       tail("t");
+                       vmacchng(0);
+                       move();
+                       continue;
+
+               case 'u':
+                       if (peekchar() == 'n') {
+                               ignchar();
+                               switch(peekchar()) {
+/* unmap */
+                               case 'm':
+                                       tail2of("unmap");
+                                       setnoaddr();
+                                       mapcmd(1, 0);
+                                       continue;
+/* unabbreviate */
+                               case 'a':
+                                       tail2of("unabbreviate");
+                                       setnoaddr();
+                                       mapcmd(1, 1);
+                                       anyabbrs = 1;
+                                       continue;
+                               }
+/* undo */
+                               tail2of("undo");
+                       } else
+                               tail("undo");
+                       setnoaddr();
+                       markDOT();
+                       c = exclam();
+                       newline();
+                       undo(c);
+                       continue;
+
+               case 'v':
+                       switch (peekchar()) {
+
+                       case 'e':
+/* version */
+                               tail("version");
+                               setNAEOL();
+                               printver();
+                               noonl();
+                               continue;
+
+/* visual */
+                       case 'i':
+                               tail("visual");
+                               if (inopen) {
+                                       c = 'e';
+                                       goto editcmd;
+                               }
+                               vop();
+                               pflag = 0;
+                               nochng();
+                               continue;
+                       }
+/* v */
+                       tail("v");
+                       global(0);
+                       nochng();
+                       continue;
+
+/* write */
+               case 'w':
+                       c = peekchar();
+                       tail(c == 'q' ? "wq" : "write");
+wq:
+                       if (skipwh() && peekchar() == '!') {
+                               pofix();
+                               ignchar();
+                               setall();
+                               unix0(0);
+                               filter(1);
+                       } else {
+                               setall();
+                               wop(1);
+                               nochng();
+                       }
+                       if (c == 'q')
+                               goto quit;
+                       continue;
+
+/* xit */
+               case 'x':
+                       tail("xit");
+                       if (!chng)
+                               goto quit;
+                       c = 'q';
+                       goto wq;
+
+/* yank */
+               case 'y':
+                       tail("yank");
+                       c = cmdreg();
+                       setcount();
+                       eol();
+                       vmacchng(0);
+                       if (c)
+                               YANKreg(c);
+                       else
+                               yank(0);
+                       vkillDEL();
+                       continue;
+
+/* z */
+               case 'z':
+                       zop(0);
+                       pflag = 0;
+                       continue;
+
+/* * */
+/* @ */
+               case '*':
+               case '@':
+                       c = getchar();
+                       if (c=='\n' || c=='\r')
+                               ungetchar(c);
+                       if (any(c, "@*\n\r")) {
+                               c = lastmac;
+                               if (c == 0)
+                                       failed = 1;
+                       }
+                       if (isupper(c))
+                               c = tolower(c);
+                       if (!islower(c))
+                               error(catgets(catd, 1, 25, "Bad register"));
+                       newline();
+                       setdot();
+                       cmdmac(c);
+                       continue;
+
+/* | */
+               case '|':
+                       endline = 0;
+                       goto caseline;
+
+/* \n */
+               case '\n':
+                       endline = 1;
+caseline:
+                       notempty();
+                       if (addr2 == 0) {
+                               if (UP != NOSTR && c == '\n' && !inglobal)
+                                       c = CTRL('k');
+                               if (inglobal)
+                                       addr1 = addr2 = dot;
+                               else {
+                                       if (dot == dol)
+                                               error(catgets(catd, 1, 26,
+                                               "At EOF|At end-of-file"));
+                                       addr1 = addr2 = dot + 1;
+                               }
+                       }
+                       setdot();
+                       nonzero();
+                       if (seensemi)
+                               addr1 = addr2;
+                       getline(*addr1);
+                       if (c == CTRL('k')) {
+                               flush1();
+                               destline--;
+                               if (hadpr)
+                                       shudclob = 1;
+                       }
+                       plines(addr1, addr2, 1);
+                       continue;
+
+/* " */
+               case '"':
+                       comment();
+                       continue;
+
+/* # */
+               case '#':
+numberit:
+                       setCNL();
+                       ignorf(setnumb(1));
+                       pflag = 0;
+                       goto print;
+
+/* = */
+               case '=':
+                       newline();
+                       setall();
+                       if (inglobal == 2)
+                               pofix();
+                       printf("%d", lineno(addr2));
+                       noonl();
+                       continue;
+
+/* ! */
+               case '!':
+                       if (addr2 != 0) {
+                               vmacchng(0);
+                               unix0(0);
+                               setdot();
+                               filter(2);
+                       } else {
+                               unix0(1);
+                               pofix();
+                               putpad(TE);
+                               flush();
+                               unixwt(1, unixex("-c", uxb, 0, 0));
+                               vclrech(1);     /* vcontin(0); */
+                               nochng();
+                       }
+                       continue;
+
+/* < */
+/* > */
+               case '<':
+               case '>':
+                       for (cnt = 1; peekchar() == c; cnt++)
+                               ignchar();
+                       setCNL();
+                       vmacchng(0);
+                       shift(c, cnt);
+                       continue;
+
+/* ^D */
+/* EOF */
+               case CTRL('d'):
+               case EOF:
+                       if (exitoneof) {
+                               if (addr2 != 0)
+                                       dot = addr2;
+                               return;
+                       }
+                       if (!isatty(0)) {
+                               if (intty)
+                                       /*
+                                        * Chtty sys call at UCB may cause a
+                                        * input which was a tty to suddenly be
+                                        * turned into /dev/null.
+                                        */
+                                       onhup(SIGHUP);
+                               return;
+                       }
+                       if (addr2 != 0) {
+                               setlastchar('\n');
+                               putnl();
+                       }
+                       if (dol == zero) {
+                               if (addr2 == 0)
+                                       putnl();
+                               notempty();
+                       }
+                       ungetchar(EOF);
+                       zop(hadpr);
+                       continue;
+
+               default:
+                       if (!isalpha(c) && (c&0200) == 0)
+                               break;
+                       ungetchar(c);
+                       tailprim("", 0, 0);
+               }
+               error(catgets(catd, 1, 27,
+                               "What?|Unknown command character '%c'"), c);
+       }
+}
diff --git a/ex_cmds2.c b/ex_cmds2.c
new file mode 100644 (file)
index 0000000..8d822bd
--- /dev/null
@@ -0,0 +1,674 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_cmds2.c 1.18 (gritter) 2/17/05";
+#endif
+#endif
+
+/* from ex_cmds2.c     7.4 (Berkeley) 6/7/85 */
+
+#include "ex.h"
+#include "ex_argv.h"
+#include "ex_temp.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+
+extern bool    pflag, nflag;           /* mjm: extern; also in ex_cmds.c */
+extern int     poffset;                /* mjm: extern; also in ex_cmds.c */
+
+/*
+ * Subroutines for major command loop.
+ */
+
+/*
+ * Is there a single letter indicating a named buffer next?
+ */
+int 
+cmdreg(void)
+{
+       register int c = 0;
+       register int wh = skipwh();
+
+       if (wh && isalpha(peekchar()))
+               c = getchar();
+       return (c);
+}
+
+/*
+ * Tell whether the character ends a command
+ */
+int 
+endcmd(int ch)
+{
+       switch (ch) {
+       
+       case '\n':
+       case EOF:
+               endline = 1;
+               return (1);
+       
+       case '|':
+       case '"':
+               endline = 0;
+               return (1);
+       }
+       return (0);
+}
+
+/*
+ * Insist on the end of the command.
+ */
+void 
+eol(void)
+{
+
+       if (!skipend())
+               error(catgets(catd, 1, 28,
+                       "Extra chars|Extra characters at end of command"));
+       ignnEOF();
+}
+
+/*
+ * Guts of the pre-printing error processing.
+ * If in visual and catching errors, then we dont mung up the internals,
+ * just fixing up the echo area for the print.
+ * Otherwise we reset a number of externals, and discard unused input.
+ */
+void 
+error0(void)
+{
+
+       if (vcatch) {
+               if (splitw == 0)
+                       fixech();
+               if (!SO || !SE)
+                       dingdong();
+               return;
+       }
+       if (input && exitoneof) {
+               input = strend(input) - 1;
+               if (*input == '\n')
+                       setlastchar('\n');
+               input = 0;
+       }
+       setoutt();
+       flush();
+       resetflav();
+       if (!SO || !SE)
+               dingdong();
+       if (inopen) {
+               /*
+                * We are coming out of open/visual ungracefully.
+                * Restore TCOLUMNS, undo, and fix tty mode.
+                */
+               TCOLUMNS = OCOLUMNS;
+               undvis();
+               ostop(normf);
+               /* ostop should be doing this
+               putpad(VE);
+               putpad(KE);
+               */
+               putnl();
+       }
+       inopen = 0;
+       holdcm = 0;
+}
+
+/*
+ * Post error printing processing.
+ * Close the i/o file if left open.
+ * If catching in visual then throw to the visual catch,
+ * else if a child after a fork, then exit.
+ * Otherwise, in the normal command mode error case,
+ * finish state reset, and throw to top.
+ */
+int 
+error1(char *str)
+{
+       bool die;
+
+       if (io > 0) {
+               close(io);
+               io = -1;
+       }
+       die = (getpid() != ppid);       /* Only children die */
+       inappend = inglobal = 0;
+       globp = NULL, vmacp = NULL, vglobp = NULL;
+       if (vcatch && !die) {
+               inopen = 1;
+               vcatch = 0;
+               if (str)
+                       noonl();
+               fixol();
+               LONGJMP(vreslab,1);
+       }
+       if (str && !vcatch)
+               putNFL();
+       if (die)
+               exitex(1);
+       if (exitoneof)
+               lseek(0, (off_t)0, SEEK_END);
+       if (inglobal)
+               setlastchar('\n');
+       while (lastchar() != '\n' && lastchar() != EOF)
+               ignchar();
+       ungetchar(0);
+       endline = 1;
+       reset();
+}
+
+/*
+ * Print out the message in the error message file at str,
+ * with i an integer argument to printf.
+ */
+/*VARARGS2*/
+void
+error(char *str, ...)
+{
+       va_list ap;
+
+       error0();
+       va_start(ap, str);
+       vmerror(str, ap);
+       va_end(ap);
+       
+       if (writing) {
+               serror(catgets(catd, 1, 29,
+                               " [Warning - %s is incomplete]"), file);
+               writing = 0;
+       }
+       error1(str);
+}
+
+/*
+ * Same as error(), but using a va_list as argument.
+ */
+void
+verror(char *str, va_list ap)
+{
+       error0();
+       vmerror(str, ap);
+       error(NULL);
+
+       if (writing) {
+               serror(catgets(catd, 1, 29,
+                               " [Warning - %s is incomplete]"), file);
+               writing = 0;
+       }
+       error1(str);
+}
+
+/*
+ * Rewind the argument list.
+ */
+void
+erewind(void)
+{
+
+       argc = argc0;
+       argv = argv0;
+       args = args0;
+       if (argc > 1 && !hush) {
+               printf(mesg(catgets(catd, 1, 30, "%d files@to edit")), argc);
+               if (inopen)
+                       putchar(' ');
+               else
+                       putNFL();
+       }
+}
+
+void
+fixol(void)
+{
+       if (Outchar != vputchar) {
+               flush();
+               if (state == ONEOPEN || state == HARDOPEN)
+                       outline = destline = 0;
+               Outchar = vputchar;
+               vcontin(1);
+       } else {
+               if (destcol)
+                       vclreol();
+               vclean();
+       }
+}
+
+/*
+ * Does an ! character follow in the command stream?
+ */
+int
+exclam(void)
+{
+
+       if (peekchar() == '!') {
+               ignchar();
+               return (1);
+       }
+       return (0);
+}
+
+/*
+ * Make an argument list for e.g. next.
+ */
+void
+makargs(void)
+{
+
+       gglob(&frob);
+       argc0 = frob.argc0;
+       argv0 = frob.argv;
+       args0 = argv0[0];
+       erewind();
+}
+
+/*
+ * Advance to next file in argument list.
+ */
+void
+next(void)
+{
+       extern short isalt;     /* defined in ex_io.c */
+
+       if (argc == 0)
+               error(catgets(catd, 1, 31, "No more files@to edit"));
+       morargc = argc;
+       isalt = (strcmp(altfile, args)==0) + 1;
+       if (savedfile[0])
+               strcpy(altfile, savedfile);
+       safecp(savedfile, args, sizeof savedfile, "File name too long");
+       argc--;
+       args = argv ? *++argv : strend(args) + 1;
+}
+
+/*
+ * Eat trailing flags and offsets after a command,
+ * saving for possible later post-command prints.
+ */
+void
+newline(void)
+{
+       register int c;
+
+       resetflav();
+       for (;;) {
+               c = getchar();
+               switch (c) {
+
+               case '^':
+               case '-':
+                       poffset--;
+                       break;
+
+               case '+':
+                       poffset++;
+                       break;
+
+               case 'l':
+                       listf++;
+                       break;
+
+               case '#':
+                       nflag++;
+                       break;
+
+               case 'p':
+                       listf = 0;
+                       break;
+
+               case ' ':
+               case '\t':
+                       continue;
+
+               case '"':
+                       comment();
+                       setflav();
+                       return;
+
+               default:
+                       if (!endcmd(c))
+serror(catgets(catd, 1, 32,
+       "Extra chars|Extra characters at end of \"%s\" command"), Command);
+                       if (c == EOF)
+                               ungetchar(c);
+                       setflav();
+                       return;
+               }
+               pflag++;
+       }
+}
+
+/*
+ * Before quit or respec of arg list, check that there are
+ * no more files in the arg list.
+ */
+void
+nomore(void)
+{
+
+       if (argc == 0 || morargc == argc)
+               return;
+       morargc = argc;
+       merror(catgets(catd, 1, 33, "%d more file"), argc);
+       serror(catgets(catd, 1, 34, "%s@to edit"), plural((long) argc));
+}
+
+/*
+ * Before edit of new file check that either an ! follows
+ * or the file has not been changed.
+ */
+int
+quickly(void)
+{
+
+       if (exclam())
+               return (1);
+       if (chng && dol > zero) {
+/*
+               chng = 0;
+*/
+               xchng = 0;
+               error(catgets(catd, 1, 35,
+               "No write@since last change (:%s! overrides)"), Command);
+       }
+       return (0);
+}
+
+/*
+ * Reset the flavor of the output to print mode with no numbering.
+ */
+void
+resetflav(void)
+{
+
+       if (inopen)
+               return;
+       listf = 0;
+       nflag = 0;
+       pflag = 0;
+       poffset = 0;
+       setflav();
+}
+
+/*
+ * Print an error message with a %s type argument to printf.
+ * Message text comes from error message file.
+ */
+void
+serror(char *str, ...)
+{
+       va_list ap;
+
+       if (str == NULL)
+               return;
+       va_start(ap, str);
+       error0();
+       vsmerror(str, ap);
+       error1(str);
+       va_end(ap);
+}
+
+/*
+ * Set the flavor of the output based on the flags given
+ * and the number and list options to either number or not number lines
+ * and either use normally decoded (ARPAnet standard) characters or list mode,
+ * where end of lines are marked and tabs print as ^I.
+ */
+void
+setflav(void)
+{
+
+       if (inopen)
+               return;
+       setnumb(nflag || value(NUMBER));
+       setlist(listf || value(LIST));
+       setoutt();
+}
+
+/*
+ * Skip white space and tell whether command ends then.
+ */
+int
+skipend(void)
+{
+
+       pastwh();
+       return (endcmd(peekchar()) && peekchar() != '"');
+}
+
+/*
+ * Set the command name for non-word commands.
+ */
+void
+tailspec(int c)
+{
+       static char foocmd[2];
+
+       foocmd[0] = c;
+       Command = foocmd;
+}
+
+/*
+ * Try to read off the rest of the command word.
+ * If alphabetics follow, then this is not the command we seek.
+ */
+void
+tail(char *comm)
+{
+
+       tailprim(comm, 1, 0);
+}
+
+void
+tail2of(char *comm)
+{
+
+       tailprim(comm, 2, 0);
+}
+
+char   tcommand[20];
+
+void
+tailprim(register char *comm, int xi, bool notinvis)
+{
+       register char *cp;
+       register int c;
+       int     i = xi;
+
+       Command = comm;
+       for (cp = tcommand; i > 0; i--)
+               *cp++ = *comm++;
+       while (*comm && peekchar() == *comm)
+               *cp++ = getchar(), comm++;
+       c = peekchar();
+       if (notinvis || isalpha(c)
+#ifdef BIT8
+                       || xi == 0 && (c&(0200|QUOTE)) == 0200
+#endif
+                       ) {
+               /*
+                * Of the trailing lp funny business, only dl and dp
+                * survive the move from ed to ex.
+                */
+               if (tcommand[0] == 'd' && any(c, "lp"))
+                       goto ret;
+               if (tcommand[0] == 's' && any(c, "gcr"))
+                       goto ret;
+               while (cp < &tcommand[19] && (c = peekchar(),
+                               isalpha(c)
+#ifdef BIT8
+                               || xi == 0 && (c&(0200|QUOTE)) == 0200
+#endif
+                               ))
+                       *cp++ = getchar();
+               *cp = 0;
+               if (notinvis)
+                       serror(catgets(catd, 1, 36,
+               "What?|%s: No such command from open/visual"), tcommand);
+               else
+                       serror(catgets(catd, 1, 37,
+                               "What?|%s: Not an editor command"), tcommand);
+       }
+ret:
+       *cp = 0;
+}
+
+/*
+ * Continue after a : command from open/visual.
+ */
+void
+vcontin(bool ask)
+{
+
+       if (vcnt > 0)
+               vcnt = -vcnt;
+       if (inopen) {
+               if (state != VISUAL) {
+                       /*
+                        * We don't know what a shell command may have left on
+                        * the screen, so we move the cursor to the right place
+                        * and then put out a newline.  But this makes an extra
+                        * blank line most of the time so we only do it for :sh
+                        * since the prompt gets left on the screen.
+                        *
+                        * BUG: :!echo longer than current line \\c
+                        * will screw it up, but be reasonable!
+                        */
+                       if (state == CRTOPEN) {
+                               termreset();
+                               vgoto(WECHO, 0);
+                       }
+                       if (!ask) {
+                               putch('\r');
+                               putch('\n');
+                       }
+                       return;
+               }
+               if (ask) {
+                       merror(catgets(catd, 1, 38,
+                                       "[Hit return to continue] "));
+                       flush();
+               }
+               if (ask) {
+#ifdef EATQS
+                       /*
+                        * Gobble ^Q/^S since the tty driver should be eating
+                        * them (as far as the user can see)
+                        */
+                       while (peekkey(0) == CTRL('Q')
+                                       || peekkey() == CTRL('S'))
+                               ignore(getkey());
+#endif
+                       if(getkey() == ':') {
+                               /* Ugh. Extra newlines, but no other way */
+                               putch('\n');
+                               outline = WECHO;
+                               ungetkey(':');
+                       }
+               }
+               vclrech(1);
+               if (Peekkey != ':') {
+                       putpad(TI);
+                       tostart();
+                       /* replaced by ostart.
+                       putpad(VS);
+                       putpad(KS);
+                       */
+               }
+       }
+}
+
+/*
+ * Put out a newline (before a shell escape)
+ * if in open/visual.
+ */
+void
+vnfl(void)
+{
+
+       if (inopen) {
+               if (state != VISUAL && state != CRTOPEN && destline <= WECHO)
+                       vclean();
+               else
+                       vmoveitup(1, 0);
+               vgoto(WECHO, 0);
+               vclrcell(vtube[WECHO], WCOLS);
+               tostop();
+               /* replaced by the ostop above
+               putpad(VE);
+               putpad(KE);
+               */
+       }
+       flush();
+}
diff --git a/ex_cmdsub.c b/ex_cmdsub.c
new file mode 100644 (file)
index 0000000..1f798d7
--- /dev/null
@@ -0,0 +1,1443 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_cmdsub.c        1.29 (gritter) 2/17/05";
+#endif
+#endif
+
+/* from ex_cmdsub.c    7.7 (Berkeley) 6/7/85 */
+
+#include "ex.h"
+#include "ex_argv.h"
+#include "ex_temp.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+
+/*
+ * Command mode subroutines implementing
+ *     append, args, copy, delete, join, move, put,
+ *     shift, tag, yank, z and undo
+ */
+
+bool   endline = 1;
+line   *tad1;
+static int jnoop(void);
+
+/*
+ * Append after line a lines returned by function f.
+ * Be careful about intermediate states to avoid scramble
+ * if an interrupt comes in.
+ */
+int 
+append(int (*f)(void), line *a)
+{
+       register line *a1, *a2, *rdot;
+       int nline;
+
+       nline = 0;
+       dot = a;
+       if(FIXUNDO && !inopen && f!=getsub) {
+               undap1 = undap2 = dot + 1;
+               undkind = UNDCHANGE;
+       }
+       while ((*f)() == 0) {
+               if (truedol >= endcore) {
+                       if (morelines() < 0) {
+                               if (FIXUNDO && f == getsub) {
+                                       undap1 = addr1;
+                                       undap2 = addr2 + 1;
+                               }
+                               error(catgets(catd, 1, 39,
+                               "Out of memory@- too many lines in file"));
+                       }
+               }
+               nline++;
+               a1 = truedol + 1;
+               a2 = a1 + 1;
+               dot++;
+               undap2++;
+               dol++;
+               unddol++;
+               truedol++;
+               for (rdot = dot; a1 > rdot;)
+                       *--a2 = *--a1;
+               *rdot = 0;
+               putmark(rdot);
+               if (f == gettty) {
+                       dirtcnt++;
+                       TSYNC();
+               }
+       }
+       return (nline);
+}
+
+void 
+appendnone(void)
+{
+
+       if(FIXUNDO) {
+               undkind = UNDCHANGE;
+               undap1 = undap2 = addr1;
+       }
+}
+
+/*
+ * Print out the argument list, with []'s around the current name.
+ */
+void 
+pargs(void)
+{
+       register char **av = argv0, *as = args0;
+       register int ac;
+
+       for (ac = 0; ac < argc0; ac++) {
+               if (ac != 0)
+                       putchar(' ' | QUOTE);
+               if (ac + argc == argc0 - 1)
+                       printf("[");
+               lprintf("%s", as);
+               if (ac + argc == argc0 - 1)
+                       printf("]");
+               as = av ? *++av : strend(as) + 1;
+       }
+       noonl();
+}
+
+/*
+ * Delete lines; two cases are if we are really deleting,
+ * more commonly we are just moving lines to the undo save area.
+ */
+void 
+delete(int hush)
+{
+       register line *a1, *a2;
+
+       nonzero();
+       if(FIXUNDO) {
+               register shand dsavint;
+
+#ifdef TRACE
+               if (trace)
+                       vudump("before delete");
+#endif
+               change();
+               dsavint = signal(SIGINT, SIG_IGN);
+               undkind = UNDCHANGE;
+               a1 = addr1;
+               squish();
+               a2 = addr2;
+               if (a2++ != dol) {
+                       reverse(a1, a2);
+                       reverse(a2, dol + 1);
+                       reverse(a1, dol + 1);
+               }
+               dol -= a2 - a1;
+               unddel = a1 - 1;
+               if (a1 > dol)
+                       a1 = dol;
+               dot = a1;
+               pkill[0] = pkill[1] = 0;
+               signal(SIGINT, dsavint);
+#ifdef TRACE
+               if (trace)
+                       vudump("after delete");
+#endif
+       } else {
+               register line *a3;
+               register int i;
+
+               change();
+               a1 = addr1;
+               a2 = addr2 + 1;
+               a3 = truedol;
+               i = a2 - a1;
+               unddol -= i;
+               undap2 -= i;
+               dol -= i;
+               truedol -= i;
+               do
+                       *a1++ = *a2++;
+               while (a2 <= a3);
+               a1 = addr1;
+               if (a1 > dol)
+                       a1 = dol;
+               dot = a1;
+       }
+       if (!hush)
+               killed();
+}
+
+void 
+deletenone(void)
+{
+
+       if(FIXUNDO) {
+               undkind = UNDCHANGE;
+               squish();
+               unddel = addr1;
+       }
+}
+
+/*
+ * Crush out the undo save area, moving the open/visual
+ * save area down in its place.
+ */
+void 
+squish(void)
+{
+       register line *a1 = dol + 1, *a2 = unddol + 1, *a3 = truedol + 1;
+
+       if(FIXUNDO) {
+               if (inopen == -1)
+                       return;
+               if (a1 < a2 && a2 < a3)
+                       do
+                               *a1++ = *a2++;
+                       while (a2 < a3);
+               truedol -= unddol - dol;
+               unddol = dol;
+       }
+}
+
+static int     jcount;
+
+/*
+ * Join lines.  Special hacks put in spaces, two spaces if
+ * preceding line ends with '.', or no spaces if next line starts with ).
+ */
+void 
+join(int c)
+{
+       register line *a1;
+       register char *cp, *cp1;
+
+       cp = genbuf;
+       *cp = 0;
+       for (a1 = addr1; a1 <= addr2; a1++) {
+               getline(*a1);
+               cp1 = linebuf;
+               if (a1 != addr1 && c == 0) {
+                       while (*cp1 == ' ' || *cp1 == '\t')
+                               cp1++;
+                       if (*cp1 && cp > genbuf && cp[-1] != ' ' && cp[-1] != '\t') {
+                               if (*cp1 != ')') {
+                                       *cp++ = ' ';
+                                       if (cp[-2] == '.')
+                                               *cp++ = ' ';
+                               }
+                       }
+               }
+               while (*cp++ = *cp1++)
+                       if (cp > &genbuf[LBSIZE-2])
+                               error(catgets(catd, 1, 40,
+               "Line overflow|Result line of join would be too long"));
+               cp--;
+       }
+       strcLIN(genbuf);
+       delete(0);
+       jcount = 1;
+       if (FIXUNDO)
+               undap1 = undap2 = addr1;
+       ignore(append(jnoop, --addr1));
+       if (FIXUNDO)
+               vundkind = VMANY;
+}
+
+static int
+jnoop(void)
+{
+
+       return(--jcount);
+}
+
+/*
+ * Move and copy lines.  Hard work is done by move1 which
+ * is also called by undo.
+ */
+
+void 
+move1(int cflag, line *addrt)
+{
+       register line *adt, *ad1, *ad2;
+       int lines;
+
+       adt = addrt;
+       lines = (addr2 - addr1) + 1;
+       if (cflag) {
+               tad1 = addr1;
+               ad1 = dol;
+               ignore(append(getcopy, ad1++));
+               ad2 = dol;
+       } else {
+               ad2 = addr2;
+               for (ad1 = addr1; ad1 <= ad2;)
+                       *ad1++ &= ~01;
+               ad1 = addr1;
+       }
+       ad2++;
+       if (adt < ad1) {
+               if (adt + 1 == ad1 && !cflag && !inglobal)
+                       error(catgets(catd, 1, 41,
+                                       "That move would do nothing!"));
+               dot = adt + (ad2 - ad1);
+               if (++adt != ad1) {
+                       reverse(adt, ad1);
+                       reverse(ad1, ad2);
+                       reverse(adt, ad2);
+               }
+       } else if (adt >= ad2) {
+               dot = adt++;
+               reverse(ad1, ad2);
+               reverse(ad2, adt);
+               reverse(ad1, adt);
+       } else
+               error(catgets(catd, 1, 42, "Move to a moved line"));
+       change();
+       if (!inglobal)
+               if(FIXUNDO) {
+                       if (cflag) {
+                               undap1 = addrt + 1;
+                               undap2 = undap1 + lines;
+                               deletenone();
+                       } else {
+                               undkind = UNDMOVE;
+                               undap1 = addr1;
+                               undap2 = addr2;
+                               unddel = addrt;
+                               squish();
+                       }
+               }
+}
+
+void 
+move(void)
+{
+       register line *adt;
+       bool iscopy = 0;
+
+       if (Command[0] == 'm') {
+               setdot1();
+               markpr(addr2 == dot ? addr1 - 1 : addr2 + 1);
+       } else {
+               iscopy++;
+               setdot();
+       }
+       nonzero();
+       adt = address((char*)0);
+       if (adt == 0)
+               serror(catgets(catd, 1, 43,
+                       "%s where?|%s requires a trailing address"), Command);
+       newline();
+       move1(iscopy, adt);
+       killed();
+}
+
+int 
+getcopy(void)
+{
+
+       if (tad1 > addr2)
+               return (EOF);
+       getline(*tad1++);
+       return (0);
+}
+
+/*
+ * Put lines in the buffer from the undo save area.
+ */
+int 
+getput(void)
+{
+
+       if (tad1 > unddol)
+               return (EOF);
+       getline(*tad1++);
+       tad1++;
+       return (0);
+}
+
+/*ARGSUSED*/
+void 
+put(int unused)
+{
+       register int cnt;
+
+       if (!FIXUNDO)
+               error(catgets(catd, 1, 44, "Cannot put inside global/macro"));
+       cnt = unddol - dol;
+       if (cnt && inopen && pkill[0] && pkill[1]) {
+               pragged(1);
+               return;
+       }
+       tad1 = dol + 1;
+       ignore(append(getput, addr2));
+       undkind = UNDPUT;
+       notecnt = cnt;
+       netchange(cnt);
+}
+
+/*
+ * A tricky put, of a group of lines in the middle
+ * of an existing line.  Only from open/visual.
+ * Argument says pkills have meaning, e.g. called from
+ * put; it is 0 on calls from putreg.
+ */
+void 
+pragged(int kill)
+{
+       extern char *cursor;
+       register char *gp = &genbuf[cursor - linebuf];
+
+       /*
+        * This kind of stuff is TECO's forte.
+        * We just grunge along, since it cuts
+        * across our line-oriented model of the world
+        * almost scrambling our addled brain.
+        */
+       if (!kill)
+               getDOT();
+       strcpy(genbuf, linebuf);
+       getline(*unddol);
+       if (kill)
+               *pkill[1] = 0;
+       strcat(linebuf, gp);
+       putmark(unddol);
+       getline(dol[1]);
+       if (kill)
+               strcLIN(pkill[0]);
+       safecp(gp, linebuf, sizeof genbuf - (gp - genbuf), "Line too long");
+       strcLIN(genbuf);
+       putmark(dol+1);
+       undkind = UNDCHANGE;
+       undap1 = dot;
+       undap2 = dot + 1;
+       unddel = dot - 1;
+       undo(1);
+}
+
+/*
+ * Shift lines, based on c.
+ * If c is neither < nor >, then this is a lisp aligning =.
+ */
+void 
+shift(int c, int cnt)
+{
+       register line *addr;
+       register char *cp = NULL;
+       char *dp;
+       register int i;
+
+       if(FIXUNDO)
+               save12(), undkind = UNDCHANGE;
+       cnt *= value(SHIFTWIDTH);
+       for (addr = addr1; addr <= addr2; addr++) {
+               dot = addr;
+#ifdef LISPCODE
+               if (c == '=' && addr == addr1 && addr != addr2)
+                       continue;
+#endif
+               getDOT();
+               i = whitecnt(linebuf);
+               switch (c) {
+
+               case '>':
+                       if (linebuf[0] == 0)
+                               continue;
+                       cp = genindent(i + cnt);
+                       break;
+
+               case '<':
+                       if (i == 0)
+                               continue;
+                       i -= cnt;
+                       cp = i > 0 ? genindent(i) : genbuf;
+                       break;
+
+#ifdef LISPCODE
+               default:
+                       i = lindent(addr);
+                       getDOT();
+                       cp = genindent(i);
+                       break;
+#endif
+               }
+               if (cp + strlen(dp = vpastwh(linebuf)) >= &genbuf[LBSIZE - 2])
+                       error(catgets(catd, 1, 45,
+               "Line too long|Result line after shift would be too long"));
+               CP(cp, dp);
+               strcLIN(genbuf);
+               putmark(addr);
+       }
+       killed();
+}
+
+/*
+ * Find a tag in the tags file.
+ * Most work here is in parsing the tags file itself.
+ */
+void
+tagfind(bool quick)
+{
+       char cmdbuf[BUFSIZ];
+       char filebuf[FNSIZE];
+       char tagfbuf[128];
+       register int c, d;
+       bool samef = 1;
+       int tfcount = 0;
+       int omagic;
+       int owrapscan;
+       char *fn, *fne;
+       struct stat sbuf;
+       char *savefirstpat = NULL;
+       int     ofailed;
+#ifdef FASTTAG
+       int ft_iof;
+       char ft_iofbuf[MAXBSIZE];
+       off_t mid;      /* assumed byte offset */
+       off_t top, bot; /* length of tag file */
+#endif
+
+       omagic = value(MAGIC);
+       owrapscan = value(WRAPSCAN);
+       ofailed = failed;
+       failed = 1;
+       if (!skipend()) {
+               register char *lp = lasttag;
+
+               while (!is_white(peekchar()) && !endcmd(peekchar()))
+                       if (lp < &lasttag[sizeof lasttag - 2])
+                               *lp++ = getchar();
+                       else
+                               ignchar();
+               *lp++ = 0;
+               if (!endcmd(peekchar()))
+badtag:
+                       error(catgets(catd, 1, 46,
+                                       "Bad tag|Give one tag per line"));
+       } else if (lasttag[0] == 0)
+               error(catgets(catd, 1, 47, "No previous tag"));
+       c = getchar();
+       if (!endcmd(c))
+               goto badtag;
+       if (c == EOF)
+               ungetchar(c);
+       clrstats();
+
+       /*
+        * Loop once for each file in tags "path".
+        */
+       safecp(tagfbuf, svalue(TAGS), sizeof tagfbuf, "Tag too long");
+       fne = tagfbuf - 1;
+       while (fne) {
+               fn = ++fne;
+               while (*fne && *fne != ' ')
+                       fne++;
+               if (*fne == 0)
+                       fne = 0;        /* done, quit after this time */
+               else
+                       *fne = 0;       /* null terminate filename */
+#ifdef FASTTAG
+               ft_iof = topen(fn, ft_iofbuf);
+               if (ft_iof == -1)
+                       continue;
+               tfcount++;
+               fstat(ft_iof, &sbuf);
+               top = sbuf.st_size;
+               if (top == (off_t) 0 )
+                       top = (off_t) -1;
+               bot = (off_t) 0;
+               while (top >= bot) {
+#else
+               /*
+                * Avoid stdio and scan tag file linearly.
+                */
+               io = open(fn, O_RDONLY);
+               if (io<0)
+                       continue;
+               tfcount++;
+               if (fstat(io, &sbuf) < 0 || sbuf.st_blksize > LBSIZE)
+                       bsize = LBSIZE;
+               else {
+                       bsize = sbuf.st_blksize;
+                       if (bsize <= 0)
+                               bsize = LBSIZE;
+               }
+               while (getfile() == 0) {
+#endif
+                       /* loop for each tags file entry */
+                       register char *cp = linebuf;
+                       register char *lp = lasttag;
+                       char *oglobp;
+
+#ifdef FASTTAG
+                       mid = (top + bot) / 2;
+                       tseek(ft_iof, mid);
+                       if (mid > 0)    /* to get first tag in file to work */
+                               /* scan to next \n */
+                               if(tgets(linebuf, sizeof linebuf, ft_iof)==0)
+                                       goto goleft;
+                       /* get the line itself */
+                       if(tgets(linebuf, sizeof linebuf, ft_iof)==0)
+                               goto goleft;
+#ifdef TDEBUG
+                       printf("tag: %o %o %o %s\n", bot, mid, top, linebuf);
+#endif
+#endif
+                       while (*cp && *lp == *cp)
+                               cp++, lp++;
+                       if ((*lp || !is_white(*cp)) && (value(TAGLENGTH)==0 ||
+                           lp-lasttag < value(TAGLENGTH))) {
+#ifdef FASTTAG
+                               if (*lp > *cp)
+                                       bot = mid + 1;
+                               else
+goleft:
+                                       top = mid - 1;
+#endif
+                               /* Not this tag.  Try the next */
+                               continue;
+                       }
+
+                       /*
+                        * We found the tag.  Decode the line in the file.
+                        */
+#ifdef FASTTAG
+                       tclose(ft_iof);
+#else
+                       close(io);
+#endif
+                       /* Rest of tag if abbreviated */
+                       while (!is_white(*cp))
+                               cp++;
+
+                       /* name of file */
+                       while (*cp && is_white(*cp))
+                               cp++;
+                       if (!*cp)
+badtags:
+                               serror(catgets(catd, 1, 48,
+                                       "%s: Bad tags file entry"), lasttag);
+                       lp = filebuf;
+                       while (*cp && *cp != ' ' && *cp != '\t') {
+                               if (lp < &filebuf[sizeof filebuf - 2])
+                                       *lp++ = *cp;
+                               cp++;
+                       }
+                       *lp++ = 0;
+
+                       if (*cp == 0)
+                               goto badtags;
+                       if (dol != zero) {
+                               /*
+                                * Save current position in 't for ^^ in visual.
+                                */
+                               names['t'-'a'] = *dot &~ 01;
+                               if (inopen) {
+                                       extern char *ncols['z'-'a'+2];
+                                       extern char *cursor;
+
+                                       ncols['t'-'a'] = cursor;
+                               }
+                       }
+                       safecp(cmdbuf, cp, sizeof cmdbuf, "command too long");
+                       if (strcmp(filebuf, savedfile) || !edited) {
+                               char cmdbuf2[sizeof filebuf + 10];
+
+                               savefirstpat = firstpat;
+                               firstpat = NULL;
+                               /* Different file.  Do autowrite & get it. */
+                               if (!quick) {
+                                       ckaw();
+                                       if (chng && dol > zero)
+                                               error(catgets(catd, 1, 49,
+                       "No write@since last change (:tag! overrides)"));
+                               }
+                               oglobp = globp;
+                               strcpy(cmdbuf2, "e! ");
+                               strcat(cmdbuf2, filebuf);
+                               globp = cmdbuf2;
+                               d = peekc; ungetchar(0);
+                               commands(1, 1);
+                               peekc = d;
+                               globp = oglobp;
+                               value(MAGIC) = omagic;
+                               if (tflag > 0)
+                                       value(WRAPSCAN) = owrapscan;
+                               samef = 0;
+                               firstpat = savefirstpat;
+                       }
+
+                       /*
+                        * Look for pattern in the current file.
+                        */
+                       oglobp = globp;
+                       globp = cmdbuf;
+                       d = peekc; ungetchar(0);
+                       if (samef)
+                               markpr(dot);
+                       /*
+                        * BUG: if it isn't found (user edited header
+                        * line) we get left in nomagic mode.
+                        */
+                       value(MAGIC) = 0;
+                       if (tflag > 0)
+                               value(WRAPSCAN) = 1;
+                       commands(1, 1);
+                       failed = ofailed;
+                       peekc = d;
+                       globp = oglobp;
+                       value(MAGIC) = omagic;
+                       if (tflag > 0) {
+                               value(WRAPSCAN) = owrapscan;
+                               if (savefirstpat) {
+                                       globp = savefirstpat;
+                                       tflag = -1;
+                               } else
+                                       tflag = 0;
+                       }
+                       return;
+               }       /* end of "for each tag in file" */
+
+               /*
+                * No such tag in this file.  Close it and try the next.
+                */
+#ifdef FASTTAG
+               tclose(ft_iof);
+#else
+               close(io);
+#endif
+       }       /* end of "for each file in path" */
+       if (tfcount <= 0)
+               error(catgets(catd, 1, 50, "No tags file"));
+       else
+               serror(catgets(catd, 1, 51,
+                               "%s: No such tag@in tags file"), lasttag);
+}
+
+/*
+ * Save lines from addr1 thru addr2 as though
+ * they had been deleted.
+ */
+/*ARGSUSED*/
+void
+yank(int unused)
+{
+
+       if (!FIXUNDO)
+               error(catgets(catd, 1, 52, "Can't yank inside global/macro"));
+       save12();
+       undkind = UNDNONE;
+       killcnt(addr2 - addr1 + 1);
+}
+
+/*
+ * z command; print windows of text in the file.
+ *
+ * If this seems unreasonably arcane, the reasons
+ * are historical.  This is one of the first commands
+ * added to the first ex (then called en) and the
+ * number of facilities here were the major advantage
+ * of en over ed since they allowed more use to be
+ * made of fast terminals w/o typing .,.22p all the time.
+ */
+bool   zhadpr;
+bool   znoclear;
+short  zweight;
+
+void
+zop(int hadpr)
+{
+       register int c, lines, op;
+       bool excl;
+
+       zhadpr = hadpr;
+       notempty();
+       znoclear = 0;
+       zweight = 0;
+       excl = exclam();
+       switch (c = op = getchar()) {
+
+       case '^':
+               zweight = 1;
+       case '-':
+       case '+':
+               while (peekchar() == op) {
+                       ignchar();
+                       zweight++;
+               }
+       case '=':
+       case '.':
+               c = getchar();
+               break;
+
+       case EOF:
+               znoclear++;
+               break;
+
+       default:
+               op = 0;
+               break;
+       }
+       if (isdigit(c)) {
+               lines = c - '0';
+               for(;;) {
+                       c = getchar();
+                       if (!isdigit(c))
+                               break;
+                       lines *= 10;
+                       lines += c - '0';
+               }
+               if (lines < TLINES)
+                       znoclear++;
+               value(WINDOW) = lines;
+               if (op == '=')
+                       lines += 2;
+       } else
+               lines = op == EOF ? value(SCROLL) : excl ? TLINES - 1 : 2*value(SCROLL);
+       if (inopen || c != EOF) {
+               ungetchar(c);
+               newline();
+       }
+       addr1 = addr2;
+       if (addr2 == 0 && dot < dol && op == 0)
+               addr1 = addr2 = dot+1;
+       setdot();
+       zop2(lines, op);
+}
+
+static void
+splitit(void)
+{
+       register int l;
+
+       for (l = TCOLUMNS > 80 ? 40 : TCOLUMNS / 2; l > 0; l--)
+               putchar('-');
+       putnl();
+}
+
+void
+zop2(register int lines, register int op)
+{
+       register line *split;
+
+       split = NULL;
+       switch (op) {
+
+       case EOF:
+               if (addr2 == dol)
+                       error(catgets(catd, 1, 53, "\nAt EOF"));
+       case '+':
+               if (addr2 == dol)
+                       error(catgets(catd, 1, 54, "At EOF"));
+               addr2 += lines * zweight;
+               if (addr2 > dol)
+                       error(catgets(catd, 1, 55, "Hit BOTTOM"));
+               addr2++;
+       default:
+               addr1 = addr2;
+               addr2 += lines-1;
+               dot = addr2;
+               break;
+
+       case '=':
+       case '.':
+               znoclear = 0;
+               lines--;
+               lines >>= 1;
+               if (op == '=')
+                       lines--;
+               addr1 = addr2 - lines;
+               if (op == '=')
+                       dot = split = addr2;
+               addr2 += lines;
+               if (op == '.') {
+                       markDOT();
+                       dot = addr2;
+               }
+               break;
+
+       case '^':
+       case '-':
+               addr2 -= lines * zweight;
+               if (addr2 < one)
+                       error(catgets(catd, 1, 56, "Hit TOP"));
+               lines--;
+               addr1 = addr2 - lines;
+               dot = addr2;
+               break;
+       }
+       if (addr1 <= zero)
+               addr1 = one;
+       if (addr2 > dol)
+               addr2 = dol;
+       if (dot > dol)
+               dot = dol;
+       if (addr1 > addr2)
+               return;
+       if (op == EOF && zhadpr) {
+               getline(*addr1);
+               putchar('\r' | QUOTE);
+               shudclob = 1;
+       } else if (znoclear == 0 && CL != NOSTR && !inopen) {
+               flush1();
+               vclear();
+       }
+       if (addr2 - addr1 > 1)
+               pstart();
+       if (split) {
+               plines(addr1, split - 1, 0);
+               splitit();
+               plines(split, split, 0);
+               splitit();
+               addr1 = split + 1;
+       }
+       plines(addr1, addr2, 0);
+}
+
+void
+plines(line *adr1, register line *adr2, bool movedot)
+{
+       register line *addr;
+
+       pofix();
+       for (addr = adr1; addr <= adr2; addr++) {
+               getline(*addr);
+               pline(lineno(addr));
+               if (inopen) {
+                       putchar('\n' | QUOTE);
+               }
+               if (movedot)
+                       dot = addr;
+       }
+}
+
+void
+pofix(void)
+{
+
+       if (inopen && Outchar != termchar) {
+               vnfl();
+               setoutt();
+       }
+}
+
+/*
+ * Be (almost completely) sure there really
+ * was a change, before claiming to undo.
+ */
+void
+somechange(void)
+{
+       register line *ip, *jp;
+
+       switch (undkind) {
+
+       case UNDMOVE:
+               return;
+
+       case UNDCHANGE:
+               if (undap1 == undap2 && dol == unddol)
+                       break;
+               return;
+
+       case UNDPUT:
+               if (undap1 != undap2)
+                       return;
+               break;
+
+       case UNDALL:
+               if (unddol - dol != lineDOL())
+                       return;
+               for (ip = one, jp = dol + 1; ip <= dol; ip++, jp++)
+                       if ((*ip &~ 01) != (*jp &~ 01))
+                               return;
+               break;
+
+       case UNDNONE:
+               error(catgets(catd, 1, 57, "Nothing to undo"));
+       }
+       error(catgets(catd, 1, 58,
+       "Nothing changed|Last undoable command didn't change anything"));
+}
+
+/*
+ * Dudley doright to the rescue.
+ * Undo saves the day again.
+ * A tip of the hatlo hat to Warren Teitleman
+ * who made undo as useful as do.
+ *
+ * Command level undo works easily because
+ * the editor has a unique temporary file
+ * index for every line which ever existed.
+ * We don't have to save large blocks of text,
+ * only the indices which are small.  We do this
+ * by moving them to after the last line in the
+ * line buffer array, and marking down info
+ * about whence they came.
+ *
+ * Undo is its own inverse.
+ */
+void
+undo(bool c)
+{
+       register int i, j;
+       register line *jp, *kp;
+       line *dolp1, *newdol, *newadot;
+
+#ifdef TRACE
+       if (trace)
+               vudump("before undo");
+#endif
+       if (inglobal && inopen <= 0)
+               error(catgets(catd, 1, 59, "Can't undo in global@commands"));
+       if (!c)
+               somechange();
+       pkill[0] = pkill[1] = 0;
+       change();
+       if (undkind == UNDMOVE) {
+               /*
+                * Command to be undone is a move command.
+                * This is handled as a special case by noting that
+                * a move "a,b m c" can be inverted by another move.
+                */
+               if ((i = (jp = unddel) - undap2) > 0) {
+                       /*
+                        * when c > b inverse is a+(c-b),c m a-1
+                        */
+                       addr2 = jp;
+                       addr1 = (jp = undap1) + i;
+                       unddel = jp-1;
+               } else {
+                       /*
+                        * when b > c inverse is  c+1,c+1+(b-a) m b
+                        */
+                       addr1 = ++jp;
+                       addr2 = jp + ((unddel = undap2) - undap1);
+               }
+               kp = undap1;
+               move1(0, unddel);
+               dot = kp;
+               Command = "move";
+               killed();
+       } else {
+               int cnt;
+
+               newadot = dot;
+               cnt = lineDOL();
+               newdol = dol;
+               dolp1 = dol + 1;
+               /*
+                * If a mark is pointing to a line between undap1 and
+                * undap2-1, it would be lost (i.e. pointing into the
+                * block between dolp and undol) after the undo. Thus
+                * these marks have to be changed to point to the line
+                * after dolp1 that is restored later during this undo
+                * operation.
+                */
+               if (anymarks)
+                       for (i = 0; &undap1[i] < undap2; i++)
+                               for (j = 0; j <= 'z'-'a'; j++)
+                                       if (names[j] == (undap1[i] & ~01))
+                                               names[j] = dolp1[i] & ~01;
+               /*
+                * Command to be undone is a non-move.
+                * All such commands are treated as a combination of
+                * a delete command and a append command.
+                * We first move the lines appended by the last command
+                * from undap1 to undap2-1 so that they are just before the
+                * saved deleted lines.
+                */
+               if ((i = (kp = undap2) - (jp = undap1)) > 0) {
+                       if (kp != dolp1) {
+                               reverse(jp, kp);
+                               reverse(kp, dolp1);
+                               reverse(jp, dolp1);
+                       }
+                       /*
+                        * Account for possible backward motion of target
+                        * for restoration of saved deleted lines.
+                        */
+                       if (unddel >= jp)
+                               unddel -= i;
+                       newdol -= i;
+                       /*
+                        * For the case where no lines are restored, dot
+                        * is the line before the first line deleted.
+                        */
+                       dot = jp-1;
+               }
+               /*
+                * Now put the deleted lines, if any, back where they were.
+                * Basic operation is: dol+1,unddol m unddel
+                */
+               if (undkind == UNDPUT) {
+                       unddel = undap1 - 1;
+                       squish();
+               }
+               jp = unddel + 1;
+               if ((i = (kp = unddol) - dol) > 0) {
+                       if (jp != dolp1) {
+                               reverse(jp, dolp1);
+                               reverse(dolp1, ++kp);
+                               reverse(jp, kp);
+                       }
+                       /*
+                        * Account for possible forward motion of the target
+                        * for restoration of the deleted lines.
+                        */
+                       if (undap1 >= jp)
+                               undap1 += i;
+                       /*
+                        * Dot is the first resurrected line.
+                        */
+                       dot = jp;
+                       newdol += i;
+               }
+               /*
+                * Clean up so we are invertible
+                */
+               unddel = undap1 - 1;
+               undap1 = jp;
+               undap2 = jp + i;
+               dol = newdol;
+               netchHAD(cnt);
+               if (undkind == UNDALL) {
+                       dot = undadot;
+                       undadot = newadot;
+               } else
+                       undkind = UNDCHANGE;
+       }
+       /*
+        * Defensive programming - after a munged undadot.
+        * Also handle empty buffer case.
+        */
+       if ((dot <= zero || dot > dol) && dot != dol)
+               dot = one;
+#ifdef TRACE
+       if (trace)
+               vudump("after undo");
+#endif
+}
+
+/*
+ * Map command:
+ * map src dest
+ */
+void
+mapcmd(int un, int ab)
+       /* int un;      /\* true if this is unmap command */
+       /*int ab;       /\* true if this is abbr command */
+{
+       char lhs[100], rhs[100];        /* max sizes resp. */
+       register char *p;
+       register int c;         /* mjm: char --> int */
+       char *dname;
+       struct maps *mp;        /* the map structure we are working on */
+
+       mp = ab ? abbrevs : exclam() ? immacs : arrows;
+       if (skipend()) {
+               int i;
+
+               /* print current mapping values */
+               if (peekchar() != EOF)
+                       ignchar();
+               if (un)
+                       error(catgets(catd, 1, 60, "Missing lhs"));
+               if (inopen)
+                       pofix();
+               for (i=0; mp[i].mapto; i++)
+                       if (mp[i].cap) {
+                               lprintf("%s", mp[i].descr);
+                               putchar('\t');
+                               lprintf("%s", mp[i].cap);
+                               putchar('\t');
+                               lprintf("%s", mp[i].mapto);
+                               putNFL();
+                       }
+               return;
+       }
+
+       ignore(skipwh());
+       for (p=lhs; ; ) {
+               c = getchar();
+               if (c == CTRL('v')) {
+                       c = getchar();
+               } else if (!un && any(c, " \t")) {
+                       /* End of lhs */
+                       break;
+               } else if (endcmd(c) && c!='"') {
+                       ungetchar(c);
+                       if (un) {
+                               newline();
+                               *p = 0;
+                               addmac(lhs, NOSTR, NOSTR, mp);
+                               return;
+                       } else
+                               error(catgets(catd, 1, 61, "Missing rhs"));
+               }
+               *p++ = c;
+       }
+       *p = 0;
+
+       if (skipend())
+               error(catgets(catd, 1, 62, "Missing rhs"));
+       for (p=rhs; ; ) {
+               c = getchar();
+               if (c == CTRL('v')) {
+                       c = getchar();
+               } else if (endcmd(c) && c!='"') {
+                       ungetchar(c);
+                       break;
+               }
+               *p++ = c;
+       }
+       *p = 0;
+       newline();
+       /*
+        * Special hack for function keys: #1 means key f1, etc.
+        * If the terminal doesn't have function keys, we just use #1.
+        */
+       if (lhs[0] == '#') {
+               char *fnkey;
+               char funkey[3];
+
+               fnkey = fkey(lhs[1] - '0');
+               funkey[0] = 'f'; funkey[1] = lhs[1]; funkey[2] = 0;
+               if (fnkey)
+                       strcpy(lhs, fnkey);
+               dname = funkey;
+       } else {
+               dname = lhs;
+       }
+       addmac(lhs,rhs,dname,mp);
+}
+
+/*
+ * Create the integer version of a macro string, for processing in visual
+ * mode. imapspace cannot overflow because an earlier overflow for mapspace
+ * would have been detected already.
+ */
+static void
+intmac(int **dp, char *cp)
+{
+       int     c, n;
+
+       if (imsnext == NULL)
+               imsnext = imapspace;
+       *dp = imsnext;
+       for (;;) {
+               nextc(c, cp, n);
+               *imsnext++ = c;
+               if (c == 0)
+                       break;
+               cp += n;
+       }
+}
+
+/*
+ * Add a macro definition to those that already exist. The sequence of
+ * chars "src" is mapped into "dest". If src is already mapped into something
+ * this overrides the mapping. There is no recursion. Unmap is done by
+ * using NOSTR for dest.  Dname is what to show in listings.  mp is
+ * the structure to affect (arrows, etc).
+ */
+void
+addmac1(register char *src,register char *dest,register char *dname,
+               register struct maps *mp, int force)
+{
+       register int slot, zer;
+
+#ifdef TRACE
+       if (trace)
+               fprintf(trace, "addmac(src='%s', dest='%s', dname='%s', mp=%x\n", src, dest, dname, mp);
+#endif
+       if (dest && mp==arrows && !force) {
+               /* Make sure user doesn't screw himself */
+               /*
+                * Prevent tail recursion. We really should be
+                * checking to see if src is a suffix of dest
+                * but this makes mapping involving escapes that
+                * is reasonable mess up.
+                */
+               if (src[1] == 0 && src[0] == dest[strlen(dest)-1])
+                       error(catgets(catd, 1, 63, "No tail recursion"));
+               /*
+                * We don't let the user rob himself of ":", and making
+                * multi char words is a bad idea so we don't allow it.
+                * Note that if user sets mapinput and maps all of return,
+                * linefeed, and escape, he can screw himself. This is
+                * so weird I don't bother to check for it.
+                */
+               if (isalpha(src[0]&0377) && src[1] || any(src[0],":"))
+                       error(catgets(catd, 1, 64,
+                                               "Too dangerous to map that"));
+       }
+       else if (dest) {
+               /* check for tail recursion in input mode: fussier */
+               if (eq(src, dest+strlen(dest)-strlen(src)))
+                       error(catgets(catd, 1, 65, "No tail recursion"));
+       }
+       /*
+        * If the src were null it would cause the dest to
+        * be mapped always forever. This is not good.
+        */
+       if (!force && (src == NOSTR || src[0] == 0))
+               error(catgets(catd, 1, 66, "Missing lhs"));
+
+       /* see if we already have a def for src */
+       zer = -1;
+       for (slot=0; mp[slot].mapto; slot++) {
+               if (mp[slot].cap) {
+                       if (eq(src, mp[slot].cap) || eq(src, mp[slot].mapto))
+                               break;  /* if so, reuse slot */
+               } else {
+                       zer = slot;     /* remember an empty slot */
+               }
+       }
+
+       if (dest == NOSTR) {
+               /* unmap */
+               if (mp[slot].cap) {
+                       mp[slot].cap = NOSTR;
+                       mp[slot].descr = NOSTR;
+               } else {
+                       error(catgets(catd, 1, 67,
+                               "Not mapped|That macro wasn't mapped"));
+               }
+               return;
+       }
+
+       /* reuse empty slot, if we found one and src isn't already defined */
+       if (zer >= 0 && mp[slot].mapto == 0)
+               slot = zer;
+
+       /* if not, append to end */
+       if (slot >= MAXNOMACS)
+               error(catgets(catd, 1, 68, "Too many macros"));
+       if (msnext == 0)        /* first time */
+               msnext = mapspace;
+       /* Check is a bit conservative, we charge for dname even if reusing src */
+       if (msnext - mapspace + strlen(dest) + (src ? strlen(src) : 0) + strlen(dname) + 3 > MAXCHARMACS)
+               error(catgets(catd, 1, 69, "Too much macro text"));
+       if (src) {
+               CP(msnext, src);
+               mp[slot].cap = msnext;
+               msnext += strlen(src) + 1;      /* plus 1 for null on the end */
+               intmac(&mp[slot].icap, src);
+       } else
+               mp[slot].cap = NULL;
+       CP(msnext, dest);
+       mp[slot].mapto = msnext;
+       msnext += strlen(dest) + 1;
+       if (dname) {
+               CP(msnext, dname);
+               mp[slot].descr = msnext;
+               msnext += strlen(dname) + 1;
+       } else {
+               /* default descr to string user enters */
+               mp[slot].descr = src;
+       }
+}
+
+/*
+ * Implements macros from command mode. c is the buffer to
+ * get the macro from.
+ */
+void
+cmdmac(char c)
+{
+       char macbuf[BUFSIZ];
+       line *ad, *a1, *a2;
+       char *oglobp;
+       short pk;
+       bool oinglobal;
+
+       lastmac = c;
+       oglobp = globp;
+       oinglobal = inglobal;
+       pk = peekc; peekc = 0;
+       if (inglobal < 2)
+               inglobal = 1;
+       regbuf(c, macbuf, sizeof(macbuf));
+       a1 = addr1; a2 = addr2;
+       for (ad=a1; ad<=a2; ad++) {
+               globp = macbuf;
+               dot = ad;
+               commands(1,1);
+       }
+       globp = oglobp;
+       inglobal = oinglobal;
+       peekc = pk;
+}
diff --git a/ex_data.c b/ex_data.c
new file mode 100644 (file)
index 0000000..6abee95
--- /dev/null
+++ b/ex_data.c
@@ -0,0 +1,170 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_data.c  1.14 (gritter) 11/23/04";
+#endif
+#endif
+
+/* from ex_data.c      7.5 (Berkeley) 8/29/85 */
+
+#include "ex.h"
+#include "ex_tty.h"
+
+/*
+ * Initialization of option values.
+ * The option #defines in ex_vars.h are made
+ * from this file by the script makeoptions.
+ *
+ * These initializations are done char by char instead of as strings
+ * to confuse xstr so it will leave them alone.
+ */
+#ifdef notdef
+char   direct[ONMSZ] =
+       {'/', 't', 'm', 'p'}; 
+#else
+char   direct[ONMSZ] =
+       {'/', 'v', 'a', 'r', '/', 't', 'm', 'p' }; 
+#endif
+char   paragraphs[ONMSZ] = {
+       'I', 'P', 'L', 'P', 'P', 'P', 'Q', 'P',         /* -ms macros */
+       'P', ' ', 'L', 'I',                             /* -mm macros */
+       'p', 'p', 'l', 'p', 'i', 'p',                   /* -me macros */
+       'b', 'p'                                        /* bare nroff */
+};
+char   sections[ONMSZ] = {
+       'N', 'H', 'S', 'H',                             /* -ms macros */
+       'H', ' ', 'H', 'U',                             /* -mm macros */
+       'n', 'h', 's', 'h'                              /* -me macros */
+};
+char   shell[ONMSZ] =
+       { '/', 'b', 'i', 'n', '/', 's', 'h' };
+char   tags[ONMSZ] = {
+       't', 'a', 'g', 's', ' ',
+       '/', 'u', 's', 'r', '/', 'l', 'i', 'b', '/', 't', 'a', 'g', 's'
+};
+char   ttylongname[ONMSZ] =
+       { 'd', 'u', 'm', 'b' };
+
+short  TCOLUMNS = 80;
+short  TLINES = 24;
+
+struct option options[NOPTS + 1] = {
+       { "autoindent", "ai",   ONOFF,          0,      0,      0, },
+       { "autoprint",  "ap",   ONOFF,          1,      1,      0, },
+       { "autowrite",  "aw",   ONOFF,          0,      0,      0, },
+       { "beautify",   "bf",   ONOFF,          0,      0,      0, },
+       { "directory",  "dir",  STRING,         0,      0,      direct, },
+       { "edcompatible","ed",  ONOFF,          0,      0,      0, },
+       { "errorbells", "eb",   ONOFF,          0,      0,      0, },
+       { "exrc",       "ex",   ONOFF,          0,      0,      0, },
+       { "flash",      "fl",   ONOFF,          1,      1,      0, },
+       { "hardtabs",   "ht",   NUMERIC,        8,      8,      0, },
+       { "ignorecase", "ic",   ONOFF,          0,      0,      0, },
+       { "lisp",       0,      ONOFF,          0,      0,      0, },
+       { "list",       0,      ONOFF,          0,      0,      0, },
+       { "magic",      0,      ONOFF,          1,      1,      0, },
+       { "mesg",       0,      ONOFF,          1,      1,      0, },
+       { "modelines",  "ml",   ONOFF,          0,      0,      0, },
+       { "number",     "nu",   ONOFF,          0,      0,      0, },
+       { "open",       0,      ONOFF,          1,      1,      0, },
+       { "optimize",   "opt",  ONOFF,          0,      0,      0, },
+       { "paragraphs", "para", STRING,         0,      0,      paragraphs, },
+       { "prompt",     0,      ONOFF,          1,      1,      0, },
+       { "readonly",   "ro",   ONOFF,          0,      0,      0, },
+       { "redraw",     0,      ONOFF,          0,      0,      0, },
+       { "remap",      0,      ONOFF,          1,      1,      0, },
+       { "report",     0,      NUMERIC,        5,      5,      0, },
+       { "scroll",     "scr",  NUMERIC,        12,     12,     0, },
+       { "sections",   "sect", STRING,         0,      0,      sections, },
+       { "shell",      "sh",   STRING,         0,      0,      shell, },
+       { "shiftwidth", "sw",   NUMERIC,        TABS,   TABS,   0, },
+       { "showmatch",  "sm",   ONOFF,          0,      0,      0, },
+       { "showmode",   "smd",  ONOFF,          0,      0,      0, },
+       { "slowopen",   "slow", ONOFF,          0,      0,      0, },
+       { "sourceany",  0,      ONOFF,          0,      0,      0, },
+       { "tabstop",    "ts",   NUMERIC,        TABS,   TABS,   0, },
+       { "taglength",  "tl",   NUMERIC,        0,      0,      0, },
+       { "tags",       "tag",  STRING,         0,      0,      tags, },
+       { "term",       0,      OTERM,          0,      0,      ttylongname, },
+       { "terse",      0,      ONOFF,          0,      0,      0, },
+       { "timeout",    "to",   ONOFF,          1,      1,      0, },
+       { "ttytype",    "tty",  OTERM,          0,      0,      ttylongname, },
+       { "warn",       0,      ONOFF,          1,      1,      0, },
+       { "window",     "wi",   NUMERIC,        23,     23,     0, },
+       { "wrapscan",   "ws",   ONOFF,          1,      1,      0, },
+       { "wrapmargin", "wm",   NUMERIC,        0,      0,      0, },
+       { "writeany",   "wa",   ONOFF,          0,      0,      0, },
+       { 0,            0,      0,              0,      0,      0, }
+};
diff --git a/ex_extern.c b/ex_extern.c
new file mode 100644 (file)
index 0000000..c6a0974
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_extern.c        1.6 (gritter) 11/23/04";
+#endif
+#endif
+
+/* from ex_extern.c    7.4 (Berkeley) 6/7/85 */
+
+/*
+ * Provide defs of the global variables.
+ * This crock is brought to you by the turkeys
+ * who broke Unix on the Bell Labs 3B machine,
+ * all in the name of "but that's what the C
+ * book says!"
+ */
+
+# define var   /* nothing */
+# include "ex.h"
+# include "ex_argv.h"
+# include "ex_re.h"
+# include "ex_temp.h"
+# include "ex_tty.h"
+/* # include "ex_tune.h" */
+# include "ex_vars.h"
+# include "ex_vis.h"
diff --git a/ex_get.c b/ex_get.c
new file mode 100644 (file)
index 0000000..f2325b9
--- /dev/null
+++ b/ex_get.c
@@ -0,0 +1,355 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_get.c   1.17 (gritter) 2/17/05";
+#endif
+#endif
+
+/* from ex_get.c       7.6 (Berkeley) 6/7/85 */
+
+#include "ex.h"
+#include "ex_tty.h"
+
+/*
+ * Input routines for command mode.
+ * Since we translate the end of reads into the implied ^D's
+ * we have different flavors of routines which do/don't return such.
+ */
+static bool junkbs;
+int    lastc = '\n';
+
+void 
+ignchar(void)
+{
+       ignore(getchar());
+}
+
+int 
+getach(void)
+{
+       register int c;
+       static char in_line[BUFSIZ];
+       /* struct stat statb; */
+
+       c = peekc;
+       if (c != 0) {
+               peekc = 0;
+               return (c);
+       }
+       if (globp) {
+               if (*globp)
+                       return (*globp++&0377);
+               globp = 0;
+               return (lastc = EOF);
+       }
+top:
+       if (input) {
+               if (c = *input++&0377) {
+                       if (verbose && !intty)
+                               write(2, &input[-1], 1);
+                       if (c &= TRIM)
+                               return (lastc = c);
+                       goto top;
+               }
+               input = 0;
+       }
+       flush();
+       if (intty) {
+               c = read(0, in_line, sizeof in_line - 4);
+               if (c < 0)
+                       return (lastc = EOF);
+               if (c == 0 || in_line[c-1] != '\n')
+                       in_line[c++] = CTRL('d');
+               if (in_line[c-1] == '\n')
+                       noteinp();
+               in_line[c] = 0;
+               for (c--; c >= 0; c--)
+                       if (in_line[c] == 0)
+#ifndef        BIT8
+                               in_line[c] = QUOTE;
+#else
+                               in_line[c] = '\200';
+#endif
+               input = in_line;
+               goto top;
+       }
+       c = read(0, in_line, sizeof in_line - 1);
+       if(c <= 0)
+               return(lastc = EOF);
+       in_line[c] = '\0';
+       input = in_line;
+       goto top;
+}
+
+int
+getchar(void)
+{
+       register int c;
+
+       do
+               c = getcd();
+       while (!globp && c == CTRL('d'));
+       return (c);
+}
+
+void 
+checkjunk(int c)
+{
+
+       if (junkbs == 0 && c == '\b') {
+               write(2, cntrlhm, 13);
+               junkbs = 1;
+       }
+}
+
+int 
+getcd(void)
+{
+       register int c;
+
+again:
+       c = getach();
+       if (c == EOF)
+               return (c);
+       c &= TRIM;
+       if (!inopen)
+               if (!globp && c == CTRL('d'))
+                       setlastchar('\n');
+               else if (junk(c)) {
+                       checkjunk(c);
+                       goto again;
+               }
+       return (c);
+}
+
+int 
+peekchar(void)
+{
+
+       if (peekc == 0)
+               peekc = getchar();
+       return (peekc);
+}
+
+int 
+peekcd(void)
+{
+       if (peekc == 0)
+               peekc = getcd();
+       return (peekc);
+}
+
+/*
+ * Crunch the indent.
+ * Hard thing here is that in command mode some of the indent
+ * is only implicit, so we must seed the column counter.
+ * This should really be done differently so as to use the whitecnt routine
+ * and also to hack indenting for LISP.
+ */
+int 
+smunch(register int col, char *ocp)
+{
+       register char *cp;
+
+       cp = ocp;
+       for (;;)
+               switch (*cp++) {
+
+               case ' ':
+                       col++;
+                       continue;
+
+               case '\t':
+                       col += value(TABSTOP) - (col % value(TABSTOP));
+                       continue;
+
+               default:
+                       cp--;
+                       CP(ocp, cp);
+                       return (col);
+               }
+}
+
+/*
+ * Input routine for insert/append/change in command mode.
+ * Most work here is in handling autoindent.
+ */
+static short   lastin;
+
+int 
+gettty(void)
+{
+       register int c = 0;
+       register char *cp = genbuf;
+       char hadup = 0;
+       int offset = Pline == numbline ? 8 : 0;
+       int ch;
+
+       if (intty && !inglobal) {
+               if (offset) {
+                       holdcm = 1;
+                       printf("  %4d  ", lineDOT() + 1);
+                       flush();
+                       holdcm = 0;
+               }
+               if (value(AUTOINDENT) ^ aiflag) {
+                       holdcm = 1;
+#ifdef LISPCODE
+                       if (value(LISP))
+                               lastin = lindent(dot + 1);
+#endif
+                       tab(lastin + offset);
+                       while ((c = getcd()) == CTRL('d')) {
+                               if (lastin == 0 && isatty(0) == -1) {
+                                       holdcm = 0;
+                                       return (EOF);
+                               }
+                               lastin = backtab(lastin);
+                               tab(lastin + offset);
+                       }
+                       switch (c) {
+
+                       case '^':
+                       case '0':
+                               ch = getcd();
+                               if (ch == CTRL('d')) {
+                                       if (c == '0')
+                                               lastin = 0;
+                                       if (!OS) {
+                                               putchar('\b' | QUOTE);
+                                               putchar(' ' | QUOTE);
+                                               putchar('\b' | QUOTE);
+                                       }
+                                       tab(offset);
+                                       hadup = 1;
+                                       c = getchar();
+                               } else
+                                       ungetchar(ch);
+                               break;
+
+                       case '.':
+                               if (peekchar() == '\n') {
+                                       ignchar();
+                                       noteinp();
+                                       holdcm = 0;
+                                       return (EOF);
+                               }
+                               break;
+
+                       case '\n':
+                               hadup = 1;
+                               break;
+                       }
+               }
+               flush();
+               holdcm = 0;
+       }
+       if (c == 0)
+               c = getchar();
+       while (c != EOF && c != '\n') {
+               if (cp > &genbuf[LBSIZE - 2])
+                       error(catgets(catd, 1, 71, "Input line too long"));
+               *cp++ = c;
+               c = getchar();
+       }
+       if (c == EOF) {
+               if (inglobal)
+                       ungetchar(EOF);
+               return (EOF);
+       }
+       *cp = 0;
+       cp = linebuf;
+       if ((value(AUTOINDENT) ^ aiflag) && hadup == 0 && intty && !inglobal) {
+               lastin = c = smunch(lastin, genbuf);
+               for (c = lastin; c >= value(TABSTOP); c -= value(TABSTOP))
+                       *cp++ = '\t';
+               for (; c > 0; c--)
+                       *cp++ = ' ';
+       }
+       CP(cp, genbuf);
+       if (linebuf[0] == '.' && linebuf[1] == 0)
+               return (EOF);
+       return (0);
+}
+
+void
+setin(line *addr)
+{
+
+       if (addr == zero)
+               lastin = 0;
+       else
+               getline(*addr), lastin = smunch(0, linebuf);
+}
diff --git a/ex_io.c b/ex_io.c
new file mode 100644 (file)
index 0000000..8769a9d
--- /dev/null
+++ b/ex_io.c
@@ -0,0 +1,1125 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_io.c    1.40 (gritter) 2/17/05";
+#endif
+#endif
+
+/* from ex_io.c        7.11.1.1 (Berkeley) 8/12/86 */
+
+#include "ex.h"
+#include "ex_argv.h"
+#include "ex_temp.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+
+/*
+ * File input/output, source, preserve and recover
+ */
+
+/*
+ * Following remember where . was in the previous file for return
+ * on file switching.
+ */
+int    altdot;
+int    oldadot;
+bool   wasalt;
+short  isalt;
+
+long   cntch;                  /* Count of characters on unit io */
+#ifndef VMUNIX
+short  cntln;                  /* Count of lines " */
+#else
+int    cntln;
+#endif
+long   cntnull;                /* Count of nulls " */
+#ifndef        BIT8
+long   cntodd;                 /* Count of non-ascii characters " */
+#endif
+
+/*
+ * Parse file name for command encoded by comm.
+ * If comm is E then command is doomed and we are
+ * parsing just so user won't have to retype the name.
+ */
+void 
+filename(int comm)
+{
+       register int c = comm, d;
+       register int i;
+
+       d = getchar();
+       if (endcmd(d)) {
+               if (savedfile[0] == 0 && comm != 'f')
+                       error(catgets(catd, 1, 72,
+                                       "No file|No current filename"));
+               CP(file, savedfile);
+               wasalt = (isalt > 0) ? isalt-1 : 0;
+               isalt = 0;
+               oldadot = altdot;
+               if (c == 'e' || c == 'E')
+                       altdot = lineDOT();
+               if (d == EOF)
+                       ungetchar(d);
+       } else {
+               ungetchar(d);
+               getone();
+               eol();
+               if (savedfile[0] == 0 && c != 'E' && c != 'e') {
+                       c = 'e';
+                       edited = 0;
+               }
+               wasalt = strcmp(file, altfile) == 0;
+               oldadot = altdot;
+               switch (c) {
+
+               case 'f':
+                       edited = 0;
+                       /* fall into ... */
+
+               case 'e':
+                       if (savedfile[0]) {
+                               altdot = lineDOT();
+                               CP(altfile, savedfile);
+                       }
+                       CP(savedfile, file);
+                       break;
+
+               default:
+                       if (file[0]) {
+                               if (c != 'E')
+                                       altdot = lineDOT();
+                               CP(altfile, file);
+                       }
+                       break;
+               }
+       }
+       if (hush && comm != 'f' || comm == 'E')
+               return;
+       if (file[0] != 0) {
+               lprintf("\"%s\"", file);
+               if (comm == 'f') {
+                       if (value(READONLY))
+                               printf(catgets(catd, 1, 73, " [Read only]"));
+                       if (!edited)
+                               printf(catgets(catd, 1, 74, " [Not edited]"));
+                       if (tchng)
+                               printf(catgets(catd, 1, 75, " [Modified]"));
+               }
+               flush();
+       } else
+               printf(catgets(catd, 1, 76, "No file "));
+       if (comm == 'f') {
+               if (!(i = lineDOL()))
+                       i++;
+               printf(catgets(catd, 1, 77,
+                       " line %d of %d --%ld%%--"), lineDOT(), lineDOL(),
+                   (long) 100 * lineDOT() / i);
+       }
+}
+
+/*
+ * Get the argument words for a command into genbuf
+ * expanding # and %.
+ */
+int 
+getargs(void)
+{
+       register int c;
+       register char *cp, *fp;
+       static char fpatbuf[32];        /* hence limit on :next +/pat */
+
+       pastwh();
+       if (peekchar() == '+') {
+               for (cp = fpatbuf;;) {
+                       c = *cp++ = getchar();
+                       if (cp >= &fpatbuf[sizeof(fpatbuf)])
+                               error(catgets(catd, 1, 78, "Pattern too long"));
+                       if (c == '\\' && isspace(peekchar()))
+                               c = getchar();
+                       if (c == EOF || isspace(c)) {
+                               ungetchar(c);
+                               *--cp = 0;
+                               firstpat = &fpatbuf[1];
+                               break;
+                       }
+               }
+       }
+       if (skipend())
+               return (0);
+       CP(genbuf, "echo "); cp = &genbuf[5];
+       for (;;) {
+               c = getchar();
+               if (endcmd(c)) {
+                       ungetchar(c);
+                       break;
+               }
+               switch (c) {
+
+               case '\\':
+                       if (any(peekchar(), "#%|"))
+                               c = getchar();
+                       /* fall into... */
+
+               default:
+                       if (cp > &genbuf[LBSIZE - 2])
+flong:
+                               error(catgets(catd, 1, 79,
+                                               "Argument buffer overflow"));
+                       *cp++ = c;
+                       break;
+
+               case '#':
+                       fp = altfile;
+                       if (*fp == 0)
+                               error(catgets(catd, 1, 80,
+                               "No alternate filename@to substitute for #"));
+                       goto filexp;
+
+               case '%':
+                       fp = savedfile;
+                       if (*fp == 0)
+                               error(catgets(catd, 1, 81,
+                               "No current filename@to substitute for %%"));
+filexp:
+                       while (*fp) {
+                               if (cp > &genbuf[LBSIZE - 2])
+                                       goto flong;
+                               *cp++ = *fp++;
+                       }
+                       break;
+               }
+       }
+       *cp = 0;
+       return (1);
+}
+
+/*
+ * Scan genbuf for shell metacharacters.
+ * Set is union of v7 shell and csh metas.
+ */
+int 
+gscan(void)
+{
+       register char *cp;
+
+       for (cp = genbuf; *cp; cp++)
+               if (any(*cp, "~{[*?$`'\"\\"))
+                       return (1);
+       return (0);
+}
+
+/*
+ * Glob the argument words in genbuf, or if no globbing
+ * is implied, just split them up directly.
+ */
+void 
+gglob(struct glob *gp)
+{
+       int pvec[2];
+       register char **argv = gp->argv;
+       register char *cp = gp->argspac;
+       register int c;
+       char ch;
+       int nleft = NCARGS;
+
+       gp->argc0 = 0;
+       if (gscan() == 0) {
+               register char *v = genbuf + 5;          /* strlen("echo ") */
+
+               for (;;) {
+                       while (isspace(*v&0377))
+                               v++;
+                       if (!*v)
+                               break;
+                       *argv++ = cp;
+                       while (*v && !isspace(*v&0377))
+                               *cp++ = *v++;
+                       *cp++ = 0;
+                       gp->argc0++;
+               }
+               *argv = 0;
+               return;
+       }
+       if (pipe(pvec) < 0)
+               error(catgets(catd, 1, 82, "Can't make pipe to glob"));
+       pid = fork();
+       io = pvec[0];
+       if (pid < 0) {
+               close(pvec[1]);
+               error(catgets(catd, 1, 83, "Can't fork to do glob"));
+       }
+       if (pid == 0) {
+               int oerrno;
+
+               close(1);
+               dup(pvec[1]);
+               close(pvec[0]);
+               close(2);       /* so errors don't mess up the screen */
+               open("/dev/null", O_WRONLY);
+               execl(svalue(SHELL), "sh", "-c", genbuf, (char *)0);
+               oerrno = errno; close(1); dup(2); errno = oerrno;
+               filioerr(svalue(SHELL));
+       }
+       close(pvec[1]);
+       do {
+               *argv = cp;
+               for (;;) {
+                       if (read(io, &ch, 1) != 1) {
+                               close(io);
+                               c = -1;
+                       } else
+                               c = ch & TRIM;
+                       if (c <= 0 || isspace(c))
+                               break;
+                       *cp++ = c;
+                       if (--nleft <= 0)
+                               error(catgets(catd, 1, 84,
+                                                       "Arg list too long"));
+               }
+               if (cp != *argv) {
+                       --nleft;
+                       *cp++ = 0;
+                       gp->argc0++;
+                       if (gp->argc0 >= NARGS)
+                               error(catgets(catd, 1, 85,
+                                                       "Arg list too long"));
+                       argv++;
+               }
+       } while (c >= 0);
+       waitfor();
+       if (gp->argc0 == 0)
+               error(catgets(catd, 1, 86, "No match"));
+}
+
+/*
+ * Parse one filename into file.
+ */
+struct glob G;
+void
+getone(void)
+{
+       register char *str;
+
+       if (getargs() == 0)
+missing:
+               error(catgets(catd, 1, 87, "Missing filename"));
+       gglob(&G);
+       if (G.argc0 > 1)
+               error(catgets(catd, 1, 88, "Ambiguous|Too many file names"));
+       if ((str = G.argv[G.argc0 - 1]) == NULL)
+               goto missing;
+       if (strlen(str) > FNSIZE - 4)
+               error(catgets(catd, 1, 89, "Filename too long"));
+/* samef: */
+       CP(file, str);
+}
+
+/*
+ * Are these two really the same inode?
+ */
+int 
+samei(struct stat *sp, char *cp)
+{
+       struct stat stb;
+
+       if (stat(cp, &stb) < 0 || sp->st_dev != stb.st_dev)
+               return (0);
+       return (sp->st_ino == stb.st_ino);
+}
+
+/*
+ * Read a file from the world.
+ * C is command, 'e' if this really an edit (or a recover).
+ */
+void 
+rop(int c)
+{
+       register int i;
+       struct stat stbuf;
+       char magic[4];
+       static int ovro;        /* old value(READONLY) */
+       static int denied;      /* 1 if READONLY was set due to file permissions */
+
+       io = open(file, O_RDONLY);
+       if (io < 0) {
+               if (c == 'e' && errno == ENOENT) {
+                       edited++;
+                       /*
+                        * If the user just did "ex foo" he is probably
+                        * creating a new file.  Don't be an error, since
+                        * this is ugly, and it screws up the + option.
+                        *
+                        * POSIX.2 specifies that this be done for all
+                        * "edit" commands, not just for the first one.
+                        */
+                       if (1 || !seenprompt) {
+                               printf(catgets(catd, 1, 90, " [New file]"));
+                               noonl();
+                               return;
+                       }
+               }
+               failed = 1;
+               syserror();
+       }
+       if (fstat(io, &stbuf))
+               syserror();
+       switch (stbuf.st_mode & S_IFMT) {
+
+       case S_IFBLK:
+               error(catgets(catd, 1, 91, " Block special file"));
+
+       case S_IFCHR:
+               if (isatty(io))
+                       error(catgets(catd, 1, 92, " Teletype"));
+               if (samei(&stbuf, "/dev/null"))
+                       break;
+               error(catgets(catd, 1, 93, " Character special file"));
+
+       case S_IFDIR:
+               error(catgets(catd, 1, 94, " Directory"));
+
+#ifdef S_IFSOCK
+       case S_IFSOCK:
+               error(catgets(catd, 1, 95, " Socket"));
+#endif
+#ifdef S_IFIFO
+       case S_IFIFO:
+               error(catgets(catd, 1, 96, " Named pipe"));
+#endif
+
+       case S_IFREG:
+               /*
+                * The magics are checked byte-wise now to avoid
+                * endianness problems. Some quite old types
+                * were omitted.
+                *
+                * Feel free too add more magics here, but do not
+                * make this a copy of the `file' program.
+                *
+                * GR
+                */
+               i = read(io, magic, sizeof(magic));
+               lseek(io, (off_t) 0, SEEK_SET);
+               if (i != sizeof(magic))
+                       break;
+               switch (magic[0]&0377) {
+
+               case 01:        /* big endian a.out */
+                       if (magic[1] != 05 && magic[1] != 07
+                               && magic[1] != 010 && magic[1] != 011
+                               && magic[1] != 013 && magic[1] != 030
+                               && magic[1] != 031)
+                               break;
+                       goto is_exec;
+               case 0314:      /* Linux/ia32 QMAGIC */
+                       if (magic[1] != 0 || magic[2] != 0144)
+                               break;
+                       goto is_exec;
+               case 05:        /* data overlay on exec */
+               case 07:        /* unshared */
+               case 010:       /* shared text */
+               case 011:       /* separate I/D */
+               case 013:       /* VM/Unix demand paged */
+               case 030:       /* PDP-11 Overlay shared */
+               case 031:       /* PDP-11 Overlay sep I/D */
+                       if (magic[1] == 01)
+is_exec:
+                               error(catgets(catd, 1, 97, " Executable"));
+                       break;
+
+               case 037:
+                       switch (magic[1]&0377) {
+                       case 036:       /* pack */
+                       case 037:       /* compact */
+                       case 0235:      /* compress */
+                       case 0213:      /* gzip */
+                       /*
+                        * We omit bzip2 here since it has
+                        * an ASCII header.
+                        */
+                               error(catgets(catd, 1, 98, " Compressed Data"));
+                       }
+                       break;
+
+               case 0177:
+                       if (magic[1] == 'E' && magic[2] == 'L'
+                                       && magic[3] == 'F')
+                               error(catgets(catd, 1, 99, " ELF object"));
+                       break;
+
+               default:
+                       break;
+               }
+#ifdef notdef
+               /*
+                * We do not forbid the editing of portable archives
+                * because it is reasonable to edit them, especially
+                * if they are archives of text files.  This is
+                * especially useful if you archive source files together
+                * and copy them to another system with ~%take, since
+                * the files sometimes show up munged and must be fixed.
+                */
+               case 0177545:
+               case 0177555:
+                       error(catgets(catd, 1, 100, " Archive"));
+
+               default:
+#ifdef mbb
+                       /* C/70 has a 10 bit byte */
+                       if (magic & 03401600)
+#else
+                       /* Everybody else has an 8 bit byte */
+                       if (magic & 0100200)
+#endif
+                               error(catgets(catd, 1, 101, " Non-ascii file"));
+                       break;
+               }
+#endif /* notdef */
+       }
+       if (c != 'r') {
+               if (value(READONLY) && denied) {
+                       value(READONLY) = ovro;
+                       denied = 0;
+               }
+               if ((stbuf.st_mode & 0222) == 0 || access(file, W_OK) < 0) {
+                       ovro = value(READONLY);
+                       denied = 1;
+                       value(READONLY) = 1;
+               }
+       }
+       if (value(READONLY) && !hush) {
+               printf(catgets(catd, 1, 102, " [Read only]"));
+               flush();
+       }
+       if (c == 'r')
+               setdot();
+       else
+               setall();
+       if (FIXUNDO && inopen && c == 'r')
+               undap1 = undap2 = addr1 + 1;
+       rop2();
+       rop3(c);
+}
+
+void 
+rop2(void)
+{
+       line *first, *last, *a;
+       struct stat statb;
+
+       deletenone();
+       clrstats();
+       first = addr2 + 1;
+       if (fstat(io, &statb) < 0 || statb.st_blksize > LBSIZE)
+               bsize = LBSIZE;
+       else {
+               bsize = statb.st_blksize;
+               if (bsize <= 0)
+                       bsize = LBSIZE;
+       }
+       ignore(append(getfile, addr2));
+       last = dot;
+       /*
+        *      if the modelines variable is set,
+        *      check the first and last five lines of the file
+        *      for a mode line.
+        */
+       if (value(MODELINES)) {
+               for (a=first; a<=last; a++) {
+                       if (a==first+5 && last-first > 10)
+                               a = last - 4;
+                       getline(*a);
+                       checkmodeline(linebuf);
+               }
+       }
+}
+
+/*
+ * Io is finished, close the unit and print statistics.
+ */
+int 
+iostats(void)
+{
+
+       fsync(io);
+       close(io);
+       io = -1;
+       if (hush == 0) {
+               if (value(TERSE))
+                       printf(catgets(catd, 1, 103,
+                                               " %d/%d"), cntln, (int)cntch);
+               else
+                       printf(catgets(catd, 1, 104,
+               " %d line%s, %d character%s"), cntln, plural((long) cntln),
+                           (int)cntch, plural((int)cntch));
+               if (cntnull
+#ifndef        BIT8
+                               || cntodd
+#endif
+                               ) {
+                       printf(catgets(catd, 1, 105, " ("));
+                       if (cntnull) {
+                               printf(catgets(catd, 1, 106,
+                                               "%d null"), (int)cntnull);
+#ifndef        BIT8
+                               if (cntodd)
+                                       printf(catgets(catd, 1, 107, ", "));
+#endif
+                       }
+#ifndef        BIT8
+                       if (cntodd)
+                               printf(catgets(catd, 1, 108,
+                                       "%d non-ASCII"), (int)cntodd);
+#endif
+                       putchar(')');
+               }
+               noonl();
+               flush();
+       }
+       return (cntnull != 0
+#ifndef        BIT8
+                       || cntodd != 0
+#endif
+                       );
+}
+
+void
+rop3(int c)
+{
+
+       if (iostats() == 0 && c == 'e')
+               edited++;
+       if (c == 'e') {
+               if (wasalt || firstpat) {
+                       register line *addr = zero + oldadot;
+
+                       if (addr > dol)
+                               addr = dol;
+                       if (firstpat) {
+                               globp = (*firstpat) ? firstpat : "$";
+                               commands(1,1);
+                               firstpat = 0;
+                       } else if (addr >= one) {
+                               if (inopen)
+                                       dot = addr;
+                               markpr(addr);
+                       } else
+                               goto other;
+               } else
+other:
+                       if (dol > zero) {
+                               if (inopen)
+                                       dot = one;
+                               markpr(one);
+                       }
+               if(FIXUNDO)
+                       undkind = UNDNONE;
+               if (inopen) {
+                       vcline = 0;
+                       vreplace(0, TLINES, lineDOL());
+               }
+       }
+}
+
+/* Returns from edited() */
+#define        EDF     0               /* Edited file */
+#define        NOTEDF  -1              /* Not edited file */
+#define        PARTBUF 1               /* Write of partial buffer to Edited file */
+
+/*
+ * Is file the edited file?
+ * Work here is that it is not considered edited
+ * if this is a partial buffer, and distinguish
+ * all cases.
+ */
+int 
+edfile(void)
+{
+
+       if (!edited || !eq(file, savedfile))
+               return (NOTEDF);
+       return (addr1 == one && addr2 == dol ? EDF : PARTBUF);
+}
+
+/*
+ * Write a file.
+ */
+void
+wop(bool dofname)
+/*bool dofname;        /\* if 1 call filename, else use savedfile */
+{
+       register int c, exclam, nonexist;
+       line *saddr1 = NULL, *saddr2 = NULL;
+       struct stat stbuf;
+
+       c = 0;
+       exclam = 0;
+       if (dofname) {
+               if (peekchar() == '!')
+                       exclam++, ignchar();
+               ignore(skipwh());
+               while (peekchar() == '>')
+                       ignchar(), c++, ignore(skipwh());
+               if (c != 0 && c != 2)
+                       error(catgets(catd, 1, 109,
+                                       "Write forms are 'w' and 'w>>'"));
+               filename('w');
+       } else {
+               if (savedfile[0] == 0)
+                       error(catgets(catd, 1, 110,
+                                       "No file|No current filename"));
+               saddr1=addr1;
+               saddr2=addr2;
+               addr1=one;
+               addr2=dol;
+               CP(file, savedfile);
+               if (inopen) {
+                       vclrech(0);
+                       splitw++;
+               }
+               lprintf("\"%s\"", file);
+       }
+       nonexist = stat(file, &stbuf);
+       switch (c) {
+
+       case 0:
+               if (!exclam && (!value(WRITEANY) || value(READONLY)))
+               switch (edfile()) {
+               
+               case NOTEDF:
+                       if (nonexist)
+                               break;
+                       if ((stbuf.st_mode & S_IFMT) == S_IFCHR) {
+                               if (samei(&stbuf, "/dev/null"))
+                                       break;
+                               if (samei(&stbuf, "/dev/tty"))
+                                       break;
+                       }
+                       io = open(file, O_WRONLY);
+                       if (io < 0)
+                               syserror();
+                       if (!isatty(io))
+                               serror(catgets(catd, 1, 111,
+       " File exists| File exists - use \"w! %s\" to overwrite"), file);
+                       close(io);
+                       break;
+
+               case EDF:
+                       if (value(READONLY))
+                               error(catgets(catd, 1, 112,
+                                               " File is read only"));
+                       break;
+
+               case PARTBUF:
+                       if (value(READONLY))
+                               error(catgets(catd, 1, 113,
+                                               " File is read only"));
+                       error(catgets(catd, 1, 114,
+                               " Use \"w!\" to write partial buffer"));
+               }
+cre:
+/*
+               synctmp();
+*/
+               io = creat(file, 0666);
+               if (io < 0)
+                       syserror();
+               writing = 1;
+               if (hush == 0)
+                       if (nonexist)
+                               printf(catgets(catd, 1, 115, " [New file]"));
+                       else if (value(WRITEANY) && edfile() != EDF)
+                               printf(catgets(catd, 1, 116,
+                                                       " [Existing file]"));
+               break;
+
+       case 2:
+               io = open(file, O_WRONLY);
+               if (io < 0) {
+                       if (exclam || value(WRITEANY))
+                               goto cre;
+                       syserror();
+               }
+               lseek(io, (off_t) 0, SEEK_END);
+               break;
+       }
+       putfile(0);
+       ignore(iostats());
+       if (c != 2 && addr1 == one && addr2 == dol) {
+               if (eq(file, savedfile))
+                       edited = 1;
+               synced();
+       }
+       if (!dofname) {
+               addr1 = saddr1;
+               addr2 = saddr2;
+       }
+       writing = 0;
+}
+
+/*
+ * Extract the next line from the io stream.
+ */
+char *nextip;
+
+int
+getfile(void)
+{
+       register short c;
+       register char *lp, *fp;
+
+       lp = linebuf;
+       fp = nextip;
+       do {
+               if (--ninbuf < 0) {
+                       ninbuf = read(io, genbuf, bsize) - 1;
+                       if (ninbuf < 0) {
+                               if (lp != linebuf) {
+                                       lp++;
+                                       printf(catgets(catd, 1, 117,
+                                               " [Incomplete last line]"));
+                                       break;
+                               }
+                               return (EOF);
+                       }
+                       fp = genbuf;
+                       cntch += ninbuf+1;
+               }
+               if (lp >= &linebuf[LBSIZE]) {
+                       synced();
+                       error(catgets(catd, 1, 118, " Line too long"));
+               }
+               c = *fp++;
+               if (c == 0) {
+                       cntnull++;
+#ifndef        BIT8
+                       continue;
+#else
+                       c = 0200;
+#endif
+               }
+#ifndef        BIT8
+               if (c & QUOTE) {
+                       cntodd++;
+                       c &= TRIM;
+                       if (c == 0)
+                               continue;
+               }
+#endif
+               *lp++ = c;
+       } while (c != '\n');
+       *--lp = 0;
+       nextip = fp;
+       cntln++;
+       return (0);
+}
+
+/*
+ * Write a range onto the io stream.
+ */
+void
+putfile(int isfilter)
+{
+       line *a1;
+       register char *fp, *lp;
+       register int nib;
+       struct stat statb;
+
+       a1 = addr1;
+       clrstats();
+       cntln = fixedzero ? 0 : addr2 - a1 + 1;
+       if (cntln == 0 || fixedzero)
+               return;
+       if (fstat(io, &statb) < 0 || statb.st_blksize > LBSIZE)
+               bsize = LBSIZE;
+       else {
+               bsize = statb.st_blksize;
+               if (bsize <= 0)
+                       bsize = LBSIZE;
+       }
+       nib = bsize;
+       fp = genbuf;
+       do {
+               getline(*a1++);
+               lp = linebuf;
+               for (;;) {
+                       if (--nib < 0) {
+                               nib = fp - genbuf;
+                               if (write(io, genbuf, nib) != nib) {
+                                       wrerror();
+                               }
+                               cntch += nib;
+                               nib = bsize - 1;
+                               fp = genbuf;
+                       }
+                       if ((*fp++ = *lp++) == 0) {
+                               fp[-1] = '\n';
+                               break;
+                       }
+               }
+       } while (a1 <= addr2);
+       nib = fp - genbuf;
+       if (write(io, genbuf, nib) != nib) {
+               wrerror();
+       }
+       cntch += nib;
+}
+
+/*
+ * A write error has occurred;  if the file being written was
+ * the edited file then we consider it to have changed since it is
+ * now likely scrambled.
+ */
+void
+wrerror(void)
+{
+
+       if (eq(file, savedfile) && edited)
+               change();
+       syserror();
+}
+
+/*
+ * Source command, handles nested sources.
+ * Traps errors since it mungs unit 0 during the source.
+ */
+short slevel;
+short ttyindes;
+
+void
+source(char *fil, bool okfail)
+{
+       JMP_BUF osetexit;
+       register int saveinp, ointty, oerrno;
+       char *saveglobp, *saveinput;
+       char    saveinline[BUFSIZ];
+       int savepeekc, savelastc;
+
+       signal(SIGINT, SIG_IGN);
+       saveinp = dup(0);
+       savepeekc = peekc;
+       savelastc = lastc;
+       saveglobp = globp;
+       saveinput = input;
+       if (input)
+               strcpy(saveinline, input);
+       peekc = 0; lastc = 0; globp = 0; input = 0;
+       if (saveinp < 0)
+               error(catgets(catd, 1, 119, "Too many nested sources"));
+       if (slevel <= 0)
+               ttyindes = saveinp;
+       close(0);
+       if (open(fil, O_RDONLY) < 0) {
+               oerrno = errno;
+               setrupt();
+               dup(saveinp);
+               close(saveinp);
+               input = saveinput;
+               if (input)
+                       strcpy(input, saveinline);
+               lastc = savelastc;
+               errno = oerrno;
+               if (!okfail)
+                       filioerr(fil);
+               return;
+       }
+       slevel++;
+       ointty = intty;
+       intty = isatty(0);
+       oprompt = value(PROMPT);
+       value(PROMPT) &= intty;
+       getexit(osetexit);
+       setrupt();
+       if (setexit() == 0)
+               commands(1, 1);
+       else if (slevel > 1) {
+               close(0);
+               dup(saveinp);
+               close(saveinp);
+               input = saveinput;
+               if (input)
+                       strcpy(input, saveinline);
+               lastc = savelastc;
+               slevel--;
+               resexit(osetexit);
+               reset();
+       }
+       intty = ointty;
+       value(PROMPT) = oprompt;
+       close(0);
+       dup(saveinp);
+       close(saveinp);
+       globp = saveglobp;
+       input = saveinput;
+       if (input)
+               strcpy(input, saveinline);
+       peekc = savepeekc;
+       lastc = savelastc;
+       slevel--;
+       resexit(osetexit);
+}
+
+/*
+ * Clear io statistics before a read or write.
+ */
+void
+clrstats(void)
+{
+
+       ninbuf = 0;
+       cntch = 0;
+       cntln = 0;
+       cntnull = 0;
+#ifndef        BIT8
+       cntodd = 0;
+#endif
+}
+
+/* It's so wonderful how we all speak the same language... */
+# define index strchr
+# define rindex strrchr
+
+void
+checkmodeline(char *lin)
+{
+       char *beg, *end;
+       char cmdbuf[BUFSIZ];
+
+       beg = index(lin, ':');
+       if (beg == NULL)
+               return;
+       if (&beg[-2] < lin)
+               return;
+       if (!((beg[-2] == 'e' && beg[-1] == 'x')
+            || (beg[-2] == 'v' && beg[-1] == 'i'))) return;
+       strncpy(cmdbuf, beg+1, sizeof cmdbuf);
+       end = rindex(cmdbuf, ':');
+       if (end == NULL)
+               return;
+       *end = 0;
+       globp = cmdbuf;
+       commands(1, 1);
+}
+
+#ifdef MB
+int
+mbtowi(int *cp, const char *s, size_t n)
+{
+       wchar_t wc;
+       int     i;
+
+       i = mbtowc(&wc, s, n);
+       if (i >= 0 && widthok(wc) && !(wc & 0x70000000))
+               *cp = wc;
+       else {
+               *cp = *s&0377 | INVBIT;
+               i = 1;
+       }
+       return i;
+}
+
+int
+widthok(int c)
+{
+       return inopen ? wcwidth(c) <= 2 : 1;
+}
+#endif /* MB */
+
+int
+GETWC(char *mb)
+{
+       int     c, n;
+
+       n = 1;
+       mb[0] = c = getchar();
+       mb[1] = '\0';
+#ifdef MB
+       if (mb_cur_max > 1 && c & 0200 && c != EOF) {
+               int     m;
+               wchar_t wc;
+               while ((m = mbtowc(&wc, mb, mb_cur_max)) < 0 && n<mb_cur_max) {
+                       mb[n++] = c = getchar();
+                       mb[n] = '\0';
+                       if (c == '\n' || c == EOF)
+                               break;
+               }
+               if (m != n || c & 0x70000000)
+                       error("illegal multibyte sequence");
+               return wc;
+       } else
+#endif /* MB */
+               return c;
+}
diff --git a/ex_proto.h b/ex_proto.h
new file mode 100644 (file)
index 0000000..6af8c18
--- /dev/null
@@ -0,0 +1,567 @@
+/*
+ *
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *     @(#)ex_proto.h  1.28 (gritter) 2/19/05
+ */
+
+/*
+ * Function type definitions
+ */
+
+#define        join    xjoin
+#define        word    xword
+#define        getline xgetline
+
+/* ex.c */
+extern void erropen(void);
+extern void usage(void);
+extern void needarg(int);
+extern void invopt(int);
+extern char *tailpath(register char *);
+extern int iownit(char *);
+extern shand setsig(int, shand);
+extern void init(void);
+/* ex_addr.c */
+extern void setdot(void);
+extern void setdot1(void);
+extern void setcount(void);
+extern int getnum(void);
+extern void setall(void);
+extern void setnoaddr(void);
+extern line *address(char *);
+extern void setCNL(void);
+extern void setNAEOL(void);
+/* ex_cmds.c */
+extern void commands(int, int);
+/* ex_cmds2.c */
+extern int cmdreg(void);
+extern int endcmd(int);
+extern void eol(void);
+extern void error0(void);
+extern int error1(char *);
+extern void verror(char *, va_list);
+extern void error(char *, ...);
+extern void erewind(void);
+extern void fixol(void);
+extern int exclam(void);
+extern void makargs(void);
+extern void next(void);
+extern void newline(void);
+extern void nomore(void);
+extern int quickly(void);
+extern void resetflav(void);
+extern void serror(char *, ...);
+extern void setflav(void);
+extern int skipend(void);
+extern void tailspec(int);
+extern void tail(char *);
+extern void tail2of(char *);
+extern void tailprim(register char *, int, bool);
+extern void vcontin(bool);
+extern void vnfl(void);
+/* ex_cmdsub.c */
+extern int append(int (*)(void), line *);
+extern void appendnone(void);
+extern void pargs(void);
+extern void delete(int);
+extern void deletenone(void);
+extern void squish(void);
+extern void join(int);
+extern void move1(int, line *);
+extern void move(void);
+extern int getcopy(void);
+extern int getput(void);
+extern void put(int);
+extern void pragged(int);
+extern void shift(int, int);
+extern void tagfind(bool);
+extern void yank(int);
+extern void zop(int);
+extern void zop2(register int, register int);
+extern void plines(line *, register line *, bool);
+extern void pofix(void);
+extern void somechange(void);
+extern void undo(bool);
+extern void mapcmd(int, int);
+extern void cmdmac(char);
+/* ex_data.c */
+/* ex_extern.c */
+/* ex_get.c */
+extern void ignchar(void);
+extern int getach(void);
+extern int getchar(void);
+extern void checkjunk(int);
+extern int getcd(void);
+extern int peekchar(void);
+extern int peekcd(void);
+extern int smunch(register int, char *);
+extern int gettty(void);
+extern void setin(line *);
+/* ex_io.c */
+extern void filename(int);
+extern int getargs(void);
+extern int gscan(void);
+extern void getone(void);
+extern int samei(struct stat *, char *);
+extern void rop(int);
+extern void rop2(void);
+extern int iostats(void);
+extern void rop3(int);
+extern int edfile(void);
+extern void wop(bool);
+extern int getfile(void);
+extern void putfile(int);
+extern void wrerror(void);
+extern void source(char *, bool);
+extern void clrstats(void);
+extern void checkmodeline(char *);
+#ifdef MB
+extern int mbtowi(int *c, const char *s, size_t n);
+extern int widthok(int c);
+#endif /* MB */
+extern int GETWC(char *);
+/* ex_put.c */
+extern int (*setlist(int))(int);
+extern void (*setnumb(int))(int);
+extern int listchar(int);
+extern int normchar(register int);
+extern void slobber(int);
+extern void numbline(int);
+extern void normline(int);
+extern int putchar(int);
+extern int termchar(int);
+extern void flush2(void);
+extern void flush(void);
+extern void flush1(void);
+extern int plodput(int);
+extern int plod(int);
+extern void fgoto(void);
+extern void tab(int);
+extern void noteinp(void);
+extern void termreset(void);
+extern void draino(void);
+extern void flusho(void);
+extern void putnl(void);
+extern void putS(char *);
+extern int putch(int);
+extern void putpad(char *);
+extern void setoutt(void);
+extern void vlprintf(char *, va_list);
+extern void lprintf(char *, ...);
+extern void putNFL(void);
+extern void sTTY(int);
+extern void pstart(void);
+extern void pstop(void);
+extern void ttcharoff(void);
+extern struct termios ostart(void);
+extern void normal(struct termios);
+/* ex_re.c */
+extern void global(int);
+extern void gdelete(void);
+extern int substitute(int);
+extern int compsub(int);
+extern void comprhs(int);
+extern int getsub(void);
+extern int dosubcon(bool, line *);
+extern int confirmed(line *);
+extern void ugo(int, int);
+extern void dosub(void);
+extern int fixcase(register int);
+extern char *place(register char *, register char *, register char *);
+extern void snote(register int, register int);
+extern void cerror(char *);
+extern struct regexp *savere(struct regexp *);
+extern struct regexp *resre(struct regexp *);
+extern int compile(int, int);
+extern int same(register int, register int);
+extern int ecmp(register char *, register char *, register int);
+extern int execute(int, line *);
+/* ex_set.c */
+extern void set(void);
+extern int setend(void);
+extern void prall(void);
+extern void propts(void);
+extern void propt(register struct option *);
+/* ex_subr.c */
+extern int any(int, register char *);
+extern int backtab(register int);
+extern void change(void);
+extern int column(register char *);
+extern int lcolumn(register char *);
+extern void comment(void);
+extern void Copy(register char *, register char *, register int);
+extern void copyw(register line *, register line *, register int);
+extern void copywR(register line *, register line *, register int);
+extern int ctlof(int);
+extern void dingdong(void);
+extern int fixindent(int);
+extern void filioerr(char *);
+extern char *genindent(register int);
+extern void getDOT(void);
+extern line *getmark(register int);
+extern int getn(register char *);
+extern void ignnEOF(void);
+extern int is_white(int);
+extern int junk(register int);
+extern void killed(void);
+extern void killcnt(register int);
+extern int lineno(line *);
+extern int lineDOL(void);
+extern int lineDOT(void);
+extern void markDOT(void);
+extern void markpr(line *);
+extern int markreg(register int);
+extern char *mesg(register char *);
+extern void merror1(intptr_t);
+extern void vmerror(char *, va_list);
+extern void merror(char *, ...);
+extern int morelines(void);
+extern void nonzero(void);
+extern int notable(int);
+extern void notempty(void);
+extern void netchHAD(int);
+extern void netchange(register int);
+extern int printof(int);
+extern void putmark(line *);
+extern void putmk1(register line *, int);
+extern char *plural(long);
+extern int qcolumn(register char *, register char *);
+extern int qcount(int);
+extern void reverse(register line *, register line *);
+extern void save(line *, register line *);
+extern void save12(void);
+extern void saveall(void);
+extern int span(void);
+extern void synced(void);
+extern int skipwh(void);
+extern void vsmerror(char *, va_list);
+extern void smerror(char *, ...);
+extern char *strend(register char *);
+extern void strcLIN(char *);
+extern void syserror(void);
+extern int tabcol(int, int);
+extern char *vfindcol(int);
+extern char *vskipwh(register char *);
+extern char *vpastwh(register char *);
+extern int whitecnt(register char *);
+extern void markit(line *);
+extern void onhup(int);
+extern void onintr(int);
+extern void setrupt(void);
+extern int preserve(void);
+extern int exitex(int);
+extern void onsusp(int);
+extern void onemt(int);
+extern char *movestr(char *, const char *);
+extern char *safecp(char *, const char *, size_t, char *, ...);
+extern char *safecat(char *, const char *, size_t, char *, ...);
+/* ex_tagio.c */
+extern int topen(char *, char *);
+extern int tseek(int, off_t);
+extern int tgets(char *, int, int);
+extern void tclose(int);
+/* ex_temp.c */
+extern void fileinit(void);
+extern void cleanup(bool);
+extern void getline(line);
+extern line putline(void);
+extern char *getblock(line, int);
+extern void blkio(bloc, char *, ssize_t (*)(int, void *, size_t));
+extern void tlaste(void);
+extern void tflush(void);
+extern void synctmp(void);
+extern void TSYNC(void);
+extern void regio(short, ssize_t (*)(int, void *, size_t));
+extern int REGblk(void);
+extern struct strreg *mapreg(register int);
+extern void KILLreg(register int);
+extern ssize_t shread(void);
+extern void putreg(int);
+extern int partreg(int);
+extern void notpart(register int);
+extern int getREG(void);
+extern void YANKreg(register int);
+extern void kshift(void);
+extern void YANKline(void);
+extern void rbflush(void);
+extern void regbuf(char, char *, int);
+extern void tlaste(void);
+/* ex_tty.c */
+extern void gettmode(void);
+extern void setterm(char *);
+extern void setsize(void);
+extern void zap(void);
+extern char *gettlongname(register char *, char *);
+extern char *fkey(int);
+extern int cost(char *);
+extern int countnum(int);
+extern struct termios ostart(void);
+extern void tostart(void);
+extern void ostop(struct termios);
+extern void tostop(void);
+extern struct termios setty(struct termios);
+extern void gTTY(int);
+extern void noonl(void);
+/* ex_unix.c */
+extern void unix0(int);
+extern struct termios unixex(char *, char *, int, int);
+extern void unixwt(int, struct termios);
+extern void filter(register int);
+extern void recover(void);
+extern void waitfor(void);
+extern void revocer(void);
+/* ex_v.c */
+extern void oop(void);
+extern void ovbeg(void);
+extern void ovend(struct termios);
+extern void vop(void);
+extern void fixzero(void);
+extern void savevis(void);
+extern void undvis(void);
+extern void setwind(void);
+extern void vok(register cell *);
+extern void vintr(int);
+extern void vsetsiz(int);
+extern void onwinch(int);
+/* ex_vadj.c */
+extern void vopen(line *, int);
+extern int vreopen(int, int, int);
+extern int vglitchup(int, int);
+extern void vinslin(register int, register int, int);
+extern void vopenup(int, int, int);
+extern void vadjAL(int, int);
+extern void vrollup(int);
+extern void vup1(void);
+extern void vmoveitup(register int, int);
+extern void vscroll(register int);
+extern void vscrap(void);
+extern void vrepaint(char *);
+extern void vredraw(register int);
+extern void vdellin(int, int, int);
+extern void vadjDL(int, int);
+extern void vsyncCL(void);
+extern void vsync(register int);
+extern void vsync1(register int);
+extern void vcloseup(int, register int);
+extern void vreplace(int, int, int);
+extern void sethard(void);
+extern void vdirty(register int, register int);
+/* ex_version.c */
+extern void printver(void);
+/* ex_vget.c */
+extern void ungetkey(int);
+extern int getkey(void);
+extern int peekbr(void);
+extern int getbr(void);
+extern int getesc(void);
+extern int peekkey(void);
+extern int readecho(int);
+extern void setLAST(void);
+extern void addtext(char *);
+extern void setDEL(void);
+extern void setBUF(register cell *);
+extern void addto(register cell *, register char *);
+extern int noteit(int);
+extern void obeep(void);
+extern void macpush(char *, int);
+extern int vgetcnt(void);
+extern void trapalarm(int);
+extern int fastpeekkey(void);
+/* ex_vmain.c */
+extern void vmain(void);
+extern void grabtag(void);
+extern void prepapp(void);
+extern void vremote(int, void (*)(int), int);
+extern void vsave(void);
+extern void vzop(int, int, register int);
+extern cell *str2cell(cell *, register char *);
+extern char *cell2str(char *, register cell *);
+extern cell *cellcpy(cell *, register cell *);
+extern size_t cellen(register cell *);
+extern cell *cellcat(cell *, register cell *);
+/* ex_voper.c */
+extern void operate(register int, register int);
+extern int find(int);
+extern int word(register void (*)(int), int);
+extern void eend(register void (*)(int));
+extern int wordof(int, register char *);
+extern int wordch(char *);
+extern int edge(void);
+extern int margin(void);
+/* ex_vops.c */
+extern void vUndo(void);
+extern void vundo(int);
+extern void vmacchng(int);
+extern void vnoapp(void);
+extern void vmove(int);
+extern void vdelete(int);
+extern void vchange(int);
+extern void voOpen(int, register int);
+extern void vshftop(int);
+extern void vfilter(int);
+extern int xdw(void);
+extern void vshift(int);
+extern void vrep(register int);
+extern void vyankit(int);
+extern void setpk(void);
+extern void vkillDEL(void);
+/* ex_vops2.c */
+extern void bleep(register int, char *);
+extern int vdcMID(void);
+extern void takeout(cell *);
+extern int ateopr(void);
+extern void showmode(int);
+extern void addc(cell);
+extern void vappend(int, int, int);
+extern void back1(void);
+extern char *vgetline(int, register char *, bool *, int);
+extern void vdoappend(char *);
+extern int vgetsplit(void);
+extern int vmaxrep(int, register int);
+/* ex_vops3.c */
+extern int llfind(bool, int, void (*)(int), line *);
+extern int endsent(bool);
+extern int endPS(void);
+extern int lindent(line *);
+extern int lmatchp(line *);
+extern void lsmatch(char *);
+extern int ltosolid(void);
+extern int ltosol1(register char *);
+extern int lskipbal(register char *);
+extern int lskipatom(void);
+extern int lskipa1(register char *);
+extern int lnext(void);
+extern int lbrack(register int, void (*)(int));
+extern int isa(register char *);
+extern void vswitch(int);
+#ifdef MB
+extern int     wskipleft(char *, char *);
+extern int     wskipright(char *, char *);
+extern int     wsamechar(char *, int);
+#endif /* MB */
+/* ex_vput.c */
+extern void vclear(void);
+extern void vclrcell(register cell *, register int);
+extern void vclrlin(int, line *);
+extern void vclreol(void);
+extern void vclrech(bool);
+extern void fixech(void);
+extern void vcursbef(register char *);
+extern void vcursat(register char *);
+extern void vcursaft(register char *);
+extern void vfixcurs(void);
+extern void vsetcurs(register char *);
+extern void vigoto(int, int);
+extern void vcsync(void);
+extern void vgotoCL(register int);
+extern void vigotoCL(register int);
+extern void vgoto(register int, register int);
+extern void vgotab(void);
+extern void vprepins(void);
+extern void vmaktop(register int, cell *);
+extern int vinschar(int);
+extern void vrigid(void);
+extern void vneedpos(int);
+extern void vnpins(int);
+extern void vishft(void);
+extern void viin(int);
+extern void godm(void);
+extern void enddm(void);
+extern void goim(void);
+extern void endim(void);
+extern int vputchar(register int);
+extern void physdc(int, int);
+extern int vputch(int);
+/* ex_vwind.c */
+extern void vmoveto(register line *, char *, int);
+extern void vjumpto(register line *, char *, int);
+extern void vupdown(register int, char *);
+extern void vup(register int, register int, int);
+extern void vdown(register int, register int, int);
+extern void vcontext(register line *, int);
+extern void vclean(void);
+extern void vshow(line *, line *);
+extern void vreset(int);
+extern line *vback(register line *, register int);
+extern int vfit(register line *, int);
+extern void vroll(register int);
+extern void vrollR(register int);
+extern int vcookit(register int);
+extern int vdepth(void);
+extern void vnline(char *);
+/* malloc.c */
+/* mapmalloc.c */
+extern char *poolsbrk(intptr_t);
+/* printf.c */
+extern int printf(const char *, ...);
+extern int vprintf(const char *, va_list);
+extern char *p_dconv(long, char *);
diff --git a/ex_put.c b/ex_put.c
new file mode 100644 (file)
index 0000000..7f0e0d0
--- /dev/null
+++ b/ex_put.c
@@ -0,0 +1,1310 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_put.c   1.32 (gritter) 2/17/05";
+#endif
+#endif
+
+/* from ex_put.c       7.9.1 (2.11BSD GTE) 12/9/94 */
+
+#include "ex.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+
+/*
+ * Terminal driving and line formatting routines.
+ * Basic motion optimizations are done here as well
+ * as formatting of lines (printing of control characters,
+ * line numbering and the like).
+ */
+
+/*
+ * The routines outchar, putchar and pline are actually
+ * variables, and these variables point at the current definitions
+ * of the routines.  See the routine setflav.
+ * We sometimes make outchar be routines which catch the characters
+ * to be printed, e.g. if we want to see how long a line is.
+ * During open/visual, outchar and putchar will be set to
+ * routines in the file ex_vput.c (vputchar, vinschar, etc.).
+ */
+int    (*Outchar)(int) = termchar;
+int    (*Putchar)(int) = normchar;
+void   (*Pline)(int) = normline;
+
+int (*
+setlist(int t))(int)
+{
+       register int (*P)(int);
+
+       listf = t;
+       P = Putchar;
+       Putchar = t ? listchar : normchar;
+       return (P);
+}
+
+void (*
+setnumb(int t))(int)
+{
+       register void (*P)(int);
+
+       numberf = t;
+       P = Pline;
+       Pline = t ? numbline : normline;
+       return (P);
+}
+
+/*
+ * Format c for list mode; leave things in common
+ * with normal print mode to be done by normchar.
+ */
+int 
+listchar(int c)
+{
+
+       if (c & MULTICOL) {
+               c &= ~MULTICOL;
+               if (c == 0)
+                       return MULTICOL;
+       }
+       c &= (TRIM|QUOTE);
+       switch (c) {
+       case '\t':
+       case '\b':
+               c = ctlof(c);
+               outchar('^');
+               break;
+
+       case '\n':
+               break;
+
+       default:
+               if (c == ('\n' | QUOTE))
+                       outchar('$');
+               if (c & QUOTE)
+                       break;
+#ifndef        BIT8
+               if (c < ' ' && c != '\n')
+                       outchar('^'), c = ctlof(c);
+#else  /* !BIT8 */
+               if (!printable(c) && c != '\n' || c == DELETE)
+                       c = printof(c);
+#endif
+               break;
+       }
+       return normchar(c);
+}
+
+/*
+ * Format c for printing. Handle funnies of upper case terminals
+ * and crocky hazeltines which don't have ~.
+ */
+int
+normchar(register int c)
+{
+       int     u;
+
+#ifdef UCVISUAL
+       register char *colp;
+
+       if (c == '~' && xHZ) {
+               normchar('\\');
+               c = '^';
+       }
+#endif
+
+       if (c & MULTICOL) {
+               c &= ~MULTICOL;
+               if (c == 0)
+                       return MULTICOL;
+       }
+       c &= (TRIM|QUOTE);
+       u = c & TRIM;
+       if (c & QUOTE) {
+               if (c == (' ' | QUOTE) || c == ('\b' | QUOTE))
+                       /*EMPTY*/;
+               else if (c == QUOTE)
+                       return c;
+               else
+                       c &= TRIM;
+       }
+#ifdef BIT8
+       else {
+               if (!printable(c) && (u != '\b' || !OS) &&
+                               u != '\n' && u != '\t')
+                       c = printof(u);
+               else {
+                       c = u;
+                       if (0)
+                               /*EMPTY*/;
+#else  /* !BIT8 */
+       else if (c < ' ' && (c != '\b' || !OS) && c != '\n' && c != '\t')
+               putchar('^'), c = ctlof(c);
+#endif /* !BIT8 */
+#ifdef UCVISUAL
+       else if (UPPERCASE)
+               if (xisupper(c)) {
+                       outchar('\\');
+                       c = tolower(c);
+               } else {
+                       colp = "({)}!|^~'`";
+                       while (*colp++)
+                               if (c == *colp++) {
+                                       outchar('\\');
+                                       c = colp[-2];
+                                       break;
+                               }
+               }
+#endif /* UCVISUAL */
+#ifdef BIT8
+               }
+       }
+#endif
+       outchar(c);
+       return c;
+}
+
+/*
+ * Given c at the beginning of a line, determine whether
+ * the printing of the line will erase or otherwise obliterate
+ * the prompt which was printed before.  If it won't, do it now.
+ */
+void
+slobber(int c)
+{
+
+       shudclob = 0;
+       switch (c) {
+
+       case '\t':
+               if (Putchar == listchar)
+                       return;
+               break;
+
+       default:
+               return;
+
+       case ' ':
+       case 0:
+               break;
+       }
+       if (OS)
+               return;
+       flush();
+       putch(' ');
+       if (BC)
+               tputs(BC, 0, putch);
+       else
+               putch('\b');
+}
+
+/*
+ * Print a line with a number.
+ */
+void
+numbline(int i)
+{
+
+       if (shudclob)
+               slobber(' ');
+       printf("%6d  ", i);
+       normline(0);
+}
+
+/*
+ * Normal line output, no numbering.
+ */
+/*ARGSUSED*/
+void
+normline(int unused)
+{
+       register char *cp;
+       int     c, n;
+
+       if (shudclob)
+               slobber(linebuf[0]);
+       /* pdp-11 doprnt is not reentrant so can't use "printf" here
+          in case we are tracing */
+       cp = linebuf;
+       vcolbp = cp;
+       while (*cp) {
+               vcolbp = cp;
+               nextc(c, cp, n);
+               cp += n;
+               putchar(c);
+       }
+       if (!inopen) {
+               putchar('\n' | QUOTE);
+       }
+}
+
+/*
+ * The output buffer is initialized with a useful error
+ * message so we don't have to keep it in data space.
+ */
+static char linb[66+MB_LEN_MAX];
+char *linp = linb;
+
+/*
+ * Phadnl records when we have already had a complete line ending with \n.
+ * If another line starts without a flush, and the terminal suggests it,
+ * we switch into -nl mode so that we can send lineffeeds to avoid
+ * a lot of spacing.
+ */
+static bool phadnl;
+
+/*
+ * Indirect to current definition of putchar.
+ */
+int
+putchar(int c)
+{
+       if (c & MULTICOL) {
+               c &= ~MULTICOL;
+               if (c == 0)
+                       return MULTICOL;
+       }
+       (*Putchar)(c);
+       return c;
+}
+
+/*
+ * Termchar routine for command mode.
+ * Watch for possible switching to -nl mode.
+ * Otherwise flush into next level of buffering when
+ * small buffer fills or at a newline.
+ */
+int
+termchar(int c)
+{
+
+       if (pfast == 0 && phadnl)
+               pstart();
+       if (c == '\n')
+               phadnl = 1;
+       else if (linp >= &linb[63])
+               flush1();
+#ifdef MB
+       if (mb_cur_max > 1 && c & ~(wchar_t)0177) {
+               char    mb[MB_LEN_MAX];
+               int     i, n;
+               n = wctomb(mb, c&TRIM);
+               for (i = 0; i < n; i++)
+                       *linp++ = mb[i];
+       } else
+#endif /* MB */
+               *linp++ = c;
+       if (linp >= &linb[63]) {
+               fgoto();
+               flush1();
+       }
+       return c;
+}
+
+void
+flush2(void)
+{
+
+       fgoto();
+       flusho();
+       pstop();
+}
+
+void
+flush(void)
+{
+
+       flush1();
+       flush2();
+}
+
+/*
+ * Flush from small line buffer into output buffer.
+ * Work here is destroying motion into positions, and then
+ * letting fgoto do the optimized motion.
+ */
+void
+flush1(void)
+{
+       register char *lp;
+       int c, n;
+
+       *linp = 0;
+       lp = linb;
+       while (*lp) {
+               nextc(c, lp, n);
+               lp += n;
+               switch (c) {
+
+               case '\r':
+                       destline += destcol / TCOLUMNS;
+                       destcol = 0;
+                       continue;
+
+               case '\b':
+                       if (destcol)
+                               destcol--;
+                       continue;
+
+               case ' ':
+                       destcol++;
+                       continue;
+
+               case '\t':
+                       destcol += value(TABSTOP) - destcol % value(TABSTOP);
+                       continue;
+
+               case '\n':
+                       destline += destcol / TCOLUMNS + 1;
+                       if (destcol != 0 && destcol % TCOLUMNS == 0)
+                               destline--;
+                       destcol = 0;
+                       continue;
+
+               default:
+                       fgoto();
+                       for (;;) {
+                               if (AM == 0 && outcol == TCOLUMNS)
+                                       fgoto();
+                               c &= TRIM;
+                               putch(c);
+                               if (c == '\b') {
+                                       outcol--;
+                                       destcol--;
+#ifndef        BIT8
+                               } else if ( c >= ' ' && c != DELETE) {
+#else
+                               } else if (printable(c)) {
+#endif
+#ifdef MB
+                                       n = colsc(c);
+                                       outcol += n;
+                                       destcol += n;
+#else  /* !MB */
+                                       outcol++;
+                                       destcol++;
+#endif /* !MB */
+                                       if (XN && outcol % TCOLUMNS == 0)
+                                               putch('\r'), putch('\n');
+                               }
+                               nextc(c, lp, n);
+                               lp += n;
+#ifndef BIT8
+                               if (c <= ' ')
+#else
+                               if (c == ' ' || !printable(c))
+#endif
+                                       break;
+                       }
+                       --lp;
+                       continue;
+               }
+       }
+       linp = linb;
+}
+
+static int plodcnt, plodflg;
+
+/*
+ * Move (slowly) to destination.
+ * Hard thing here is using home cursor on really deficient terminals.
+ * Otherwise just use cursor motions, hacking use of tabs and overtabbing
+ * and backspace.
+ */
+
+int
+plodput(int c)
+{
+
+       if (plodflg)
+               plodcnt--;
+       else
+               putch(c);
+       return c;
+}
+
+int
+plod(int cnt)
+{
+       register int i, j, k = 0;
+       register int soutcol, soutline;
+
+       plodcnt = plodflg = cnt;
+       soutcol = outcol;
+       soutline = outline;
+       /*
+        * Consider homing and moving down/right from there, vs moving
+        * directly with local motions to the right spot.
+        */
+       if (HO) {
+               /*
+                * i is the cost to home and tab/space to the right to
+                * get to the proper column.  This assumes ND space costs
+                * 1 char.  So i+destcol is cost of motion with home.
+                */
+               if (GT)
+                       i = (destcol / value(HARDTABS)) + (destcol % value(HARDTABS));
+               else
+                       i = destcol;
+               /*
+                * j is cost to move locally without homing
+                */
+               if (destcol >= outcol) {        /* if motion is to the right */
+                       j = destcol / value(HARDTABS) - outcol / value(HARDTABS);
+                       if (GT && j)
+                               j += destcol % value(HARDTABS);
+                       else
+                               j = destcol - outcol;
+               } else
+                       /* leftward motion only works if we can backspace. */
+                       if (outcol - destcol <= i && (BS || BC))
+                               i = j = outcol - destcol; /* cheaper to backspace */
+                       else
+                               j = i + 1; /* impossibly expensive */
+
+               /* k is the absolute value of vertical distance */
+               k = outline - destline;
+               if (k < 0)
+                       k = -k;
+               j += k;
+
+               /*
+                * Decision.  We may not have a choice if no UP.
+                */
+               if (i + destline < j || (!UP && destline < outline)) {
+                       /*
+                        * Cheaper to home.  Do it now and pretend it's a
+                        * regular local motion.
+                        */
+                       tputs(HO, 0, plodput);
+                       outcol = outline = 0;
+               } else if (LL) {
+                       /*
+                        * Quickly consider homing down and moving from there.
+                        * Assume cost of LL is 2.
+                        */
+                       k = (TLINES - 1) - destline;
+                       if (i + k + 2 < j && (k<=0 || UP)) {
+                               tputs(LL, 0, plodput);
+                               outcol = 0;
+                               outline = TLINES - 1;
+                       }
+               }
+       } else
+       /*
+        * No home and no up means it's impossible, so we return an
+        * incredibly big number to make cursor motion win out.
+        */
+               if (!UP && destline < outline)
+                       return (500);
+       if (GT)
+               i = destcol % value(HARDTABS)
+                   + destcol / value(HARDTABS);
+       else
+               i = destcol;
+/*
+       if (BT && outcol > destcol && (j = (((outcol+7) & ~7) - destcol - 1) >> 3)) {
+               j *= (k = strlen(BT));
+               if ((k += (destcol&7)) > 4)
+                       j += 8 - (destcol&7);
+               else
+                       j += k;
+       } else
+*/
+               j = outcol - destcol;
+       /*
+        * If we will later need a \n which will turn into a \r\n by
+        * the system or the terminal, then don't bother to try to \r.
+        */
+       if ((NONL || !pfast) && outline < destline)
+               goto dontcr;
+       /*
+        * If the terminal will do a \r\n and there isn't room for it,
+        * then we can't afford a \r.
+        */
+       if (NC && outline >= destline)
+               goto dontcr;
+       /*
+        * If it will be cheaper, or if we can't back up, then send
+        * a return preliminarily.
+        */
+       if (j > i + 1 || outcol > destcol && !BS && !BC) {
+               /*
+                * BUG: this doesn't take the (possibly long) length
+                * of xCR into account.
+                */
+               if (ospeed != B0) {
+                       if (xCR)
+                               tputs(xCR, 0, plodput);
+                       else
+                               plodput('\r');
+               }
+               if (NC) {
+                       if (xNL)
+                               tputs(xNL, 0, plodput);
+                       else
+                               plodput('\n');
+                       outline++;
+               }
+               outcol = 0;
+       }
+dontcr:
+       /* Move down, if necessary, until we are at the desired line */
+       while (outline < destline) {
+               j = destline - outline;
+               if (j > costDP && DOWN_PARM) {
+                       /* Win big on Tek 4025 */
+                       tputs(tgoto(DOWN_PARM, 0, j), j, plodput);
+                       outline += j;
+               }
+               else {
+                       outline++;
+                       if (xNL && pfast)
+                               tputs(xNL, 0, plodput);
+                       else
+                               plodput('\n');
+               }
+               if (plodcnt < 0)
+                       goto out;
+               if (NONL || pfast == 0)
+                       outcol = 0;
+       }
+       if (BT)
+               k = strlen(BT); /* should probably be cost(BT) and moved out */
+       /* Move left, if necessary, to desired column */
+       while (outcol > destcol) {
+               if (plodcnt < 0)
+                       goto out;
+               if (BT && !insmode && outcol - destcol > 4+k) {
+                       tputs(BT, 0, plodput);
+                       outcol--;
+                       outcol -= outcol % value(HARDTABS); /* outcol &= ~7; */
+                       continue;
+               }
+               j = outcol - destcol;
+               if (j > costLP && LEFT_PARM) {
+                       tputs(tgoto(LEFT_PARM, 0, j), j, plodput);
+                       outcol -= j;
+               }
+               else {
+                       outcol--;
+                       if (BC)
+                               tputs(BC, 0, plodput);
+                       else
+                               plodput('\b');
+               }
+       }
+       /* Move up, if necessary, to desired row */
+       while (outline > destline) {
+               j = outline - destline;
+               if (UP_PARM && j > 1) {
+                       /* Win big on Tek 4025 */
+                       tputs(tgoto(UP_PARM, 0, j), j, plodput);
+                       outline -= j;
+               }
+               else {
+                       outline--;
+                       tputs(UP, 0, plodput);
+               }
+               if (plodcnt < 0)
+                       goto out;
+       }
+       /*
+        * Now move to the right, if necessary.  We first tab to
+        * as close as we can get.
+        */
+       if (GT && !insmode && destcol - outcol > 1) {
+               /* tab to right as far as possible without passing col */
+               for (;;) {
+                       i = tabcol(outcol, value(HARDTABS));
+                       if (i > destcol)
+                               break;
+                       if (TA)
+                               tputs(TA, 0, plodput);
+                       else
+                               plodput('\t');
+                       outcol = i;
+               }
+               /* consider another tab and then some backspaces */
+               if (destcol - outcol > 4 && i < TCOLUMNS && (BC || BS)) {
+                       if (TA)
+                               tputs(TA, 0, plodput);
+                       else
+                               plodput('\t');
+                       outcol = i;
+                       /*
+                        * Back up.  Don't worry about LEFT_PARM because
+                        * it's never more than 4 spaces anyway.
+                        */
+                       while (outcol > destcol) {
+                               outcol--;
+                               if (BC)
+                                       tputs(BC, 0, plodput);
+                               else
+                                       plodput('\b');
+                       }
+               }
+       }
+       /*
+        * We've tabbed as much as possible.  If we still need to go
+        * further (not exact or can't tab) space over.  This is a
+        * very common case when moving to the right with space.
+        */
+       while (outcol < destcol) {
+               j = destcol - outcol;
+               if (j > costRP && RIGHT_PARM) {
+                       /*
+                        * This probably happens rarely, if at all.
+                        * It seems mainly useful for ANSI terminals
+                        * with no hardware tabs, and I don't know
+                        * of any such terminal at the moment.
+                        */
+                       tputs(tgoto(RIGHT_PARM, 0, j), j, plodput);
+                       outcol += j;
+               }
+               else {
+                       /*
+                        * move one char to the right.  We don't use ND space
+                        * because it's better to just print the char we are
+                        * moving over.  There are various exceptions, however.
+                        * If !inopen, vtube contains garbage.  If the char is
+                        * a null or a tab we want to print a space.  Other
+                        * random chars we use space for instead, too.
+                        */
+                       if (!inopen || vtube[outline]==NULL ||
+#ifndef        BIT8
+                               ((i=vtube[outline][outcol]) < ' ')
+#else
+                               ((i=vtube[outline][outcol]) == 0)
+                                       || (i!=MULTICOL && !printable(i&~INVBIT&~MULTICOL))
+#endif
+                               )
+                               i = ' ';
+                       if((i & (QUOTE|INVBIT)) == QUOTE) /* mjm: no sign
+                                                            extension on 3B */
+                               i = ' ';
+                       if ((insmode || i == MULTICOL) && ND)
+                               tputs(ND, 0, plodput);
+                       else if (i == MULTICOL) {
+                               if (BS && BC)
+                                       tputs(BC, 0, plodput);
+                               else
+                                       plodput('\b');
+                               plodput(vtube[outline][outcol-1]);
+                       } else
+                               plodput(i);
+                       outcol += i == MULTICOL ? 1 : colsc(i & ~MULTICOL);
+               }
+               if (plodcnt < 0)
+                       goto out;
+       }
+out:
+       if (plodflg) {
+               outcol = soutcol;
+               outline = soutline;
+       }
+       return(plodcnt);
+}
+
+/*
+ * Sync the position of the output cursor.
+ * Most work here is rounding for terminal boundaries getting the
+ * column position implied by wraparound or the lack thereof and
+ * rolling up the screen to get destline on the screen.
+ */
+void
+fgoto(void)
+{
+       register int l, c;
+
+       if (destcol > TCOLUMNS - 1) {
+               destline += destcol / TCOLUMNS;
+               destcol %= TCOLUMNS;
+       }
+       if (outcol > TCOLUMNS - 1) {
+               l = (outcol + 1) / TCOLUMNS;
+               outline += l;
+               outcol %= TCOLUMNS;
+               if (AM == 0) {
+                       while (l > 0) {
+                               if (pfast && ospeed != B0)
+                                       if (xCR)
+                                               tputs(xCR, 0, putch);
+                                       else
+                                               putch('\r');
+                               if (xNL)
+                                       tputs(xNL, 0, putch);
+                               else
+                                       putch('\n');
+                               l--;
+                       }
+                       outcol = 0;
+               }
+               if (outline > TLINES - 1) {
+                       destline -= outline - (TLINES - 1);
+                       outline = TLINES - 1;
+               }
+       }
+       if (destline > TLINES - 1) {
+               l = destline;
+               destline = TLINES - 1;
+               if (outline < TLINES - 1) {
+                       c = destcol;
+                       if (pfast == 0 && (!CA || holdcm))
+                               destcol = 0;
+                       fgoto();
+                       destcol = c;
+               }
+               while (l > TLINES - 1) {
+                       /*
+                        * The following linefeed (or simulation thereof)
+                        * is supposed to scroll up the screen, since we
+                        * are on the bottom line.  We make the assumption
+                        * that linefeed will scroll.  If ns is in the
+                        * capability list this won't work.  We should
+                        * probably have an sc capability but sf will
+                        * generally take the place if it works.
+                        *
+                        * Superbee glitch:  in the middle of the screen we
+                        * have to use esc B (down) because linefeed screws up
+                        * in "Efficient Paging" (what a joke) mode (which is
+                        * essential in some SB's because CRLF mode puts garbage
+                        * in at end of memory), but you must use linefeed to
+                        * scroll since down arrow won't go past memory end.
+                        * I turned this off after recieving Paul Eggert's
+                        * Superbee description which wins better.
+                        */
+                       if (xNL /* && !XB */ && pfast)
+                               tputs(xNL, 0, putch);
+                       else
+                               putch('\n');
+                       l--;
+                       if (pfast == 0)
+                               outcol = 0;
+               }
+       }
+       if (destline < outline && !(CA && !holdcm || UP != NOSTR))
+               destline = outline;
+       if (CA && !holdcm)
+               if (plod(costCM) > 0)
+                       plod(0);
+               else
+                       tputs(tgoto(CM, destcol, destline), 0, putch);
+       else
+               plod(0);
+       outline = destline;
+       outcol = destcol;
+}
+
+/*
+ * Tab to column col by flushing and then setting destcol.
+ * Used by "set all".
+ */
+void
+tab(int col)
+{
+
+       flush1();
+       destcol = col;
+}
+
+/*
+ * An input line arrived.
+ * Calculate new (approximate) screen line position.
+ * Approximate because kill character echoes newline with
+ * no feedback and also because of long input lines.
+ */
+void
+noteinp(void)
+{
+
+       outline++;
+       if (outline > TLINES - 1)
+               outline = TLINES - 1;
+       destline = outline;
+       destcol = outcol = 0;
+}
+
+/*
+ * Something weird just happened and we
+ * lost track of whats happening out there.
+ * Since we cant, in general, read where we are
+ * we just reset to some known state.
+ * On cursor addressible terminals setting to unknown
+ * will force a cursor address soon.
+ */
+void
+termreset(void)
+{
+
+       endim();
+       if (TI) /* otherwise it flushes anyway, and 'set tty=dumb' vomits */
+               putpad(TI);      /*adb change -- emit terminal initial sequence */
+       destcol = 0;
+       destline = TLINES - 1;
+       if (CA) {
+               outcol = UKCOL;
+               outline = UKCOL;
+       } else {
+               outcol = destcol;
+               outline = destline;
+       }
+}
+
+/*
+ * Low level buffering, with the ability to drain
+ * buffered output without printing it.
+ */
+char   *obp = obuf;
+
+void
+draino(void)
+{
+
+       obp = obuf;
+}
+
+void
+flusho(void)
+{
+
+       if (obp != obuf) {
+               write(1, obuf, obp - obuf);
+               obp = obuf;
+       }
+}
+
+void
+putnl(void)
+{
+
+       putchar('\n');
+}
+
+void
+putS(char *cp)
+{
+
+       if (cp == NULL)
+               return;
+       while (*cp)
+               putch(*cp++);
+}
+
+
+int
+putch(int c)
+{
+
+#ifdef OLD3BTTY                /* mjm */
+       if(c == '\n')   /* mjm: Fake "\n\r" for '\n' til fix in 3B firmware */
+               putch('\r');    /* mjm: vi does "stty -icanon" => -onlcr !! */
+#endif
+       if (c & MULTICOL) {
+               c &= ~MULTICOL;
+               if (c == 0)
+                       return MULTICOL;
+       }
+       c &= ~INVBIT;   /* strip '~' | INVBIT multicolumn filler */
+#ifdef MB
+       if (mb_cur_max > 1 && c & ~(wchar_t)0177) {
+               char    mb[MB_LEN_MAX];
+               int     i, n;
+               n = wctomb(mb, c&TRIM);
+               for (i = 0; i < n; i++) {
+                       *obp++ = mb[i];
+                       if (obp >= &obuf[sizeof obuf])
+                               flusho();
+               }
+       } else
+#endif /* MB */
+               *obp++ = c & TRIM;
+       if (obp >= &obuf[sizeof obuf])
+               flusho();
+       return c;
+}
+
+/*
+ * Miscellaneous routines related to output.
+ */
+
+/*
+ * Put with padding
+ */
+void
+putpad(char *cp)
+{
+
+       flush();
+       tputs(cp, 0, putch);
+}
+
+/*
+ * Set output through normal command mode routine.
+ */
+void
+setoutt(void)
+{
+
+       Outchar = termchar;
+}
+
+/*
+ * Printf (temporarily) in list mode.
+ */
+/*VARARGS2*/
+void
+vlprintf(char *cp, va_list ap)
+{
+       register int (*P)();
+
+       P = setlist(1);
+       vprintf(cp, ap);
+       Putchar = P;
+}
+
+void
+lprintf(char *cp, ...)
+{
+       va_list ap;
+
+       va_start(ap, cp);
+       vlprintf(cp, ap);
+       va_end(ap);
+}
+
+/*
+ * Newline + flush.
+ */
+void
+putNFL(void)
+{
+
+       putnl();
+       flush();
+}
+
+/*
+ * sTTY: set the tty modes on file descriptor i to be what's
+ * currently in global "tty".  (Also use nttyc if needed.)
+ */
+void
+sTTY(int i)
+{
+
+       tcsetattr(i, TCSADRAIN, &tty);
+}
+
+/*
+ * Try to start -nl mode.
+ */
+void
+pstart(void)
+{
+
+       if (NONL)
+               return;
+       if (!value(OPTIMIZE))
+               return;
+       if (ruptible == 0 || pfast)
+               return;
+       fgoto();
+       flusho();
+       pfast = 1;
+       normtty++;
+       tty = normf;
+       tty.c_oflag &= ~(ONLCR
+#if defined (TAB3)
+                       | TAB3
+#elif defined (XTABS)
+                       | XTABS
+#endif
+                       );
+       tty.c_lflag &= ~ECHO;
+       sTTY(1);
+}
+
+/*
+ * Stop -nl mode.
+ */
+void
+pstop(void)
+{
+
+       if (inopen)
+               return;
+       phadnl = 0;
+       linp = linb;
+       draino();
+       normal(normf);
+       pfast &= ~1;
+}
+
+/*
+ * Turn off start/stop chars if they aren't the default ^S/^Q.
+ * This is so idiots who make esc their start/stop don't lose.
+ * We always turn off quit since datamedias send ^\ for their
+ * right arrow key.
+ */
+void
+ttcharoff(void)
+{
+#ifdef _PC_VDISABLE
+       long vdis;
+
+       errno = 0;
+#ifndef        __dietlibc__
+       vdis = fpathconf(1, _PC_VDISABLE);
+       if (errno)
+               /*
+                * Use the old value of 0377, hope it is not
+                * the user's favourite character.
+                */
+#endif /* !__dietlibc__ */
+               vdis = '\377';
+#else  /* !_PC_VDISABLE */
+#define        vdis    '\377';
+#endif /* !_PC_VDISABLE */
+       tty.c_cc[VQUIT] = vdis;
+#ifdef VSUSP
+       tty.c_cc[VSUSP] = vdis;
+#endif
+#ifdef VDSUSP
+       tty.c_cc[VDSUSP] = vdis;
+#endif
+#ifdef VREPRINT
+       tty.c_cc[VREPRINT] = vdis;
+#endif
+#ifdef VDISCRD
+       tty.c_cc[VDISCRD] = vdis;
+#endif
+#ifdef VWERASE
+       tty.c_cc[VWERASE] = vdis;
+#endif
+#ifdef VLNEXT
+       tty.c_cc[VLNEXT] = vdis;
+#endif
+#ifdef VSTATUS
+       tty.c_cc[VSTATUS] = vdis;
+#endif
+# ifdef VSTART
+       /*
+        * The following is sample code if USG ever lets people change
+        * their start/stop chars.  As long as they can't we can't get
+        * into trouble so we just leave them alone.
+        */
+       if (tty.c_cc[VSTART] != CTRL('q'))
+               tty.c_cc[VSTART] = vdis;
+       if (tty.c_cc[VSTOP] != CTRL('s'))
+               tty.c_cc[VSTOP] = vdis;
+# endif
+}
+
+/*
+ * Prep tty for open mode.
+ */
+struct termios
+ostart(void)
+{
+       struct termios f;
+
+       if (!intty)
+               error(catgets(catd, 1, 120,
+                               "Open and visual must be used interactively"));
+       gTTY(1);
+       normtty++;
+       f = tty;
+       tty = normf;
+       tty.c_iflag &= ~ICRNL;
+       tty.c_lflag &= ~(ECHO|ICANON);
+       tty.c_oflag &= ~(ONLCR
+#if defined (TAB3)
+                       | TAB3
+#elif defined (XTABS)
+                       | XTABS
+#endif
+                       );
+       tty.c_cc[VMIN] = 1;
+       tty.c_cc[VTIME] = 1;
+       ttcharoff();
+       sTTY(1);
+       tostart();
+       pfast |= 2;
+       return (f);
+}
+
+/* actions associated with putting the terminal in open mode */
+void
+tostart(void)
+{
+       putpad(VS);
+       putpad(KS);
+       if (!value(MESG)) {
+               if (ttynbuf[0] == 0) {
+                       register char *tn;
+                       if ((tn=ttyname(2)) == NULL &&
+                           (tn=ttyname(1)) == NULL &&
+                           (tn=ttyname(0)) == NULL)
+                               ttynbuf[0] = 1;
+                       else
+                               safecp(ttynbuf, tn, sizeof ttynbuf,
+                                               "%s too long", tn);
+               }
+               if (ttynbuf[0] != 1) {
+                       struct stat sbuf;
+                       stat(ttynbuf, &sbuf);
+                       ttymesg = sbuf.st_mode & 0777;
+                       chmod(ttynbuf,
+#ifdef UCBV7
+       /*
+        * This applies to the UCB V7 Pdp-11 system with the
+        * -u write option only.
+        */
+                                       0611    /* 11 = urgent only allowed */
+#else
+                                       0600
+#endif
+                                               );
+               }
+       }
+}
+
+/*
+ * Stop open, restoring tty modes.
+ */
+void
+ostop(struct termios f)
+{
+
+       pfast = (f.c_oflag & ONLCR) == 0;
+       termreset(), fgoto(), flusho();
+       normal(f);
+       tostop();
+}
+
+/* Actions associated with putting the terminal in the right mode. */
+void
+tostop(void)
+{
+       putpad(VE);
+       putpad(KE);
+       if (!value(MESG) && ttynbuf[0]>1)
+               chmod(ttynbuf, ttymesg);
+}
+
+/*
+ * Restore flags to normal state f.
+ */
+void
+normal(struct termios f)
+{
+
+       if (normtty > 0) {
+               setty(f);
+               normtty--;
+       }
+}
+
+/*
+ * Straight set of flags to state f.
+ */
+struct termios
+setty(struct termios f)
+{
+       struct termios ot;
+       ot = tty;
+
+       if (tty.c_lflag & ICANON)
+               ttcharoff();
+       tty = f;
+       sTTY(1);
+       return (ot);
+}
+
+void
+gTTY(int i)
+{
+
+       tcgetattr(i, &tty);
+}
+
+/*
+ * Print newline, or blank if in open/visual
+ */
+void
+noonl(void)
+{
+
+       putchar(Outchar != termchar ? ' ' : '\n');
+}
diff --git a/ex_re.c b/ex_re.c
new file mode 100644 (file)
index 0000000..7f903eb
--- /dev/null
+++ b/ex_re.c
@@ -0,0 +1,1313 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_re.c    1.56 (gritter) 3/25/05";
+#endif
+#endif
+
+/* from ex_re.c        7.5 (Berkeley) 6/7/85 */
+
+#include "ex.h"
+#include "ex_re.h"
+
+#ifdef UXRE
+
+#include <regex.h>
+
+char   *braslist[NBRA];
+char   *braelist[NBRA];
+char   *loc1;
+char   *loc2;
+
+#else  /* !UXRE */
+static int     regerrno;
+
+#define        INIT                    register char *sp = instring;
+#define        GETC()                  (*sp++)
+#define        PEEKC()                 (*sp)
+#define        UNGETC(c)               (--sp)
+#define        RETURN(c)               return(ep);
+#define        ERROR(c)                { regerrno = c; return 0; }
+
+#define        compile(a, b, c, d)     _compile(a, b, c, d)
+#define        regexp_h_static         static
+
+#ifndef        NO_BE_BACKSLASH
+#define        REGEXP_H_VI_BACKSLASH
+#endif /* !NO_BE_BACKSLASH */
+
+#ifdef MB
+#define        REGEXP_H_WCHARS
+#endif /* MB */
+
+#define        REGEXP_H_USED_FROM_VI
+
+#include "regexp.h"
+
+#ifndef        REG_ICASE
+#define        REG_ICASE       1
+#endif
+
+static size_t
+loconv(register char *dst, register const char *src)
+{
+       char    *odst = dst;
+
+#ifdef MB
+       if (mb_cur_max > 1) {
+               char    mb[MB_LEN_MAX];
+               wchar_t wc;
+               int len, i, nlen;
+
+               for (;;) {
+                       if ((*src & 0200) == 0) {
+                               *dst++ = tolower(*src);
+                               if (*src++ == '\0')
+                                       break;
+                       } else if ((len = mbtowc(&wc, src, mb_cur_max)) <= 0) {
+                               *dst++ = *src++;
+                       } else {
+                               wc = towlower(wc);
+                               if (len >= mb_cur_max) {
+                                       if ((nlen = wctomb(dst, wc)) <= len) {
+                                               dst += nlen;
+                                               src += len;
+                                       } else {
+                                               *dst++ = *src++;
+                                       }
+                               } else {
+                                       if ((nlen = wctomb(mb, wc)) <= len) {
+                                               src += len;
+                                               for (i = 0; i < nlen; i++)
+                                                       *dst++ = mb[i];
+                                       } else {
+                                               *dst++ = *src++;
+                                       }
+                               }
+                       }
+               }
+       } else
+#endif /* MB */
+       {
+               do
+                       *dst++ = tolower(*src & 0377);
+               while (*src++);
+       }
+       return dst - odst;
+}
+
+#undef compile
+
+#endif /* !UXRE */
+
+/*
+ * Global, substitute and regular expressions.
+ * Very similar to ed, with some re extensions and
+ * confirmed substitute.
+ */
+void 
+global(int k)
+{
+       register char *gp;
+       register int c, i;
+       register line *a1;
+       char    mb[MB_LEN_MAX+1];
+       char globuf[GBSIZE], *Cwas;
+       int lines = lineDOL();
+       int oinglobal = inglobal;
+       char *oglobp = globp;
+
+       Cwas = Command;
+       /*
+        * States of inglobal:
+        *  0: ordinary - not in a global command.
+        *  1: text coming from some buffer, not tty.
+        *  2: like 1, but the source of the buffer is a global command.
+        * Hence you're only in a global command if inglobal==2. This
+        * strange sounding convention is historically derived from
+        * everybody simulating a global command.
+        */
+       if (inglobal==2)
+               error(catgets(catd, 1, 121,
+                               "Global within global@not allowed"));
+       markDOT();
+       setall();
+       nonzero();
+       if (skipend())
+               error(catgets(catd, 1, 122,
+               "Global needs re|Missing regular expression for global"));
+       c = GETWC(mb);
+       ignore(compile(c, 1));
+       savere(&scanre);
+       gp = globuf;
+       while ((c = GETWC(mb)) != '\n') {
+               switch (c) {
+
+               case EOF:
+                       c = '\n';
+                       goto brkwh;
+
+               case '\\':
+                       c = GETWC(mb);
+                       switch (c) {
+
+                       case '\\':
+                               ungetchar(c);
+                               break;
+
+                       case '\n':
+                               break;
+
+                       default:
+                               *gp++ = '\\';
+                               break;
+                       }
+                       break;
+               }
+               for (i = 0; mb[i]; i++) {
+                       *gp++ = mb[i];
+                       if (gp >= &globuf[GBSIZE - 2])
+                               error(catgets(catd, 1, 123,
+                                               "Global command too long"));
+               }
+       }
+brkwh:
+       ungetchar(c);
+/* out: */
+       newline();
+       *gp++ = c;
+       *gp++ = 0;
+       saveall();
+       inglobal = 2;
+       for (a1 = one; a1 <= dol; a1++) {
+               *a1 &= ~01;
+               if (a1 >= addr1 && a1 <= addr2 && execute(0, a1) == k)
+                       *a1 |= 01;
+       }
+#ifdef notdef
+/*
+ * This code is commented out for now.  The problem is that we don't
+ * fix up the undo area the way we should.  Basically, I think what has
+ * to be done is to copy the undo area down (since we shrunk everything)
+ * and move the various pointers into it down too.  I will do this later
+ * when I have time. (Mark, 10-20-80)
+ */
+       /*
+        * Special case: g/.../d (avoid n^2 algorithm)
+        */
+       if (globuf[0]=='d' && globuf[1]=='\n' && globuf[2]=='\0') {
+               gdelete();
+               return;
+       }
+#endif
+       if (inopen)
+               inopen = -1;
+       /*
+        * Now for each marked line, set dot there and do the commands.
+        * Note the n^2 behavior here for lots of lines matching.
+        * This is really needed: in some cases you could delete lines,
+        * causing a marked line to be moved before a1 and missed if
+        * we didn't restart at zero each time.
+        */
+       for (a1 = one; a1 <= dol; a1++) {
+               if (*a1 & 01) {
+                       *a1 &= ~01;
+                       dot = a1;
+                       globp = globuf;
+                       commands(1, 1);
+                       a1 = zero;
+               }
+       }
+       globp = oglobp;
+       inglobal = oinglobal;
+       endline = 1;
+       Command = Cwas;
+       netchHAD(lines);
+       setlastchar(EOF);
+       if (inopen) {
+               ungetchar(EOF);
+               inopen = 1;
+       }
+}
+
+/*
+ * gdelete: delete inside a global command. Handles the
+ * special case g/r.e./d. All lines to be deleted have
+ * already been marked. Squeeze the remaining lines together.
+ * Note that other cases such as g/r.e./p, g/r.e./s/r.e.2/rhs/,
+ * and g/r.e./.,/r.e.2/d are not treated specially.  There is no
+ * good reason for this except the question: where to you draw the line?
+ */
+void 
+gdelete(void)
+{
+       register line *a1, *a2, *a3;
+
+       a3 = dol;
+       /* find first marked line. can skip all before it */
+       for (a1=zero; (*a1&01)==0; a1++)
+               if (a1>=a3)
+                       return;
+       /* copy down unmarked lines, compacting as we go. */
+       for (a2=a1+1; a2<=a3;) {
+               if (*a2&01) {
+                       a2++;           /* line is marked, skip it */
+                       dot = a1;       /* dot left after line deletion */
+               } else
+                       *a1++ = *a2++;  /* unmarked, copy it */
+       }
+       dol = a1-1;
+       if (dot>dol)
+               dot = dol;
+       change();
+}
+
+bool   cflag;
+int    scount, slines, stotal;
+
+int 
+substitute(int c)
+{
+       register line *addr;
+       register int n;
+       int gsubf, hopcount;
+
+       gsubf = compsub(c);
+       if(FIXUNDO)
+               save12(), undkind = UNDCHANGE;
+       stotal = 0;
+       slines = 0;
+       for (addr = addr1; addr <= addr2; addr++) {
+               scount = hopcount = 0;
+               if (dosubcon(0, addr) == 0)
+                       continue;
+               if (gsubf) {
+                       /*
+                        * The loop can happen from s/\</&/g
+                        * but we don't want to break other, reasonable cases.
+                        */
+                       while (*loc2) {
+                               if (++hopcount > sizeof linebuf)
+                                       error(catgets(catd, 1, 124,
+                                                       "substitution loop"));
+                               if (dosubcon(1, addr) == 0)
+                                       break;
+                       }
+               }
+               if (scount) {
+                       stotal += scount;
+                       slines++;
+                       putmark(addr);
+                       n = append(getsub, addr);
+                       addr += n;
+                       addr2 += n;
+               }
+       }
+       if (stotal == 0 && !inglobal && !cflag)
+               error(catgets(catd, 1, 125,
+                               "Fail|Substitute pattern match failed"));
+       snote(stotal, slines);
+       return (stotal);
+}
+
+int 
+compsub(int ch)
+{
+       register int seof, c, uselastre;
+       char    mb[MB_LEN_MAX+1];
+       static int gsubf;
+
+       if (!value(EDCOMPATIBLE))
+               gsubf = cflag = 0;
+       uselastre = 0;
+       switch (ch) {
+
+       case 's':
+               ignore(skipwh());
+               seof = GETWC(mb);
+               if (endcmd(seof) || any(seof, "gcr")) {
+                       ungetchar(seof);
+                       goto redo;
+               }
+               if (xisalnum(seof))
+                       error(catgets(catd, 1, 126,
+       "Substitute needs re|Missing regular expression for substitute"));
+               seof = compile(seof, 1);
+               uselastre = 1;
+               comprhs(seof);
+               gsubf = 0;
+               cflag = 0;
+               break;
+
+       case '~':
+               uselastre = 1;
+               /* fall into ... */
+       case '&':
+       redo:
+               if (re.Patbuf[0] == 0)
+                       error(catgets(catd, 1, 127,
+                       "No previous re|No previous regular expression"));
+               if (subre.Patbuf[0] == 0)
+                       error(catgets(catd, 1, 128,
+       "No previous substitute re|No previous substitute to repeat"));
+               break;
+       }
+       for (;;) {
+               c = getchar();
+               switch (c) {
+
+               case 'g':
+                       gsubf = !gsubf;
+                       continue;
+
+               case 'c':
+                       cflag = !cflag;
+                       continue;
+
+               case 'r':
+                       uselastre = 1;
+                       continue;
+
+               default:
+                       ungetchar(c);
+                       setcount();
+                       newline();
+                       if (uselastre)
+                               savere(&subre);
+                       else
+                               resre(&subre);
+                       return (gsubf);
+               }
+       }
+}
+
+void
+comprhs(int seof)
+{
+       register char *rp, *orp;
+       char    mb[MB_LEN_MAX+1];
+#ifdef BIT8
+       char *qp, *oqp;
+#endif
+       register int c, i;
+#ifdef BIT8
+       int q;
+#endif
+       char orhsbuf[RHSSIZE];
+#ifdef BIT8
+       char orhsquo[RHSSIZE];
+#endif
+       int     hashflag = 0;
+
+       rp = rhsbuf;
+#ifdef BIT8
+       qp = rhsquo;
+#endif
+       CP(orhsbuf, rp);
+#ifdef BIT8
+       copy(orhsquo, qp, (size_t) strlen(rp));
+#endif
+       for (;;) {
+               c = GETWC(mb);
+#ifdef BIT8
+               q = 0;
+#endif
+               if (c == seof)
+                       break;
+               switch (c) {
+
+               case '%':
+                       if (rp == rhsbuf)
+                               hashflag = 1;
+                       break;
+
+               case '\\':
+                       c = GETWC(mb);
+                       if (c == EOF) {
+                               ungetchar(c);
+                               break;
+                       }
+                       if (value(MAGIC)) {
+                               /*
+                                * When "magic", \& turns into a plain &,
+                                * and all other chars work fine quoted.
+                                */
+                               if (c != '&')
+#ifndef        BIT8
+                                       c |= QUOTE;
+#else
+                                       q = 1;
+#endif
+                               break;
+                       }
+magic:
+                       if (c == '~') {
+hash:
+#ifndef        BIT8
+                               for (orp = orhsbuf; *orp; *rp++ = *orp++) {
+#else
+                               for (orp = orhsbuf, oqp = orhsquo;
+                                               *orp; *rp++ = *orp++) {
+                                       *qp++ = *oqp++;
+#endif
+                                       if (rp >= &rhsbuf[RHSSIZE - 1])
+                                               goto toobig;
+                               }
+                               if (hashflag & 2)
+                                       goto endrhs;
+                               continue;
+                       }
+#ifndef        BIT8
+                       c |= QUOTE;
+#else
+                       q = 1;
+#endif
+                       break;
+
+               case '\n':
+               case EOF:
+                       if (!(globp && globp[0])) {
+                               ungetchar(c);
+                               goto endrhs;
+                       }
+
+               case '~':
+               case '&':
+                       if (value(MAGIC))
+                               goto magic;
+                       break;
+               }
+               if (rp >= &rhsbuf[RHSSIZE - 1]) {
+toobig:
+                       *rp = 0;
+                       error(catgets(catd, 1, 129,
+               "Replacement pattern too long@- limit 256 characters"));
+               }
+               for (i = 0; mb[i]; i++) {
+                       *rp++ = mb[i];
+#ifdef BIT8
+                       *qp++ = q;
+#endif
+               }
+       }
+endrhs:
+       if (hashflag == 1 && rhsbuf[0] == '%' && rp == &rhsbuf[1]) {
+               rp = rhsbuf;
+               hashflag |= 2;
+               goto hash;
+       }
+       *rp++ = 0;
+}
+
+int
+getsub(void)
+{
+       register char *p;
+
+       if ((p = linebp) == 0)
+               return (EOF);
+       strcLIN(p);
+       linebp = 0;
+       return (0);
+}
+
+int
+dosubcon(bool f, line *a)
+{
+
+       if (execute(f, a) == 0)
+               return (0);
+       if (confirmed(a)) {
+               dosub();
+               scount++;
+       }
+       return (1);
+}
+
+int
+confirmed(line *a)
+{
+       register int c;
+       char *yesstr = catgets(catd, 1, 249, "y");
+       int okay = -1;
+
+       if (cflag == 0)
+               return (1);
+       pofix();
+       pline(lineno(a));
+       if (inopen)
+               putchar('\n' | QUOTE);
+       c = column(loc1 - 1);
+       ugo(c - 1 + (inopen ? 1 : 0), ' ');
+       ugo(column(loc2 - 1) - c, '^');
+       flush();
+       c = getkey();
+again:
+       if (c == '\r')
+               c = '\n';
+       if (inopen)
+               putchar(c), flush();
+       if (c != '\n' && c != EOF) {
+               if (okay && *yesstr) {
+                       if (c == (*yesstr++ & 0377))
+                               okay = 1;
+                       else
+                               okay = 0;
+               }
+               c = getkey();
+               goto again;
+       }
+       noteinp();
+       return (okay > 0);
+}
+
+#ifdef notdef
+int
+ex_getch(void)
+{
+       char c;
+
+       if (read(2, &c, 1) != 1)
+               return (EOF);
+#ifndef        BIT8
+       return (c & TRIM);
+#else
+       return c;
+#endif
+}
+#endif /* notdef */
+
+void
+ugo(int cnt, int with)
+{
+
+       if (cnt > 0)
+               do
+                       putchar(with);
+               while (--cnt > 0);
+}
+
+int    casecnt;
+bool   destuc;
+
+void
+dosub(void)
+{
+       register char *lp, *sp, *rp;
+       int c, n;
+#ifdef BIT8
+       register char *qp;
+       int q;
+#endif
+
+       lp = linebuf;
+       sp = genbuf;
+       rp = rhsbuf;
+#ifdef BIT8
+       qp = rhsquo;
+#endif
+       while (lp < loc1)
+               *sp++ = *lp++;
+       casecnt = 0;
+       while (*rp) {
+               nextc(c, rp, n);
+               rp += n;
+#ifdef BIT8
+               c &= TRIM;
+               q = *qp;
+               qp += n;
+#endif
+               /* ^V <return> from vi to split lines */
+               if (c == '\r')
+                       c = '\n';
+
+#ifndef        BIT8
+               if (c & QUOTE)
+                       switch (c & TRIM) {
+#else
+               if (q)
+                       switch (c) {
+#endif
+
+                       case '&':
+                               sp = place(sp, loc1, loc2);
+                               if (sp == 0)
+                                       goto ovflo;
+                               continue;
+
+                       case 'l':
+                               casecnt = 1;
+                               destuc = 0;
+                               continue;
+
+                       case 'L':
+                               casecnt = LBSIZE;
+                               destuc = 0;
+                               continue;
+
+                       case 'u':
+                               casecnt = 1;
+                               destuc = 1;
+                               continue;
+
+                       case 'U':
+                               casecnt = LBSIZE;
+                               destuc = 1;
+                               continue;
+
+                       case 'E':
+                       case 'e':
+                               casecnt = 0;
+                               continue;
+                       }
+#ifndef        BIT8
+               if (c < 0 && (c &= TRIM) >= '1' && c < re.Nbra + '1') {
+#else
+               if (q && c >= '1' && c < re.Nbra + '1') {
+#endif
+                       sp = place(sp, braslist[c - '1'], braelist[c - '1']);
+                       if (sp == 0)
+                               goto ovflo;
+                       continue;
+               }
+#ifdef MB
+               if (mb_cur_max > 1) {
+                       char    mb[MB_LEN_MAX+1];
+                       int     i, m;
+                       if (casecnt)
+                               c = fixcase(c & TRIM);
+                       if (c & INVBIT || (m = wctomb(mb, c)) <= 0) {
+                               mb[0] = rp[-n];
+                               m = 1;
+                       }
+                       for (i = 0; i < m; i++) {
+                               *sp++ = mb[i];
+                               if (sp >= &genbuf[LBSIZE])
+                                       goto ovflo;
+                       }
+               } else
+#endif /* MB */
+               {
+                       if (casecnt)
+                               *sp++ = fixcase(c & TRIM);
+                       else
+                               *sp++ = c & TRIM;
+               }
+               if (sp >= &genbuf[LBSIZE])
+ovflo:
+                       error(catgets(catd, 1, 130,
+                                       "Line overflow@in substitute"));
+       }
+       lp = loc2;
+       loc2 = sp + (linebuf - genbuf);
+#ifdef UXRE
+       if (loc1 == lp) {
+               nextc(c, loc2, n);
+               loc2 += n;
+       }
+#endif /* UXRE */
+       while (*sp++ = *lp++)
+               if (sp >= &genbuf[LBSIZE])
+                       goto ovflo;
+       strcLIN(genbuf);
+}
+
+int
+fixcase(register int c)
+{
+
+       if (casecnt == 0)
+               return (c);
+       casecnt--;
+#ifdef MB
+       if (c & INVBIT)
+               return (c);
+       if (mb_cur_max > 1) {
+               if (destuc) {
+                       if (iswlower(c))
+                               c = towupper(c);
+               } else
+                       if (iswupper(c))
+                               c = towlower(c);
+       } else
+#endif /* MB */
+       {
+               if (destuc) {
+                       if (islower(c))
+                               c = toupper(c);
+               } else
+                       if (isupper(c))
+                               c = tolower(c);
+       }
+       return (c);
+}
+
+char *
+place(register char *sp, register char *l1, register char *l2)
+{
+       while (l1 < l2) {
+#ifdef MB
+               if (mb_cur_max > 1) {
+                       char    mb[MB_LEN_MAX+1];
+                       int     c, i, m, n;
+
+                       nextc(c, l1, m);
+                       if (c & INVBIT) {
+                               m = n = 1;
+                               *mb = *l1;
+                       } else {
+                               c = fixcase(c);
+                               if ((n = wctomb(mb, c)) <= 0) {
+                                       n = 1;
+                                       *mb = *l1;
+                               }
+                       }
+                       l1 += m;
+                       for (i = 0; i < n; i++) {
+                               *sp++ = mb[i];
+                               if (sp >= &genbuf[LBSIZE])
+                                       return (0);
+                       }
+               } else
+#endif /* MB */
+               {
+                       *sp++ = fixcase(*l1++);
+                       if (sp >= &genbuf[LBSIZE])
+                               return (0);
+               }
+       }
+       return (sp);
+}
+
+void
+snote(register int total, register int lines)
+{
+
+       if (!notable(total))
+               return;
+       printf(mesg(catgets(catd, 1, 131, "%d subs|%d substitutions")), total);
+       if (lines != 1 && lines != total)
+               printf(catgets(catd, 1, 132, " on %d lines"), lines);
+       noonl();
+       flush();
+}
+
+void
+cerror(char *s)
+{
+       re.Patbuf[0] = '\0';
+       error(s);
+}
+
+void
+refree(struct regexp *rp)
+{
+       struct regexp *r1 = NULL, *r2 = NULL;
+       
+       if (rp->Expbuf == 0)
+               return;
+       if (rp == &re) {
+               r1 = &scanre;
+               r2 = &subre;
+       } else if (rp == &scanre) {
+               r1 = &re;
+               r2 = &subre;
+       } else if (rp == &subre) {
+               r1 = &re;
+               r2 = &scanre;
+       }
+       if ((r1->Expbuf == 0 || rp->Re_ident != r1->Re_ident) &&
+                       (r2->Expbuf == 0 || rp->Re_ident != r2->Re_ident)) {
+#ifdef UXRE
+               regfree(rp->Expbuf);
+#endif /* UXRE */
+               free(rp->Expbuf);
+       }
+       rp->Expbuf = 0;
+}
+
+struct regexp *
+savere(struct regexp *store)
+{
+       refree(store);
+       copy(store, &re, sizeof re);
+       return store;
+}
+
+struct regexp *
+resre(struct regexp *store)
+{
+       refree(&re);
+       copy(&re, store, sizeof re);
+       return store;
+}
+
+static void
+compile1(void)
+{
+#ifdef UXRE
+       int     n;
+#else  /* !UXRE */
+       char    *r;
+       char    *p;
+#endif /* !UXRE */
+
+       refree(&re);
+       re.Flags = value(IGNORECASE) ? REG_ICASE : 0;
+#ifdef UXRE
+       re.Flags |= REG_ANGLES;
+#ifndef        NO_BE_BACKSLASH
+       re.Flags |= REG_BKTESCAPE | REG_BADRANGE;
+#endif /* !NO_BE_BACKSLASH */
+       if (re.Expbuf == NULL)
+               re.Expbuf = calloc(1, sizeof (regex_t));
+       if ((n = regcomp(re.Expbuf, re.Patbuf, re.Flags)) != 0) {
+               switch (n) {
+               case REG_EBRACK:
+                       free(re.Expbuf);
+                       re.Expbuf = 0;
+                       cerror(catgets(catd, 1, 154, "Missing ]"));
+                       /*NOTREACHED*/
+                       break;
+               default:
+                       regerror(n, re.Expbuf, &re.Patbuf[1],
+                                       sizeof re.Patbuf - 1);
+                       free(re.Expbuf);
+                       re.Expbuf = 0;
+                       cerror(&re.Patbuf[1]);
+               }
+       }
+       if ((re.Nbra = ((regex_t *)re.Expbuf)->re_nsub) > NBRA)
+               re.Nbra = NBRA;
+#else  /* !UXRE */
+       if ((re.Expbuf = malloc(re.Length)) == NULL)
+               cerror("Re too complex|Regular expression too complicated");
+       if (re.Flags & REG_ICASE) {
+               p = malloc(strlen(re.Patbuf) + 1);
+               loconv(p, re.Patbuf);
+       } else
+               p = re.Patbuf;
+       r = _compile(p, re.Expbuf, &((char *)re.Expbuf)[re.Length], '\0');
+       if (p != re.Patbuf)
+               free(p);
+       if (r == 0) {
+               char    *cp;
+               free(re.Expbuf);
+               re.Expbuf = 0;
+               switch (regerrno) {
+               case 11:
+                       cp = "Range endpoint too large|Range endpoint "
+                                       "too large in regular expression";
+                       break;
+               case 16:
+                       cp = "Bad number|Bad number in regular expression";
+                       break;
+               case 25:
+                       cp = "\"\\digit\" out of range";
+                       break;
+               case 36:
+                       cp = "Badly formed re|Missing closing delimiter "
+                               "for regular expression";
+                       break;
+               case 42:
+                       cp = "\\( \\) Imbalance";
+                       break;
+               case 43:
+                       cp = "Awash in \\('s!|Too many \\('d subexressions "
+                               "in a regular expression";
+                       break;
+               case 44:
+                       cp = "More than 2 numbers given in \\{~\\}";
+                       break;
+               case 45:
+                       cp = "} expected after \\";
+                       break;
+               case 46:
+                       cp = "First number exceeds second in \\{~\\}";
+                       break;
+               case 49:
+                       cp = "Missing ]";
+                       break;
+               case 67:
+                       cp = "Illegal byte sequence|Regular expression "
+                               "has illegal byte sequence";
+                       break;
+               default:
+                       cp = "Unknown regexp error code!!";
+               }
+               cerror(cp);
+       }
+       re.Circfl = circf;
+       re.Nbra = nbra;
+#endif /* !UXRE */
+       re.Re_ident++;
+}
+
+int
+compile(int eof, int oknl)
+{
+       int c, d, i, n = 0;
+       char    mb[MB_LEN_MAX+1];
+       char *p = re.Patbuf, *end = re.Patbuf + sizeof re.Patbuf;
+       int nomagic = value(MAGIC) ? 0 : 1, esc, rcnt = 0;
+       char *rhsp;
+#ifdef BIT8
+       char *rhsq;
+#endif
+
+       if (isalpha(eof) || isdigit(eof))
+               error(catgets(catd, 1, 133,
+       "Regular expressions cannot be delimited by letters or digits"));
+       c = GETWC(mb);
+       if (eof == '\\') {
+               switch (c) {
+               case '/':
+               case '?':
+                       if (scanre.Patbuf[0] == 0)
+                               error(catgets(catd, 1, 134,
+       "No previous scan re|No previous scanning regular expression"));
+                       resre(&scanre);
+                       return c;
+               case '&':
+                       if (subre.Patbuf[0] == 0)
+                               error(catgets(catd, 1, 135,
+       "No previous substitute re|No previous substitute regular expression"));
+                       resre(&subre);
+                       return c;
+               default:
+                       error(catgets(catd, 1, 136,
+       "Badly formed re|Regular expression \\ must be followed by / or ?"));
+               }
+       }
+       if (c == eof || c == '\n' || c == EOF) {
+               if (c == '\n' && oknl == 0)
+                       error(catgets(catd, 1, 138,
+                       "Missing closing delimiter@for regular expression"));
+               if (c != eof)
+                       ungetchar(c);
+               if (re.Expbuf == 0)
+                       error(catgets(catd, 1, 137,
+                       "No previous re|No previous regular expression"));
+               return eof;
+       }
+       re.Nbra = re.Circfl = 0;
+       if (c == '^')
+               re.Circfl++;
+       esc = 0;
+       goto havec;
+       /*
+        * Fetch the search pattern. This is quite a mess since we have
+        * to handle nomagic and ~.
+        */
+       for (;;) {
+               esc = 0;
+               c = GETWC(mb);
+       havec:  if (c == eof || c == EOF) {
+                       if (c == EOF)
+                               ungetchar(c);
+                       break;
+               } else if (c == '\n') {
+                       if (!oknl)
+                               cerror(catgets(catd, 1, 157,
+       "Badly formed re|Missing closing delimiter for regular expression"));
+                       ungetchar(c);
+                       break;
+               } else if (nomagic) {
+                       switch (c) {
+                       case '.':
+                       case '*':
+                       case '[':
+                       case '~':
+                               *p++ = '\\';
+                               esc = 1;
+                               break;
+                       case '\\':
+                               c = GETWC(mb);
+                               if (c != '.' && c != '*' && c != '[' &&
+                                               c != '~') {
+                                       *p++ = '\\';
+                                       esc = 1;
+                               }
+                       }
+               } else if (c == '\\') {
+                       c = GETWC(mb);
+                       if (c != '~')
+                               *p++ = '\\';
+                       esc = 1;
+               }
+               if (c == EOF) {
+                       ungetchar(c);
+                       break;
+               }
+               if (!esc && c == '~') {
+                       rhsp = rhsbuf;
+#ifdef BIT8
+                       rhsq = rhsquo;
+#endif
+                       while (*rhsp) {
+#ifndef        BIT8
+                               if (*rhsp & QUOTE) {
+                                       nextc(c, rhsp, n);
+                                       c &= TRIM;
+#else  /* BIT8 */
+                               if (*rhsq) {
+                                       nextc(c, rhsp, n);
+#endif /* BIT8 */
+                                       if (c == '&')
+                                               error(catgets(catd, 1, 149,
+                       "Replacement pattern contains &@- cannot use in re"));
+                                       if (c >= '1' && c <= '9')
+                                               error(catgets(catd, 1, 150,
+                       "Replacement pattern contains \\d@- cannot use in re"));
+                               }
+                               if (p >= end - 3)
+                                       goto complex;
+                               if (*rhsp == '\\' || *rhsp == '[' ||
+                                               *rhsp == '.' ||
+                                               *rhsp == '^' ||
+                                               *rhsp == '*' ||
+                                               *rhsp == '$')
+                                       *p++ = '\\';
+#ifdef BIT8
+                               nextc(c, rhsp, n);
+                               for (i = 0; i < n; i++) {
+                                       *p++ = *rhsp++;
+                                       rhsq++;
+                               }
+#else
+                               *p++ = *rhsp++ & TRIM;
+#endif
+                       }
+               } else if (!esc && c == '[') {
+                       rcnt++;
+                       /*
+                        * Search for the end of the bracket expression
+                        * since '~' may not be recognized inside.
+                        */
+                       *p++ = (char)c;
+                       if (p >= end)
+                               goto complex;
+                       d = EOF;
+                       do {
+                               c = GETWC(mb);
+                               if (c == '\n' || c == EOF)
+                                       cerror("Missing ]");
+                               for (i = 0; mb[i]; i++) {
+                                       *p++ = mb[i];
+                                       if (p >= end)
+                                               goto complex;
+                               }
+#ifdef UXRE
+                               if (d == '[' && (c == ':' || c == '.' ||
+                                                       c == '=')) {
+                                       d = c;
+                                       do {
+                                               c = GETWC(mb);
+                                               if (c == '\n' || c == EOF)
+                                                       cerror("Missing ]");
+                                               for (i = 0; mb[i]; i++) {
+                                                       *p++ = mb[i];
+                                                       if (p >= end)
+                                                               goto complex;
+                                               }
+                                       } while (c != d || peekchar() != ']');
+                                       c = GETWC(mb);
+                                       for (i = 0; mb[i]; i++) {
+                                               *p++ = mb[i];
+                                               if (p >= end)
+                                                       goto complex;
+                                       }
+                                       c = EOF; /* -> reset d and continue */
+                               }
+#endif /* UXRE */
+                               d = c;
+                       } while (c != ']');
+               } else if (esc && c == '{') {
+                       /*
+                        * Search for the end of the interval expression
+                        * since '~' may not be recognized inside.
+                        */
+                       for (i = 0; mb[i]; i++) {
+                               *p++ = mb[i];
+                               if (p >= end)
+                                       goto complex;
+                       }
+                       do {
+                               c = GETWC(mb);
+                               if (c == '\n' || c == EOF)
+                                       cerror(catgets(catd, 1, 143,
+                       "Bad number|Bad number in regular expression"));
+                               for (i = 0; mb[i]; i++) {
+                                       *p++ = mb[i];
+                                       if (p >= end)
+                                               goto complex;
+                               }
+                       } while (c != '\\');
+                       c = GETWC(mb);
+                       if (c != '}')
+                               cerror(catgets(catd, 1, 146,
+                                       "} expected after \\"));
+                       *p++ = (char)c;
+               } else {
+                       for (i = 0; mb[i]; i++) {
+                               *p++ = mb[i];
+                               if (p >= end)
+                                       goto complex;
+                       }
+               }
+               if (p >= end)
+complex:               cerror(catgets(catd, 1, 139,
+                       "Re too complex|Regular expression too complicated"));
+       }
+       if (p == re.Patbuf)
+               *p++ = '.';     /* approximate historical behavior */
+       *p = '\0';
+       re.Length = rcnt*32 + 2*(p-re.Patbuf) + 5;
+       compile1();
+       return eof;
+}
+
+#ifdef UXRE
+int
+execute(int gf, line *addr)
+{
+       char *p;
+       int c;
+       int eflags = 0, nsub;
+       regmatch_t bralist[NBRA + 1];
+
+       if (gf) {
+               if (re.Circfl)
+                       return 0;
+               eflags |= REG_NOTBOL;
+               p = loc2;
+       } else {
+               if (addr == zero)
+                       return 0;
+               if ((value(IGNORECASE) ? 1:0) ^ (re.Flags & REG_ICASE ? 1:0))
+                       compile1();
+               p = linebuf;
+               getline(*addr);
+       }
+       /*
+        * Need subexpression matches only for substitute command,
+        * so don't fetch them otherwise (enables use of DFA).
+        */
+       nsub = (re.Re_ident == subre.Re_ident ? NBRA : 0);
+       switch (regexec(re.Expbuf, p, nsub + 1, bralist, eflags)) {
+       case 0:
+               break;
+       case REG_NOMATCH:
+               return 0;
+       default:
+               cerror(catgets(catd, 1, 139,
+                       "Re too complex|Regular expression too complicated"));
+       }
+       loc1 = p + bralist[0].rm_so;
+       loc2 = p + bralist[0].rm_eo;
+       for (c = 0; c < nsub; c++) {
+               if (bralist[c + 1].rm_so != -1) {
+                       braslist[c] = p + bralist[c + 1].rm_so;
+                       braelist[c] = p + bralist[c + 1].rm_eo;
+               } else
+                       braslist[c] = braelist[c] = NULL;
+       }
+       return 1;
+}
+#else  /* !UXRE */
+int
+execute(int gf, line *addr)
+{
+       char *p;
+
+       if (gf) {
+               if (re.Circfl)
+                       return 0;
+               p = locs = loc2;
+       } else {
+               if (addr == zero)
+                       return 0;
+               p = linebuf;
+               getline(*addr);
+               if ((value(IGNORECASE) ? 1:0) ^ (re.Flags & REG_ICASE ? 1:0))
+                       compile1();
+               if (value(IGNORECASE))
+                       loconv(linebuf, linebuf);
+               locs = 0;
+       }
+       circf = re.Circfl;
+       return step(p, re.Expbuf);
+}
+#endif /* !UXRE */
diff --git a/ex_re.h b/ex_re.h
new file mode 100644 (file)
index 0000000..2966ef0
--- /dev/null
+++ b/ex_re.h
@@ -0,0 +1,125 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+ *
+ *     from ex_re.h    7.3 (Berkeley) 5/31/85
+ *
+ *     @(#)ex_re.h     1.22 (gritter) 2/19/05
+ */
+
+/*
+ * Regular expression definitions.
+ * The regular expressions in ex are similar to those in ed,
+ * with the addition of the word boundaries from Toronto ed
+ * and allowing character classes to have [a-b] as in the shell.
+ * The numbers for the nodes below are spaced further apart then
+ * necessary because I at one time partially put in + and | (one or
+ * more and alternation.)
+ */
+struct regexp {
+       char    Patbuf[2*LBSIZE + 1];
+       long    Re_ident;
+       void    *Expbuf;
+       bool    Circfl;
+       short   Nbra;
+       int     Flags;
+       int     Length;
+};
+
+/*
+ * There are three regular expressions here, the previous (in re),
+ * the previous substitute (in subre) and the previous scanning (in scanre).
+ * It would be possible to get rid of "re" by making it a stack parameter
+ * to the appropriate routines.
+ */
+var struct     regexp re;              /* Last re */
+var struct     regexp scanre;          /* Last scanning re */
+var struct     regexp subre;           /* Last substitute re */
+
+extern char    *loc1;          /* Where re began to match (in linebuf) */
+extern char    *loc2;          /* First char after re match (") */
+
+/*
+ * Since the phototypesetter v7-epsilon
+ * C compiler doesn't have structure assignment...
+ */
+extern struct regexp *savere(struct regexp *);
+extern struct regexp *resre(struct regexp *);
+
+/*
+ * Definitions for substitute
+ */
+extern char    *braslist[NBRA];        /* Starts of \(\)'ed text in lhs */
+extern char    *braelist[NBRA];        /* Ends... */
+var char       rhsbuf[RHSSIZE];        /* Rhs of last substitute */
+#ifdef BIT8
+var char       rhsquo[RHSSIZE];        /* Quote indicator for rhsbuf */
+#endif
diff --git a/ex_set.c b/ex_set.c
new file mode 100644 (file)
index 0000000..ca7fe45
--- /dev/null
+++ b/ex_set.c
@@ -0,0 +1,313 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_set.c   1.11 (gritter) 11/24/04";
+#endif
+#endif
+
+/* from ex_set.c       7.4 (Berkeley) 6/7/85 */
+
+#include "ex.h"
+#include "ex_temp.h"
+#include "ex_tty.h"
+
+/*
+ * Set command.
+ */
+char   optname[ONMSZ];
+
+void
+set(void)
+{
+       register char *cp;
+       register struct option *op;
+       register int c;
+       bool no;
+
+       setnoaddr();
+       if (skipend()) {
+               if (peekchar() != EOF)
+                       ignchar();
+               propts();
+               return;
+       }
+       do {
+               cp = optname;
+               do {
+                       if (cp < &optname[ONMSZ - 2])
+                               *cp++ = getchar();
+               } while (isalnum(peekchar()));
+               *cp = 0;
+               cp = optname;
+               if (eq("all", cp)) {
+                       if (inopen)
+                               pofix();
+                       prall();
+                       goto setnext;
+               }
+               no = 0;
+               if (cp[0] == 'n' && cp[1] == 'o') {
+                       cp += 2;
+                       no++;
+               }
+               /* Implement w300, w1200, and w9600 specially */
+               if (eq(cp, "w300")) {
+                       if (ospeed >= B1200) {
+dontset:
+                               ignore(getchar());      /* = */
+                               ignore(getnum());       /* value */
+                               continue;
+                       }
+                       cp = "window";
+               } else if (eq(cp, "w1200")) {
+                       if (ospeed < B1200 || ospeed >= B2400)
+                               goto dontset;
+                       cp = "window";
+               } else if (eq(cp, "w9600")) {
+                       if (ospeed < B2400)
+                               goto dontset;
+                       cp = "window";
+               }
+               for (op = options; op < &options[NOPTS]; op++)
+                       if (eq(op->oname, cp) || op->oabbrev && eq(op->oabbrev, cp))
+                               break;
+               if (op->oname == 0)
+                       serror(catgets(catd, 1, 159,
+               "%s: No such option@- 'set all' gives all option values"), cp);
+               c = skipwh();
+               if (peekchar() == '?') {
+                       ignchar();
+printone:
+                       propt(op);
+                       noonl();
+                       goto setnext;
+               }
+               if (op->otype == ONOFF) {
+                       op->ovalue = 1 - no;
+                       if (op == &options[PROMPT])
+                               oprompt = 1 - no;
+                       goto setnext;
+               }
+               if (no)
+                       serror(catgets(catd, 1, 160,
+                               "Option %s is not a toggle"), op->oname);
+               if (c != 0 || setend())
+                       goto printone;
+               if (getchar() != '=')
+                       serror(catgets(catd, 1, 161,
+                       "Missing =@in assignment to option %s"), op->oname);
+               switch (op->otype) {
+
+               case NUMERIC:
+                       if (!isdigit(peekchar()))
+                               error(catgets(catd, 1, 162,
+                                               "Digits required@after ="));
+                       op->ovalue = getnum();
+                       if (value(TABSTOP) <= 0)
+                               value(TABSTOP) = TABS;
+                       if (value(HARDTABS) <= 0)
+                               value(HARDTABS) = TABS;
+                       if (op == &options[WINDOW]) {
+                               if (value(WINDOW) >= TLINES)
+                                       value(WINDOW) = TLINES-1;
+                               vsetsiz(value(WINDOW));
+                       }
+                       break;
+
+               case STRING:
+               case OTERM:
+                       cp = optname;
+                       while (!setend()) {
+                               if (cp >= &optname[ONMSZ])
+                                       error(catgets(catd, 1, 163,
+                               "String too long@in option assignment"));
+                               /* adb change:  allow whitepace in strings */
+                               if( (*cp = getchar()) == '\\')
+                                       if( peekchar() != EOF)
+                                               *cp = getchar();
+                               cp++;
+                       }
+                       *cp = 0;
+                       if (op->otype == OTERM) {
+/*
+ * At first glance it seems like we shouldn't care if the terminal type
+ * is changed inside visual mode, as long as we assume the screen is
+ * a mess and redraw it. However, it's a much harder problem than that.
+ * If you happen to change from 1 crt to another that both have the same
+ * size screen, it's OK. But if the screen size if different, the stuff
+ * that gets initialized in vop() will be wrong. This could be overcome
+ * by redoing the initialization, e.g. making the first 90% of vop into
+ * a subroutine. However, the most useful case is where you forgot to do
+ * a setenv before you went into the editor and it thinks you're on a dumb
+ * terminal. Ex treats this like hardcopy and goes into HARDOPEN mode.
+ * This loses because the first part of vop calls oop in this case.
+ * The problem is so hard I gave up. I'm not saying it can't be done,
+ * but I am saying it probably isn't worth the effort.
+ */
+                               if (inopen)
+                                       error(catgets(catd, 1, 164,
+               "Can't change type of terminal from within open/visual"));
+                               setterm(optname);
+                       } else {
+                               CP(op->osvalue, optname);
+                               op->odefault = 1;
+                       }
+                       break;
+               }
+setnext:
+               flush();
+       } while (!skipend());
+       eol();
+}
+
+int 
+setend(void)
+{
+
+       return (is_white(peekchar()) || endcmd(peekchar()));
+}
+
+void
+prall(void)
+{
+       register int incr = (NOPTS + 2) / 3;
+       register int rows = incr;
+       register struct option *op = options;
+
+       for (; rows; rows--, op++) {
+               propt(op);
+               tab(24);
+               propt(&op[incr]);
+               if (&op[2*incr] < &options[NOPTS]) {
+                       tab(56);
+                       propt(&op[2 * incr]);
+               }
+               putNFL();
+       }
+}
+
+void
+propts(void)
+{
+       register struct option *op;
+
+       for (op = options; op < &options[NOPTS]; op++) {
+               if (op == &options[TTYTYPE])
+                       continue;
+               switch (op->otype) {
+
+               case ONOFF:
+               case NUMERIC:
+                       if (op->ovalue == op->odefault)
+                               continue;
+                       break;
+
+               case STRING:
+                       if (op->odefault == 0)
+                               continue;
+                       break;
+               }
+               propt(op);
+               putchar(' ');
+       }
+       noonl();
+       flush();
+}
+
+void
+propt(register struct option *op)
+{
+       register char *name;
+       
+       name = op->oname;
+
+       switch (op->otype) {
+
+       case ONOFF:
+               printf(catgets(catd, 1, 165, "%s%s"),
+                               op->ovalue ? catgets(catd, 1, 166, "")
+                               : catgets(catd, 1, 167, "no"), name);
+               break;
+
+       case NUMERIC:
+               printf(catgets(catd, 1, 168, "%s=%d"), name, op->ovalue);
+               break;
+
+       case STRING:
+       case OTERM:
+               printf(catgets(catd, 1, 169, "%s=%s"), name, op->osvalue);
+               break;
+       }
+}
diff --git a/ex_subr.c b/ex_subr.c
new file mode 100644 (file)
index 0000000..6f884aa
--- /dev/null
+++ b/ex_subr.c
@@ -0,0 +1,1153 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_subr.c  1.37 (gritter) 2/15/05";
+#endif
+#endif
+
+/* from ex_subr.c      7.10.1 (2.11BSD) 1996/3/22 */
+
+#include "ex.h"
+#include "ex_re.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+
+static short   lastsc;
+
+/*
+ * Random routines, in alphabetical order.
+ */
+
+int 
+any(int c, register char *s)
+{
+       register int x;
+
+       while (x = *s++)
+               if (x == c)
+                       return (1);
+       return (0);
+}
+
+int 
+backtab(register int i)
+{
+       register int j;
+
+       j = i % value(SHIFTWIDTH);
+       if (j == 0)
+               j = value(SHIFTWIDTH);
+       i -= j;
+       if (i < 0)
+               i = 0;
+       return (i);
+}
+
+void 
+change(void)
+{
+
+       tchng++;
+       chng = tchng;
+       fixedzero = 0;
+}
+
+/*
+ * Column returns the number of
+ * columns occupied by printing the
+ * characters through position cp of the
+ * current line.
+ */
+int 
+column(register char *cp)
+{
+
+       if (cp == 0)
+               cp = &linebuf[LBSIZE - 2];
+       return (qcolumn(cp, NULL));
+}
+
+int
+lcolumn(register char *cp)
+{
+       return column(cp) - (lastsc - 1);
+}
+
+/*
+ * Ignore a comment to the end of the line.
+ * This routine eats the trailing newline so don't call newline().
+ */
+void 
+comment(void)
+{
+       register int c;
+
+       do {
+               c = getchar();
+       } while (c != '\n' && c != EOF);
+       if (c == EOF)
+               ungetchar(c);
+}
+
+void 
+Copy(register char *to, register char *from, register int size)
+{
+
+       if (size > 0)
+               do
+                       *to++ = *from++;
+               while (--size > 0);
+}
+
+void 
+copyw(register line *to, register line *from, register int size)
+{
+
+       if (size > 0)
+               do
+                       *to++ = *from++;
+               while (--size > 0);
+}
+
+void 
+copywR(register line *to, register line *from, register int size)
+{
+
+       while (--size >= 0)
+               to[size] = from[size];
+}
+
+int 
+ctlof(int c)
+{
+
+       return (c == DELETE ? '?' : c | ('A' - 1));
+}
+
+void 
+dingdong(void)
+{
+
+       if (VB)
+               putpad(VB);
+       else if (value(ERRORBELLS))
+               putch('\207');
+}
+
+int 
+fixindent(int indent)
+{
+       register int i;
+       register char *cp;
+
+       i = whitecnt(genbuf);
+       cp = vpastwh(genbuf);
+       if (*cp == 0 && i == indent && linebuf[0] == 0) {
+               genbuf[0] = 0;
+               return (i);
+       }
+       CP(genindent(i), cp);
+       return (i);
+}
+
+void 
+filioerr(char *cp)
+{
+       register int oerrno = errno;
+
+       lprintf("\"%s\"", cp);
+       errno = oerrno;
+       syserror();
+}
+
+char *
+genindent(register int indent)
+{
+       register char *cp;
+
+       for (cp = genbuf; indent >= value(TABSTOP); indent -= value(TABSTOP))
+               *cp++ = '\t';
+       for (; indent > 0; indent--)
+               *cp++ = ' ';
+       return (cp);
+}
+
+void 
+getDOT(void)
+{
+
+       getline(*dot);
+}
+
+line *
+getmark(register int c)
+{
+       register line *addr;
+       
+       for (addr = one; addr <= dol; addr++)
+               if (names[c - 'a'] == (*addr &~ 01)) {
+                       return (addr);
+               }
+       return (0);
+}
+
+int 
+getn(register char *cp)
+{
+       register int i = 0;
+
+       while (isdigit(*cp&0377))
+               i = i * 10 + *cp++ - '0';
+       if (*cp)
+               return (0);
+       return (i);
+}
+
+void 
+ignnEOF(void)
+{
+       register int c = getchar();
+
+       if (c == EOF)
+               ungetchar(c);
+       else if (c=='"')
+               comment();
+}
+
+int 
+is_white(int c)
+{
+
+#ifndef        BIT8
+       return (c == ' ' || c == '\t');
+#else
+       return (isspace(c&0377) && c != '\n' && c != '\r'
+                       && c != '\f' && c != '\v');
+#endif
+}
+
+int 
+junk(register int c)
+{
+
+       if (c && !value(BEAUTIFY))
+               return (0);
+#ifndef        BIT8
+       if (c >= ' ' && c != DELETE)
+#else
+       if (printable(c))
+#endif
+               return (0);
+       switch (c) {
+
+       case '\t':
+       case '\n':
+       case '\f':
+               return (0);
+
+       default:
+               return (1);
+       }
+}
+
+void 
+killed(void)
+{
+
+       killcnt(addr2 - addr1 + 1);
+}
+
+void 
+killcnt(register int cnt)
+{
+
+       if (inopen) {
+               notecnt = cnt;
+               notenam = notesgn = "";
+               return;
+       }
+       if (!notable(cnt))
+               return;
+       printf(catgets(catd, 1, 170, "%d lines"), cnt);
+       if (value(TERSE) == 0) {
+               printf(catgets(catd, 1, 171, " %c%s"),
+                               Command[0] | ' ', Command + 1);
+               if (Command[strlen(Command) - 1] != 'e')
+                       putchar('e');
+               putchar('d');
+       }
+       putNFL();
+}
+
+int 
+lineno(line *a)
+{
+
+       return (a - zero);
+}
+
+int 
+lineDOL(void)
+{
+
+       return (lineno(dol));
+}
+
+int 
+lineDOT(void)
+{
+
+       return (lineno(dot));
+}
+
+void 
+markDOT(void)
+{
+
+       markpr(dot);
+}
+
+void 
+markpr(line *which)
+{
+
+       if ((inglobal == 0 || inopen) && which <= endcore) {
+               names['z'-'a'+1] = *which & ~01;
+               if (inopen)
+                       ncols['z'-'a'+1] = cursor;
+       }
+}
+
+int 
+markreg(register int c)
+{
+
+       if (c == '\'' || c == '`')
+               return ('z' + 1);
+       if (c >= 'a' && c <= 'z')
+               return (c);
+       return (0);
+}
+
+/*
+ * Mesg decodes the terse/verbose strings. Thus
+ *     'xxx@yyy' -> 'xxx' if terse, else 'xxx yyy'
+ *     'xxx|yyy' -> 'xxx' if terse, else 'yyy'
+ * All others map to themselves.
+ */
+char *
+mesg(register char *str)
+{
+       register char *cp;
+
+       str = strcpy(genbuf, str);
+       for (cp = str; *cp; cp++)
+               switch (*cp) {
+
+               case '@':
+                       if (value(TERSE))
+                               *cp = 0;
+                       else
+                               *cp = ' ';
+                       break;
+
+               case '|':
+                       if (value(TERSE) == 0)
+                               return (cp + 1);
+                       *cp = 0;
+                       break;
+               }
+       return (str);
+}
+
+void 
+merror1(intptr_t seekpt)
+{
+
+#ifdef VMUNIX
+       strcpy(linebuf, (char *)seekpt);
+#else
+       lseek(erfile, (off_t) seekpt, SEEK_SET);
+       if (read(erfile, linebuf, 128) < 2)
+               CP(linebuf, "ERROR");
+#endif
+}
+
+/*VARARGS2*/
+void
+vmerror(char *seekpt, va_list ap)
+{
+
+       register char *cp = linebuf;
+
+       if (seekpt == 0)
+               return;
+       merror1((intptr_t)seekpt);
+       if (*cp == '\n')
+               putnl(), cp++;
+       if (inopen > 0 && CE)
+               vclreol();
+       if (SO && SE)
+               putpad(SO);
+       vprintf(mesg(cp), ap);
+       if (SO && SE)
+               putpad(SE);
+}
+
+void
+merror(char *cp, ...)
+{
+       va_list ap;
+
+       if (cp == NULL)
+               return;
+       va_start(ap, cp);
+       vmerror(cp, ap);
+       va_end(ap);
+}
+
+int 
+morelines(void)
+{
+#ifdef _SC_PAGESIZE
+       static long pg;
+
+       if (pg == 0) {
+               pg = sysconf(_SC_PAGESIZE);
+               if (pg <= 0 || pg >= 65536)
+                       pg = 4096;
+               pg /= sizeof (line);
+       }
+       if ((char *)sbrk(pg * sizeof (line)) == (char *)-1)
+               return (-1);
+       endcore += pg;
+       return (0);
+#else  /* !_SC_PAGESIZE */
+       if (sbrk(1024 * sizeof (line)) == (char *)-1)
+               return (-1);
+       endcore += 1024;
+       return (0);
+#endif /* !_SC_PAGESIZE */
+}
+
+void 
+nonzero(void)
+{
+
+       if (addr1 == zero) {
+               notempty();
+               error(catgets(catd, 1, 172,
+                       "Nonzero address required@on this command"));
+       }
+}
+
+int 
+notable(int i)
+{
+
+       return (hush == 0 && !inglobal && i > value(REPORT));
+}
+
+
+void 
+notempty(void)
+{
+
+       if (dol == zero)
+               error(catgets(catd, 1, 173, "No lines@in the buffer"));
+}
+
+
+void 
+netchHAD(int cnt)
+{
+
+       netchange(lineDOL() - cnt);
+}
+
+void 
+netchange(register int i)
+{
+       register char *cp;
+
+       if (i > 0)
+               notesgn = cp = catgets(catd, 1, 174, "more ");
+       else
+               notesgn = cp = catgets(catd, 1, 175, "fewer "), i = -i;
+       if (inopen) {
+               notecnt = i;
+               notenam = catgets(catd, 1, 176, "");
+               return;
+       }
+       if (!notable(i))
+               return;
+       printf(mesg(catgets(catd, 1, 177, "%d %slines@in file after %s")),
+                       i, cp, Command);
+       putNFL();
+}
+
+/*
+ * Print an escape sequence corresponding to c.
+ */
+#ifdef BIT8
+int 
+printof(int c)
+{
+       char *nums = "01234567";
+       int d;
+
+#ifdef MB
+       if (mb_cur_max > 1 && (c & INVBIT) == 0 && c & ~0177) {
+               char    mb[MB_LEN_MAX];
+               int     i, n, x = EOF;
+               if ((n = wctomb(mb, c & TRIM)) <= 0) {
+                       n = 1;
+                       *mb = 0;
+               }
+               for (i = 0; i < n; i++) {
+                       x = printof(mb[i] | INVBIT);
+                       if (i+1 < n)
+                               normchar(x);
+               }
+               return x;
+       }
+#endif /* MB */
+       c &= 0377;
+       if (c < 040 || c == DELETE) {
+               normchar('^');
+               return (c == DELETE ? '?' : c | ('A' - 1));
+       }
+       normchar('\\');
+       normchar(nums[(c & ~077) >> 6]);
+       c &= 077;
+       d = c & 07;
+       if (c > d)
+               normchar(nums[(c - d) >> 3]);
+       else
+               normchar(nums[0]);
+       return nums[d];
+}
+#endif
+
+void 
+putmark(line *addr)
+{
+
+       putmk1(addr, putline());
+}
+
+void 
+putmk1(register line *addr, int n)
+{
+       register line *markp;
+       register int oldglobmk;
+
+       oldglobmk = *addr & 1;
+       *addr &= ~1;
+       for (markp = (anymarks ? names : &names['z'-'a'+1]);
+         markp <= &names['z'-'a'+1]; markp++)
+               if (*markp == *addr)
+                       *markp = n;
+       *addr = n | oldglobmk;
+}
+
+char *
+plural(long i)
+{
+
+       return (i == 1 ? catgets(catd, 1, 178, "")
+                       : catgets(catd, 1, 179, "s"));
+}
+
+static short   vcntcol;
+
+int 
+qcolumn(register char *lim, register char *gp)
+{
+       register int x = 0, n = 1;
+       int     c, i;
+       int (*OO)();
+
+       OO = Outchar;
+       Outchar = qcount;
+       vcntcol = 0;
+       if (lim != NULL) {
+               if (lim < linebuf) {
+                       lim = linebuf;
+                       n = 0;
+               } else
+                       n = skipright(linebuf, lim);
+               x = lim[n], lim[n] = 0;
+       }
+       pline(0);
+       if (lim != NULL)
+               lim[n] = x;
+       if (gp)
+               while (*gp) {
+                       nextc(c, gp, i);
+                       putchar(c);
+                       gp += i;
+               }
+       Outchar = OO;
+       return (vcntcol);
+}
+
+int 
+qcount(int c)
+{
+       if (c == '\t') {
+               vcntcol += value(TABSTOP) - vcntcol % value(TABSTOP);
+               lastsc = 1;
+               return c;
+       }
+       /*
+        * Take account of filler characters inserted at the end of
+        * the visual line if a multi-column character does not fit.
+        */
+       lastsc = colsc(c&TRIM&~MULTICOL);
+       while (vcntcol < WCOLS && vcntcol + lastsc - 1 >= WCOLS)
+               vcntcol++;
+       vcntcol += c & MULTICOL ? 1 : lastsc;
+       return c;
+}
+
+void 
+reverse(register line *a1, register line *a2)
+{
+       register line t;
+
+       for (;;) {
+               t = *--a2;
+               if (a2 <= a1)
+                       return;
+               *a2 = *a1;
+               *a1++ = t;
+       }
+}
+
+void 
+save(line *a1, register line *a2)
+{
+       register int more;
+
+       if (!FIXUNDO)
+               return;
+#ifdef TRACE
+       if (trace)
+               vudump("before save");
+#endif
+       undkind = UNDNONE;
+       undadot = dot;
+       more = (a2 - a1 + 1) - (unddol - dol);
+       while (more > (endcore - truedol))
+               if (morelines() < 0)
+                       error(catgets(catd, 1, 180,
+               "Out of memory@saving lines for undo - try using ed"));
+       if (more)
+               (*(more > 0 ? copywR : copyw))(unddol + more + 1, unddol + 1,
+                   (truedol - unddol));
+       unddol += more;
+       truedol += more;
+       copyw(dol + 1, a1, a2 - a1 + 1);
+       undkind = UNDALL;
+       unddel = a1 - 1;
+       undap1 = a1;
+       undap2 = a2 + 1;
+#ifdef TRACE
+       if (trace)
+               vudump("after save");
+#endif
+}
+
+void 
+save12(void)
+{
+
+       save(addr1, addr2);
+}
+
+void 
+saveall(void)
+{
+
+       save(one, dol);
+}
+
+int 
+span(void)
+{
+
+       return (addr2 - addr1 + 1);
+}
+
+void 
+synced(void)
+{
+
+       chng = 0;
+       tchng = 0;
+       xchng = 0;
+}
+
+
+int 
+skipwh(void)
+{
+       register int wh;
+
+       wh = 0;
+       while (is_white(peekchar())) {
+               wh++;
+               ignchar();
+       }
+       return (wh);
+}
+
+void
+vsmerror(char *seekpt, va_list ap)
+{
+
+       if (seekpt == 0)
+               return;
+       merror1((intptr_t)seekpt);
+       if (inopen && CE)
+               vclreol();
+       if (SO && SE)
+               putpad(SO);
+       vlprintf(mesg(linebuf), ap);
+       if (SO && SE)
+               putpad(SE);
+}
+
+void
+smerror(char *seekpt, ...)
+{
+       va_list ap;
+
+       if (seekpt == NULL)
+               return;
+       va_start(ap, seekpt);
+       vsmerror(seekpt, ap);
+       va_end(ap);
+}
+
+char *
+strend(register char *cp)
+{
+
+       while (*cp)
+               cp++;
+       return (cp);
+}
+
+void
+strcLIN(char *dp)
+{
+
+       CP(linebuf, dp);
+}
+
+void
+syserror(void)
+{
+
+       dirtcnt = 0;
+       putchar(' ');
+       error("%s", strerror(errno));
+}
+
+/*
+ * Return the column number that results from being in column col and
+ * hitting a tab, where tabs are set every ts columns.  Work right for
+ * the case where col > TCOLUMNS, even if ts does not divide TCOLUMNS.
+ */
+int
+tabcol(int col, int ts)
+{
+       int offset, result;
+
+       if (col >= TCOLUMNS) {
+               offset = TCOLUMNS * (col/TCOLUMNS);
+               col -= offset;
+       } else
+               offset = 0;
+       result = col + ts - (col % ts) + offset;
+       return (result);
+}
+
+char *
+vfindcol(int i)
+{
+       register char *cp;
+       register int (*OO)() = Outchar;
+       int     c, n = 0;
+
+       Outchar = qcount;
+       ignore(qcolumn(linebuf - 1, NOSTR));
+       for (cp = linebuf; *cp && vcntcol < i; cp += n) {
+               nextc(c, cp, n);
+               putchar(c);
+       }
+       if (cp != linebuf)
+               cp -= n;
+       Outchar = OO;
+       return (cp);
+}
+
+char *
+vskipwh(register char *cp)
+{
+
+       while (is_white(*cp) && cp[1])
+               cp++;
+       return (cp);
+}
+
+
+char *
+vpastwh(register char *cp)
+{
+
+       while (is_white(*cp))
+               cp++;
+       return (cp);
+}
+
+int
+whitecnt(register char *cp)
+{
+       register int i;
+
+       i = 0;
+       for (;;)
+               switch (*cp++) {
+
+               case '\t':
+                       i += value(TABSTOP) - i % value(TABSTOP);
+                       break;
+
+               case ' ':
+                       i++;
+                       break;
+
+               default:
+                       return (i);
+               }
+}
+
+void
+markit(line *addr)
+{
+
+       if (addr != dot && addr >= one && addr <= dol)
+               markDOT();
+}
+
+#ifdef SIGEMT
+/*
+ * The following code is defensive programming against a bug in the
+ * pdp-11 overlay implementation.  Sometimes it goes nuts and asks
+ * for an overlay with some garbage number, which generates an emt
+ * trap.  This is a less than elegant solution, but it is somewhat
+ * better than core dumping and losing your work, leaving your tty
+ * in a weird state, etc.
+ */
+int _ovno;
+void
+onemt(int signum)
+{
+       int oovno;
+
+       oovno = _ovno;
+       /* 2 and 3 are valid on 11/40 type vi, so */
+       if (_ovno < 0 || _ovno > 3)
+               _ovno = 0;
+       error(catgets(catd, 1, 181, "emt trap, _ovno is %d @ - try again"));
+}
+#endif
+
+/*
+ * When a hangup occurs our actions are similar to a preserve
+ * command.  If the buffer has not been [Modified], then we do
+ * nothing but remove the temporary files and exit.
+ * Otherwise, we sync the temp file and then attempt a preserve.
+ * If the preserve succeeds, we unlink our temp files.
+ * If the preserve fails, we leave the temp files as they are
+ * as they are a backup even without preservation if they
+ * are not removed.
+ */
+void
+onhup(int signum)
+{
+
+       /*
+        * USG tty driver can send multiple HUP's!!
+        */
+       signal(SIGINT, SIG_IGN);
+       signal(SIGHUP, SIG_IGN);
+       if (chng == 0) {
+               cleanup(1);
+               exitex(0);
+       }
+       if (setexit() == 0) {
+               if (preserve()) {
+                       cleanup(1);
+                       exitex(0);
+               }
+       }
+       exitex(1);
+}
+
+/*
+ * An interrupt occurred.  Drain any output which
+ * is still in the output buffering pipeline.
+ * Catch interrupts again.  Unless we are in visual
+ * reset the output state (out of -nl mode, e.g).
+ * Then like a normal error (with the \n before Interrupt
+ * suppressed in visual mode).
+ */
+void
+onintr(int signum)
+{
+
+       alarm(0);       /* in case we were called from map */
+       draino();
+       if (!inopen) {
+               pstop();
+               setlastchar('\n');
+       }
+       error(catgets(catd, 1, 182, "\nInterrupt") + inopen);
+}
+
+/*
+ * If we are interruptible, enable interrupts again.
+ * In some critical sections we turn interrupts off,
+ * but not very often.
+ */
+void 
+setrupt(void)
+{
+
+       if (ruptible) {
+               signal(SIGINT, inopen ? vintr : onintr);
+#ifdef SIGTSTP
+               if (dosusp)
+                       signal(SIGTSTP, onsusp);
+#endif
+       }
+}
+
+int 
+preserve(void)
+{
+
+#ifdef INCORB
+       tflush();
+#endif
+       synctmp();
+       pid = fork();
+       if (pid < 0)
+               return (0);
+       if (pid == 0) {
+               close(0);
+               dup(tfile);
+               execl(EXPRESERVE, "expreserve", (char *)0);
+               exitex(1);
+       }
+       waitfor();
+       if (rpid == pid && status == 0)
+               return (1);
+       return (0);
+}
+
+int 
+exitex(int i)
+{
+
+# ifdef TRACE
+       if (trace)
+               fclose(trace);
+# endif
+       if (failed != 0 && i == 0)
+               i = failed;
+       _exit(i);
+       /*NOTREACHED*/
+       return 0;
+}
+
+#ifdef SIGTSTP
+/*
+ * We have just gotten a susp.  Suspend and prepare to resume.
+ */
+void 
+onsusp(int signum)
+{
+       struct termios f;
+       /* int omask; */
+#ifdef TIOCGWINSZ
+       struct winsize win;
+#endif
+       sigset_t set;
+
+       f = setty(normf);
+       vnfl();
+       putpad(TE);
+       flush();
+
+       sigemptyset(&set);
+       sigprocmask(SIG_SETMASK, &set, NULL);
+       signal(SIGTSTP, SIG_DFL);
+       kill(0, SIGTSTP);
+
+       /* the pc stops here */
+
+       signal(SIGTSTP, onsusp);
+       vcontin(0);
+       setty(f);
+       if (!inopen)
+               error(0);
+#ifdef TIOCGWINSZ
+       else {
+               if (ioctl(0, TIOCGWINSZ, &win) >= 0)
+                       if (win.ws_row != winsz.ws_row ||
+                           win.ws_col != winsz.ws_col)
+                               onwinch(SIGWINCH);
+               if (vcnt < 0) {
+                       vcnt = -vcnt;
+                       if (state == VISUAL)
+                               vclear();
+                       else if (state == CRTOPEN)
+                               vcnt = 0;
+               }
+               vdirty(0, TLINES);
+               vrepaint(cursor);
+       }
+#endif /* TIOCGWINSZ */
+}
+#endif /* SIGTSTP */
+
+/*
+ * For regular strcpy(), source and destination may not overlap.
+ */
+char *
+movestr(char *s1, const char *s2)
+{
+       char    *cp = s1;
+
+       while (*s1++ = *s2++);
+       return cp;
+}
+
+/*
+ * strcpy() checking the maximum size of s1, printing msg in case of overflow.
+ */
+char *
+safecp(char *s1, const char *s2, size_t max, char *msg, ...)
+{
+       va_list ap;
+       char    *cp = s1;
+
+       while (max--)
+               if ((*s1++ = *s2++) == '\0')
+                       return cp;
+       va_start(ap, msg);
+       verror(msg, ap);
+       va_end(ap);
+       exitex(0175);
+       /*NOTREACHED*/
+       return NULL;
+}
+
+/*
+ * strcat() checking the maximum size of s1, printing msg in case of overflow.
+ */
+char *
+safecat(char *s1, const char *s2, size_t max, char *msg, ...)
+{
+       va_list ap;
+       char    *cp = s1;
+
+       while (max && *s1)
+               max--, s1++;
+       while (max--)
+               if ((*s1++ = *s2++) == '\0')
+                       return cp;
+       va_start(ap, msg);
+       verror(msg, ap);
+       va_end(ap);
+       exitex(0175);
+       /*NOTREACHED*/
+       return NULL;
+}
diff --git a/ex_tagio.c b/ex_tagio.c
new file mode 100644 (file)
index 0000000..f7570f5
--- /dev/null
@@ -0,0 +1,177 @@
+/* Copyright (c) 1985 Regents of the University of California */
+
+/*
+ * These routines are for faster tag lookup.  They support the binary
+ * search used in tagfind() instead of the linear search.  The speedup
+ * is quite noticable looking for a tag at the end of a long tags
+ * file.  Define FASTTAG in the Makefile to use these routines.
+ *
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+ */
+
+#ifdef FASTTAG
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_tagio.c 1.11 (gritter) 11/27/04";
+#endif
+#endif
+
+/* from ex_tagio.c     7.3 (Berkeley) 1/31/86 */
+
+#include "ex.h"
+
+static long offset = -1;
+static long block = -1;
+static int bcnt = 0;
+static int b_size = MAXBSIZE;
+static char *ibuf;
+
+int 
+topen(char *file, char *buf)
+{
+       int fd;
+       struct stat statb;
+
+       offset = -1;
+       block = -1;
+       if ((fd = open(file, O_RDONLY, 0)) < 0)
+               return(-1);
+       if (fstat(fd, &statb) < 0) {
+               close(fd);
+               return(-1);
+       }
+       ibuf = buf;
+       if (statb.st_blksize <= MAXBSIZE)
+               b_size = statb.st_blksize;
+       return(fd);
+}
+
+int 
+tseek(int fd, off_t off)
+{
+       off_t nblock;
+
+       nblock = off / b_size * b_size;
+       offset = off % b_size;
+       if (nblock == block)
+               return(0);
+       block = nblock;
+       if (lseek(fd, (off_t) nblock, SEEK_SET) < 0)
+               return(-1);
+       if ((bcnt = read(fd, ibuf, b_size)) < 0)
+               return(-1);
+       return(0);
+}
+
+int 
+tgets(register char *buf, int cnt, int fd)
+{
+       register char *cp;
+       register int cc;
+
+       cc = offset;
+       if (cc == -1) {
+               if ((bcnt = read(fd, ibuf, b_size)) <= 0)
+                       return 0;
+               cc = 0;
+               block = 0;
+       }
+       if (bcnt == 0)          /* EOF */
+               return 0;
+       cp = ibuf + cc;
+       while (--cnt > 0) {
+               if (++cc > bcnt) {
+                       block += b_size;
+                       if ((bcnt = read(fd, ibuf, b_size)) <= 0) {
+                               offset = cc;
+                               return 0;
+                       }
+                       cp = ibuf;
+                       cc = 1;
+               }
+               if ((*buf++ = *cp++) == '\n')
+                       break;
+       }
+       *--buf = 0;
+       offset = cc;
+       return(1);
+}
+
+void 
+tclose(int fd)
+{
+       close(fd);
+       offset = -1;
+       block = -1;
+       bcnt = 0;
+}
+#endif
diff --git a/ex_temp.c b/ex_temp.c
new file mode 100644 (file)
index 0000000..a1127f9
--- /dev/null
+++ b/ex_temp.c
@@ -0,0 +1,771 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_temp.c  1.24 (gritter) 11/24/04";
+#endif
+#endif
+
+/* from ex_temp.c      7.5.1.1 (Berkeley) 8/12/86 */
+
+#include "ex.h"
+#include "ex_temp.h"
+#include "ex_vis.h"
+#include "ex_tty.h"
+#include <sys/wait.h>
+#include <time.h>
+
+/*
+ * Editor temporary file routines.
+ * Very similar to those of ed, except uses 2 input buffers.
+ */
+#define        READ    0
+#define        WRITE   1
+
+/*
+ * Maximum number of attempts to create temporary file.
+ */
+#define        ATTEMPTS        20
+
+char   *tfname;
+char   *rfname;
+int    havetmp;
+int    tfile = -1;
+int    rfile = -1;
+
+void
+fileinit(void)
+{
+       register char *p;
+       struct stat stbuf;
+       register int i, j;
+       pid_t mypid = getpid();
+       char *tfend;
+       int attempts = 0;
+
+       CLOBBGRD(attempts);
+       if (tline == INCRMT * (HBLKS+2))
+               return;
+       cleanup(0);
+       if (tfile != -1)
+               close(tfile);
+       tline = INCRMT * (HBLKS+2);
+       blocks[0] = HBLKS;
+       blocks[1] = HBLKS+1;
+       blocks[2] = -1;
+       dirtcnt = 0;
+       iblock = -1;
+       iblock2 = -1;
+       oblock = -1;
+       tfname = realloc(tfname, strlen(svalue(DIRECTORY)) + 14);
+       CP(tfname, svalue(DIRECTORY));
+       if (stat(tfname, &stbuf)) {
+dumbness:
+               if (setexit() == 0)
+                       filioerr(tfname);
+               else
+                       putNFL();
+               cleanup(1);
+               exitex(1);
+       }
+       if ((stbuf.st_mode & S_IFMT) != S_IFDIR) {
+               errno = ENOTDIR;
+               goto dumbness;
+       }
+       ichanged = 0;
+       ichang2 = 0;
+#ifdef notdef  /* GR */
+       ignore(strcat(tfname, "/ExXXXXX"));
+       for (p = strend(tfname), i = 5, j = getpid(); i > 0; i--, j /= 10)
+               *--p = j % 10 | '0';
+       tfile = creat(tfname, 0600);
+#else
+       ignore(strcat(tfname, "/ExXXXXXXXXXX"));
+       tfend = strend(tfname);
+       do {
+               for (p = tfend, i = 10, j = mypid + attempts;
+                               i > 0; i--, j /= 10)
+                       *--p = j % 10 | '0';
+               tfile = open(tfname, O_CREAT|O_EXCL|O_RDWR
+#ifdef O_NOFOLLOW
+                               |O_NOFOLLOW
+#endif /* O_NOFOLLOW */
+                               , 0600);
+       } while (tfile < 0 && attempts++ < ATTEMPTS);
+#endif /* !notdef */
+       if (tfile < 0)
+               goto dumbness;
+#ifdef INCORB
+       {
+               extern bloc stilinc;            /* see below */
+               stilinc = 0;
+       }
+#endif
+       havetmp = 1;
+/*     brk((char *)fendcore); */
+}
+
+void
+cleanup(bool all)
+{
+       if (all) {
+               putpad(TE);
+               flush();
+       }
+       if (havetmp)
+               unlink(tfname);
+       havetmp = 0;
+       if (all && rfile >= 0) {
+               unlink(rfname);
+               close(rfile);
+               rfile = -1;
+       }
+}
+
+void
+getline(line tl)
+{
+       register char *bp, *lp;
+       register bbloc nl;
+
+       lp = linebuf;
+       bp = getblock(tl, READ);
+       nl = nleft;
+       tl &= ~OFFMSK;
+       while (*lp++ = *bp++)
+               if (--nl == 0) {
+                       bp = getblock(tl += INCRMT, READ);
+                       nl = nleft;
+               }
+}
+
+line
+putline(void)
+{
+       register char *bp, *lp;
+       register bbloc nl;
+       line tl;
+
+       dirtcnt++;
+       lp = linebuf;
+       change();
+       tl = tline;
+       bp = getblock(tl, WRITE);
+       nl = nleft;
+       tl &= ~OFFMSK;
+       while (*bp = *lp++) {
+               if (*bp++ == '\n') {
+                       *--bp = 0;
+                       linebp = lp;
+                       break;
+               }
+               if (--nl == 0) {
+                       bp = getblock(tl += INCRMT, WRITE);
+                       nl = nleft;
+               }
+       }
+       tl = tline;
+       tline += (((lp - linebuf) + BNDRY - 1) >> SHFT) & 077776;
+       return (tl);
+}
+
+char *
+getblock(line atl, int iof)
+{
+       register bbloc bno, off;
+       
+       bno = (atl >> OFFBTS) & BLKMSK;
+       off = (atl << SHFT) & LBTMSK;
+       if (bno >= NMBLKS)
+               error(catgets(catd, 1, 183, " Tmp file too large"));
+       nleft = BUFSIZ - off;
+       if (bno == iblock) {
+               ichanged |= iof;
+               hitin2 = 0;
+               return (ibuff + off);
+       }
+       if (bno == iblock2) {
+               ichang2 |= iof;
+               hitin2 = 1;
+               return (ibuff2 + off);
+       }
+       if (bno == oblock)
+               return (obuff + off);
+       if (iof == READ) {
+               if (hitin2 == 0) {
+                       if (ichang2) {
+                               blkio(iblock2, ibuff2, (ssize_t(*)())write);
+                       }
+                       ichang2 = 0;
+                       iblock2 = bno;
+                       blkio(bno, ibuff2, (ssize_t(*)())read);
+                       hitin2 = 1;
+                       return (ibuff2 + off);
+               }
+               hitin2 = 0;
+               if (ichanged) {
+                       blkio(iblock, ibuff, (ssize_t(*)())write);
+               }
+               ichanged = 0;
+               iblock = bno;
+               blkio(bno, ibuff, (ssize_t(*)())read);
+               return (ibuff + off);
+       }
+       if (oblock >= 0) {
+                       blkio(oblock, obuff, (ssize_t(*)())write);
+       }
+       oblock = bno;
+       return (obuff + off);
+}
+
+#ifdef INCORB
+char   incorb[INCORB+1][BUFSIZ];
+#define        pagrnd(a)       ((char *)(((size_t)a)&~(BUFSIZ-1)))
+bloc   stilinc;        /* up to here not written yet */
+#endif
+
+void
+blkio(bloc b, char *buf, ssize_t (*iofcn)(int, void *, size_t))
+{
+
+#ifdef INCORB
+       if (b < INCORB) {
+               if (iofcn == (ssize_t(*)())read) {
+                       copy(buf, pagrnd(incorb[b+1]), (size_t) BUFSIZ);
+                       return;
+               }
+               copy(pagrnd(incorb[b+1]), buf, (size_t) BUFSIZ);
+               if (laste) {
+                       if (b >= stilinc)
+                               stilinc = b + 1;
+                       return;
+               }
+       } else if (stilinc)
+               tflush();
+#endif
+       lseek(tfile, (off_t) ((b & BLKMSK) * BUFSIZ), SEEK_SET);
+       if ((*iofcn)(tfile, buf, BUFSIZ) != BUFSIZ)
+               filioerr(tfname);
+}
+
+#ifdef INCORB
+void
+tlaste(void)
+{
+
+       if (stilinc)
+               dirtcnt = 0;
+}
+
+void
+tflush(void)
+{
+       bbloc i = stilinc;
+       
+       stilinc = 0;
+       lseek(tfile, (off_t) 0, SEEK_SET);
+       if (write(tfile, pagrnd(incorb[1]), i * BUFSIZ) != (i * BUFSIZ))
+               filioerr(tfname);
+}
+#endif
+
+/*
+ * Synchronize the state of the temporary file in case
+ * a crash occurs.
+ */
+void
+synctmp(void)
+{
+       register bbloc cnt;
+       register line *a;
+       register bloc *bp, *up;
+
+#ifdef INCORB
+       if (stilinc)
+               return;
+#endif
+       if (dol == zero)
+               return;
+       if (ichanged)
+               blkio(iblock, ibuff, (ssize_t(*)())write);
+       ichanged = 0;
+       if (ichang2)
+               blkio(iblock2, ibuff2, (ssize_t(*)())write);
+       ichang2 = 0;
+       if (oblock != -1)
+               blkio(oblock, obuff, (ssize_t(*)())write);
+       time(&H.Time);
+       uid = getuid();
+       *zero = (line) H.Time;
+       up = blocks + LBLKS;
+       for (a = zero, bp = blocks; a <= dol; a += BUFSIZ / sizeof *a, bp++) {
+               if (bp >= up)
+                       error(catgets(catd, 1, 184, " Tmp file too large"));
+               if (*bp < 0) {
+                       tline = (tline + OFFMSK) &~ OFFMSK;
+                       *bp = ((tline >> OFFBTS) & BLKMSK);
+                       if (*bp > NMBLKS)
+                               error(catgets(catd, 1, 185,
+                                               " Tmp file too large"));
+                       tline += INCRMT;
+                       oblock = *bp + 1;
+                       bp[1] = -1;
+               }
+               lseek(tfile, (off_t) ((*bp & BLKMSK) * BUFSIZ), SEEK_SET);
+               cnt = ((dol - a) + 2) * sizeof (line);
+               if (cnt > BUFSIZ)
+                       cnt = BUFSIZ;
+               if (write(tfile, (char *) a, cnt) != cnt) {
+oops:
+                       *zero = 0;
+                       filioerr(tfname);
+               }
+               *zero = 0;
+       }
+       flines = lineDOL();
+       lseek(tfile, (off_t) 0, SEEK_SET);
+       if (write(tfile, (char *) &H, sizeof H) != sizeof H)
+               goto oops;
+#ifdef notdef
+       /*
+        * This will insure that exrecover gets as much
+        * back after a crash as is absolutely possible,
+        * but can result in pregnant pauses between commands
+        * when the TSYNC call is made, so...
+        */
+       fsync(tfile);
+#endif
+}
+
+void
+TSYNC(void)
+{
+
+       if (dirtcnt > MAXDIRT) {        /* mjm: 12 --> MAXDIRT */
+#ifdef INCORB
+               if (stilinc)
+                       tflush();
+#endif
+               dirtcnt = 0;
+               synctmp();
+       }
+}
+
+/*
+ * Named buffer routines.
+ * These are implemented differently than the main buffer.
+ * Each named buffer has a chain of blocks in the register file.
+ * Each block contains roughly 508 chars of text,
+ * and a previous and next block number.  We also have information
+ * about which blocks came from deletes of multiple partial lines,
+ * e.g. deleting a sentence or a LISP object.
+ *
+ * We maintain a free map for the temp file.  To free the blocks
+ * in a register we must read the blocks to find how they are chained
+ * together.
+ *
+ * BUG:                The default savind of deleted lines in numbered
+ *             buffers may be rather inefficient; it hasn't been profiled.
+ */
+struct strreg {
+       short   rg_flags;
+       short   rg_nleft;
+       short   rg_first;
+       short   rg_last;
+} strregs[('z'-'a'+1) + ('9'-'0'+1)], *strp;
+
+struct rbuf {
+       short   rb_prev;
+       short   rb_next;
+       char    rb_text[BUFSIZ - 2 * sizeof (short)];
+} *rbuf, KILLrbuf, putrbuf, YANKrbuf, regrbuf;
+#ifdef VMUNIX
+#ifdef LARGEF
+short  rused[4096];
+#else  /* !LARGEF */
+short  rused[256];
+#endif /* !LARGEF */
+#else  /* !VMUNIX */
+short  rused[32];
+#endif /* !VMUNIX */
+short  rnleft;
+short  rblock;
+short  rnext;
+char   *rbufcp;
+
+void
+regio(short b, ssize_t (*iofcn)(int, void *, size_t))
+{
+       register char *p;
+       char *rfend;
+       int attempts = 0;
+       register int i, j;
+       pid_t mypid = getpid();
+
+       if (rfile == -1) {
+               rfname = realloc(rfname, strlen(svalue(DIRECTORY)) + 14);
+               CP(rfname, tfname);
+               rfend = strend(rfname);
+#ifdef notdef  /* GR */
+               *(rfend - 7) = 'R';
+#else
+               *(rfend - 12) = 'R';
+#endif
+               do {
+                       for (p = rfend, i = 10, j = mypid + attempts;
+                                       i > 0; i--, j /= 10)
+                               *--p = j % 10 | '0';
+                       rfile = open(rfname, O_CREAT|O_EXCL|O_RDWR
+#ifdef O_NOFOLLOW
+                                       |O_NOFOLLOW
+#endif /* O_NOFOLLOW */
+                                       , 0600);
+               } while (rfile < 0 && attempts++ < ATTEMPTS);
+               if (rfile < 0)
+oops:
+                       filioerr(rfname);
+       }
+       lseek(rfile, (off_t) ((b & BLKMSK) * BUFSIZ), SEEK_SET);
+       if ((*iofcn)(rfile, rbuf, BUFSIZ) != BUFSIZ)
+               goto oops;
+       rblock = b;
+}
+
+int
+REGblk(void)
+{
+       register int i, j, m;
+
+       for (i = 0; i < sizeof rused / sizeof rused[0]; i++) {
+               m = (rused[i] ^ 0177777) & 0177777;
+               if (i == 0)
+                       m &= ~1;
+               if (m != 0) {
+                       j = 0;
+                       while ((m & 1) == 0)
+                               j++, m >>= 1;
+                       rused[i] |= (1 << j);
+#ifdef RDEBUG
+                       printf("allocating block %d\n", i * 16 + j);
+#endif
+                       return (i * 16 + j);
+               }
+       }
+       error(catgets(catd, 1, 186, "Out of register space (ugh)"));
+       /*NOTREACHED*/
+       return 0;
+}
+
+struct strreg *
+mapreg(register int c)
+{
+
+       if (isupper(c))
+               c = tolower(c);
+       return (isdigit(c) ? &strregs[('z'-'a'+1)+(c-'0')] : &strregs[c-'a']);
+}
+
+void
+KILLreg(register int c)
+{
+       register struct strreg *sp;
+
+       rbuf = &KILLrbuf;
+       sp = mapreg(c);
+       rblock = sp->rg_first;
+       sp->rg_first = sp->rg_last = 0;
+       sp->rg_flags = sp->rg_nleft = 0;
+       while (rblock != 0) {
+#ifdef RDEBUG
+               printf("freeing block %d\n", rblock);
+#endif
+               rused[rblock / 16] &= ~(1 << (rblock % 16));
+               regio(rblock, (ssize_t (*)(int, void *, size_t))shread);
+               rblock = rbuf->rb_next;
+       }
+}
+
+ssize_t
+shread(void)
+{
+       struct front { short a; short b; };
+
+       if (read(rfile, (char *) rbuf, sizeof (struct front)) == sizeof (struct front))
+               return (sizeof (struct rbuf));
+       return (0);
+}
+
+int    getREG();
+
+void
+putreg(int c)
+{
+       register line *odot = dot;
+       register line *odol = dol;
+       register int cnt;
+
+       deletenone();
+       appendnone();
+       rbuf = &putrbuf;
+       rnleft = 0;
+       rblock = 0;
+       rnext = mapreg(c)->rg_first;
+       if (rnext == 0) {
+               if (inopen) {
+                       splitw++;
+                       vclean();
+                       vgoto(WECHO, 0);
+               }
+               vreg = -1;
+               error(catgets(catd, 1, 187, "Nothing in register %c"), c);
+       }
+       if (inopen && partreg(c)) {
+               if (!FIXUNDO) {
+                       splitw++; vclean(); vgoto(WECHO, 0); vreg = -1;
+                       error(catgets(catd, 1, 188,
+                               "Can't put partial line inside macro"));
+               }
+               squish();
+               addr1 = addr2 = dol;
+       }
+       cnt = append(getREG, addr2);
+       if (inopen && partreg(c)) {
+               unddol = dol;
+               dol = odol;
+               dot = odot;
+               pragged(0);
+       }
+       killcnt(cnt);
+       notecnt = cnt;
+}
+
+int
+partreg(int c)
+{
+
+       return (mapreg(c)->rg_flags);
+}
+
+void
+notpart(register int c)
+{
+
+       if (c)
+               mapreg(c)->rg_flags = 0;
+}
+
+int
+getREG(void)
+{
+       register char *lp = linebuf;
+       register int c;
+
+       for (;;) {
+               if (rnleft == 0) {
+                       if (rnext == 0)
+                               return (EOF);
+                       regio(rnext, read);
+                       rnext = rbuf->rb_next;
+                       rbufcp = rbuf->rb_text;
+                       rnleft = sizeof rbuf->rb_text;
+               }
+               c = *rbufcp;
+               if (c == 0)
+                       return (EOF);
+               rbufcp++, --rnleft;
+               if (c == '\n') {
+                       *lp++ = 0;
+                       return (0);
+               }
+               *lp++ = c;
+       }
+}
+
+void
+YANKreg(register int c)
+{
+       register line *addr;
+       register struct strreg *sp;
+       char savelb[LBSIZE];
+
+       if (isdigit(c))
+               kshift();
+       if (islower(c))
+               KILLreg(c);
+       strp = sp = mapreg(c);
+       sp->rg_flags = inopen && cursor && wcursor;
+       rbuf = &YANKrbuf;
+       if (sp->rg_last) {
+               regio(sp->rg_last, read);
+               rnleft = sp->rg_nleft;
+               rbufcp = &rbuf->rb_text[sizeof rbuf->rb_text - rnleft];
+       } else {
+               rblock = 0;
+               rnleft = 0;
+       }
+       CP(savelb,linebuf);
+       for (addr = addr1; addr <= addr2; addr++) {
+               getline(*addr);
+               if (sp->rg_flags) {
+                       if (addr == addr2)
+                               *wcursor = 0;
+                       if (addr == addr1)
+                               strcpy(linebuf, cursor);
+               }
+               YANKline();
+       }
+       rbflush();
+       killed();
+       CP(linebuf,savelb);
+}
+
+void
+kshift(void)
+{
+       register int i;
+
+       KILLreg('9');
+       for (i = '8'; i >= '0'; i--)
+               copy(mapreg(i+1), mapreg(i), sizeof (struct strreg));
+}
+
+void
+YANKline(void)
+{
+       register char *lp = linebuf;
+       register struct rbuf *rp = rbuf;
+       register int c;
+
+       do {
+               c = *lp++;
+               if (c == 0)
+                       c = '\n';
+               if (rnleft == 0) {
+                       rp->rb_next = REGblk();
+                       rbflush();
+                       rblock = rp->rb_next;
+                       rp->rb_next = 0;
+                       rp->rb_prev = rblock;
+                       rnleft = sizeof rp->rb_text;
+                       rbufcp = rp->rb_text;
+               }
+               *rbufcp++ = c;
+               --rnleft;
+       } while (c != '\n');
+       if (rnleft)
+               *rbufcp = 0;
+}
+
+void
+rbflush(void)
+{
+       register struct strreg *sp = strp;
+
+       if (rblock == 0)
+               return;
+       regio(rblock, (ssize_t (*)(int, void *, size_t))write);
+       if (sp->rg_first == 0)
+               sp->rg_first = rblock;
+       sp->rg_last = rblock;
+       sp->rg_nleft = rnleft;
+}
+
+/* Register c to char buffer buf of size buflen */
+void
+regbuf(char c, char *buf, int buflen)
+{
+       register char *p, *lp;
+
+       rbuf = &regrbuf;
+       rnleft = 0;
+       rblock = 0;
+       rnext = mapreg(c)->rg_first;
+       if (rnext==0) {
+               *buf = 0;
+               error(catgets(catd, 1, 189, "Nothing in register %c"),c);
+       }
+       p = buf;
+       while (getREG()==0) {
+               for (lp=linebuf; *lp;) {
+                       if (p >= &buf[buflen])
+                               error(catgets(catd, 1, 190,
+                                       "Register too long@to fit in memory"));
+                       *p++ = *lp++;
+               }
+               *p++ = '\n';
+       }
+       if (partreg(c)) p--;
+       *p = '\0';
+       getDOT();
+}
diff --git a/ex_temp.h b/ex_temp.h
new file mode 100644 (file)
index 0000000..8bbb1d1
--- /dev/null
+++ b/ex_temp.h
@@ -0,0 +1,191 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+ *
+ *     from ex_temp.h  7.4 (Berkeley) 5/31/85
+ *
+ *     @(#)ex_temp.h   1.8 (gritter) 1/26/02
+ */
+
+/*
+ * The editor uses a temporary file for files being edited, in a structure
+ * similar to that of ed.  The first block of the file is used for a header
+ * block which guides recovery after editor/system crashes.
+ * Lines are represented in core by a pointer into the temporary file which
+ * is packed into 16 bits (32 on VMUNIX).  All but the low bit index the temp
+ * file; the last is used by global commands.  The parameters below control
+ * how much the other bits are shifted left before they index the temp file.
+ * Larger shifts give more slop in the temp file but allow larger files
+ * to be edited.
+ *
+ * The editor does not garbage collect the temporary file.  When a new
+ * file is edited, the temporary file is rather discarded and a new one
+ * created for the new file.  Garbage collection would be rather complicated
+ * in ex because of the general undo, and in any case would require more
+ * work when throwing lines away because marks would have be carefully
+ * checked before reallocating temporary file space.  Said another way,
+ * each time you create a new line in the temporary file you get a unique
+ * number back, and this is a property used by marks.
+ *
+ * The following temp file parameters allow 256k bytes in the temporary
+ * file.  By changing to the numbers in comments you can get 512k.
+ * For VMUNIX you get more than you could ever want.
+ * VMUNIX uses long (32 bit) integers giving much more
+ * space in the temp file and no waste.  This doubles core
+ * requirements but allows files of essentially unlimited size to be edited.
+ */
+#ifndef VMUNIX
+#define        BLKMSK  0777            /* 01777 */
+#define        BNDRY   8               /* 16 */
+#define        INCRMT  0200            /* 0100 */
+#define        LBTMSK  0770            /* 0760 */
+#define        NMBLKS  506             /* 1018 */
+#define        OFFBTS  7               /* 6 */
+#define        OFFMSK  0177            /* 077 */
+#define        SHFT    2               /* 3 */
+#else
+#ifdef LARGEF
+#define        BLKMSK  017777777777
+#else
+#define        BLKMSK  077777
+#endif
+#define        BNDRY   2
+#define        INCRMT  02000
+#define        LBTMSK  01776
+#ifdef LARGEF
+#define        NMBLKS  017777777770
+#else
+#define        NMBLKS  077770
+#endif
+#define        OFFBTS  10
+#define        OFFMSK  01777
+#define        SHFT    0
+#endif
+
+/*
+ * The editor uses three buffers into the temporary file (ed uses two
+ * and is very similar).  These are two read buffers and one write buffer.
+ * Basically, the editor deals with the file as a sequence of BUFSIZ character
+ * blocks.  Each block contains some number of lines (and lines
+ * can run across block boundaries.
+ *
+ * New lines are written into the last block in the temporary file
+ * which is in core as obuf.  When a line is needed which isn't in obuf,
+ * then it is brought into an input buffer.  As there are two, the choice
+ * is to take the buffer into which the last read (of the two) didn't go.
+ * Thus this is a 2 buffer LRU replacement strategy.  Measurement
+ * shows that this saves roughly 25% of the buffer reads over a one
+ * input buffer strategy.  Since the editor (on our VAX over 1 week)
+ * spends (spent) roughly 30% of its time in the system read routine,
+ * this can be a big help.
+ */
+var bool       hitin2;         /* Last read hit was ibuff2 not ibuff */
+var bool       ichang2;        /* Have actually changed ibuff2 */
+var bool       ichanged;       /* Have actually changed ibuff */
+var bloc       iblock;         /* Temp file block number of ibuff (or -1) */
+var bloc       iblock2;        /* Temp file block number of ibuff2 (or -1) */
+var bloc       ninbuf;         /* Number useful chars left in input buffer */
+var bloc       nleft;          /* Number usable chars left in output buffer */
+var bloc       oblock;         /* Temp file block number of obuff (or -1) */
+var bbloc      tline;          /* Current temp file ptr */
+
+var char       ibuff[BUFSIZ];
+var char       ibuff2[BUFSIZ];
+var char       obuff[BUFSIZ];
+
+/*
+ * Structure of the descriptor block which resides
+ * in the first block of the temporary file and is
+ * the guiding light for crash recovery.
+ *
+ * As the Blocks field below implies, there are temporary file blocks
+ * devoted to (some) image of the incore array of pointers into the temp
+ * file.  Thus, to recover from a crash we use these indices to get the
+ * line pointers back, and then use the line pointers to get the text back.
+ * Except for possible lost lines due to sandbagged I/O, the entire
+ * file (at the time of the last editor "sync") can be recovered from
+ * the temp file.
+ */
+
+/* This definition also appears in expreserve.c... beware */
+struct         header {
+       time_t  Time;                   /* Time temp file last updated */
+       uid_t   Uid;
+       bbloc   Flines;                 /* Number of lines in file */
+       char    Savedfile[FNSIZE];      /* The current file name */
+       bloc    Blocks[LBLKS];          /* Blocks where line pointers stashed */
+}; 
+var struct     header H;
+
+#define        uid             H.Uid
+#define        flines          H.Flines
+#define        savedfile       H.Savedfile
+#define        blocks          H.Blocks
diff --git a/ex_tty.c b/ex_tty.c
new file mode 100644 (file)
index 0000000..05702b6
--- /dev/null
+++ b/ex_tty.c
@@ -0,0 +1,407 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_tty.c   1.29 (gritter) 2/17/05";
+#endif
+#endif
+
+/* from ex_tty.c       7.10.1 (2.11BSD GTE) 12/9/94 */
+
+#include "ex.h"
+#include "ex_tty.h"
+
+int ATTN = DELETE;
+
+/*
+ * Terminal type initialization routines,
+ * and calculation of flags at entry or after
+ * a shell escape which may change them.
+ */
+/* short       ospeed = -1;    mjm: def also in tputs.c of termcap.a  */
+
+void 
+gettmode(void)
+{
+       speed_t pospeed;
+
+       if (tcgetattr(1, &tty) < 0) {
+               ospeed = B0;
+               return;
+       }
+       pospeed = cfgetospeed(&tty);
+       if (ospeed != pospeed)
+               value(SLOWOPEN) = pospeed < B1200;
+       ospeed = pospeed;
+       normf = tty;
+#if defined (UCVISUAL) && defined (IUCLC)
+       UPPERCASE = (tty.c_iflag & IUCLC) != 0;
+#endif
+#if defined (TAB3)
+       GT = (tty.c_oflag & TABDLY) != TAB3 && !XT;
+#elif defined (XTABS)
+       GT = (tty.c_oflag & TABDLY) != XTABS && !XT;
+#else
+       GT = !XT;
+#endif /* !TAB3, XTABS */
+       /*
+        * Tabs and multi-column characters do not combine properly
+        * unless vi performs a look-ahead on the current line. Just
+        * do not use them for now.
+        */
+       if (mb_cur_max > 1)
+               GT = 0;
+       NONL = (tty.c_oflag & ONLCR) == 0;
+       ATTN = tty.c_cc[VINTR];
+}
+
+char *xPC;
+char **sstrs[] = {
+       &AL, &BC, &BT, &CD, &CE, &CL, &CM, &xCR, &xCS, &DC, &DL, &DM, &DO,
+       &ED, &EI, &F0, &F1, &F2, &F3, &F4, &F5, &F6, &F7, &F8, &F9,
+       &HO, &IC, &IM, &IP, &KD, &KE, &KH, &KL, &KR, &KS, &KU, &LL, &ND, &xNL,
+       &xPC, &RC, &SC, &SE, &SF, &SO, &SR, &TA, &TE, &TI, &UP, &VB, &VS, &VE,
+       &AL_PARM, &DL_PARM, &UP_PARM, &DOWN_PARM, &LEFT_PARM, &RIGHT_PARM
+};
+bool *sflags[] = {
+       &AM, &BS, &DA, &DB, &EO, &HC,
+#ifdef UCVISUAL
+       &xHZ,
+#endif
+       &IN, &MI, &NC, &NS, &OS, &UL,
+       &XB, &XN, &XT, &XX
+};
+char **fkeys[10] = {
+       &F0, &F1, &F2, &F3, &F4, &F5, &F6, &F7, &F8, &F9
+};
+void
+setterm(char *type)
+{
+       register int unknown;
+       char ltcbuf[TCBUFSIZE];
+
+       if (type[0] == 0)
+               type = "xx";
+       unknown = 0;
+       putpad(TE);
+       if (tgetent(ltcbuf, type) != 1) {
+               unknown++;
+               CP(ltcbuf, "xx|dumb:");
+       }
+       gettmode(); /* must call gettmode() before setsize(). GR */
+       setsize();
+       aoftspace = tspace;
+       zap();
+       /*
+        * Initialize keypad arrow keys.
+        */
+       addmac1(KU, "k", "up", arrows, 1);
+       addmac1(KD, "j", "down", arrows, 1);
+       addmac1(KL, "h", "left", arrows, 1);
+       addmac1(KR, "l", "right", arrows, 1);
+       addmac1(KH, "H", "home", arrows, 1);
+
+       /*
+        * Handle funny termcap capabilities
+        */
+       if (xCS && SC && RC) {
+               if (AL==NULL) AL="";
+               if (DL==NULL) DL="";
+       }
+       if (AL_PARM && AL==NULL) AL="";
+       if (DL_PARM && DL==NULL) DL="";
+       if (IC && IM==NULL) IM="";
+       if (IC && EI==NULL) EI="";
+       if (!GT) BT=NULL;       /* If we can't tab, we can't backtab either */
+
+#ifdef TIOCLGET
+#define        HAS_JOB_CONTROL
+#endif
+#ifdef _SC_JOB_CONTROL
+#define        HAS_JOB_CONTROL
+#endif
+#ifdef HAS_JOB_CONTROL
+       /*
+        * Now map users susp char to ^Z, being careful that the susp
+        * overrides any arrow key, but only for hackers (=new tty driver).
+        */
+       {
+               static char sc[2];
+               int i /* , fnd */;
+
+               if (sysconf(_SC_JOB_CONTROL) != -1)
+               {
+                       /*
+                        * If a system supports job control but no job
+                        * control shell is used, only one method of
+                        * detection remains: Our session id equals our
+                        * process group id. Any job control shell would
+                        * have created at least one new process group.
+                        * But as the VSUSP key may be active, we have
+                        * to override arrow keys either.
+                        */
+#ifndef        _CRAY   /* getsid() is a bad syscall on UNICOS */
+                       if (getsid(0) != getpgid(0))
+#endif /* !_CRAY */
+                               ldisc = 2;      /* value of NTTYDISC */
+                       sc[0] = tty.c_cc[VSUSP];
+                       sc[1] = 0;
+                       if (tty.c_cc[VSUSP] == CTRL('z')) {
+                               for (i=0; i<=4; i++)
+                                       if (arrows[i].cap &&
+                                           arrows[i].cap[0] == CTRL('z'))
+                                               addmac(sc, NULL, NULL, arrows);
+                       } else if (sc[0]
+#ifdef _PC_VDISABLE
+                                       && sc[0] != fpathconf(1, _PC_VDISABLE)
+#endif
+                                       )
+                               addmac(sc, "\32", "susp", arrows);
+               }
+       }
+#endif /* HAS_JOB_CONTROL */
+
+       if (CM != 0) {
+               if (tgoto(CM, 2, 2)[0] == 'O')  /* OOPS */
+                       CA = 0, CM = 0;
+               else
+                       CA = 1, costCM = cost(tgoto(CM, 8, 10));
+       } else {
+               CA = 0, CM = 0;
+       }
+       costSR = cost(SR);
+       costAL = cost(AL);
+       costDP = cost(tgoto(DOWN_PARM, 10, 10));
+       costLP = cost(tgoto(LEFT_PARM, 10, 10));
+       costRP = cost(tgoto(RIGHT_PARM, 10, 10));
+       PC = xPC ? xPC[0] : 0;
+       aoftspace = tspace;
+       safecp(ttylongname, gettlongname(ltcbuf, type), sizeof ttylongname,
+                       "Terminal name too long");
+       /* proper strings to change tty type */
+       termreset();
+       gettmode();
+       value(REDRAW) = AL && DL;
+       value(OPTIMIZE) = !CA && !GT;
+       if (ospeed == B1200 && !value(REDRAW))
+               value(SLOWOPEN) = 1;    /* see also gettmode above */
+       if (unknown)
+               serror(catgets(catd, 1, 191,
+                               "%s: Unknown terminal type"), type);
+}
+
+void
+setsize(void)
+{
+       register int l, i;
+#ifdef TIOCGWINSZ
+       struct winsize win;
+#endif
+
+       char *e;
+
+#ifdef TIOCGWINSZ
+       i = ioctl(0, TIOCGWINSZ, &win);
+#endif
+       TLINES = TCOLUMNS = 0;
+       e = getenv("COLUMNS");
+       if (e != NULL && *e != '\0')
+               TCOLUMNS = atoi(e);
+       if (TCOLUMNS <= 0) {
+#ifdef TIOCGWINSZ
+               if (i >= 0 && win.ws_col != 0)
+                       TCOLUMNS = winsz.ws_col = win.ws_col;
+               else
+#endif
+                       TCOLUMNS = tgetnum("co");
+       }
+       e = getenv("LINES");
+       if (e != NULL && *e != '\0')
+               TLINES = atoi(e);
+       if (TLINES <= 0) {
+#ifdef TIOCGWINSZ
+               if (i >= 0 && win.ws_row != 0)
+                       TLINES = winsz.ws_row = win.ws_row;
+               else
+#endif
+                       TLINES = tgetnum("li");
+       }
+       i = TLINES;
+       if (TLINES <= 5)
+               TLINES = 24;
+       if (TLINES > TUBELINES)
+               TLINES = TUBELINES;
+       l = TLINES;
+       if (ospeed < B1200)
+               l = 9;  /* including the message line at the bottom */
+       else if (ospeed < B2400)
+               l = 17;
+       if (l > TLINES)
+               l = TLINES;
+       if (TCOLUMNS <= 4)
+               TCOLUMNS = 1000;
+       options[WINDOW].ovalue = options[WINDOW].odefault = l - 1;
+       if (defwind) {
+               options[WINDOW].ovalue = defwind;
+               l = defwind + 1;
+       }
+       options[SCROLL].ovalue = options[SCROLL].odefault = HC ? 11 : ((l-1) / 2);
+       if (i <= 0)
+               TLINES = 2;
+}
+
+void
+zap(void)
+{
+       register char *namp;
+       register bool **fp;
+       register char ***sp;
+       int flag;
+       char *string;
+
+#ifndef        UCVISUAL
+       namp = "ambsdadbeohcinmincnsosulxbxnxtxx";
+#else
+       namp = "ambsdadbeohchzinmincnsosulxbxnxtxx";
+#endif
+       fp = sflags;
+       do {
+               flag = tgetflag(namp);
+               *(*fp++) = flag;
+               namp += 2;
+       } while (*namp);
+       namp = "albcbtcdceclcmcrcsdcdldmdoedeik0k1k2k3k4k5k6k7k8k9hoicimipkdkekhklkrkskullndnlpcrcscsesfsosrtatetiupvbvsveALDLUPDOLERI";
+       sp = sstrs;
+       do {
+               string = tgetstr(namp, &aoftspace);
+               *(*sp++) = string;
+               namp += 2;
+       } while (*namp);
+}
+
+char *
+gettlongname(register char *bp, char *def)
+{
+       register char *cp;
+
+       while (*bp && *bp != ':' && *bp != '|')
+               bp++;
+       if (*bp == '|') {
+               bp++;
+               cp = bp;
+               while (*cp && *cp != ':' && *cp != '|')
+                       cp++;
+               *cp = 0;
+               return (bp);
+       }
+       return (def);
+}
+
+char *
+fkey(int i)
+{
+       if (0 <= i && i <= 9)
+               return(*fkeys[i]);
+       else
+               return(NOSTR);
+}
+
+/*
+ * cost figures out how much (in characters) it costs to send the string
+ * str to the terminal.  It takes into account padding information, as
+ * much as it can, for a typical case.  (Right now the typical case assumes
+ * the number of lines affected is the size of the screen, since this is
+ * mainly used to decide if AL or SR is better, and this always happens
+ * at the top of the screen.  We assume cursor motion (CM) has little
+ * padding, if any, required, so that case, which is really more important
+ * than AL vs SR, won't be really affected.)
+ */
+static int costnum;
+int
+cost(char *str)
+{
+       if (str == NULL || *str=='O')   /* OOPS */
+               return 10000;   /* infinity */
+       costnum = 0;
+       tputs(str, TLINES, countnum);
+       return costnum;
+}
+
+/*ARGSUSED*/
+int
+countnum(int ch)
+{
+       costnum++;
+       return ch;
+}
diff --git a/ex_tty.h b/ex_tty.h
new file mode 100644 (file)
index 0000000..66f0573
--- /dev/null
+++ b/ex_tty.h
@@ -0,0 +1,249 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+ *
+ *     from ex_tty.h   7.5.1 (2.11BSD GTE) 12/9/94
+ *
+ *     @(#)ex_tty.h    1.13 (gritter) 12/1/04
+ */
+#include "libterm/libterm.h"
+
+/*
+ * Capabilities from termcap
+ *
+ * The description of terminals is a difficult business, and we only
+ * attempt to summarize the capabilities here;  for a full description
+ * see the paper describing termcap.
+ *
+ * Capabilities from termcap are of three kinds - string valued options,
+ * numeric valued options, and boolean options.  The string valued options
+ * are the most complicated, since they may include padding information,
+ * which we describe now.
+ *
+ * Intelligent terminals often require padding on intelligent operations
+ * at high (and sometimes even low) speed.  This is specified by
+ * a number before the string in the capability, and has meaning for the
+ * capabilities which have a P at the front of their comment.
+ * This normally is a number of milliseconds to pad the operation.
+ * In the current system which has no true programmible delays, we
+ * do this by sending a sequence of pad characters (normally nulls, but
+ * specifiable as "pc").  In some cases, the pad is better computed
+ * as some number of milliseconds times the number of affected lines
+ * (to bottom of screen usually, except when terminals have insert modes
+ * which will shift several lines.)  This is specified as '12*' e.g.
+ * before the capability to say 12 milliseconds per affected whatever
+ * (currently always line).  Capabilities where this makes sense say P*.
+ */
+#ifndef        VMUNIX
+var    char    tspace[256];    /* Space for capability strings */
+#else
+var    char    tspace[1024];   /* Space for capability strings */
+#endif
+var    char    *aoftspace;     /* Address of tspace for relocation */
+
+var    char    *AL;            /* P* Add new blank line */
+var    char    *AL_PARM;       /* P* Add n new blank lines */
+extern char    *BC;            /*    Back cursor */
+var    char    *BT;            /* P  Back tab */
+var    char    *CD;            /* P* Clear to end of display */
+var    char    *CE;            /* P  Clear to end of line */
+var    char    *CL;            /* P* Clear screen */
+var    char    *CM;            /* PG Cursor motion */
+var    char    *xCS;           /* PG Change scrolling region (vt100) */
+var    char    *xCR;           /* P  Carriage return */
+var    char    *DC;            /* P* Delete character */
+var    char    *DL;            /* P* Delete line sequence */
+var    char    *DL_PARM;       /* P* Delete n lines */
+var    char    *DM;            /*    Delete mode (enter)  */
+var    char    *DO;            /*    Down line sequence */
+var    char    *DOWN_PARM;     /*    Down n lines */
+var    char    *ED;            /*    End delete mode */
+var    char    *EI;            /*    End insert mode */
+var    char    *F0,*F1,*F2,*F3,*F4,*F5,*F6,*F7,*F8,*F9;
+                               /*    Strings sent by various function keys */
+var    char    *HO;            /*    Home cursor */
+var    char    *IC;            /* P  Insert character */
+var    char    *IM;            /*    Insert mode (give as ':im=:' if 'ic' */
+var    char    *IP;            /* P* Insert pad after char ins'd using IM+IE */
+var    char    *KD;            /*    Keypad down arrow */
+var    char    *KE;            /*    Keypad don't xmit */
+var    char    *KH;            /*    Keypad home key */
+var    char    *KL;            /*    Keypad left arrow */
+var    char    *KR;            /*    Keypad right arrow */
+var    char    *KS;            /*    Keypad start xmitting */
+var    char    *KU;            /*    Keypad up arrow */
+var    char    *LEFT_PARM;     /*    Left n chars */
+var    char    *LL;            /*    Quick to last line, column 0 */
+var    char    *ND;            /*    Non-destructive space */
+var    char    *RIGHT_PARM;    /*    Right n spaces */
+var    char    *xNL;           /*    Line feed (new line) */
+extern char    PC;             /*    Pad character */
+var    char    *RC;            /*    Restore cursor from last SC */
+var    char    *SC;            /*    Save cursor */
+var    char    *SE;            /*    Standout end (may leave space) */
+var    char    *SF;            /* P  Scroll forwards */
+var    char    *SO;            /*    Stand out begin (may leave space) */
+var    char    *SR;            /* P  Scroll backwards */
+var    char    *TA;            /* P  Tab (other than ^I or with padding) */
+var    char    *TE;            /*    Terminal end sequence */
+var    char    *TI;            /*    Terminal initial sequence */
+extern char    *UP;            /*    Upline */
+var    char    *UP_PARM;       /*    Up n lines */
+var    char    *VB;            /*    Visible bell */
+var    char    *VE;            /*    Visual end sequence */
+var    char    *VS;            /*    Visual start sequence */
+var    bool    AM;             /* Automatic margins */
+var    bool    BS;             /* Backspace works */
+var    bool    CA;             /* Cursor addressible */
+var    bool    DA;             /* Display may be retained above */
+var    bool    DB;             /* Display may be retained below */
+var    bool    EO;             /* Can erase overstrikes with ' ' */
+var    bool    GT;             /* Gtty indicates tabs */
+var    bool    HC;             /* Hard copy terminal */
+#ifdef UCVISUAL
+var    bool    xHZ;            /* Hazeltine ~ braindamage */
+#endif
+var    bool    IN;             /* Insert-null blessing */
+var    bool    MI;             /* can move in insert mode */
+var    bool    NC;             /* No Cr - \r snds \r\n then eats \n (dm2500) */
+var    bool    NS;             /* No scroll - linefeed at bottom won't scroll */
+var    bool    OS;             /* Overstrike works */
+var    bool    UL;             /* Underlining works even though !os */
+var    bool    XB;             /* Beehive (no escape key, simulate with f1) */
+var    bool    XN;             /* A newline gets eaten after wrap (concept) */
+var    bool    XT;             /* Tabs are destructive */
+var    bool    XX;             /* Tektronix 4025 insert line */
+       /* X? is reserved for severely nauseous glitches */
+       /* If there are enough of these we may need bit masks! */
+
+/*
+ * From the tty modes...
+ */
+var    bool    NONL;           /* Terminal can't hack linefeeds doing a CR */
+#ifdef UCVISUAL
+var    bool    UPPERCASE;      /* Ick! */
+#endif
+extern short   TLINES;         /* Number of lines on screen */
+extern short   TCOLUMNS;
+var    short   OCOLUMNS;       /* Save TCOLUMNS for a hack in open mode */
+#ifdef TIOCGWINSZ
+var    struct winsize winsz;   /* Save window size for stopping comparisons */
+#endif
+
+var    short   outcol;         /* Where the cursor is */
+var    short   outline;
+
+var    short   destcol;        /* Where the cursor should be */
+var    short   destline;
+
+var    struct  termios tty;    /* Use this one structure to change modes */
+
+var    struct termios  normf;          /* Restore tty flags to this (someday) */
+var    bool    normtty;        /* Have to restore normal mode from normf */
+
+var    short   costCM; /* # chars to output a typical CM, with padding etc. */
+var    short   costSR; /* likewise for scroll reverse */
+var    short   costAL; /* likewise for insert line */
+var    short   costDP; /* likewise for DOWN_PARM */
+var    short   costLP; /* likewise for LEFT_PARM */
+var    short   costRP; /* likewise for RIGHT_PARM */
+
+#ifdef VMUNIX
+# define MAXNOMACS     128     /* max number of macros of each kind */
+# define MAXCHARMACS   2048    /* max # of chars total in macros */
+#else
+# define MAXNOMACS     48      /* max number of macros of each kind */
+# define MAXCHARMACS   1536    /* max # of chars total in macros */
+#endif
+struct maps {
+       char *cap;      /* pressing button that sends this.. */
+       int  *icap;     /* same as int */
+       char *mapto;    /* .. maps to this string */
+       char *descr;    /* legible description of key */
+       bool hadthis;   /* did this mapping already (avoid recursion) */
+};
+var    struct maps arrows[MAXNOMACS];  /* macro defs - 1st 5 built in */
+var    struct maps immacs[MAXNOMACS];  /* for while in insert mode */
+var    struct maps abbrevs[MAXNOMACS]; /* for word abbreviations */
+var    int     ldisc;                  /* line discipline for ucb tty driver */
+var    char    mapspace[MAXCHARMACS];
+var    int     imapspace[MAXCHARMACS];
+var    char    *msnext;        /* next free location in mapspace */
+var    int     *imsnext;       /* next free location in imapspace */
+var    int     maphopcnt;      /* check for infinite mapping loops */
+var    bool    anyabbrs;       /* true if abbr or unabbr has been done */
+var    char    ttynbuf[255];   /* result of ttyname() */
+var    int     ttymesg;        /* original mode of users tty */
+
+extern int map(register int, register struct maps *);
+extern void addmac1(register char *, register char *, register char *,
+               register struct maps *, int);
+#define        addmac(a, b, c, d)      addmac1(a, b, c, d, 0)
diff --git a/ex_tune.h b/ex_tune.h
new file mode 100644 (file)
index 0000000..2026cea
--- /dev/null
+++ b/ex_tune.h
@@ -0,0 +1,225 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+ *
+ *     from ex_tune.h  7.8.1 (2.11BSD) 1996/10/23
+ *
+ *     @(#)ex_tune.h   1.12 (gritter) 12/1/04
+ */
+
+/*
+ * Note: the parameters that are actually tuneable have been moved to
+ * config.h. Do not make changes here unless you know what you are
+ * doing! GR
+ */
+
+/*
+ * Definitions of editor parameters and limits
+ */
+
+/*
+ * Pathnames (will be predefined in Makefile).
+ */
+#ifndef        EXRECOVER
+#define        EXRECOVER       "/usr/sbin/exrecover"
+#endif
+#ifndef        EXPRESERVE
+#define        EXPRESERVE      "/usr/sbin/expreserve"
+#endif
+#ifndef VMUNIX
+#ifndef        EXSTRINGS
+#define        EXSTRINGS       "/usr/share/misc/exstrings"
+#endif
+#endif
+
+/*
+ * If your system believes that tabs expand to a width other than
+ * 8 then your makefile should cc with -DTABS=whatever, otherwise we use 8.
+ */
+#ifndef TABS
+#define        TABS    8
+#endif
+
+/*
+ * Maximums
+ *
+ * The definition of LBSIZE should be the same as BUFSIZ (512 usually).
+ * Most other definitions are quite generous.
+ */
+/* FNSIZE is also defined in expreserve.c */
+#ifdef _POSIX_PATH_MAX
+#define        FNSIZE          _POSIX_PATH_MAX
+#else
+#define        FNSIZE          128             /* File name size */
+#endif
+#ifdef VMUNIX
+#define        LBSIZE          BUFSIZ          /* Line buffer size */
+#ifndef        ESIZE   /* see config.h */
+#define        ESIZE           512             /* Regular expression buffer size */
+#endif
+#define CRSIZE         BUFSIZ          /* Crypt buffer size */
+#else  /* !VMUNIX */
+#ifdef u370
+#define LBSIZE         4096
+#ifndef        ESIZE   /* see config.h */
+#define ESIZE          512
+#endif
+#define CRSIZE         4096
+#else
+#define        LBSIZE          512             /* Line length */
+#ifndef        ESIZE   /* see config.h */
+#define        ESIZE           128             /* Size of compiled re */
+#endif
+#define CRSIZE         512
+#endif
+#endif
+#define        NBRA            9               /* Number of re \( \) pairs */
+#define        GBSIZE          256             /* Buffer size */
+#define        UXBSIZE         128             /* Unix command buffer size */
+#define        VBSIZE          128             /* Partial line max size in visual */
+/* LBLKS is also defined in expreserve.c */
+#ifndef VMUNIX
+#define        LBLKS           125             /* Line pointer blocks in temp file */
+#define        HBLKS           1               /* struct header fits in BUFSIZ*HBLKS */
+#else  /* VMUNIX */
+#ifdef LARGEF
+#define        LBLKS           20000
+#else  /* !LARGEF */
+#define        LBLKS           900
+#endif /* !LARGEF */
+#define        HBLKS           (1 + (FNSIZE + LBLKS * sizeof(bloc)) / BUFSIZ)
+#endif /* VMUNIX */
+#define        MAXDIRT         12              /* Max dirtcnt before sync tfile */
+
+/*
+ * Size of in-core buffers for temporary file. Since this is
+ * sizeof (char) * (INCORB + 1) * BUFSIZ, it should not be too
+ * large.
+ *
+ * If not defined, no in-core buffers are used.
+ */
+#ifdef VMUNIX
+#if    (BUFSIZ - 0) <= 16384
+#define        INCORB          (65536/BUFSIZ)
+#else  /* Huge-memory systems. */
+#define        INCORB          4
+#endif /* Huge-memory systems. */
+#endif /* VMUNIX */
+
+/*
+ * Except on VMUNIX, these are a ridiculously small due to the
+ * lousy arglist processing implementation which fixes core
+ * proportional to them.  Argv (and hence NARGS) is really unnecessary,
+ * and argument character space not needed except when
+ * arguments exist.  Argument lists should be saved before the "zero"
+ * of the incore line information and could then
+ * be reasonably large.
+ */
+#undef NCARGS
+#ifndef VMUNIX
+#define        NARGS   100             /* Maximum number of names in "next" */
+#define        NCARGS  LBSIZE          /* Maximum arglist chars in "next" */
+#else
+#define        NCARGS  5120
+#define        NARGS   (NCARGS/6)
+#endif
+
+/*
+ * Output column (and line) are set to this value on cursor addressible
+ * terminals when we lose track of the cursor to force cursor
+ * addressing to occur.
+ */
+#define        UKCOL           -20     /* Prototype unknown column */
+
+/*
+ * Attention is the interrupt character (normally 0177 -- delete).
+ * Quit is the quit signal (normally FS -- control-\) and quits open/visual.
+ */
+extern int     ATTN;
+#define        QUIT    ('\\' & 037)
+
+#define        LRGINT  INT_MAX         /* largest normal length positive integer */
+
+#ifdef LONG_BIT
+#if (LONG_BIT > 32)
+#define        MAXOCT  22              /* Maximum octal digits in a long */
+#define        BIG     10000000000000000000UL /* largest power of 10 < uns. long */
+#define        MAXDIGS 20              /* number of digits in BIG */
+#else  /* LONG_BIT <= 32 */
+#define        MAXOCT  11              /* Maximum octal digits in a long */
+#define        BIG     1000000000UL    /* largest power of 10 < unsigned long */
+#define        MAXDIGS 10              /* number of digits in BIG */
+#endif /* LONG_BIT <= 32 */
+#else  /* !LONG_BIT */
+#define        MAXOCT  11              /* Maximum octal digits in a long */
+#define        BIG     1000000000      /* largest power of 10 < unsigned long */
+#define        MAXDIGS 10              /* number of digits in BIG */
+#endif /* !LONG_BIT */
diff --git a/ex_unix.c b/ex_unix.c
new file mode 100644 (file)
index 0000000..4be0e67
--- /dev/null
+++ b/ex_unix.c
@@ -0,0 +1,450 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_unix.c  1.16 (gritter) 11/23/04";
+#endif
+#endif
+
+/* from ex_unix.c      7.6 (Berkeley) 10/22/85 */
+
+#include "ex.h"
+#include "ex_temp.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+#include <sys/wait.h>
+
+/*
+ * Unix escapes, filtering
+ */
+
+/*
+ * First part of a shell escape,
+ * parse the line, expanding # and % and ! and printing if implied.
+ */
+void 
+unix0(int warn)
+{
+       register char *up, *fp;
+       register short c;
+       char printub, puxb[UXBSIZE + sizeof (int)];
+
+       printub = 0;
+       CP(puxb, uxb);
+       c = getchar();
+       if (c == '\n' || c == EOF)
+               error(catgets(catd, 1, 192,
+       "Incomplete shell escape command@- use 'shell' to get a shell"));
+       up = uxb;
+       do {
+               switch (c) {
+
+               case '\\':
+                       if (any(peekchar(), "%#!"))
+                               c = getchar();
+               default:
+                       if (up >= &uxb[UXBSIZE]) {
+tunix:
+                               uxb[0] = 0;
+                               error(catgets(catd, 1, 193,
+                                                       "Command too long"));
+                       }
+                       *up++ = c;
+                       break;
+
+               case '!':
+                       fp = puxb;
+                       if (*fp == 0) {
+                               uxb[0] = 0;
+                               error(catgets(catd, 1, 194,
+                       "No previous command@to substitute for !"));
+                       }
+                       printub++;
+                       while (*fp) {
+                               if (up >= &uxb[UXBSIZE])
+                                       goto tunix;
+                               *up++ = *fp++;
+                       }
+                       break;
+
+               case '#':
+                       fp = altfile;
+                       if (*fp == 0) {
+                               uxb[0] = 0;
+                               error(catgets(catd, 1, 195,
+                               "No alternate filename@to substitute for #"));
+                       }
+                       goto uexp;
+
+               case '%':
+                       fp = savedfile;
+                       if (*fp == 0) {
+                               uxb[0] = 0;
+                               error(catgets(catd, 1, 196,
+                                       "No filename@to substitute for %%"));
+                       }
+uexp:
+                       printub++;
+                       while (*fp) {
+                               if (up >= &uxb[UXBSIZE])
+                                       goto tunix;
+#ifndef        BIT8
+                               *up++ = *fp++ | QUOTE;
+#else
+                               *up++ = *fp++;
+#endif
+                       }
+                       break;
+               }
+               c = getchar();
+       } while (c == '"' || c == '|' || !endcmd(c));
+       if (c == EOF)
+               ungetchar(c);
+       *up = 0;
+       if (!inopen)
+               resetflav();
+       if (warn)
+               ckaw();
+       if (warn && hush == 0 && chng && xchng != chng && value(WARN) && dol > zero) {
+               xchng = chng;
+               vnfl();
+               printf(mesg(catgets(catd, 1, 197,
+                               "[No write]|[No write since last change]")));
+               noonl();
+               flush();
+       } else
+               warn = 0;
+       if (printub) {
+               if (uxb[0] == 0)
+                       error(catgets(catd, 1, 198,
+                                       "No previous command@to repeat"));
+               if (inopen) {
+                       splitw++;
+                       vclean();
+                       vgoto(WECHO, 0);
+               }
+               if (warn)
+                       vnfl();
+               if (hush == 0)
+                       lprintf("!%s", uxb);
+               if (inopen && Outchar != termchar) {
+                       vclreol();
+                       vgoto(WECHO, 0);
+               } else
+                       putnl();
+               flush();
+       }
+}
+
+/*
+ * Do the real work for execution of a shell escape.
+ * Mode is like the number passed to open system calls
+ * and indicates filtering.  If input is implied, newstdin
+ * must have been setup already.
+ */
+struct termios 
+unixex(char *opt, char *up, int newstdin, int mode)
+{
+       int pvec[2];
+       struct termios f;
+
+       signal(SIGINT, SIG_IGN);
+#ifdef SIGTSTP
+       if (dosusp)
+               signal(SIGTSTP, SIG_DFL);
+#endif
+       if (inopen)
+               f = setty(normf);
+       if ((mode & 1) && pipe(pvec) < 0) {
+               /* Newstdin should be io so it will be closed */
+               if (inopen)
+                       setty(f);
+               error(catgets(catd, 1, 199, "Can't make pipe for filter"));
+       }
+#ifndef VFORK
+       pid = fork();
+#else
+       pid = vfork();
+#endif
+       if (pid < 0) {
+               if (mode & 1) {
+                       close(pvec[0]);
+                       close(pvec[1]);
+               }
+               setrupt();
+               error(catgets(catd, 1, 200, "No more processes"));
+       }
+       if (pid == 0) {
+               if (mode & 2) {
+                       close(0);
+                       dup(newstdin);
+                       close(newstdin);
+               }
+               if (mode & 1) {
+                       close(pvec[0]);
+                       close(1);
+                       dup(pvec[1]);
+                       if (inopen) {
+                               close(2);
+                               dup(1);
+                       }
+                       close(pvec[1]);
+               }
+               if (io)
+                       close(io);
+               if (tfile)
+                       close(tfile);
+#ifndef VMUNIX
+               close(erfile);
+#endif
+               signal(SIGHUP, oldhup);
+               signal(SIGQUIT, oldquit);
+#ifdef SIGXFSZ
+               signal(SIGXFSZ, oldxfsz);
+#endif
+               if (ruptible)
+                       signal(SIGINT, SIG_DFL);
+               execl(svalue(SHELL), "sh", opt, up, (char *)0);
+               printf(catgets(catd, 1, 201, "No %s!\n"), svalue(SHELL));
+               error(NOSTR);
+       }
+       if (mode & 1) {
+               io = pvec[0];
+               close(pvec[1]);
+       }
+       if (newstdin)
+               close(newstdin);
+       return (f);
+}
+
+/*
+ * Wait for the command to complete.
+ * F is for restoration of tty mode if from open/visual.
+ * C flags suppression of printing.
+ */
+void 
+unixwt(int c, struct termios f)
+{
+
+       waitfor();
+#ifdef SIGTSTP
+       if (dosusp)
+               signal(SIGTSTP, onsusp);
+#endif
+       if (inopen)
+               setty(f);
+       setrupt();
+       if (!inopen && c && hush == 0) {
+               printf("!\n");
+               flush();
+               termreset();
+               gettmode();
+       }
+}
+
+/*
+ * Setup a pipeline for the filtration implied by mode
+ * which is like a open number.  If input is required to
+ * the filter, then a child editor is created to write it.
+ * If output is catch it from io which is created by unixex.
+ */
+void 
+filter(register int mode)
+{
+       static int pvec[2];
+       struct termios f;       /* mjm: was register */
+       register int lines = lineDOL();
+       struct stat statb;
+
+       mode++;
+       if (mode & 2) {
+               signal(SIGINT, SIG_IGN);
+               if (pipe(pvec) < 0)
+                       error(catgets(catd, 1, 202, "Can't make pipe"));
+               pid = fork();
+               io = pvec[0];
+               if (pid < 0) {
+                       setrupt();
+                       close(pvec[1]);
+                       error(catgets(catd, 1, 203, "No more processes"));
+               }
+               if (pid == 0) {
+                       setrupt();
+                       io = pvec[1];
+                       close(pvec[0]);
+                       putfile(1);
+                       exitex(0);
+               }
+               close(pvec[1]);
+               io = pvec[0];
+               setrupt();
+       }
+       f = unixex("-c", uxb, (mode & 2) ? pvec[0] : 0, mode);
+       if (mode == 3) {
+               delete(0);
+               addr2 = addr1 - 1;
+       } else if (mode == 1)
+               deletenone();
+       if (mode & 1) {
+               if(FIXUNDO)
+                       undap1 = undap2 = addr2+1;
+               if (fstat(io, &statb) < 0 || statb.st_blksize > LBSIZE)
+                       bsize = LBSIZE;
+               else {
+                       bsize = statb.st_blksize;
+                       if (bsize <= 0)
+                               bsize = LBSIZE;
+               }
+               ignore(append(getfile, addr2));
+#ifdef TRACE
+               if (trace)
+                       vudump("after append in filter");
+#endif
+       }
+       close(io);
+       io = -1;
+       unixwt(!inopen, f);
+       netchHAD(lines);
+}
+
+/*
+ * Set up to do a recover, getting io to be a pipe from
+ * the recover process.
+ */
+void 
+recover(void)
+{
+       static int pvec[2];
+
+       if (pipe(pvec) < 0)
+               error(catgets(catd, 1, 204, " Can't make pipe for recovery"));
+       pid = fork();
+       io = pvec[0];
+       if (pid < 0) {
+               close(pvec[1]);
+               error(catgets(catd, 1, 205, " Can't fork to execute recovery"));
+       }
+       if (pid == 0) {
+               close(2);
+               dup(1);
+               close(1);
+               dup(pvec[1]);
+               close(pvec[1]);
+               execl(EXRECOVER, "exrecover", svalue(DIRECTORY), file, (char *) 0);
+               close(1);
+               dup(2);
+               error(catgets(catd, 1, 206, " No recovery routine"));
+       }
+       close(pvec[1]);
+}
+
+/*
+ * Wait for the process (pid an external) to complete.
+ */
+void 
+waitfor(void)
+{
+       int stat = 0;
+       pid_t wpid;
+
+       do {
+               wpid = wait(&stat);
+               if (wpid == pid) {
+                       status = stat;
+                       rpid = wpid;
+               }
+       } while (wpid != -1);
+       if (status) {
+               if (WIFEXITED(status))
+                       status = WEXITSTATUS(status);
+               else
+                       status = 0;
+       }
+}
+
+/*
+ * The end of a recover operation.  If the process
+ * exits non-zero, force not edited; otherwise force
+ * a write.
+ */
+void 
+revocer(void)
+{
+
+       waitfor();
+       if (pid == rpid && status != 0)
+               edited = 0;
+       else
+               change();
+}
diff --git a/ex_v.c b/ex_v.c
new file mode 100644 (file)
index 0000000..fe984ee
--- /dev/null
+++ b/ex_v.c
@@ -0,0 +1,527 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_v.c     1.17 (gritter) 11/27/04";
+#endif
+#endif
+
+/* from ex_v.c 7.8.1 (2.11BSD GTE) 12/9/94 */
+
+#include "ex.h"
+#include "ex_re.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+
+/*
+ * Entry points to open and visual from command mode processor.
+ * The open/visual code breaks down roughly as follows:
+ *
+ * ex_v.c      entry points, checking of terminal characteristics
+ *
+ * ex_vadj.c   logical screen control, use of intelligent operations
+ *             insert/delete line and coordination with screen image;
+ *             updating of screen after changes.
+ *
+ * ex_vget.c   input of single keys and reading of input lines
+ *             from the echo area, handling of memory for repeated
+ *             commands and small saved texts from inserts and partline
+ *             deletes, notification of multi line changes in the echo
+ *             area.
+ *
+ * ex_vmain.c  main command decoding, some command processing.
+ *
+ * ex_voperate.c   decoding of operator/operand sequences and
+ *             contextual scans, implementation of word motions.
+ *
+ * ex_vops.c   major operator interfaces, undos, motions, deletes,
+ *             changes, opening new lines, shifts, replacements and yanks
+ *             coordinating logical and physical changes.
+ *
+ * ex_vops2.c  subroutines for operator interfaces in ex_vops.c,
+ *             insert mode, read input line processing at lowest level.
+ *
+ * ex_vops3.c  structured motion definitions of ( ) { } and [ ] operators,
+ *             indent for lisp routines, () and {} balancing. 
+ *
+ * ex_vput.c   output routines, clearing, physical mapping of logical cursor
+ *             positioning, cursor motions, handling of insert character
+ *             and delete character functions of intelligent and unintelligent
+ *             terminals, visual mode tracing routines (for debugging),
+ *             control of screen image and its updating.
+ *
+ * ex_vwind.c  window level control of display, forward and backward rolls,
+ *             absolute motions, contextual displays, line depth determination
+ */
+
+JMP_BUF venv;
+
+/*
+ * Enter open mode
+ */
+#ifdef u370
+cell   atube[TUBESIZE+LBSIZE];
+#endif
+void 
+oop(void)
+{
+       register char *ic;
+#ifndef u370
+       cell atube[TUBESIZE + LBSIZE];
+#endif
+       struct termios f;       /* mjm: was register */
+       int resize;
+
+       resize = SETJMP(venv);
+       if (resize) {
+               setsize();
+               initev = (char *)0;
+               inopen = 0;
+               addr1 = addr2 = dot;
+       }
+#ifdef SIGWINCH
+       signal(SIGWINCH, onwinch);
+#endif
+       ovbeg();
+       if (peekchar() == '/') {
+               ignore(compile(getchar(), 1));
+               savere(&scanre);
+               if (execute(0, dot) == 0)
+                       error(catgets(catd, 1, 207,
+                               "Fail|Pattern not found on addressed line"));
+               ic = loc1;
+               if (ic > linebuf && *ic == 0)
+                       ic--;
+       } else {
+               getDOT();
+               ic = vskipwh(linebuf);
+       }
+       newline();
+
+       /*
+        * If overstrike then have to HARDOPEN
+        * else if can move cursor up off current line can use CRTOPEN (~~vi1)
+        * otherwise (ugh) have to use ONEOPEN (like adm3)
+        */
+       if (OS && !EO)
+               bastate = HARDOPEN;
+       else if (CA || UP)
+               bastate = CRTOPEN;
+       else
+               bastate = ONEOPEN;
+       setwind();
+
+       /*
+        * To avoid bombing on glass-crt's when the line is too long
+        * pretend that such terminals are 160 columns wide.
+        * If a line is too wide for display, we will dynamically
+        * switch to hardcopy open mode.
+        */
+       if (state != CRTOPEN)
+               WCOLS = TUBECOLS;
+       if (!inglobal)
+               savevis();
+       vok(atube);
+       if (state != CRTOPEN)
+               TCOLUMNS = WCOLS;
+       Outchar = vputchar;
+       f = ostart();
+       if (state == CRTOPEN) {
+               if (outcol == UKCOL)
+                       outcol = 0;
+               vmoveitup(1, 1);
+       } else
+               outline = destline = WBOT;
+       vshow(dot, NOLINE);
+       vnline(ic);
+       vmain();
+       if (state != CRTOPEN)
+               vclean();
+       Command = "open";
+       ovend(f);
+#ifdef SIGWINCH
+       signal(SIGWINCH, SIG_DFL);
+#endif
+}
+
+void 
+ovbeg(void)
+{
+
+       if (!value(OPEN))
+               error(catgets(catd, 1, 208,
+                       "Can't use open/visual unless open option is set"));
+       if (inopen)
+               error(catgets(catd, 1, 209,
+                                       "Recursive open/visual not allowed"));
+       Vlines = lineDOL();
+       fixzero();
+       setdot();
+       pastwh();
+       dot = addr2;
+}
+
+void 
+ovend(struct termios f)
+{
+
+       splitw++;
+       vgoto(WECHO, 0);
+       vclreol();
+       vgoto(WECHO, 0);
+       holdcm = 0;
+       splitw = 0;
+       ostop(f);
+       setoutt();
+       undvis();
+       TCOLUMNS = OCOLUMNS;
+       inopen = 0;
+       flusho();
+       netchHAD(Vlines);
+}
+
+/*
+ * Enter visual mode
+ */
+void 
+vop(void)
+{
+       register int c;
+#ifndef u370
+       cell atube[TUBESIZE + LBSIZE];
+#endif
+       struct termios f;       /* mjm: was register */
+       int resize;
+
+       if (!CA && UP == NOSTR) {
+               if (initev) {
+toopen:
+                       merror(catgets(catd, 1, 210, "[Using open mode]"));
+                       putNFL();
+                       oop();
+                       return;
+               }
+               error(catgets(catd, 1, 211,
+               "Visual needs addressible cursor or upline capability"));
+       }
+       if (OS && !EO) {
+               if (initev)
+                       goto toopen;
+               error(catgets(catd, 1, 212,
+                       "Can't use visual on a terminal which overstrikes"));
+       }
+       if (!CL) {
+               if (initev)
+                       goto toopen;
+               error(catgets(catd, 1, 213,
+                       "Visual requires clear screen capability"));
+       }
+       if (NS && !SF) {
+               if (initev)
+                       goto toopen;
+               error(catgets(catd, 1, 214, "Visual requires scrolling"));
+       }
+       resize = SETJMP(venv);
+       if (resize) {
+               setsize();
+               initev = (char *)0;
+               inopen = 0;
+               addr1 = addr2 = dot;
+       }
+#ifdef SIGWINCH
+       signal(SIGWINCH, onwinch);
+#endif
+       ovbeg();
+       bastate = VISUAL;
+       c = 0;
+       if (any(peekchar(), "+-^."))
+               c = getchar();
+       pastwh();
+       vsetsiz(isdigit(peekchar()) ? getnum() : value(WINDOW));
+       setwind();
+       newline();
+       vok(atube);
+       if (!inglobal)
+               savevis();
+       Outchar = vputchar;
+       vmoving = 0;
+       f = ostart();
+       if (initev == 0) {
+               vcontext(dot, c);
+               vnline(NOSTR);
+       }
+       vmain();
+       Command = "visual";
+       ovend(f);
+#ifdef SIGWINCH
+       signal(SIGWINCH, SIG_DFL);
+#endif
+}
+
+/*
+ * Hack to allow entry to visual with
+ * empty buffer since routines internally
+ * demand at least one line.
+ */
+void 
+fixzero(void)
+{
+
+       if (dol == zero) {
+               register bool ochng = chng;
+
+               vdoappend("");
+               if (!ochng)
+                       synced();
+               fixedzero++;
+               addr1 = addr2 = one;
+       } else if (addr2 == zero)
+               addr2 = one;
+}
+
+/*
+ * Save lines before visual between unddol and truedol.
+ * Accomplish this by throwing away current [unddol,truedol]
+ * and then saving all the lines in the buffer and moving
+ * unddol back to dol.  Don't do this if in a global.
+ *
+ * If you do
+ *     g/xxx/vi.
+ * and then do a
+ *     :e xxxx
+ * at some point, and then quit from the visual and undo
+ * you get the old file back.  Somewhat weird.
+ */
+void 
+savevis(void)
+{
+
+       if (inglobal)
+               return;
+       truedol = unddol;
+       saveall();
+       unddol = dol;
+       undkind = UNDNONE;
+}
+
+/*
+ * Restore a sensible state after a visual/open, moving the saved
+ * stuff back to [unddol,dol], and killing the partial line kill indicators.
+ */
+void 
+undvis(void)
+{
+
+       if (ruptible)
+               signal(SIGINT, onintr);
+       squish();
+       pkill[0] = pkill[1] = 0;
+       unddol = truedol;
+       unddel = zero;
+       undap1 = one;
+       undap2 = dol + 1;
+       undkind = UNDALL;
+       if (undadot <= zero || undadot > dol)
+               undadot = zero+1;
+}
+
+/*
+ * Set the window parameters based on the base state bastate
+ * and the available buffer space.
+ */
+void 
+setwind(void)
+{
+
+       WCOLS = TCOLUMNS;
+       switch (bastate) {
+
+       case ONEOPEN:
+               if (AM)
+                       WCOLS--;
+               /* fall into ... */
+
+       case HARDOPEN:
+               basWTOP = WTOP = WBOT = WECHO = 0;
+               ZERO = 0;
+               holdcm++;
+               break;
+
+       case CRTOPEN:
+               basWTOP = TLINES - 2;
+               /* fall into */
+
+       case VISUAL:
+               ZERO = TLINES - TUBESIZE / WCOLS;
+               if (ZERO < 0)
+                       ZERO = 0;
+               if (ZERO > basWTOP)
+                       error(catgets(catd, 1, 215,
+                               "Screen too large for internal buffer"));
+               WTOP = basWTOP; WBOT = TLINES - 2; WECHO = TLINES - 1;
+               break;
+       }
+       state = bastate;
+       basWLINES = WLINES = WBOT - WTOP + 1;
+}
+
+/*
+ * Can we hack an open/visual on this terminal?
+ * If so, then divide the screen buffer up into lines,
+ * and initialize a bunch of state variables before we start.
+ */
+void 
+vok(register cell *atube)
+{
+       register int i;
+
+       if (WCOLS == 1000)
+               serror(catgets(catd, 1, 216,
+               "Don't know enough about your terminal to use %s"), Command);
+       if (WCOLS > TUBECOLS)
+               error(catgets(catd, 1, 217, "Terminal too wide"));
+       if (WLINES >= TUBELINES || WCOLS * (WECHO - ZERO + 1) > TUBESIZE)
+               error(catgets(catd, 1, 218, "Screen too large"));
+
+       vtube0 = atube;
+       vclrcell(atube, WCOLS * (WECHO - ZERO + 1));
+       for (i = 0; i < ZERO; i++)
+               vtube[i] = (cell *) 0;
+       for (; i <= WECHO; i++)
+               vtube[i] = atube, atube += WCOLS;
+       for (; i < TUBELINES; i++)
+               vtube[i] = (cell *) 0;
+       vutmp = (char *)atube;
+       vundkind = VNONE;
+       vUNDdot = 0;
+       OCOLUMNS = TCOLUMNS;
+       inopen = 1;
+       signal(SIGINT, vintr);
+       vmoving = 0;
+       splitw = 0;
+       doomed = 0;
+       holdupd = 0;
+       Peekkey = 0;
+       vcnt = vcline = 0;
+       if (vSCROLL == 0)
+               vSCROLL = value(SCROLL);
+               /*old vSCROLL = (value(WINDOW)+1)/2;*//* round up so dft=6,11 */
+}
+
+void 
+vintr(int signum)
+{
+       extern JMP_BUF readbuf;
+       extern int doingread;
+
+       signal(SIGINT, vintr);
+       if (vcatch)
+               onintr(SIGINT);
+       ungetkey(ATTN);
+       draino();
+       if (doingread) {
+               doingread = 0;
+               LONGJMP(readbuf, 1);
+       }
+}
+
+/*
+ * Set the size of the screen to size lines, to take effect the
+ * next time the screen is redrawn.
+ */
+void
+vsetsiz(int size)
+{
+       register int b;
+
+       if (bastate != VISUAL)
+               return;
+       b = TLINES - 1 - size;
+       if (b >= TLINES - 1)
+               b = TLINES - 2;
+       if (b < 0)
+               b = 0;
+       basWTOP = b;
+       basWLINES = WBOT - b + 1;
+}
+
+#ifdef SIGWINCH
+void 
+onwinch(int signum)
+{
+       vsave();
+       setty(normf);
+       LONGJMP(venv, 1);
+}
+#endif
diff --git a/ex_vadj.c b/ex_vadj.c
new file mode 100644 (file)
index 0000000..0bb62c7
--- /dev/null
+++ b/ex_vadj.c
@@ -0,0 +1,1162 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_vadj.c  1.11 (gritter) 3/4/05";
+#endif
+#endif
+
+/* from ex_vadj.c      7.9 (Berkeley) 6/7/85 */
+
+#include "ex.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+
+/*
+ * Routines to deal with management of logical versus physical
+ * display, opening and redisplaying lines on the screen, and
+ * use of intelligent terminal operations.  Routines to deal with
+ * screen cleanup after a change.
+ */
+
+/*
+ * Display a new line at physical line p, returning
+ * the depth of the newly displayed line.  We may decide
+ * to expand the window on an intelligent terminal if it is
+ * less than a full screen by deleting a line above the top of the
+ * window before doing an insert line to keep all the good text
+ * on the screen in which case the line may actually end up
+ * somewhere other than line p.
+ */
+void 
+vopen(line *tp, int p)
+{
+       register int cnt;
+       register struct vlinfo *vp, *vpc;
+
+#ifdef ADEBUG
+       if (trace != NULL)
+               tfixnl(), fprintf(trace, "vopen(%d, %d)\n", lineno(tp), p);
+#endif
+       if (state != VISUAL) {
+               if (vcnt)
+                       if (hold & HOLDROL)
+                               vup1();
+                       else
+                               vclean();
+
+               /*
+                * Forget all that we once knew.
+                */
+               vcnt = vcline = 0;
+               p = WBOT; LASTLINE = WBOT + 1;
+               state = bastate;
+               WTOP = basWTOP;
+               WLINES = basWLINES;
+       }
+       vpc = &vlinfo[vcline];
+       for (vp = &vlinfo[vcnt]; vp >= vpc; vp--)
+               vlcopy(vp[1], vp[0]);
+       vcnt++;
+       if (Pline == numbline)
+               /*
+                * Dirtying all the lines is rather inefficient
+                * internally, but number mode is used rarely
+                * and so its not worth optimizing.
+                */
+               vdirty(vcline+1, WECHO);
+       getline(*tp);
+
+       /*
+        * If we are opening at the top of the window, can try a window
+        * expansion at the top.
+        */
+       if (state == VISUAL && vcline == 0 && vcnt > 1 && p > ZERO) {
+               cnt = p + vdepth() - LINE(1);
+               if (cnt > 0) {
+                       p -= cnt;
+                       if (p < ZERO)
+                               p = ZERO;
+                       WTOP = p;
+                       WLINES = WBOT - WTOP + 1;
+               }
+       }
+       vpc->vliny = p, vpc->vdepth = 0, vpc->vflags = 0;
+       cnt = vreopen(p, lineno(tp), vcline);
+       if (vcline + 1 == vcnt)
+               LINE(vcnt) = LINE(vcline) + cnt;
+}
+
+/*
+ * Redisplay logical line l at physical line p with line number lineno.
+ */
+int 
+vreopen(int p, int lineno, int l)
+{
+       register int d;
+       register struct vlinfo *vp = &vlinfo[l];
+
+       if (p < 0)
+               error("Line too long to fit on screen");
+       d = vp->vdepth;
+       if (d == 0 || (vp->vflags & VDIRT))
+               vp->vdepth = d = vdepth();
+       vp->vliny = p, vp->vflags &= ~VDIRT;
+
+       /*
+        * Try to win by making the screen larger rather than inserting
+        * a line and driving text off the bottom.
+        */
+       p = vglitchup(l, 0);
+
+       /*
+        * BUG:         Should consider using CE here to clear to end of line.
+        *              As it stands we always strike over the current text.
+        *              Since often the current text is the same as what
+        *              we are overstriking with, it tends not to show.
+        *              On the other hand if it is different and we end up
+        *              spacing out a lot of text, we could have won with
+        *              a CE.  This is probably worthwhile at low speed
+        *              only however, since clearly computation will be
+        *              necessary to determine which way to go.
+        */
+       vigoto(p, 0);
+       pline(lineno);
+
+       /*
+        * When we are typing part of a line for hardcopy open, don't
+        * want to type the '$' marking an end of line if in list mode.
+        */
+       if (hold & HOLDDOL)
+               return (d);
+       if (Putchar == listchar)
+               putchar('$');
+
+       /*
+        * Optimization of cursor motion may prevent screen rollup if the
+        * line has blanks/tabs at the end unless we force the cursor to appear
+        * on the last line segment.
+        */
+       if (vp->vliny + d - 1 > WBOT)
+               vcsync();
+
+       /*
+        * Switch into hardcopy open mode if we are in one line (adm3)
+        * open mode and this line is now too long.  If in hardcopy
+        * open mode, then call sethard to move onto the next line
+        * with appropriate positioning.
+        */
+       if (state == ONEOPEN) {
+               WCOLS = OCOLUMNS;
+               if (vdepth() > 1) {
+                       WCOLS = TUBECOLS;
+                       sethard();
+               } else
+                       WCOLS = TUBECOLS;
+       } else if (state == HARDOPEN)
+               sethard();
+
+       /*
+        * Unless we filled (completely) the last line we typed on,
+        * we have to clear to the end of the line
+        * in case stuff is left from before.
+        */
+       if (vp->vliny + d > destline) {
+               if (IN && destcol == WCOLS)
+                       vigoto(vp->vliny + d - 1, 0);
+               vclreol();
+       }
+       return (d);
+}
+
+/*
+ * Real work for winning growing of window at top
+ * when inserting in the middle of a partially full
+ * screen on an intelligent terminal.  We have as argument
+ * the logical line number to be inserted after, and the offset
+ * from that line where the insert will go.
+ * We look at the picture of depths and positions, and if we can
+ * delete some (blank) lines from the top of the screen so that
+ * later inserts will not push stuff off the bottom.
+ */
+int 
+vglitchup(int l, int o)
+{
+       register struct vlinfo *vp = &vlinfo[l];
+       register int need;
+       register int p = vp->vliny;
+       short oldhold = 0, oldheldech = 0;
+       bool glitched = 0;
+
+       if (l < vcnt - 1) {
+               need = p + vp->vdepth - (vp+1)->vliny;
+               if (need > 0) {
+                       if (state == VISUAL && WTOP - ZERO >= need && AL && DL) {
+                               glitched++;
+                               WTOP -= need;
+                               WLINES = WBOT - WTOP + 1;
+                               p -= need;
+                               if (p + o == WTOP) {
+                                       vp->vliny = WTOP;
+                                       return (WTOP + o);
+                               }
+                               vdellin(WTOP, need, -1);
+                               oldheldech = heldech;
+                               oldhold = hold;
+                               hold |= HOLDECH;
+                       }
+                       vinslin((vp+1)->vliny, need, l);
+                       if (glitched) {
+                               hold = oldhold;
+                               heldech = oldheldech;
+                       }
+               }
+       } else
+               vp[1].vliny = vp[0].vliny + vp->vdepth;
+       return (p + o);
+}
+
+/*
+ * Insert cnt blank lines before line p,
+ * logically and (if supported) physically.
+ */
+void 
+vinslin(register int p, register int cnt, int l)
+{
+       register int i;
+       bool could = 1;
+
+#ifdef ADEBUG
+       if (trace)
+               tfixnl(), fprintf(trace, "vinslin(%d, %d, %d)\n", p, cnt, l);
+#endif
+       if (p + cnt > WBOT && CD) {
+               /*
+                * Really quick -- clear to end of screen.
+                */
+               cnt = WECHO + 1 - p;
+               vgoto(p, 0), vputp(CD, cnt);
+               vclrech(1);
+               vadjAL(p, cnt);
+       } else if (SR && p == WTOP && costSR < costAL) {
+               /*
+                * Use reverse scroll mode of the terminal, at
+                * the top of the window.  Reverse linefeed works
+                * too, since we only use it from line WTOP.
+                */
+               for (i = cnt; i > 0; i--) {
+                       vgoto(p, 0), vputp(SR, 0);
+                       if (i > 1 && (hold & HOLDAT) == 0)
+                               putchar('@');
+                       /*
+                        * If we are at the top of the screen, and the
+                        * terminal retains display above, then we
+                        * should try to clear to end of line.
+                        * Have to use CE since we don't remember what is
+                        * actually on the line.
+                        */
+                       if (CE && (DA || p != 0))
+                               vputp(CE, 1);
+               }
+               vadjAL(p, cnt);
+       } else if (AL) {
+               /*
+                * Use insert line.
+                */
+               vgoto(p, 0);
+               if (AL_PARM && (cnt>1 || *AL==0)) {
+                       /* insert cnt lines.  Should do @'s too. */
+                       vputp(tgoto(AL_PARM, p, cnt), WECHO+1-p);
+               }
+               else if (xCS && *AL==0) {
+                       /* vt100 change scrolling region to fake AL */
+                       vputp(SC, 1);
+                       vputp(tgoto(xCS, TLINES-1,p), 1);
+                       vputp(RC, 1);   /* xCS homes stupid cursor */
+                       for (i=cnt; i>0; i--)
+                               vputp(SR, 1);   /* should do @'s */
+                       vputp(tgoto(xCS, TLINES-1,0), 1);
+                       vputp(RC, 1);   /* Once again put it back */
+               }
+               else {
+                       vputp(AL, WECHO + 1 - p);
+                       for (i = cnt - 1; i > 0; i--) {
+                               vgoto(outline+1, 0);
+                               vputp(AL, WECHO + 1 - outline);
+                               if ((hold & HOLDAT) == 0)
+                                       putchar('@');
+                       }
+               }
+               vadjAL(p, cnt);
+       } else
+               could = 0;
+       vopenup(cnt, could, l);
+}
+
+/*
+ * Logically open up after line l, cnt of them.
+ * We need to know if it was done ``physically'' since in this
+ * case we accept what the hardware gives us.  If we have to do
+ * it ourselves (brute force) we will squish out @ lines in the process
+ * if this will save us work.
+ */
+void 
+vopenup(int cnt, int could, int l)
+{
+       register struct vlinfo *vc = &vlinfo[l + 1];
+       register struct vlinfo *ve = &vlinfo[vcnt];
+
+#ifdef ADEBUG
+       if (trace)
+               tfixnl(), fprintf(trace, "vopenup(%d, %d, %d)\n", cnt, could, l);
+#endif
+       if (could)
+               /*
+                * This will push @ lines down the screen,
+                * just as the hardware did.  Since the default
+                * for intelligent terminals is to never have @
+                * lines on the screen, this should never happen,
+                * and the code makes no special effort to be nice in this
+                * case, e.g. squishing out the @ lines by delete lines
+                * before doing append lines.
+                */
+               for (; vc <= ve; vc++)
+                       vc->vliny += cnt;
+       else {
+               /*
+                * Will have to clean up brute force eventually,
+                * so push the line data around as little as possible.
+                */
+               vc->vliny += cnt, vc->vflags |= VDIRT;
+               while (vc < ve) {
+                       register int i = vc->vliny + vc->vdepth;
+
+                       vc++;
+                       if (i <= vc->vliny)
+                               break;
+                       vc->vliny = i, vc->vflags |= VDIRT;
+               }
+       }
+       vscrap();
+}
+
+/*
+ * Adjust data structure internally to account for insertion of
+ * blank lines on the screen.
+ */
+void 
+vadjAL(int p, int cnt)
+{
+       cell *tlines[TUBELINES];
+       register int from, to;
+
+#ifdef ADEBUG
+       if (trace)
+               tfixnl(), fprintf(trace, "vadjal(%d, %d)\n", p, cnt);
+#endif
+       copy(tlines, vtube, sizeof vtube);      /*SASSIGN*/
+       for (from = p, to = p + cnt; to <= WECHO; from++, to++)
+               vtube[to] = tlines[from];
+       for (to = p; from <= WECHO; from++, to++) {
+               vtube[to] = tlines[from];
+               vclrcell(vtube[to], WCOLS);
+       }
+       /*
+        * Have to clear the echo area since its contents aren't
+        * necessarily consistent with the rest of the display.
+        */
+       vclrech(0);
+}
+
+/*
+ * Roll the screen up logically and physically
+ * so that line dl is the bottom line on the screen.
+ */
+void 
+vrollup(int dl)
+{
+       register int cnt;
+       register int dc = destcol;
+
+#ifdef ADEBUG
+       if (trace)
+               tfixnl(), fprintf(trace, "vrollup(%d)\n", dl);
+#endif
+       cnt = dl - (splitw ? WECHO : WBOT);
+       if (splitw && (state == VISUAL || state == CRTOPEN))
+               holdupd = 1;
+       vmoveitup(cnt, 1);
+       vscroll(cnt);
+       destline = dl - cnt, destcol = dc;
+}
+
+void 
+vup1(void)
+{
+
+       vrollup(WBOT + 1);
+}
+
+/*
+ * Scroll the screen up cnt lines physically.
+ * If doclr is true, do a clear eol if the terminal
+ * has standout (to prevent it from scrolling up)
+ */
+void 
+vmoveitup(register int cnt, int doclr)
+{
+
+       if (cnt == 0)
+               return;
+#ifdef ADEBUG
+       if (trace)
+               tfixnl(), fprintf(trace, "vmoveitup(%d)\n", cnt);
+#endif
+       if (doclr && (SO || SE))
+               vclrech(0);
+       if (SF) {
+               destline = WECHO;
+               destcol = (NONL ? 0 : outcol % WCOLS);
+               fgoto();
+               while (cnt > 0)
+                       vputp(SF, 0), cnt--;
+               return;
+       }
+       destline = WECHO + cnt;
+       destcol = (NONL ? 0 : outcol % WCOLS);
+       fgoto();
+       if (state == ONEOPEN || state == HARDOPEN) {
+               outline = destline = 0;
+               vclrcell(vtube[0], WCOLS);
+       }
+}
+
+/*
+ * Scroll the screen up cnt lines logically.
+ */
+void 
+vscroll(register int cnt)
+{
+       register int from, to;
+       cell *tlines[TUBELINES];
+
+#ifdef ADEBUG
+       if (trace)
+               fprintf(trace, "vscroll(%d)\n", cnt);
+#endif
+       if (cnt < 0 || cnt > TUBELINES)
+               error(catgets(catd, 1, 219, "Internal error: vscroll"));
+       if (cnt == 0)
+               return;
+       copy(tlines, vtube, sizeof vtube);
+       for (to = ZERO, from = ZERO + cnt; to <= WECHO - cnt; to++, from++)
+               vtube[to] = tlines[from];
+       for (from = ZERO; to <= WECHO; to++, from++) {
+               vtube[to] = tlines[from];
+               vclrcell(vtube[to], WCOLS);
+       }
+       for (from = 0; from <= vcnt; from++)
+               LINE(from) -= cnt;
+}
+
+/*
+ * Discard logical lines due to physical wandering off the screen.
+ */
+void 
+vscrap(void)
+{
+       register int i, j;
+
+#ifdef ADEBUG
+       if (trace)
+               tfixnl(), fprintf(trace, "vscrap\n"), tvliny();
+#endif
+       if (splitw)
+               return;
+       if (vcnt && WBOT != WECHO && LINE(0) < WTOP && LINE(0) >= ZERO) {
+               WTOP = LINE(0);
+               WLINES = WBOT - WTOP + 1;
+       }
+       for (j = 0; j < vcnt; j++)
+               if (LINE(j) >= WTOP) {
+                       if (j == 0)
+                               break;
+                       /*
+                        * Discard the first j physical lines off the top.
+                        */
+                       vcnt -= j, vcline -= j;
+                       for (i = 0; i <= vcnt; i++)
+                               vlcopy(vlinfo[i], vlinfo[i + j]);
+                       break;
+               }
+       /*
+        * Discard lines off the bottom.
+        */
+       if (vcnt) {
+               for (j = 0; j <= vcnt; j++)
+                       if (LINE(j) > WBOT || LINE(j) + DEPTH(j) - 1 > WBOT) {
+                               vcnt = j;
+                               break;
+                       }
+               LASTLINE = LINE(vcnt-1) + DEPTH(vcnt-1);
+       }
+#ifdef ADEBUG
+       if (trace)
+               tvliny();
+#endif
+       /*
+        * May have no lines!
+        */
+}
+
+/*
+ * Repaint the screen, with cursor at curs, aftern an arbitrary change.
+ * Handle notification on large changes.
+ */
+void 
+vrepaint(char *curs)
+{
+
+       wdot = NOLINE;
+       /*
+        * In open want to notify first.
+        */
+       noteit(0);
+       vscrap();
+
+       /*
+        * Deal with a totally useless display.
+        */
+       if (vcnt == 0 || vcline < 0 || vcline > vcnt || holdupd && state != VISUAL) {
+               register line *odol = dol;
+
+               vcnt = 0;
+               if (holdupd)
+                       if (state == VISUAL)
+                               ignore(peekkey());
+                       else
+                               vup1();
+               holdupd = 0;
+               if (odol == zero)
+                       fixzero();
+               vcontext(dot, '.');
+               noteit(1);
+               if (noteit(1) == 0 && odol == zero) {
+                       CATCH
+                               error(catgets(catd, 1, 220,
+                                               "No lines in buffer"));
+                       ENDCATCH
+                       linebuf[0] = 0;
+                       splitw = 0;
+               }
+               vnline(curs);
+               return;
+       }
+
+       /*
+        * Have some useful displayed text; refresh it.
+        */
+       getDOT();
+
+       /*
+        * This is for boundary conditions in open mode.
+        */
+       if (FLAGS(0) & VDIRT)
+               vsync(WTOP);
+       
+       /*
+        * If the current line is after the last displayed line
+        * or the bottom of the screen, then special effort is needed
+        * to get it on the screen.  We first try a redraw at the
+        * last line on the screen, hoping it will fill in where @
+        * lines are now.  If this doesn't work, then roll it onto
+        * the screen.
+        */
+       if (vcline >= vcnt || LINE(vcline) > WBOT) {
+               short oldhold = hold;
+               hold |= HOLDAT, vredraw(LASTLINE), hold = oldhold;
+               if (vcline >= vcnt) {
+                       register int i = vcline - vcnt + 1;
+
+                       dot -= i;
+                       vcline -= i;
+                       vroll(i);
+               } else
+                       vsyncCL();
+       } else
+               vsync(vcline > 0 ? LINE(vcline - 1) : WTOP);
+
+       /*
+        * Notification on large change for visual
+        * has to be done last or we may lose
+        * the echo area with redisplay.
+        */
+       noteit(1);
+
+       /*
+        * Finally.  Move the cursor onto the current line.
+        */
+       vnline(curs);
+}
+
+/*
+ * Fully cleanup the screen, leaving no @ lines except at end when
+ * line after last won't completely fit.  The routine vsync is
+ * more conservative and much less work on dumb terminals.
+ */
+void 
+vredraw(register int p)
+{
+       register int l;
+       register line *tp;
+       char temp[LBSIZE];
+       bool anydl = 0;
+       short oldhold = hold;
+
+#ifdef ADEBUG
+       if (trace)
+               tfixnl(), fprintf(trace, "vredraw(%d)\n", p), tvliny();
+#endif
+       if (holdupd) {
+               holdupd = 3;
+               return;
+       }
+       if (state == HARDOPEN || splitw)
+               return;
+       if (p < 0 /* || p > WECHO */)
+               error(catgets(catd, 1, 221, "Internal error: vredraw"));
+
+       /*
+        * Trim the ragged edges (lines which are off the screen but
+        * not yet logically discarded), save the current line, and
+        * search for first logical line affected by the redraw.
+        */
+       vscrap();
+       CP(temp, linebuf);
+       l = 0;
+       tp = dot - vcline;
+       if (vcnt == 0)
+               LINE(0) = WTOP;
+       while (l < vcnt && LINE(l) < p)
+               l++, tp++;
+
+       /*
+        * We hold off echo area clearing during the redraw in deference
+        * to a final clear of the echo area at the end if appropriate.
+        */
+       heldech = 0;
+       hold |= HOLDECH;
+       for (; l < vcnt && Peekkey != ATTN; l++) {
+               if (l == vcline)
+                       strcLIN(temp);
+               else
+                       getline(*tp);
+
+               /*
+                * Delete junk between displayed lines.
+                */
+               if (LINE(l) != LINE(l + 1) && LINE(l) != p) {
+                       if (anydl == 0 && DB && CD) {
+                               hold = oldhold;
+                               vclrech(0);
+                               anydl = 1;
+                               hold |= HOLDECH;
+                               heldech = 0;
+                       }
+                       vdellin(p, LINE(l) - p, l);
+               }
+
+               /*
+                * If line image is not know to be up to date, then
+                * redisplay it;  else just skip onward.
+                */
+               LINE(l) = p;
+               if (FLAGS(l) & VDIRT) {
+                       DEPTH(l) = vdepth();
+                       if (l != vcline && p + DEPTH(l) - 1 > WBOT) {
+                               vscrap();
+                               break;
+                       }
+                       FLAGS(l) &= ~VDIRT;
+                       vreopen(p, lineno(tp), l);
+                       p = LINE(l) + DEPTH(l);
+               } else
+                       p += DEPTH(l);
+               tp++;
+       }
+
+       /*
+        * That takes care of lines which were already partially displayed.
+        * Now try to fill the rest of the screen with text.
+        */
+       if (state == VISUAL && p <= WBOT) {
+               int ovcline = vcline;
+
+               vcline = l;
+               for (; tp <= dol && Peekkey != ATTN; tp++) {
+                       getline(*tp);
+                       if (p + vdepth() - 1 > WBOT)
+                               break;
+                       vopen(tp, p);
+                       p += DEPTH(vcline);
+                       vcline++;
+               }
+               vcline = ovcline;
+       }
+
+       /*
+        * Thats all the text we can get on.
+        * Now rest of lines (if any) get either a ~ if they
+        * are past end of file, or an @ if the next line won't fit.
+        */
+       for (; p <= WBOT && Peekkey != ATTN; p++)                       
+               vclrlin(p, tp);
+       strcLIN(temp);
+       hold = oldhold;
+       if (heldech)
+               vclrech(0);
+#ifdef ADEBUG
+       if (trace)
+               tvliny();
+#endif
+}
+
+/*
+ * Do the real work in deleting cnt lines starting at line p from
+ * the display.  First affected line is line l.
+ */
+void 
+vdellin(int p, int cnt, int l)
+{
+       register int i;
+
+       if (cnt == 0)
+               return;
+       if (DL == NOSTR || cnt < 0) {
+               /*
+                * Can't do it; just remember that line l is munged.
+                */
+               FLAGS(l) |= VDIRT;
+               return;
+       }
+#ifdef ADEBUG
+       if (trace)
+               tfixnl(), fprintf(trace, "vdellin(%d, %d, %d)\n", p, cnt, l);
+#endif
+       /*
+        * Send the deletes to the screen and then adjust logical
+        * and physical internal data structures.
+        */
+       vgoto(p, 0);
+       if (DL_PARM && (cnt>1 || *DL==0)) {
+               vputp(tgoto(DL_PARM, p, cnt), WECHO-p);
+       }
+       else if (xCS && *DL==0) {
+               /* vt100: fake DL by changing scrolling region */
+               vputp(SC, 1);   /* Save since xCS homes stupid cursor */
+               vputp(tgoto(xCS, TLINES-1, p), 1);
+               vputp(tgoto(CM, 0, TLINES-1), 1);/* Go to lower left corner */
+               for (i=0; i<cnt; i++)           /* .. and scroll cnt times */
+                       putch('\n');            /* should check NL too */
+               vputp(tgoto(xCS, TLINES-1, 0), 1);/* restore scrolling region */
+               vputp(RC, 1);                   /* put cursor back */
+       }
+       else {
+               for (i = 0; i < cnt; i++)
+                       vputp(DL, WECHO - p);
+       }
+       vadjDL(p, cnt);
+       vcloseup(l, cnt);
+}
+/*
+ * Adjust internal physical screen image to account for deleted lines.
+ */
+void 
+vadjDL(int p, int cnt)
+{
+       cell *tlines[TUBELINES];
+       register int from, to;
+
+#ifdef ADEBUG
+       if (trace)
+               tfixnl(), fprintf(trace, "vadjDL(%d, %d)\n", p, cnt);
+#endif
+       /*
+        * Would like to use structured assignment but early
+        * v7 compiler (released with phototypesetter for v6)
+        * can't hack it.
+        */
+       copy(tlines, vtube, sizeof vtube);      /*SASSIGN*/
+       for (from = p + cnt, to = p; from <= WECHO; from++, to++)
+               vtube[to] = tlines[from];
+       for (from = p; to <= WECHO; from++, to++) {
+               vtube[to] = tlines[from];
+               vclrcell(vtube[to], WCOLS);
+       }
+}
+/*
+ * Sync the screen, like redraw but more lazy and willing to leave
+ * @ lines on the screen.  VsyncCL syncs starting at the current line.
+ * In any case, if the redraw option is set then all syncs map to redraws
+ * as if vsync didn't exist.
+ */
+void 
+vsyncCL(void)
+{
+
+       vsync(LINE(vcline));
+}
+
+void 
+vsync(register int p)
+{
+
+       if (value(REDRAW))
+               vredraw(p);
+       else
+               vsync1(p);
+}
+
+/*
+ * The guts of a sync.  Similar to redraw but
+ * just less ambitous.
+ */
+void 
+vsync1(register int p)
+{
+       register int l;
+       char temp[LBSIZE];
+       register struct vlinfo *vp = &vlinfo[0];
+       short oldhold = hold;
+
+#ifdef ADEBUG
+       if (trace)
+               tfixnl(), fprintf(trace, "vsync1(%d)\n", p), tvliny();
+#endif
+       if (holdupd) {
+               if (holdupd < 3)
+                       holdupd = 2;
+               return;
+       }
+       if (state == HARDOPEN || splitw)
+               return;
+       vscrap();
+       CP(temp, linebuf);
+       if (vcnt == 0)
+               LINE(0) = WTOP;
+       l = 0;
+       while (l < vcnt && vp->vliny < p)
+               l++, vp++;
+       heldech = 0;
+       hold |= HOLDECH;
+       while (p <= WBOT && Peekkey != ATTN) {
+               /*
+                * Want to put a line here if not in visual and first line
+                * or if there are lies left and this line starts before
+                * the current line, or if this line is piled under the
+                * next line (vreplace does this and we undo it).
+                */
+               if (l == 0 && state != VISUAL ||
+                   (l < vcnt && (vp->vliny <= p || vp[0].vliny == vp[1].vliny))) {
+                       if (l == 0 || vp->vliny < p || (vp->vflags & VDIRT)) {
+                               if (l == vcline)
+                                       strcLIN(temp);
+                               else
+                                       getline(dot[l - vcline]);
+                               /*
+                                * Be careful that a long line doesn't cause the
+                                * screen to shoot up.
+                                */
+                               if (l != vcline && (vp->vflags & VDIRT)) {
+                                       vp->vdepth = vdepth();
+                                       vp->vflags &= ~VDIRT;
+                                       if (p + vp->vdepth - 1 > WBOT)
+                                               break;
+                               }
+                               vreopen(p, lineDOT() + (l - vcline), l);
+                       }
+                       p = vp->vliny + vp->vdepth;
+                       vp++;
+                       l++;
+               } else
+                       /*
+                        * A physical line between logical lines,
+                        * so we settle for an @ at the beginning.
+                        */
+                       vclrlin(p, dot + (l - vcline)), p++;
+       }
+       strcLIN(temp);
+       hold = oldhold;
+       if (heldech)
+               vclrech(0);
+}
+
+/*
+ * Subtract (logically) cnt physical lines from the 
+ * displayed position of lines starting with line l.
+ */
+void 
+vcloseup(int l, register int cnt)
+{
+       register int i;
+
+#ifdef ADEBUG
+       if (trace)
+               tfixnl(), fprintf(trace, "vcloseup(%d, %d)\n", l, cnt);
+#endif
+       for (i = l + 1; i <= vcnt; i++)
+               LINE(i) -= cnt;
+}
+
+/*
+ * Workhorse for rearranging line descriptors on changes.
+ * The idea here is that, starting with line l, cnt lines
+ * have been replaced with newcnt lines.  All of these may
+ * be ridiculous, i.e. l may be -1000, cnt 50 and newcnt 0,
+ * since we may be called from an undo after the screen has
+ * moved a lot.  Thus we have to be careful.
+ *
+ * Many boundary conditions here.
+ */
+void 
+vreplace(int l, int cnt, int newcnt)
+{
+       register int from, to, i;
+       bool savenote = 0;
+
+#ifdef ADEBUG
+       if (trace) {
+               tfixnl(), fprintf(trace, "vreplace(%d, %d, %d)\n", l, cnt, newcnt);
+               tvliny();
+       }
+#endif
+       if (l >= vcnt)
+               return;
+       if (l < 0) {
+               if (l + cnt < 0) {
+                       /*
+                        * Nothing on the screen is relevant.
+                        * Settle for redrawing from scratch (later).
+                        */
+                       vcnt = 0;
+                       return;
+               }
+               /*
+                * Normalize l to top of screen; the add is
+                * really a subtract from cnt since l is negative.
+                */
+               cnt += l;
+               l = 0;
+
+               /*
+                * Unseen lines were affect so notify (later).
+                */
+               savenote++;
+       }
+
+       /*
+        * These shouldn't happen
+        * but would cause great havoc.
+        */
+       if (cnt < 0)
+               cnt = 0;
+       if (newcnt < 0)
+               newcnt = 0;
+
+       /*
+        * Surely worthy of note if more than report
+        * lines were changed.
+        */
+       if (cnt > value(REPORT) || newcnt > value(REPORT))
+               savenote++;
+
+       /*
+        * Same number of lines affeted as on screen, and we
+        * can insert and delete lines.  Thus we just type
+        * over them, since otherwise we will push them
+        * slowly off the screen, a clear lose.
+        */
+       if (cnt == newcnt || vcnt - l == newcnt && AL && DL) {
+               if (cnt > 1 && l + cnt > vcnt)
+                       savenote++;
+               vdirty(l, newcnt);
+       } else {
+               /*
+                * Lines are going away, squish them out.
+                */
+               if (cnt > 0) {
+                       /*
+                        * If non-displayed lines went away,
+                        * always notify.
+                        */
+                       if (cnt > 1 && l + cnt > vcnt)
+                               savenote++;
+                       if (l + cnt >= vcnt)
+                               cnt = vcnt - l;
+                       else
+                               for (from = l + cnt, to = l; from <= vcnt; to++, from++)
+                                       vlcopy(vlinfo[to], vlinfo[from]);
+                       vcnt -= cnt;
+               }
+               /*
+                * Open up space for new lines appearing.
+                * All new lines are piled in the same place,
+                * and will be unpiled by vredraw/vsync, which
+                * inserts lines in front as it unpiles.
+                */
+               if (newcnt > 0) {
+                       /*
+                        * Newlines are appearing which may not show,
+                        * so notify (this is only approximately correct
+                        * when long lines are present).
+                        */
+                       if (newcnt > 1 && l + newcnt > vcnt + 1)
+                               savenote++;
+
+                       /*
+                        * If there will be more lines than fit, then
+                        * just throw way the rest of the stuff on the screen.
+                        */
+                       if (l + newcnt > WBOT && AL && DL) {
+                               vcnt = l;
+                               goto skip;
+                       }
+                       from = vcnt, to = vcnt + newcnt;
+                       i = TUBELINES - to;
+                       if (i < 0)
+                               from += i, to += i;
+                       vcnt = to;
+                       for (; from >= l; from--, to--)
+                               vlcopy(vlinfo[to], vlinfo[from]);
+                       for (from = to + 1, to = l; to < l + newcnt && to <= WBOT + 1; to++) {
+                               LINE(to) = LINE(from);
+                               DEPTH(to) = 0;
+                               FLAGS(to) = VDIRT;
+                       }
+               }
+       }
+skip:
+       if (Pline == numbline && cnt != newcnt)
+               /*
+                * When lines positions are shifted, the numbers
+                * will be wrong.
+                */
+               vdirty(l, WECHO);
+       if (!savenote)
+               notecnt = 0;
+#ifdef ADEBUG
+       if (trace)
+               tvliny();
+#endif
+}
+
+/*
+ * Start harcopy open.
+ * Print an image of the line to the left of the cursor
+ * under the full print of the line and position the cursor.
+ * If we are in a scroll ^D within hardcopy open then all this
+ * is suppressed.
+ */
+void 
+sethard(void)
+{
+
+       if (state == VISUAL)
+               return;
+       rubble = 0;
+       state = HARDOPEN;
+       if (hold & HOLDROL)
+               return;
+       vup1();
+       LINE(0) = WBOT;
+       if (Pline == numbline)
+               vgoto(WBOT, 0), printf("%6d  ", lineDOT());
+}
+
+/*
+ * Mark the lines starting at base for i lines
+ * as dirty so that they will be checked for correct
+ * display at next sync/redraw.
+ */
+void 
+vdirty(register int base, register int i)
+{
+       register int l;
+       
+       for (l = base; l < vcnt; l++) {
+               if (--i < 0)
+                       return;
+               FLAGS(l) |= VDIRT;
+       }
+}
diff --git a/ex_vars.h b/ex_vars.h
new file mode 100644 (file)
index 0000000..59caa1f
--- /dev/null
+++ b/ex_vars.h
@@ -0,0 +1,48 @@
+       /* sccs id   @(#)ex_vars.h makeoptions  1.8 (gritter) 7/1/02 */
+#define AUTOINDENT      0
+#define AUTOPRINT       1
+#define AUTOWRITE       2
+#define BEAUTIFY        3
+#define DIRECTORY       4
+#define EDCOMPATIBLE    5
+#define ERRORBELLS      6
+#define EXRC            7
+#define FLASH           8
+#define HARDTABS        9
+#define IGNORECASE      10
+#define LISP            11
+#define LIST            12
+#define MAGIC           13
+#define MESG            14
+#define MODELINES       15
+#define NUMBER          16
+#define OPEN            17
+#define OPTIMIZE        18
+#define PARAGRAPHS      19
+#define PROMPT          20
+#define READONLY        21
+#define REDRAW          22
+#define REMAP           23
+#define REPORT          24
+#define SCROLL          25
+#define SECTIONS        26
+#define SHELL           27
+#define SHIFTWIDTH      28
+#define SHOWMATCH       29
+#define SHOWMODE        30
+#define SLOWOPEN        31
+#define SOURCEANY       32
+#define TABSTOP         33
+#define TAGLENGTH       34
+#define TAGS            35
+#define TERM            36
+#define TERSE           37
+#define TIMEOUT         38
+#define TTYTYPE         39
+#define WARN            40
+#define WINDOW          41
+#define WRAPSCAN        42
+#define WRAPMARGIN      43
+#define WRITEANY        44
+
+#define        NOPTS   45
diff --git a/ex_version.c b/ex_version.c
new file mode 100644 (file)
index 0000000..317fdd3
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+ *
+ * Sccsid @(#)ex_version.c     1.132 (gritter) 3/25/05
+ */
+
+#include "ex.h"
+
+static char *versionstring = "@(#)Version 4.0 (gritter) 3/25/05";
+
+void 
+printver(void)
+{
+       printf("%s%s%s", versionstring + 4,
+#ifdef BIT8
+               "", ""
+#else
+               ",", "@(#) 7bit" + 4
+#endif
+       );
+}
diff --git a/ex_vget.c b/ex_vget.c
new file mode 100644 (file)
index 0000000..e3a50d0
--- /dev/null
+++ b/ex_vget.c
@@ -0,0 +1,879 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_vget.c  1.29 (gritter) 2/15/05";
+#endif
+#endif
+
+/* from ex_vget.c      6.8.1 (2.11BSD GTE) 12/9/94 */
+
+#include "ex.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+
+/*
+ * Input routines for open/visual.
+ * We handle reading from the echo area here as well as notification on 
+ * large changes which appears in the echo area.
+ */
+
+/*
+ * Return the key.
+ */
+void 
+ungetkey (
+    int c              /* mjm: char --> int */
+)
+{
+
+       if (Peekkey != ATTN)
+               Peekkey = c;
+}
+
+/*
+ * Return a keystroke, but never a ^@.
+ */
+int 
+getkey(void)
+{
+       register int c;         /* mjm: char --> int */
+
+       do {
+               c = getbr();
+               if (c==0)
+                       beep();
+       } while (c == 0);
+       return (c);
+}
+
+/*
+ * Tell whether next keystroke would be a ^@.
+ */
+int 
+peekbr(void)
+{
+
+       Peekkey = getbr();
+       return (Peekkey == 0);
+}
+
+short  precbksl;
+JMP_BUF        readbuf;
+int    doingread = 0;
+
+static int
+readwc(int fd, int *cp)
+{
+       int     c;
+       char    b;
+
+#ifdef MB
+       if (mb_cur_max > 1) {
+               static char     pbuf[2][MB_LEN_MAX], *pend[2], *pcur[2];
+               static mbstate_t        state[2];
+               static int      incompl[2];
+               int     i, rest;
+               int     idx = fd ? 1 : 0;
+               wchar_t wc;
+               size_t  sz;
+
+               i = 0;
+               rest = pend[idx] - pcur[idx];
+               if (rest && pcur[idx] > pbuf[idx]) {
+                       do
+                               pbuf[idx][i] = pcur[idx][i];
+                       while (i++, --rest);
+               } else if (incompl[idx]) {
+                       pend[idx] = pcur[idx] = NULL;
+                       return -1;
+               }
+               if (i == 0) {
+                       if ((c = read(fd, &b, 1)) <= 0) {
+                               pend[idx] = pcur[idx] = NULL;
+                               return c;
+                       }
+                       pbuf[idx][i++] = b;
+               }
+               if (pbuf[idx][0] & 0200) {
+                       sz = 1;
+                       while ((sz = mbrtowc(&wc, pbuf[idx], i, &state[idx]))
+                                       == (size_t)-2 && i < mb_cur_max) {
+                               if ((c = read(fd, &b, 1)) <= 0) {
+                                       incompl[idx] = 1;
+                                       break;
+                               } else
+                                       pbuf[idx][i++] = b;
+                               memset(&state[idx], 0, sizeof state[idx]);
+                       }
+                       if (sz == (size_t)-2 || sz == (size_t)-1 ||
+                                       !widthok(wc)) {
+                               memset(&state[idx], 0, sizeof state[idx]);
+                               c = 1;
+                               *cp = pbuf[idx][0] | INVBIT;
+                       } else if (sz == 0) {
+                               c = 1;
+                               *cp = wc;
+                       } else {
+                               c = sz;
+                               *cp = wc;
+                       }
+               } else {
+                       c = 1;
+                       *cp = pbuf[idx][0];
+               }
+               pcur[idx] = &pbuf[idx][c];
+               pend[idx] = &pcur[idx][i-c];
+               return c;
+       } else
+#endif /* MB */
+       {
+               c = read(fd, &b, 1);
+               *cp = b;
+               return c;
+       }
+}
+
+/*
+ * Get a keystroke, including a ^@.
+ * If an key was returned with ungetkey, that
+ * comes back first.  Next comes unread input (e.g.
+ * from repeating commands with .), and finally new
+ * keystrokes.
+ */
+int 
+getbr(void)
+{
+       int ch;
+       register int c;
+#ifdef UCVISUAL
+       register int d;
+       register char *colp;
+#endif
+#ifdef BEEHIVE
+       int cnt;
+       static char Peek2key;
+#endif
+       extern short slevel, ttyindes;
+
+getATTN:
+       if (Peekkey) {
+               c = Peekkey;
+               Peekkey = 0;
+               return (c);
+       }
+#ifdef BEEHIVE
+       if (Peek2key) {
+               c = Peek2key;
+               Peek2key = 0;
+               return (c);
+       }
+#endif
+       if (vglobp) {
+               if (*vglobp)
+                       return (lastvgk = *vglobp++);
+               lastvgk = 0;
+               return (ESCAPE);
+       }
+       if (vmacp) {
+               if (*vmacp) {
+                       int     n;
+                       nextc(ch, vmacp, n);
+                       vmacp += n;
+                       return (ch);
+               }
+               /* End of a macro or set of nested macros */
+               vmacp = 0;
+               if (inopen == -1)       /* don't screw up undo for esc esc */
+                       vundkind = VMANY;
+               inopen = 1;     /* restore old setting now that macro done */
+               vch_mac = VC_NOTINMAC;
+       }
+       flusho();
+       for (c =0; abbrevs[c].mapto; c++)
+               abbrevs[c].hadthis = 0;
+#ifdef UCVISUAL
+again:
+#endif
+       if (SETJMP(readbuf))
+               goto getATTN;
+       doingread = 1;
+       c = readwc(slevel == 0 ? 0 : ttyindes, &ch);
+       doingread = 0;
+       if (c < 1) {
+               if (errno == EINTR)
+                       goto getATTN;
+               error(catgets(catd, 1, 222, "Input read error"));
+       }
+       c = ch & TRIM;
+#ifdef BEEHIVE
+       if (XB && slevel==0 && c == ESCAPE) {
+               if (readwc(0, &Peek2key) < 1)
+                       goto getATTN;
+               Peek2key &= TRIM;
+               switch (Peek2key) {
+               case 'C':       /* SPOW mode sometimes sends \EC for space */
+                       c = ' ';
+                       Peek2key = 0;
+                       break;
+               case 'q':       /* f2 -> ^C */
+                       c = CTRL('c');
+                       Peek2key = 0;
+                       break;
+               case 'p':       /* f1 -> esc */
+                       Peek2key = 0;
+                       break;
+               }
+       }
+#endif
+
+#ifdef UCVISUAL
+        /*
+         * The algorithm here is that of the UNIX kernel.
+         * See the description in the programmers manual.
+         */
+        if (UPPERCASE) {
+                if (xisupper(c))
+                        c = xtolower(c);
+                if (c == '\\') {
+                        if (precbksl < 2)
+                                precbksl++;
+                        if (precbksl == 1)
+                                goto again;
+                } else if (precbksl) {
+                        d = 0;
+                        if (xislower(c))
+                                d = xtoupper(c);
+                        else {
+                                colp = "({)}!|^~'~";
+                                while (d = *colp++)
+                                        if (d == c) {
+                                                d = *colp++;
+                                                break;
+                                        } else
+                                                colp++;
+                        }
+                        if (precbksl == 2) {
+                                if (!d) {
+                                        Peekkey = c;
+                                        precbksl = 0;
+                                        c = '\\';
+                                }
+                        } else if (d)
+                                c = d;
+                        else {
+                                Peekkey = c;
+                                precbksl = 0;
+                                c = '\\';
+                        }
+                }
+                if (c != '\\')
+                        precbksl = 0;
+        }
+#endif
+
+#ifdef TRACE
+       if (trace) {
+               if (!techoin) {
+                       tfixnl();
+                       techoin = 1;
+                       fprintf(trace, "*** Input: ");
+               }
+               tracec(c);
+       }
+#endif
+       lastvgk = 0;
+       return (c);
+}
+
+/*
+ * Get a key, but if a delete, quit or attention
+ * is typed return 0 so we will abort a partial command.
+ */
+int 
+getesc(void)
+{
+       register int c;
+
+       c = getkey();
+       if (c == ATTN)
+               goto case_ATTN;
+       switch (c) {
+
+       case CTRL('v'):
+       case CTRL('q'):
+               c = getkey();
+               return (c);
+
+       case QUIT:
+case_ATTN:
+               ungetkey(c);
+               return (0);
+
+       case ESCAPE:
+               return (0);
+       }
+       return (c);
+}
+
+/*
+ * Peek at the next keystroke.
+ */
+int 
+peekkey(void)
+{
+
+       Peekkey = getkey();
+       return (Peekkey);
+}
+
+/*
+ * Read a line from the echo area, with single character prompt c.
+ * A return value of 1 means the user blewit or blewit away.
+ */
+int 
+readecho(int c)
+{
+       register char *sc = cursor;
+       register void (*OP)(int);
+       bool waste;
+       register int OPeek;
+
+       if (WBOT == WECHO)
+               vclean();
+       else
+               vclrech(0);
+       splitw++;
+       vgoto(WECHO, 0);
+       putchar(c);
+       vclreol();
+       vgoto(WECHO, 1);
+       cursor = linebuf; linebuf[0] = 0; genbuf[0] = c;
+       if (peekbr()) {
+               if (!INS[0] || (INS[0] & (QUOTE|TRIM)) == OVERBUF)
+                       goto blewit;
+               vglobp = INS;
+       }
+       OP = Pline; Pline = normline;
+       ignore(vgetline(0, genbuf + 1, &waste, c));
+       if (Outchar == termchar)
+               putchar('\n');
+       vscrap();
+       Pline = OP;
+       if (Peekkey != ATTN && Peekkey != QUIT && Peekkey != CTRL('h')) {
+               cursor = sc;
+               vclreol();
+               return (0);
+       }
+blewit:
+       OPeek = Peekkey==CTRL('h') ? 0 : Peekkey; Peekkey = 0;
+       splitw = 0;
+       vclean();
+       vshow(dot, NOLINE);
+       vnline(sc);
+       Peekkey = OPeek;
+       return (1);
+}
+
+/*
+ * A complete command has been defined for
+ * the purposes of repeat, so copy it from
+ * the working to the previous command buffer.
+ */
+void 
+setLAST(void)
+{
+
+       if (vglobp || vmacp)
+               return;
+       lastreg = vreg;
+       lasthad = Xhadcnt;
+       lastcnt = Xcnt;
+       *lastcp = 0;
+       cellcpy(lastcmd, workcmd);
+}
+
+/*
+ * Gather up some more text from an insert.
+ * If the insertion buffer oveflows, then destroy
+ * the repeatability of the insert.
+ */
+void 
+addtext(char *cp)
+{
+
+       if (vglobp)
+               return;
+       addto(INS, cp);
+       if ((INS[0] & (QUOTE|TRIM)) == OVERBUF)
+               lastcmd[0] = 0;
+}
+
+void 
+setDEL(void)
+{
+
+       setBUF(DEL);
+}
+
+/*
+ * Put text from cursor upto wcursor in BUF.
+ */
+void 
+setBUF(register cell *BUF)
+{
+       register int c;
+       register char *wp = wcursor;
+
+       c = *wp;
+       *wp = 0;
+       BUF[0] = 0;
+       addto(BUF, cursor);
+       *wp = c;
+}
+
+void 
+addto(register cell *buf, register char *str)
+{
+
+       if ((buf[0] & (QUOTE|TRIM)) == OVERBUF)
+               return;
+       if (cellen(buf) + strlen(str) + 1 >= VBSIZE) {
+               buf[0] = OVERBUF;
+               return;
+       }
+       while (*buf)
+               buf++;
+       str2cell(buf, str);
+}
+
+/*
+ * Note a change affecting a lot of lines, or non-visible
+ * lines.  If the parameter must is set, then we only want
+ * to do this for open modes now; return and save for later
+ * notification in visual.
+ */
+int 
+noteit(int must)
+{
+       register int sdl = destline, sdc = destcol;
+
+       if (notecnt < 2 || !must && state == VISUAL)
+               return (0);
+       splitw++;
+       if (WBOT == WECHO)
+               vmoveitup(1, 1);
+       vigoto(WECHO, 0);
+       printf(catgets(catd, 1, 223, "%d %sline"), notecnt, notesgn);
+       if (notecnt > 1)
+               putchar('s');
+       if (*notenam) {
+               printf(" %s", notenam);
+               if (*(strend(notenam) - 1) != 'e')
+                       putchar('e');
+               putchar('d');
+       }
+       vclreol();
+       notecnt = 0;
+       if (state != VISUAL)
+               vcnt = vcline = 0;
+       splitw = 0;
+       if (state == ONEOPEN || state == CRTOPEN)
+               vup1();
+       destline = sdl; destcol = sdc;
+       return (1);
+}
+
+/*
+ * Rrrrringgggggg.
+ * If possible, use flash (VB).
+ */
+void
+beep(void)
+{
+
+       if (VB && value(FLASH))
+               vputp(VB, 0);
+       else
+               vputc(CTRL('g'));
+}
+
+/*
+ * Push an integer string as a macro.
+ */
+static void
+imacpush(int *ip, int canundo)
+{
+       char    buf[BUFSIZ], *bp = buf;
+
+#ifdef MB
+       do {
+               int     n;
+               n = wctomb(bp, *ip&TRIM);
+               bp += n;
+       } while (*ip++);
+#else  /* !MB */
+       while (*bp++ = *ip++);
+#endif /* !MB */
+       macpush(buf, canundo);
+}
+
+/*
+ * Map the command input character c,
+ * for keypads and labelled keys which do cursor
+ * motions.  I.e. on an adm3a we might map ^K to ^P.
+ * DM1520 for example has a lot of mappable characters.
+ */
+
+int 
+map(register int c, register struct maps *maps)
+{
+       register int d;
+       register int *p, *q;
+       int b[10+MB_LEN_MAX];   /* Assumption: no keypad sends string longer than 10 */
+
+       /*
+        * Mapping for special keys on the terminal only.
+        * BUG: if there's a long sequence and it matches
+        * some chars and then misses, we lose some chars.
+        *
+        * For this to work, some conditions must be met.
+        * 1) Keypad sends SHORT (2 or 3 char) strings
+        * 2) All strings sent are same length & similar
+        * 3) The user is unlikely to type the first few chars of
+        *    one of these strings very fast.
+        * Note: some code has been fixed up since the above was laid out,
+        * so conditions 1 & 2 are probably not required anymore.
+        * However, this hasn't been tested with any first char
+        * that means anything else except escape.
+        */
+#ifdef MDEBUG
+       if (trace)
+               fprintf(trace,"map(%c): ",c);
+#endif
+       /*
+        * If c==0, the char came from getesc typing escape.  Pass it through
+        * unchanged.  0 messes up the following code anyway.
+        */
+       if (c==0)
+               return(0);
+
+       b[0] = c;
+       b[1] = 0;
+       for (d=0; maps[d].mapto; d++) {
+#ifdef MDEBUG
+               if (trace)
+                       fprintf(trace,"\ntry '%s', ",maps[d].cap);
+#endif
+               if (p = maps[d].icap) {
+                       for (q=b; *p; p++, q++) {
+#ifdef MDEBUG
+                               if (trace)
+                                       fprintf(trace,"q->b[%d], ",q-b);
+#endif
+                               if (*q==0) {
+                                       /*
+                                        * Is there another char waiting?
+                                        *
+                                        * This test is oversimplified, but
+                                        * should work mostly. It handles the
+                                        * case where we get an ESCAPE that
+                                        * wasn't part of a keypad string.
+                                        */
+                                       if ((c=='#' ? peekkey() : fastpeekkey()) == 0) {
+#ifdef MDEBUG
+                                               if (trace)
+                                                       fprintf(trace,"fpk=0: will return '%c'",c);
+#endif
+                                               /*
+                                                * Nothing waiting.  Push back
+                                                * what we peeked at & return
+                                                * failure (c).
+                                                *
+                                                * We want to be able to undo
+                                                * commands, but it's nonsense
+                                                * to undo part of an insertion
+                                                * so if in input mode don't.
+                                                */
+#ifdef MDEBUG
+                                               if (trace)
+                                                       fprintf(trace, "Call macpush, b %d %d %d\n", b[0], b[1], b[2]);
+#endif
+                                               imacpush(&b[1],maps == arrows);
+#ifdef MDEBUG
+                                               if (trace)
+                                                       fprintf(trace, "return %d\n", c);       
+#endif
+                                               return(c);
+                                       }
+                                       *q = getkey();
+                                       q[1] = 0;
+                               }
+                               if (*p != *q)
+                                       goto contin;
+                       }
+                       macpush(maps[d].mapto,maps == arrows);
+                       c = getkey();
+#ifdef MDEBUG
+                       if (trace)
+                               fprintf(trace,"Success: push(%s), return %c",maps[d].mapto, c);
+#endif
+                       return(c);      /* first char of map string */
+                       contin:;
+               }
+       }
+#ifdef MDEBUG
+       if (trace)
+               fprintf(trace,"Fail: push(%s), return %c", &b[1], c);
+#endif
+       imacpush(&b[1],0);
+       return(c);
+}
+
+/*
+ * Push st onto the front of vmacp. This is tricky because we have to
+ * worry about where vmacp was previously pointing. We also have to
+ * check for overflow (which is typically from a recursive macro)
+ * Finally we have to set a flag so the whole thing can be undone.
+ * canundo is 1 iff we want to be able to undo the macro.  This
+ * is false for, for example, pushing back lookahead from fastpeekkey(),
+ * since otherwise two fast escapes can clobber our undo.
+ */
+void 
+macpush(char *st, int canundo)
+{
+       char tmpbuf[BUFSIZ];
+
+       if (st==0 || *st==0)
+               return;
+#ifdef MDEBUG
+       if (trace)
+               fprintf(trace, "macpush(%s), canundo=%d\n",st,canundo);
+#endif
+       if ((vmacp ? strlen(vmacp) : 0) + strlen(st) > BUFSIZ)
+               error(catgets(catd, 1, 224,
+                               "Macro too long@ - maybe recursive?"));
+       if (vmacp) {
+               strcpy(tmpbuf, vmacp);
+               if (!FIXUNDO)
+                       canundo = 0;    /* can't undo inside a macro anyway */
+       }
+       strcpy(vmacbuf, st);
+       if (vmacp)
+               strcat(vmacbuf, tmpbuf);
+       vmacp = vmacbuf;
+       /* arrange to be able to undo the whole macro */
+       if (canundo) {
+#ifdef notdef
+               otchng = tchng;
+               vsave();
+               saveall();
+               inopen = -1;    /* no need to save since it had to be 1 or -1 before */
+               vundkind = VMANY;
+#endif
+               vch_mac = VC_NOCHANGE;
+       }
+}
+
+#ifdef TRACE
+void 
+visdump(char *s)
+{
+       register int i;
+
+       if (!trace) return;
+
+       fprintf(trace, "\n%s: basWTOP=%d, basWLINES=%d, WTOP=%d, WBOT=%d, WLINES=%d, WCOLS=%d, WECHO=%d\n",
+               s, basWTOP, basWLINES, WTOP, WBOT, WLINES, WCOLS, WECHO);
+       fprintf(trace, "   vcnt=%d, vcline=%d, cursor=%d, wcursor=%d, wdot=%d\n",
+               vcnt, vcline, cursor-linebuf, wcursor-linebuf, wdot-zero);
+       for (i=0; i<TUBELINES; i++)
+               if (vtube[i] && *vtube[i])
+                       fprintf(trace, "%d: '%s'\n", i, vtube[i]);
+       tvliny();
+}
+
+void 
+vudump(char *s)
+{
+       register line *p;
+       char savelb[1024];
+
+       if (!trace) return;
+
+       fprintf(trace, "\n%s: undkind=%d, vundkind=%d, unddel=%d, undap1=%d, undap2=%d,\n",
+               s, undkind, vundkind, lineno(unddel), lineno(undap1), lineno(undap2));
+       fprintf(trace, "  undadot=%d, dot=%d, dol=%d, unddol=%d, truedol=%d\n",
+               lineno(undadot), lineno(dot), lineno(dol), lineno(unddol), lineno(truedol));
+       fprintf(trace, "  [\n");
+       CP(savelb, linebuf);
+       fprintf(trace, "linebuf = '%s'\n", linebuf);
+       for (p=zero+1; p<=truedol; p++) {
+               fprintf(trace, "%o ", *p);
+               getline(*p);
+               fprintf(trace, "'%s'\n", linebuf);
+       }
+       fprintf(trace, "]\n");
+       CP(linebuf, savelb);
+}
+#endif
+
+/*
+ * Get a count from the keyed input stream.
+ * A zero count is indistinguishable from no count.
+ */
+int 
+vgetcnt(void)
+{
+       register int c, cnt;
+
+       cnt = 0;
+       for (;;) {
+               c = getkey();
+               if (!xisdigit(c))
+                       break;
+               cnt *= 10, cnt += c - '0';
+       }
+       ungetkey(c);
+       Xhadcnt = 1;
+       Xcnt = cnt;
+       return(cnt);
+}
+
+void 
+trapalarm(int signum) {
+       alarm(0);
+       if (vcatch)
+               LONGJMP(vreslab,1);
+}
+
+/*
+ * fastpeekkey is just like peekkey but insists the character come in
+ * fast (within 1 second). This will succeed if it is the 2nd char of
+ * a machine generated sequence (such as a function pad from an escape
+ * flavor terminal) but fail for a human hitting escape then waiting.
+ */
+int 
+fastpeekkey(void)
+{
+       shand Oint;
+       register int c;
+
+       /*
+        * If the user has set notimeout, we wait forever for a key.
+        * If we are in a macro we do too, but since it's already
+        * buffered internally it will return immediately.
+        * In other cases we force this to die in 1 second.
+        * This is pretty reliable (VMUNIX rounds it to .5 - 1.5 secs,
+        * but UNIX truncates it to 0 - 1 secs) but due to system delays
+        * there are times when arrow keys or very fast typing get counted
+        * as separate.  notimeout is provided for people who dislike such
+        * nondeterminism.
+        */
+#ifdef MDEBUG
+       if (trace)
+               fprintf(trace,"\nfastpeekkey: ",c);
+#endif
+       Oint = signal(SIGINT, trapalarm);
+       if (value(TIMEOUT) && inopen >= 0) {
+               signal(SIGALRM, trapalarm);
+#ifdef MDEBUG
+               alarm(10);
+               if (trace)
+                       fprintf(trace, "set alarm ");
+#else
+               alarm(1);
+#endif
+       }
+       CATCH
+               c = peekkey();
+#ifdef MDEBUG
+       if (trace)
+               fprintf(trace,"[OK]",c);
+#endif
+               alarm(0);
+       ONERR
+               c = 0;
+#ifdef MDEBUG
+       if (trace)
+               fprintf(trace,"[TIMEOUT]",c);
+#endif
+       ENDCATCH
+#ifdef MDEBUG
+       if (trace)
+               fprintf(trace,"[fpk:%o]",c);
+#endif
+       signal(SIGINT,Oint);
+       return(c);
+}
diff --git a/ex_vis.h b/ex_vis.h
new file mode 100644 (file)
index 0000000..d6fd99c
--- /dev/null
+++ b/ex_vis.h
@@ -0,0 +1,321 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+ *
+ *     from ex_vis.h   7.4 (Berkeley) 5/31/85
+ *
+ *     @(#)ex_vis.h    1.18 (gritter) 3/24/05
+ */
+
+/*
+ * Ex version 3
+ * Mark Horton, UCB
+ * Bill Joy UCB
+ *
+ * Open and visual mode definitions.
+ * 
+ * There are actually 4 major states in open/visual modes.  These
+ * are visual, crt open (where the cursor can move about the screen and
+ * the screen can scroll and be erased), one line open (on dumb glass-crt's
+ * like the adm3), and hardcopy open (for everything else).
+ *
+ * The basic state is given by bastate, and the current state by state,
+ * since we can be in pseudo-hardcopy mode if we are on an adm3 and the
+ * line is longer than 80.
+ */
+
+var enum {
+       VISUAL          = 0,
+       CRTOPEN         = 1,
+       ONEOPEN         = 2,
+       HARDOPEN        = 3
+} bastate, state;
+
+/*
+ * The screen in visual and crtopen is of varying size; the basic
+ * window has top basWTOP and basWLINES lines are thereby implied.
+ * The current window (which may have grown from the basic size)
+ * has top WTOP and WLINES lines.  The top line of the window is WTOP,
+ * and the bottom line WBOT.  The line WECHO is used for messages,
+ * search strings and the like.  If WBOT==WECHO then we are in ONEOPEN
+ * or HARDOPEN and there is no way back to the line we were on if we
+ * go to WECHO (i.e. we will have to scroll before we go there, and
+ * we can't get back).  There are WCOLS columns per line.
+ * If WBOT!=WECHO then WECHO will be the last line on the screen
+ * and WBOT is the line before it.
+ */
+var    short   basWTOP;
+var    short   basWLINES;
+var    short   WTOP;
+var    short   WBOT;
+var    short   WLINES;
+var    short   WCOLS;
+var    short   WECHO;
+
+/*
+ * When we are dealing with the echo area we consider the window
+ * to be "split" and set the variable splitw.  Otherwise, moving
+ * off the bottom of the screen into WECHO causes a screen rollup.
+ */
+var    bool    splitw;
+
+/*
+ * Information about each line currently on the screen includes
+ * the y coordinate associated with the line, the printing depth
+ * of the line (0 indicates unknown), and a mask which indicates
+ * whether the line is "unclean", i.e. whether we should check
+ * to make sure the line is displayed correctly at the next
+ * appropriate juncture.
+ */
+struct vlinfo {
+       short   vliny;          /* Y coordinate */      /* mjm: was char */
+       short   vdepth;         /* Depth of displayed line */ /*mjm: was char */
+       short   vflags;         /* Is line potentially dirty ? */
+};
+var    struct vlinfo  vlinfo[TUBELINES + 2];
+
+#define        DEPTH(c)        (vlinfo[c].vdepth)
+#define        LINE(c)         (vlinfo[c].vliny)
+#define        FLAGS(c)        (vlinfo[c].vflags)
+
+#define        VDIRT   1
+
+/*
+ * Hacks to copy vlinfo structures around
+ */
+#      define  vlcopy(i, j)    i = j;
+
+/*
+ * The current line on the screen is represented by vcline.
+ * There are vcnt lines on the screen, the last being "vcnt - 1".
+ * Vcline is intimately tied to the current value of dot,
+ * and when command mode is used as a subroutine fancy footwork occurs.
+ */
+var    short   vcline;
+var    short   vcnt;
+
+/*
+ * To allow many optimizations on output, an exact image of the terminal
+ * screen is maintained in the space addressed by vtube0.  The vtube
+ * array indexes this space as lines, and is shuffled on scrolls, insert+delete
+ * lines and the like rather than (more expensively) shuffling the screen
+ * data itself.  It is also rearranged during insert mode across line
+ * boundaries to make incore work easier.
+ */
+var    cell    *vtube[TUBELINES];
+var    cell    *vtube0;
+
+/*
+ * The current cursor position within the current line is kept in
+ * cursor.  The current line is kept in linebuf.  During insertions
+ * we use the auxiliary array genbuf as scratch area.
+ * The cursor wcursor and wdot are used in operations within/spanning
+ * lines to mark the other end of the affected area, or the target
+ * for a motion.
+ */
+var    char    *cursor;
+var    char    *wcursor;
+var    line    *wdot;
+
+/*
+ * Undo information is saved in a LBSIZE buffer at "vutmp" for changes
+ * within the current line, or as for command mode for multi-line changes
+ * or changes on lines no longer the current line.
+ * The change kind "VCAPU" is used immediately after a U undo to prevent
+ * two successive U undo's from destroying the previous state.
+ */
+#define        VNONE   0
+#define        VCHNG   1
+#define        VMANY   2
+#define        VCAPU   3
+#define        VMCHNG  4
+#define        VMANYINS 5
+
+var    short   vundkind;       /* Which kind of undo - from above */
+var    char    *vutmp;         /* Prev line image when "VCHNG" */
+
+/*
+ * State information for undoing of macros.  The basic idea is that
+ * if the macro does only 1 change or even none, we don't treat it
+ * specially.  If it does 2 or more changes we want to be able to
+ * undo it as a unit.  We remember how many changes have been made
+ * within the current macro.  (Remember macros can be nested.)
+ */
+#define VC_NOTINMAC    0       /* Not in a macro */
+#define VC_NOCHANGE    1       /* In a macro, no changes so far */
+#define VC_ONECHANGE   2       /* In a macro, one change so far */
+#define VC_MANYCHANGE  3       /* In a macro, at least 2 changes so far */
+
+var    short   vch_mac;        /* Change state - one of the above */
+
+/*
+ * For U undo's the line is grabbed by "vmove" after it first appears
+ * on that line.  The "vUNDdot" which specifies which line has been
+ * saved is selectively cleared when changes involving other lines
+ * are made, i.e. after a 'J' join.  This is because a 'JU' would
+ * lose completely the text of the line just joined on.
+ */
+var    char    *vUNDcurs;      /* Cursor just before 'U' */
+var    line    *vUNDdot;       /* The line address of line saved in vUNDsav */
+var    line    vUNDsav;        /* Grabbed initial "*dot" */
+
+#define        killU()         vUNDdot = NOLINE
+
+/*
+ * There are a number of cases where special behaviour is needed
+ * from deeply nested routines.  This is accomplished by setting
+ * the bits of hold, which acts to change the state of the general
+ * visual editing behaviour in specific ways.
+ *
+ * HOLDAT prevents the clreol (clear to end of line) routines from
+ * putting out @'s or ~'s on empty lines.
+ *
+ * HOLDDOL prevents the reopen routine from putting a '$' at the
+ * end of a reopened line in list mode (for hardcopy mode, e.g.).
+ *
+ * HOLDROL prevents spurious blank lines when scrolling in hardcopy
+ * open mode.
+ *
+ * HOLDQIK prevents the fake insert mode during repeated commands.
+ *
+ * HOLDPUPD prevents updating of the physical screen image when
+ * mucking around while in insert mode.
+ *
+ * HOLDECH prevents clearing of the echo area while rolling the screen
+ * backwards (e.g.) in deference to the clearing of the area at the
+ * end of the scroll (1 time instead of n times).  The fact that this
+ * is actually needed is recorded in heldech, which says that a clear
+ * of the echo area was actually held off.
+ */
+var    short   hold;
+var    short   holdupd;        /* Hold off update when echo line is too long */
+
+#define        HOLDAT          1
+#define        HOLDDOL         2
+#define        HOLDROL         4
+#define        HOLDQIK         8
+#define        HOLDPUPD        16
+#define        HOLDECH         32
+#define HOLDWIG                64
+
+/*
+ * Miscellaneous variables
+ */
+var    short   CDCNT;          /* Count of ^D's in insert on this line */
+var    cell    DEL[VBSIZE];    /* Last deleted text */
+var    bool    HADUP;          /* This insert line started with ^ then ^D */
+var    bool    HADZERO;        /* This insert line started with 0 then ^D */
+var    cell    INS[VBSIZE];    /* Last inserted text */
+var    int     Vlines;         /* Number of file lines "before" vi command */
+var    int     Xcnt;           /* External variable holding last cmd's count */
+var    bool    Xhadcnt;        /* Last command had explicit count? */
+var    short   ZERO;
+var    short   dir;            /* Direction for search (+1 or -1) */
+var    short   doomed;         /* Disply chars right of cursor to be killed */
+var    bool    gobblebl;       /* Wrapmargin space generated nl, eat a space */
+var    bool    hadcnt;         /* (Almost) internal to vmain() */
+var    bool    heldech;        /* We owe a clear of echo area */
+var    bool    insmode;        /* Are in character insert mode */
+var    cell    lastcmd[5];     /* Chars in last command */
+var    int     lastcnt;        /* Count for last command */
+var    cell    *lastcp;        /* Save current command here to repeat */
+var    bool    lasthad;        /* Last command had a count? */
+var    int     lastvgk;        /* Previous input key, if not from keyboard */
+var    short   lastreg;        /* Register with last command */
+var    char    *ncols['z'-'a'+2];      /* Cursor positions of marks */
+var    char    *notenam;       /* Name to be noted with change count */
+var    char    *notesgn;       /* Change count from last command */
+var    int     op;             /* Operation of current command */
+var    int     Peekkey;        /* Peek ahead key */
+var    bool    rubble;         /* Line is filthy (in hardcopy open), redraw! */
+var    int     vSCROLL;        /* Number lines to scroll on ^D/^U */
+var    cell    *vglobp;        /* Untyped input (e.g. repeat insert text) */
+var    char    vmacbuf[VBSIZE];   /* Text of visual macro, hence nonnestable */
+var    char    *vmacp;         /* Like vglobp but for visual macros */
+var    char    *vmcurs;        /* Cursor for restore after undo d), e.g. */
+var    short   vmovcol;        /* Column to try to keep on arrow keys */
+var    bool    vmoving;        /* Are trying to keep vmovcol */
+var    short   vreg;           /* Reg for this command */   /* mjm: was char */
+var    short   wdkind;         /* Liberal/conservative words? */
+var    cell    workcmd[5];     /* Temporary for lastcmd */
+var    char    *vcolbp;        /* first byte of current character in column */
+
+
+/*
+ * Macros
+ */
+#define        INF             30000
+#define        LASTLINE        LINE(vcnt)
+#define        OVERBUF         QUOTE
+#define        beep            obeep
+#define        cindent()       ((outline - vlinfo[vcline].vliny) * WCOLS + outcol)
+#define        vputp(cp, cnt)  tputs(cp, cnt, vputch)
+#define        vputc(c)        putch(c)
diff --git a/ex_vmain.c b/ex_vmain.c
new file mode 100644 (file)
index 0000000..ac07f92
--- /dev/null
@@ -0,0 +1,1442 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_vmain.c 1.29 (gritter) 2/17/05";
+#endif
+#endif
+
+/* from ex_vmain.c     7.7 (Berkeley) 6/7/85 */
+
+#include "ex.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+
+/*
+ * This is the main routine for visual.
+ * We here decode the count and possible named buffer specification
+ * preceding a command and interpret a few of the commands.
+ * Commands which involve a target (i.e. an operator) are decoded
+ * in the routine operate in ex_voperate.c.
+ */
+
+#define        forbid(a)       { if (a) goto fonfon; }
+
+void 
+vmain(void)
+{
+       int c, cnt, i;
+       cell esave[TUBECOLS];
+       char *oglobp;
+       short d;
+       line *addr;
+       int ind, nlput;
+       int shouldpo = 0;
+       int onumber = 0, olist = 0;
+       void (*OPline)(int) = NULL;
+       int (*OPutchar)(int) = NULL;
+
+       CLOBBGRD(c);
+       CLOBBGRD(cnt);
+       CLOBBGRD(i);
+       CLOBBGRD(oglobp);
+       CLOBBGRD(addr);
+       CLOBBGRD(shouldpo);
+       CLOBBGRD(onumber);
+       CLOBBGRD(olist);
+       CLOBBGRD(OPline);
+       CLOBBGRD(OPutchar);
+
+       vch_mac = VC_NOTINMAC;
+
+       /*
+        * If we started as a vi command (on the command line)
+        * then go process initial commands (recover, next or tag).
+        */
+       if (initev) {
+               oglobp = globp;
+               globp = initev;
+               hadcnt = cnt = 0;
+               i = tchng;
+               addr = dot;
+               goto doinit;
+       }
+
+       /*
+        * NB:
+        *
+        * The current line is always in the line buffer linebuf,
+        * and the cursor at the position cursor.  You should do
+        * a vsave() before moving off the line to make sure the disk
+        * copy is updated if it has changed, and a getDOT() to get
+        * the line back if you mung linebuf.  The motion
+        * routines in ex_vwind.c handle most of this.
+        */
+       for (;;) {
+               /*
+                * Decode a visual command.
+                * First sync the temp file if there has been a reasonable
+                * amount of change.  Clear state for decoding of next
+                * command.
+                */
+               TSYNC();
+               vglobp = 0;
+               vreg = 0;
+               hold = 0;
+               seenprompt = 1;
+               wcursor = 0;
+               Xhadcnt = hadcnt = 0;
+               Xcnt = cnt = 1;
+               splitw = 0;
+               if (i = holdupd) {
+                       if (state == VISUAL)
+                               ignore(peekkey());
+                       holdupd = 0;
+/*
+                       if (LINE(0) < ZERO) {
+                               vclear();
+                               vcnt = 0;
+                               i = 3;
+                       }
+*/
+                       if (state != VISUAL) {
+                               vcnt = 0;
+                               vsave();
+                               vrepaint(cursor);
+                       } else if (i == 3)
+                               vredraw(WTOP);
+                       else
+                               vsync(WTOP);
+                       vfixcurs();
+               }
+
+               /*
+                * Gobble up counts and named buffer specifications.
+                */
+               for (;;) {
+looptop:
+#ifdef MDEBUG
+                       if (trace)
+                               fprintf(trace, "pc=%c",peekkey());
+#endif
+                       if (xisdigit(peekkey()) && peekkey() != '0') {
+                               hadcnt = 1;
+                               cnt = vgetcnt();
+                               forbid (cnt <= 0);
+                       }
+                       if (peekkey() != '"')
+                               break;
+                       ignore(getkey()), c = getkey();
+                       /*
+                        * Buffer names be letters or digits.
+                        * But not '0' as that is the source of
+                        * an 'empty' named buffer spec in the routine
+                        * kshift (see ex_temp.c).
+                        */
+                       forbid (c == '0' || !xisalpha(c) && !xisdigit(c));
+                       vreg = c;
+               }
+reread:
+               /*
+                * Come to reread from below after some macro expansions.
+                * The call to map allows use of function key pads
+                * by performing a terminal dependent mapping of inputs.
+                */
+#ifdef MDEBUG
+               if (trace)
+                       fprintf(trace,"pcb=%c,",peekkey());
+#endif
+               op = getkey();
+               maphopcnt = 0;
+               do {
+                       /*
+                        * Keep mapping the char as long as it changes.
+                        * This allows for double mappings, e.g., q to #,
+                        * #1 to something else.
+                        */
+                       c = op;
+                       op = map(c,arrows);
+#ifdef MDEBUG
+                       if (trace)
+                               fprintf(trace,"pca=%c,",c);
+#endif
+                       /*
+                        * Maybe the mapped to char is a count. If so, we have
+                        * to go back to the "for" to interpret it. Likewise
+                        * for a buffer name.
+                        */
+                       if ((xisdigit(c) && c!='0') || c == '"') {
+                               ungetkey(c);
+                               goto looptop;
+                       }
+                       if (!value(REMAP)) {
+                               c = op;
+                               break;
+                       }
+                       if (++maphopcnt > 256)
+                               error(catgets(catd, 1, 225,
+                                               "Infinite macro loop"));
+               } while (c != op);
+
+               /*
+                * Begin to build an image of this command for possible
+                * later repeat in the buffer workcmd.  It will be copied
+                * to lastcmd by the routine setLAST
+                * if/when completely specified.
+                */
+               lastcp = workcmd;
+               if (!vglobp)
+                       *lastcp++ = c;
+
+               /*
+                * First level command decode.
+                */
+               if (c == ATTN)
+                       goto case_ATTN;
+               switch (c) {
+
+               /*
+                * ^L           Clear screen e.g. after transmission error.
+                */
+
+               /*
+                * ^R           Retype screen, getting rid of @ lines.
+                *              If in open, equivalent to ^L.
+                *              On terminals where the right arrow key sends
+                *              ^L we make ^R act like ^L, since there is no
+                *              way to get ^L.  These terminals (adm31, tvi)
+                *              are intelligent so ^R is useless.  Soroc
+                *              will probably foul this up, but nobody has
+                *              one of them.
+                */
+               case CTRL('l'):
+               case CTRL('r'):
+                       if (c == CTRL('l') || (KR && *KR==CTRL('l'))) {
+                               vclear();
+                               vdirty(0, vcnt);
+                       }
+                       if (state != VISUAL) {
+                               /*
+                                * Get a clean line, throw away the
+                                * memory of what is displayed now,
+                                * and move back onto the current line.
+                                */
+                               vclean();
+                               vcnt = 0;
+                               vmoveto(dot, cursor, 0);
+                               continue;
+                       }
+                       vredraw(WTOP);
+                       /*
+                        * Weird glitch -- when we enter visual
+                        * in a very small window we may end up with
+                        * no lines on the screen because the line
+                        * at the top is too long.  This forces the screen
+                        * to be expanded to make room for it (after
+                        * we have printed @'s ick showing we goofed).
+                        */
+                       if (vcnt == 0)
+                               vrepaint(cursor);
+                       vfixcurs();
+                       continue;
+
+               /*
+                * $            Escape just cancels the current command
+                *              with a little feedback.
+                */
+               case ESCAPE:
+                       beep();
+                       continue;
+
+               /*
+                * @            Macros. Bring in the macro and put it
+                *              in vmacbuf, point vglobp there and punt.
+                */
+                case '@':
+                       c = getesc();
+                       if (c == 0)
+                               continue;
+                       if (c == '@')
+                               c = lastmac;
+                       if (xisupper(c))
+                               c = xtolower(c);
+                       forbid(!xislower(c));
+                       lastmac = c;
+                       vsave();
+                       CATCH
+                               char tmpbuf[BUFSIZ];
+
+                               regbuf(c,tmpbuf,sizeof(vmacbuf));
+                               macpush(tmpbuf, 1);
+                       ONERR
+                               lastmac = 0;
+                               splitw = 0;
+                               getDOT();
+                               vrepaint(cursor);
+                               continue;
+                       ENDCATCH
+                       vmacp = vmacbuf;
+                       goto reread;
+
+               /*
+                * .            Repeat the last (modifying) open/visual command.
+                */
+               case '.':
+                       /*
+                        * Check that there was a last command, and
+                        * take its count and named buffer unless they
+                        * were given anew.  Special case if last command
+                        * referenced a numeric named buffer -- increment
+                        * the number and go to a named buffer again.
+                        * This allows a sequence like "1pu.u.u...
+                        * to successively look for stuff in the kill chain
+                        * much as one does in EMACS with C-Y and M-Y.
+                        */
+                       forbid (lastcmd[0] == 0);
+                       if (hadcnt)
+                               lastcnt = cnt;
+                       if (vreg)
+                               lastreg = vreg;
+                       else if (xisdigit(lastreg) && lastreg < '9')
+                               lastreg++;
+                       vreg = lastreg;
+                       cnt = lastcnt;
+                       hadcnt = lasthad;
+                       vglobp = lastcmd;
+                       goto reread;
+
+               /*
+                * ^U           Scroll up.  A count sticks around for
+                *              future scrolls as the scroll amount.
+                *              Attempt to hold the indentation from the
+                *              top of the screen (in logical lines).
+                *
+                * BUG:         A ^U near the bottom of the screen
+                *              on a dumb terminal (which can't roll back)
+                *              causes the screen to be cleared and then
+                *              redrawn almost as it was.  In this case
+                *              one should simply move the cursor.
+                */
+               case CTRL('u'):
+                       if (hadcnt)
+                               vSCROLL = cnt;
+                       cnt = vSCROLL;
+                       if (state == VISUAL)
+                               ind = vcline, cnt += ind;
+                       else
+                               ind = 0;
+                       vmoving = 0;
+                       vup(cnt, ind, 1);
+                       vnline(NOSTR);
+                       continue;
+
+               /*
+                * ^D           Scroll down.  Like scroll up.
+                */
+               case CTRL('d'):
+#ifdef TRACE
+               if (trace)
+                       fprintf(trace, "before vdown in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
+#endif
+                       if (hadcnt)
+                               vSCROLL = cnt;
+                       cnt = vSCROLL;
+                       if (state == VISUAL)
+                               ind = vcnt - vcline - 1, cnt += ind;
+                       else
+                               ind = 0;
+                       vmoving = 0;
+                       vdown(cnt, ind, 1);
+#ifdef TRACE
+               if (trace)
+                       fprintf(trace, "before vnline in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
+#endif
+                       vnline(NOSTR);
+#ifdef TRACE
+               if (trace)
+                       fprintf(trace, "after vnline in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
+#endif
+                       continue;
+
+               /*
+                * ^E           Glitch the screen down (one) line.
+                *              Cursor left on same line in file.
+                */
+               case CTRL('e'):
+                       if (state != VISUAL)
+                               continue;
+                       if (!hadcnt)
+                               cnt = 1;
+                       /* Bottom line of file already on screen */
+                       forbid(lineDOL()-lineDOT() <= vcnt-1-vcline);
+                       ind = vcnt - vcline - 1 + cnt;
+                       vdown(ind, ind, 1);
+                       vnline(cursor);
+                       continue;
+
+               /*
+                * ^Y           Like ^E but up
+                */
+               case CTRL('y'):
+                       if (state != VISUAL)
+                               continue;
+                       if (!hadcnt)
+                               cnt = 1;
+                       forbid(lineDOT()-1<=vcline); /* line 1 already there */
+                       ind = vcline + cnt;
+                       vup(ind, ind, 1);
+                       vnline(cursor);
+                       continue;
+
+
+               /*
+                * m            Mark position in mark register given
+                *              by following letter.  Return is
+                *              accomplished via ' or `; former
+                *              to beginning of line where mark
+                *              was set, latter to column where marked.
+                */
+               case 'm':
+                       /*
+                        * Getesc is generally used when a character
+                        * is read as a latter part of a command
+                        * to allow one to hit rubout/escape to cancel
+                        * what you have typed so far.  These characters
+                        * are mapped to 0 by the subroutine.
+                        */
+                       c = getesc();
+                       if (c == 0)
+                               continue;
+
+                       /*
+                        * Markreg checks that argument is a letter
+                        * and also maps ' and ` to the end of the range
+                        * to allow '' or `` to reference the previous
+                        * context mark.
+                        */
+                       c = markreg(c);
+                       forbid (c == 0);
+                       vsave();
+                       names[c - 'a'] = (*dot &~ 01);
+                       ncols[c - 'a'] = cursor;
+                       anymarks = 1;
+                       continue;
+
+               /*
+                * ^F           Window forwards, with 2 lines of continuity.
+                *              Count repeats.
+                */
+               case CTRL('f'):
+                       vsave();
+                       if (vcnt > 2) {
+                               addr = dot + (vcnt - vcline) - 2 + (cnt-1)*basWLINES;
+                               forbid(addr > dol);
+                               dot = (line*)addr;
+                               vcnt = vcline = 0;
+                       }
+                       vzop(0, 0, '+');
+                       continue;
+
+               /*
+                * ^B           Window backwards, with 2 lines of continuity.
+                *              Inverse of ^F.
+                */
+               case CTRL('b'):
+                       vsave();
+                       if (one + vcline != dot && vcnt > 2) {
+                               addr = dot - vcline + 2 - (cnt-1)*basWLINES;
+                               forbid (addr <= zero);
+                               dot = (line*)addr;
+                               vcnt = vcline = 0;
+                       }
+                       vzop(0, 0, '^');
+                       continue;
+
+               /*
+                * z            Screen adjustment, taking a following character:
+                *                      z<CR>           current line to top
+                *                      z<NL>           like z<CR>
+                *                      z-              current line to bottom
+                *              also z+, z^ like ^F and ^B.
+                *              A preceding count is line to use rather
+                *              than current line.  A count between z and
+                *              specifier character changes the screen size
+                *              for the redraw.
+                *
+                */
+               case 'z':
+                       if (state == VISUAL) {
+                               i = vgetcnt();
+                               if (i > 0)
+                                       vsetsiz(i);
+                               c = getesc();
+                               if (c == 0)
+                                       continue;
+                       }
+                       vsave();
+                       vzop(hadcnt, cnt, c);
+                       continue;
+
+               /*
+                * Y            Yank lines, abbreviation for y_ or yy.
+                *              Yanked lines can be put later if no
+                *              changes intervene, or can be put in named
+                *              buffers and put anytime in this session.
+                */
+               case 'Y':
+                       ungetkey('_');
+                       c = 'y';
+                       break;
+
+               /*
+                * J            Join lines, 2 by default.  Count is number
+                *              of lines to join (no join operator sorry.)
+                */
+               case 'J':
+                       forbid (dot == dol);
+                       if (cnt == 1)
+                               cnt = 2;
+                       if (cnt > (i = dol - dot + 1))
+                               cnt = i;
+                       vsave();
+                       vmacchng(1);
+                       setLAST();
+                       cursor = strend(linebuf);
+                       vremote(cnt, join, 0);
+                       notenam = "join";
+                       vmoving = 0;
+                       killU();
+                       vreplace(vcline, cnt, 1);
+                       if (!*cursor && cursor > linebuf)
+                               cursor += skipleft(linebuf, cursor);
+                       if (notecnt == 2)
+                               notecnt = 0;
+                       vrepaint(cursor);
+                       continue;
+
+               /*
+                * S            Substitute text for whole lines, abbrev for c_.
+                *              Count is number of lines to change.
+                */
+               case 'S':
+                       ungetkey('_');
+                       c = 'c';
+                       break;
+
+               /*
+                * O            Create a new line above current and accept new
+                *              input text, to an escape, there.
+                *              A count specifies, for dumb terminals when
+                *              slowopen is not set, the number of physical
+                *              line space to open on the screen.
+                *
+                * o            Like O, but opens lines below.
+                */
+               case 'O':
+               case 'o':
+                       vmacchng(1);
+                       voOpen(c, cnt);
+                       continue;
+
+               /*
+                * C            Change text to end of line, short for c$.
+                */
+               case 'C':
+                       if (*cursor) {
+                               ungetkey('$'), c = 'c';
+                               break;
+                       }
+                       goto appnd;
+
+               /*
+                * ~    Switch case of letter under cursor
+                */
+               case '~':
+                       vswitch(cnt);
+                       continue;
+
+
+               /*
+                * A            Append at end of line, short for $a.
+                */
+               case 'A':
+                       operate('$', 1);
+appnd:
+                       c = 'a';
+                       /* fall into ... */
+
+               /*
+                * a            Appends text after cursor.  Text can continue
+                *              through arbitrary number of lines.
+                */
+               case 'a':
+                       if (*cursor) {
+                               if (state == HARDOPEN) {
+                                       int     c, n;
+                                       nextc(c, cursor, n);
+                                       putchar(c);
+                                       cursor += n;
+                               } else
+                                       cursor += skipright(linebuf, cursor);
+                       }
+                       goto insrt;
+
+               /*
+                * I            Insert at beginning of whitespace of line,
+                *              short for ^i.
+                */
+               case 'I':
+                       operate('^', 1);
+                       c = 'i';
+                       /* fall into ... */
+
+               /*
+                * R            Replace characters, one for one, by input
+                *              (logically), like repeated r commands.
+                *
+                * BUG:         This is like the typeover mode of many other
+                *              editors, and is only rarely useful.  Its
+                *              implementation is a hack in a low level
+                *              routine and it doesn't work very well, e.g.
+                *              you can't move around within a R, etc.
+                */
+               case 'R':
+                       /* fall into... */
+
+               /*
+                * i            Insert text to an escape in the buffer.
+                *              Text is arbitrary.  This command reminds of
+                *              the i command in bare teco.
+                */
+               case 'i':
+insrt:
+                       /*
+                        * Common code for all the insertion commands.
+                        * Save for redo, position cursor, prepare for append
+                        * at command and in visual undo.  Note that nothing
+                        * is doomed, unless R when all is, and save the
+                        * current line in a the undo temporary buffer.
+                        */
+                       vmacchng(1);
+                       setLAST();
+                       vcursat(cursor);
+                       prepapp();
+                       vnoapp();
+                       doomed = c == 'R' ? 10000 : 0;
+                       if(FIXUNDO)
+                               vundkind = VCHNG;
+                       vmoving = 0;
+                       CP(vutmp, linebuf);
+
+                       /*
+                        * If this is a repeated command, then suppress
+                        * fake insert mode on dumb terminals which looks
+                        * ridiculous and wastes lots of time even at 9600B.
+                        */
+                       if (vglobp)
+                               hold = HOLDQIK;
+                       vappend(c, cnt, 0);
+                       continue;
+
+               /*
+                * ^?           An attention, normally a ^?, just beeps.
+                *              If you are a vi command within ex, then
+                *              two ATTN's will drop you back to command mode.
+                */
+case_ATTN:
+                       beep();
+                       if (initev || peekkey() != ATTN)
+                               continue;
+                       /* fall into... */
+
+               /*
+                * ^\           A quit always gets command mode.
+                */
+               case QUIT:
+                       /*
+                        * Have to be careful if we were called
+                        *      g/xxx/vi
+                        * since a return will just start up again.
+                        * So we simulate an interrupt.
+                        */
+                       if (inglobal)
+                               onintr(SIGINT);
+                       /* fall into... */
+
+#ifdef notdef
+               /*
+                * q            Quit back to command mode, unless called as
+                *              vi on command line in which case dont do it
+                */
+               case 'q':       /* quit */
+                       if (initev) {
+                               vsave();
+                               CATCH
+                                       error(catgets(catd, 1, 226,
+                               "Q gets ex command mode, :q leaves vi"));
+                               ENDCATCH
+                               splitw = 0;
+                               getDOT();
+                               vrepaint(cursor);
+                               continue;
+                       }
+#endif
+                       /* fall into... */
+
+               /*
+                * Q            Is like q, but always gets to command mode
+                *              even if command line invocation was as vi.
+                */
+               case 'Q':
+                       vsave();
+                       /*
+                        * If we are in the middle of a macro, throw away
+                        * the rest and fix up undo.
+                        * This code copied from getbr().
+                        */
+                       if (vmacp) {
+                               vmacp = 0;
+                               if (inopen == -1)       /* don't screw up undo for esc esc */
+                                       vundkind = VMANY;
+                               inopen = 1;     /* restore old setting now that macro done */
+                       }
+                       return;
+
+
+               /*
+                * ZZ           Like :x
+                */
+                case 'Z':
+                       forbid(getkey() != 'Z');
+                       oglobp = globp;
+                       globp = "x";
+                       vclrech(0);
+                       goto gogo;
+                       
+               /*
+                * P            Put back text before cursor or before current
+                *              line.  If text was whole lines goes back
+                *              as whole lines.  If part of a single line
+                *              or parts of whole lines splits up current
+                *              line to form many new lines.
+                *              May specify a named buffer, or the delete
+                *              saving buffers 1-9.
+                *
+                * p            Like P but after rather than before.
+                */
+               case 'P':
+               case 'p':
+                       vmoving = 0;
+#ifdef notdef
+                       forbid (!vreg && value(UNDOMACRO) && inopen < 0);
+#endif
+                       /*
+                        * If previous delete was partial line, use an
+                        * append or insert to put it back so as to
+                        * use insert mode on intelligent terminals.
+                        */
+                       if (!vreg && DEL[0]) {
+                               forbid ((DEL[0] & (QUOTE|TRIM)) == OVERBUF);
+                               vglobp = DEL;
+                               ungetkey(c == 'p' ? 'a' : 'i');
+                               goto reread;
+                       }
+
+                       /*
+                        * If a register wasn't specified, then make
+                        * sure there is something to put back.
+                        */
+                       forbid (!vreg && unddol == dol);
+                       /*
+                        * If we just did a macro the whole buffer is in
+                        * the undo save area.  We don't want to put THAT.
+                        */
+                       forbid (vundkind == VMANY && undkind==UNDALL);
+                       vsave();
+                       vmacchng(1);
+                       setLAST();
+                       i = 0;
+                       if (vreg && partreg(vreg) || !vreg && pkill[0]) {
+                               /*
+                                * Restoring multiple lines which were partial
+                                * lines; will leave cursor in middle
+                                * of line after shoving restored text in to
+                                * split the current line.
+                                */
+                               i++;
+                               if (c == 'p' && *cursor)
+                                       cursor += skipright(linebuf, cursor);
+                       } else {
+                               /*
+                                * In whole line case, have to back up dot
+                                * for P; also want to clear cursor so
+                                * cursor will eventually be positioned
+                                * at the beginning of the first put line.
+                                */
+                               cursor = 0;
+                               if (c == 'P') {
+                                       dot--, vcline--;
+                                       c = 'p';
+                               }
+                       }
+                       killU();
+
+                       /*
+                        * The call to putreg can potentially
+                        * bomb since there may be nothing in a named buffer.
+                        * We thus put a catch in here.  If we didn't and
+                        * there was an error we would end up in command mode.
+                        */
+                       addr = dol;     /* old dol */
+                       CATCH
+                               vremote(1, vreg ? putreg : put, vreg);
+                       ONERR
+                               if (vreg == -1) {
+                                       splitw = 0;
+                                       if (op == 'P')
+                                               dot++, vcline++;
+                                       goto pfixup;
+                               }
+                       ENDCATCH
+                       splitw = 0;
+                       nlput = dol - addr + 1;
+                       if (!i) {
+                               /*
+                                * Increment undap1, undap2 to make up
+                                * for their incorrect initialization in the
+                                * routine vremote before calling put/putreg.
+                                */
+                               if (FIXUNDO)
+                                       undap1++, undap2++;
+                               vcline++;
+                               nlput--;
+
+                               /*
+                                * After a put want current line first line,
+                                * and dot was made the last line put in code
+                                * run so far.  This is why we increment vcline
+                                * above and decrease dot here.
+                                */
+                               dot -= nlput - 1;
+                       }
+#ifdef TRACE
+                       if (trace)
+                               fprintf(trace, "vreplace(%d, %d, %d), undap1=%d, undap2=%d, dot=%d\n", vcline, i, nlput, lineno(undap1), lineno(undap2), lineno(dot));
+#endif
+                       vreplace(vcline, i, nlput);
+                       if (state != VISUAL) {
+                               /*
+                                * Special case in open mode.
+                                * Force action on the screen when a single
+                                * line is put even if it is identical to
+                                * the current line, e.g. on YP; otherwise
+                                * you can't tell anything happened.
+                                */
+                               vjumpto(dot, cursor, '.');
+                               continue;
+                       }
+pfixup:
+                       vrepaint(cursor);
+                       vfixcurs();
+                       continue;
+
+               /*
+                * ^^           Return to previous file.
+                *              Like a :e #, and thus can be used after a
+                *              "No Write" diagnostic.
+                */
+               case CTRL('^'):
+                       forbid (hadcnt);
+                       vsave();
+                       ckaw();
+                       oglobp = globp;
+                       if (value(AUTOWRITE))
+                               globp = "e! #";
+                       else
+                               globp = "e #";
+                       goto gogo;
+
+               /*
+                * ^]           Takes word after cursor as tag, and then does
+                *              tag command.  Read ``go right to''.
+                */
+               case CTRL(']'):
+                       grabtag();
+                       oglobp = globp;
+                       globp = "tag";
+                       goto gogo;
+
+               /*
+                * &            Like :&
+                */
+                case '&':
+                       oglobp = globp;
+                       globp = "&";
+                       goto gogo;
+                       
+               /*
+                * ^G           Bring up a status line at the bottom of
+                *              the screen, like a :file command.
+                *
+                * BUG:         Was ^S but doesn't work in cbreak mode
+                */
+               case CTRL('g'):
+                       oglobp = globp;
+                       globp = "file";
+gogo:
+                       addr = dot;
+                       vsave();
+                       goto doinit;
+
+#ifdef SIGTSTP
+               /*
+                * ^Z:  suspend editor session and temporarily return
+                *      to shell.  Only works with Berkeley/IIASA process
+                *      control in kernel.
+                */
+               case CTRL('z'):
+                       forbid(dosusp == 0 || !ldisc);
+                       vsave();
+                       oglobp = globp;
+                       globp = "stop";
+                       goto gogo;
+#endif
+
+               /*
+                * :            Read a command from the echo area and
+                *              execute it in command mode.
+                */
+               case ':':
+                       forbid (hadcnt);
+                       vsave();
+                       i = tchng;
+                       addr = dot;
+                       if (readecho(c)) {
+                               esave[0] = 0;
+                               goto fixup;
+                       }
+                       getDOT();
+                       /*
+                        * Use the visual undo buffer to store the global
+                        * string for command mode, since it is idle right now.
+                        */
+                       oglobp = globp;
+                       CP(vutmp, genbuf+1);
+                       globp = vutmp;
+doinit:
+                       esave[0] = 0;
+                       fixech();
+
+                       /*
+                        * Have to finagle around not to lose last
+                        * character after this command (when run from ex
+                        * command mode).  This is clumsy.
+                        */
+                       d = peekc; ungetchar(0);
+                       if (shouldpo) {
+                               /*
+                                * So after a "Hit return..." ":", we do
+                                * another "Hit return..." the next time
+                                */
+                               pofix();
+                               shouldpo = 0;
+                       }
+                       CATCH
+                               /*
+                                * Save old values of options so we can
+                                * notice when they change; switch into
+                                * cooked mode so we are interruptible.
+                                */
+                               onumber = value(NUMBER);
+                               olist = value(LIST);
+                               OPline = Pline;
+                               OPutchar = Putchar;
+                               commands(1, 1);
+                               if (dot == zero && dol > zero)
+                                       dot = one;
+                       ONERR
+                               copy(esave, vtube[WECHO],
+                                               TUBECOLS * sizeof *esave);
+                       ENDCATCH
+                       fixol();
+                       Pline = OPline;
+                       Putchar = OPutchar;
+                       ungetchar(d);
+                       if (globp && tflag < 0) {
+                               tflag = 0;
+                               goto doinit;
+                       }
+                       globp = oglobp;
+
+                       /*
+                        * If we ended up with no lines in the buffer, make
+                        * a line, and don't consider the buffer changed.
+                        */
+                       if (dot == zero) {
+                               fixzero();
+                               /*synced();*/
+                       }
+                       splitw = 0;
+
+                       /*
+                        * Special case: did list/number options change?
+                        */
+                       if (onumber != value(NUMBER))
+                               setnumb(value(NUMBER));
+                       if (olist != value(LIST))
+                               setlist(value(LIST));
+
+fixup:
+                       /*
+                        * If a change occurred, other than
+                        * a write which clears changes, then
+                        * we should allow an undo even if .
+                        * didn't move.
+                        *
+                        * BUG: You can make this wrong by
+                        * tricking around with multiple commands
+                        * on one line of : escape, and including
+                        * a write command there, but its not
+                        * worth worrying about.
+                        */
+                       if (FIXUNDO && tchng && tchng != i)
+                               vundkind = VMANY, cursor = 0;
+
+                       /*
+                        * If we are about to do another :, hold off
+                        * updating of screen.
+                        */
+                       if (vcnt < 0 && Peekkey == ':') {
+                               getDOT();
+                               shouldpo = 1;
+                               continue;
+                       }
+                       shouldpo = 0;
+
+                       /*
+                        * In the case where the file being edited is
+                        * new; e.g. if the initial state hasn't been
+                        * saved yet, then do so now.
+                        */
+                       if (unddol == truedol) {
+                               vundkind = VNONE;
+                               Vlines = lineDOL();
+                               if (!inglobal)
+                                       savevis();
+                               addr = zero;
+                               vcnt = 0;
+                               if (esave[0] == 0)
+                                       copy(esave, vtube[WECHO],
+                                               TUBECOLS * sizeof *esave);
+                       }
+
+                       /*
+                        * If the current line moved reset the cursor position.
+                        */
+                       if (dot != addr) {
+                               vmoving = 0;
+                               cursor = 0;
+                       }
+
+                       /*
+                        * If current line is not on screen or if we are
+                        * in open mode and . moved, then redraw.
+                        */
+                       i = vcline + (dot - addr);
+                       if (i < 0 || i >= vcnt && i >= -vcnt || state != VISUAL && dot != addr) {
+                               if (state == CRTOPEN)
+                                       vup1();
+                               if (vcnt > 0)
+                                       vcnt = 0;
+                               vjumpto(dot, (char *) 0, '.');
+                       } else {
+                               /*
+                                * Current line IS on screen.
+                                * If we did a [Hit return...] then
+                                * restore vcnt and clear screen if in visual
+                                */
+                               vcline = i;
+                               if (vcnt < 0) {
+                                       vcnt = -vcnt;
+                                       if (state == VISUAL)
+                                               vclear();
+                                       else if (state == CRTOPEN) {
+                                               vcnt = 0;
+                                       }
+                               }
+
+                               /*
+                                * Limit max value of vcnt based on $
+                                */
+                               i = vcline + lineDOL() - lineDOT() + 1;
+                               if (i < vcnt)
+                                       vcnt = i;
+                               
+                               /*
+                                * Dirty and repaint.
+                                */
+                               vdirty(0, TLINES);
+                               vrepaint(cursor);
+                       }
+
+                       /*
+                        * If in visual, put back the echo area
+                        * if it was clobberred.
+                        */
+                       if (state == VISUAL) {
+                               int sdc = destcol, sdl = destline;
+
+                               splitw++;
+                               vigoto(WECHO, 0);
+                               for (i = 0; i < TUBECOLS - 1; i++) {
+                                       if (esave[i] == 0)
+                                               break;
+                                       vputchar(esave[i]);
+                               }
+                               splitw = 0;
+                               vgoto(sdl, sdc);
+                       }
+                       continue;
+
+               /*
+                * u            undo the last changing command.
+                */
+               case 'u':
+                       vundo(1);
+                       continue;
+
+               /*
+                * U            restore current line to initial state.
+                */
+               case 'U':
+                       vUndo();
+                       continue;
+
+fonfon:
+                       beep();
+                       vmacp = 0;
+                       inopen = 1;     /* might have been -1 */
+                       continue;
+               }
+
+               /*
+                * Rest of commands are decoded by the operate
+                * routine.
+                */
+               operate(c, cnt);
+       }
+}
+
+/*
+ * Grab the word after the cursor so we can look for it as a tag.
+ */
+void 
+grabtag(void)
+{
+       register char *cp, *dp;
+
+       cp = vpastwh(cursor);
+       if (*cp) {
+               dp = lasttag;
+               do {
+                       if (dp < &lasttag[sizeof lasttag - 2])
+                               *dp++ = *cp;
+                       cp++;
+               } while (isalpha(*cp&0377) || isdigit(*cp&0377)
+                               || *cp == '_'
+#ifdef LISPCODE
+                       || (value(LISP) && *cp == '-')
+#endif /* LISPCODE */
+                       );
+               *dp++ = 0;
+       }
+}
+
+/*
+ * Before appending lines, set up addr1 and
+ * the command mode undo information.
+ */
+void 
+prepapp(void)
+{
+
+       addr1 = dot;
+       deletenone();
+       addr1++;
+       appendnone();
+}
+
+/*
+ * Execute function f with the address bounds addr1
+ * and addr2 surrounding cnt lines starting at dot.
+ */
+void 
+vremote(int cnt, void (*f)(int), int arg)
+{
+       register int oing = inglobal;
+
+       addr1 = dot;
+       addr2 = dot + cnt - 1;
+       inglobal = 0;
+       if (FIXUNDO)
+               undap1 = undap2 = dot;
+       (*f)(arg);
+       inglobal = oing;
+       if (FIXUNDO)
+               vundkind = VMANY;
+       vmcurs = 0;
+}
+
+/*
+ * Save the current contents of linebuf, if it has changed.
+ */
+void 
+vsave(void)
+{
+       char temp[LBSIZE];
+
+       CP(temp, linebuf);
+       if (FIXUNDO && vundkind == VCHNG || vundkind == VCAPU) {
+               /*
+                * If the undo state is saved in the temporary buffer
+                * vutmp, then we sync this into the temp file so that
+                * we will be able to undo even after we have moved off
+                * the line.  It would be possible to associate a line
+                * with vutmp but we assume that vutmp is only associated
+                * with line dot (e.g. in case ':') above, so beware.
+                */
+               prepapp();
+               CP(linebuf, vutmp);
+               putmark(dot);
+               vremote(1, yank, 0);
+               vundkind = VMCHNG;
+               notecnt = 0;
+               undkind = UNDCHANGE;
+       }
+       /*
+        * Get the line out of the temp file and do nothing if it hasn't
+        * changed.  This may seem like a loss, but the line will
+        * almost always be in a read buffer so this may well avoid disk i/o.
+        */
+       getDOT();
+       if (strcmp(linebuf, temp) == 0)
+               return;
+       strcLIN(temp);
+       putmark(dot);
+}
+
+#undef forbid
+#define        forbid(a)       if (a) { beep(); return; }
+
+/*
+ * Do a z operation.
+ * Code here is rather long, and very uninteresting.
+ */
+void 
+vzop(int hadcnt, int cnt, register int c)
+{
+       register line *addr;
+
+       if (state != VISUAL) {
+               /*
+                * Z from open; always like a z=.
+                * This code is a mess and should be cleaned up.
+                */
+               vmoveitup(1, 1);
+               vgoto(outline, 0);
+               ostop(normf);
+               setoutt();
+               addr2 = dot;
+               vclear();
+               destline = WECHO;
+               zop2(Xhadcnt ? Xcnt : value(WINDOW) - 1, '=');
+               if (state == CRTOPEN)
+                       putnl();
+               putNFL();
+               termreset();
+               Outchar = vputchar;
+               ignore(ostart());
+               vcnt = 0;
+               outline = destline = 0;
+               vjumpto(dot, cursor, 0);
+               return;
+       }
+       if (hadcnt) {
+               addr = zero + cnt;
+               if (addr < one)
+                       addr = one;
+               if (addr > dol)
+                       addr = dol;
+               markit(addr);
+       } else
+               switch (c) {
+
+               case '+':
+                       addr = dot + vcnt - vcline;
+                       break;
+
+               case '^':
+                       addr = dot - vcline - 1;
+                       forbid (addr < one);
+                       c = '-';
+                       break;
+
+               default:
+                       addr = dot;
+                       break;
+               }
+       switch (c) {
+
+       case '.':
+       case '-':
+               break;
+
+       case '^':
+               forbid (addr <= one);
+               break;
+
+       case '+':
+               forbid (addr >= dol);
+               /* fall into ... */
+
+       case CR:
+       case NL:
+               c = CR;
+               break;
+
+       default:
+               beep();
+               return;
+       }
+       vmoving = 0;
+       vjumpto(addr, NOSTR, c);
+}
+
+cell *
+str2cell(cell *dst, register char *src)
+{
+       register cell *cp = dst;
+
+#ifdef MB
+       if (mb_cur_max > 1) {
+               int     c, n;
+               do {
+                       nextc(c, src, n);
+                       src += n;
+                       *cp++ = c;
+               } while (src[-n]);
+       } else
+#endif /* MB */
+               while (*cp++ = *src++ & 0377);
+       return dst;
+}
+
+char *
+cell2str(char *dst, register cell *src)
+{
+       register char *cp = dst;
+
+       while (*cp++ = *src++);
+       return dst;
+}
+
+cell *
+cellcpy(cell *dst, register cell *src)
+{
+       register cell *cp = dst;
+
+       while (*cp++ = *src++);
+       return dst;
+}
+
+size_t 
+cellen(register cell *cp)
+{
+       register size_t sz = 0;
+
+       while (*cp++)
+               sz++;
+       return sz;
+}
+
+cell *
+cellcat(cell *dst, register cell *src)
+{
+       register cell *cp = dst;
+
+       while (*cp)
+               cp++;
+       cellcpy(cp, src);
+       return dst;
+}
diff --git a/ex_voper.c b/ex_voper.c
new file mode 100644 (file)
index 0000000..d86b5d9
--- /dev/null
@@ -0,0 +1,976 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_voper.c 1.27 (gritter) 2/15/05";
+#endif
+#endif
+
+/* from ex_voper.c     7.4 (Berkeley) 6/7/85 */
+
+#include "ex.h"
+#include "ex_re.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+
+#ifdef MB
+static int
+cblank(char *cp)
+{
+       if (mb_cur_max > 1 && *cp & 0200) {
+               int     c;
+               return mbtowi(&c, cp, mb_cur_max) > 0 && iswspace(c);
+       } else
+               return isspace(*cp&0377);
+}
+#define        blank()         cblank(wcursor)
+#else  /* !MB */
+#define        cblank(cp)      isspace(*cp&0377)
+#define        blank()         xisspace(wcursor[0]&TRIM)
+#endif /* !MB */
+#define        forbid(a)       if (a) goto errlab;
+
+cell   vscandir[2] =   { '/', 0 };
+
+/*
+ * Decode an operator/operand type command.
+ * Eventually we switch to an operator subroutine in ex_vops.c.
+ * The work here is setting up a function variable to point
+ * to the routine we want, and manipulation of the variables
+ * wcursor and wdot, which mark the other end of the affected
+ * area.  If wdot is zero, then the current line is the other end,
+ * and if wcursor is zero, then the first non-blank location of the
+ * other line is implied.
+ */
+void 
+operate(register int c, register int cnt)
+{
+       register int i = 0;
+       void (*moveop)(int), (*deleteop)(int);
+       void (*opf)(int);
+       bool subop = 0;
+       char *oglobp, *ocurs;
+       register line *addr;
+       line *odot;
+       static int lastFKND, lastFCHR;
+       short d;
+       cell nullcell[1], qmarkcell[2], slashcell[2];
+
+       CLOBBGRD(opf);
+       CLOBBGRD(d);
+       qmarkcell[0] = '?';
+       slashcell[0] = '/';
+       nullcell[0] = qmarkcell[1] = slashcell[1] = 0;
+       moveop = vmove, deleteop = vdelete;
+       wcursor = cursor;
+       wdot = NOLINE;
+       notecnt = 0;
+       dir = 1;
+       switch (c) {
+
+       /*
+        * d            delete operator.
+        */
+       case 'd':
+               moveop = vdelete;
+               deleteop = (void (*)(int))beep;
+               break;
+
+       /*
+        * s            substitute characters, like c\040, i.e. change space.
+        */
+       case 's':
+               ungetkey(' ');
+               subop++;
+               /* fall into ... */
+
+       /*
+        * c            Change operator.
+        */
+       case 'c':
+               if (c == 'c' && workcmd[0] == 'C' || workcmd[0] == 'S')
+                       subop++;
+               moveop = vchange;
+               deleteop = (void (*)(int))beep;
+               break;
+
+       /*
+        * !            Filter through a UNIX command.
+        */
+       case '!':
+               moveop = vfilter;
+               deleteop = (void (*)(int))beep;
+               break;
+
+       /*
+        * y            Yank operator.  Place specified text so that it
+        *              can be put back with p/P.  Also yanks to named buffers.
+        */
+       case 'y':
+               moveop = vyankit;
+               deleteop = (void (*)(int))beep;
+               break;
+
+       /*
+        * =            Reformat operator (for LISP).
+        */
+#ifdef LISPCODE
+       case '=':
+               forbid(!value(LISP));
+               /* fall into ... */
+#endif
+
+       /*
+        * >            Right shift operator.
+        * <            Left shift operator.
+        */
+       case '<':
+       case '>':
+               moveop = vshftop;
+               deleteop = (void (*)(int))beep;
+               break;
+
+       /*
+        * r            Replace character under cursor with single following
+        *              character.
+        */
+       case 'r':
+               vmacchng(1);
+               vrep(cnt);
+               return;
+
+       default:
+               goto nocount;
+       }
+       vmacchng(1);
+       /*
+        * Had an operator, so accept another count.
+        * Multiply counts together.
+        */
+       if (xisdigit(peekkey()) && peekkey() != '0') {
+               cnt *= vgetcnt();
+               Xcnt = cnt;
+               forbid (cnt <= 0);
+       }
+
+       /*
+        * Get next character, mapping it and saving as
+        * part of command for repeat.
+        */
+       c = map(getesc(),arrows);
+       if (c == 0)
+               return;
+       if (!subop)
+               *lastcp++ = c;
+nocount:
+       opf = moveop;
+       switch (c) {
+
+       /*
+        * b            Back up a word.
+        * B            Back up a word, liberal definition.
+        */
+       case 'b':
+       case 'B':
+               dir = -1;
+               /* fall into ... */
+
+       /*
+        * w            Forward a word.
+        * W            Forward a word, liberal definition.
+        */
+       case 'W':
+       case 'w':
+               wdkind = c & ' ';
+               forbid(llfind(2, cnt, opf, 0) < 0);
+               vmoving = 0;
+               break;
+
+       /*
+        * E            to end of following blank/nonblank word
+        */
+       case 'E':
+               wdkind = 0;
+               goto ein;
+
+       /*
+        * e            To end of following word.
+        */
+       case 'e':
+               wdkind = 1;
+ein:
+               forbid(llfind(3, cnt - 1, opf, 0) < 0);
+               vmoving = 0;
+               break;
+
+       /*
+        * (            Back an s-expression.
+        */
+       case '(':
+               dir = -1;
+               /* fall into... */
+
+       /*
+        * )            Forward an s-expression.
+        */
+       case ')':
+               forbid(llfind(0, cnt, opf, (line *) 0) < 0);
+               markDOT();
+               break;
+
+       /*
+        * {            Back an s-expression, but don't stop on atoms.
+        *              In text mode, a paragraph.  For C, a balanced set
+        *              of {}'s.
+        */
+       case '{':
+               dir = -1;
+               /* fall into... */
+
+       /*
+        * }            Forward an s-expression, but don't stop on atoms.
+        *              In text mode, back paragraph.  For C, back a balanced
+        *              set of {}'s.
+        */
+       case '}':
+               forbid(llfind(1, cnt, opf, (line *) 0) < 0);
+               markDOT();
+               break;
+
+       /*
+        * %            To matching () or {}.  If not at ( or { scan for
+        *              first such after cursor on this line.
+        */
+       case '%':
+               vsave();
+               i = lmatchp((line *) 0);
+#ifdef TRACE
+               if (trace)
+                       fprintf(trace, "after lmatchp in %, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
+#endif
+               getDOT();
+               forbid(!i);
+               if (opf != vmove)
+                       if (dir > 0)
+                               wcursor += skipright(linebuf, wcursor);
+                       else
+                               cursor += skipright(linebuf, cursor);
+               else
+                       markDOT();
+               vmoving = 0;
+               break;
+
+       /*
+        * [            Back to beginning of defun, i.e. an ( in column 1.
+        *              For text, back to a section macro.
+        *              For C, back to a { in column 1 (~~ beg of function.)
+        */
+       case '[':
+               dir = -1;
+               /* fall into ... */
+
+       /*
+        * ]            Forward to next defun, i.e. a ( in column 1.
+        *              For text, forward section.
+        *              For C, forward to a } in column 1 (if delete or such)
+        *              or if a move to a { in column 1.
+        */
+       case ']':
+               if (!vglobp)
+                       forbid(getkey() != c);
+               forbid (Xhadcnt);
+               vsave();
+               i = lbrack(c, opf);
+               getDOT();
+               forbid(!i);
+               markDOT();
+               if (ospeed > B300)
+                       hold |= HOLDWIG;
+               break;
+
+       /*
+        * ,            Invert last find with f F t or T, like inverse
+        *              of ;.
+        */
+       case ',':
+               forbid (lastFKND == 0);
+               c = xisupper(lastFKND&TRIM)
+                       ? xtolower(lastFKND&TRIM) : xtoupper(lastFKND&TRIM);
+               i = lastFCHR;
+               if (vglobp == 0)
+                       vglobp = nullcell;
+               subop++;
+               goto nocount;
+
+       /*
+        * 0            To beginning of real line.
+        */
+       case '0':
+               wcursor = linebuf;
+               vmoving = 0;
+               break;
+
+       /*
+        * ;            Repeat last find with f F t or T.
+        */
+       case ';':
+               forbid (lastFKND == 0);
+               c = lastFKND;
+               i = lastFCHR;
+               subop++;
+               goto nocount;
+
+       /*
+        * F            Find single character before cursor in current line.
+        * T            Like F, but stops before character.
+        */
+       case 'F':       /* inverted find */
+       case 'T':
+               dir = -1;
+               /* fall into ... */
+
+       /*
+        * f            Find single character following cursor in current line.
+        * t            Like f, but stope before character.
+        */
+       case 'f':       /* find */
+       case 't':
+               if (!subop) {
+                       i = getesc();
+                       if (i == 0)
+                               return;
+                       *lastcp++ = i;
+               }
+               if (vglobp == 0)
+                       lastFKND = c, lastFCHR = i;
+               for (; cnt > 0; cnt--)
+                       forbid (find(i) == 0);
+               vmoving = 0;
+               switch (c) {
+
+               case 'T':
+                       wcursor += skipright(linebuf, wcursor);
+                       break;
+
+               case 't':
+                       wcursor += skipleft(linebuf, wcursor);
+               case 'f':
+fixup:
+                       if (moveop != vmove)
+                               wcursor += skipright(linebuf, wcursor);
+                       break;
+               }
+               break;
+
+       /*
+        * |            Find specified print column in current line.
+        */
+       case '|':
+               if (Pline == numbline)
+                       cnt += 8;
+               vmovcol = cnt;
+               vmoving = 1;
+               wcursor = vfindcol(cnt);
+               break;
+
+       /*
+        * ^            To beginning of non-white space on line.
+        */
+       case '^':
+               wcursor = vskipwh(linebuf);
+               vmoving = 0;
+               break;
+
+       /*
+        * $            To end of line.
+        */
+       case '$':
+               if (opf == vmove) {
+                       vmoving = 1;
+                       vmovcol = 20000;
+               } else
+                       vmoving = 0;
+               if (cnt > 1) {
+                       if (opf == vmove) {
+                               wcursor = 0;
+                               cnt--;
+                       } else
+                               wcursor = linebuf;
+                       /* This is wrong at EOF */
+                       wdot = dot + cnt;
+                       break;
+               }
+               if (linebuf[0]) {
+                       wcursor = strend(linebuf) - 1;
+                       goto fixup;
+               }
+               wcursor = linebuf;
+               break;
+
+       /*
+        * h            Back a character.
+        * ^H           Back a character.
+        */
+       case 'h':
+       case CTRL('h'):
+               dir = -1;
+               /* fall into ... */
+
+       /*
+        * space        Forward a character.
+        */
+       case 'l':
+       case ' ':
+               forbid (margin() || opf == vmove && edge());
+               while (cnt > 0 && !margin()) {
+                       wcursor += dir>0 ? skipright(linebuf, wcursor) :
+                               skipleft(linebuf, wcursor);
+                       cnt--;
+               }
+               if (margin() && opf == vmove || wcursor < linebuf)
+                       wcursor -= dir;
+               vmoving = 0;
+               break;
+
+       /*
+        * D            Delete to end of line, short for d$.
+        */
+       case 'D':
+               cnt = INF;
+               goto deleteit;
+
+       /*
+        * X            Delete character before cursor.
+        */
+       case 'X':
+               dir = -1;
+               /* fall into ... */
+deleteit:
+       /*
+        * x            Delete character at cursor, leaving cursor where it is.
+        */
+       case 'x':
+               if (margin())
+                       goto errlab;
+               vmacchng(1);
+               while (cnt > 0 && !margin()) {
+                       wcursor += dir > 0 ? skipright(linebuf, wcursor) :
+                               skipleft(linebuf, wcursor);
+                       cnt--;
+               }
+               opf = deleteop;
+               vmoving = 0;
+               break;
+
+       default:
+               /*
+                * Stuttered operators are equivalent to the operator on
+                * a line, thus turn dd into d_.
+                */
+               if (opf == vmove || c != workcmd[0]) {
+errlab:
+                       beep();
+                       vmacp = 0;
+                       return;
+               }
+               /* fall into ... */
+
+       /*
+        * _            Target for a line or group of lines.
+        *              Stuttering is more convenient; this is mostly
+        *              for aesthetics.
+        */
+       case '_':
+               wdot = dot + cnt - 1;
+               vmoving = 0;
+               wcursor = 0;
+               break;
+
+       /*
+        * H            To first, home line on screen.
+        *              Count is for count'th line rather than first.
+        */
+       case 'H':
+               wdot = (dot - vcline) + cnt - 1;
+               if (opf == vmove)
+                       markit(wdot);
+               vmoving = 0;
+               wcursor = 0;
+               break;
+
+       /*
+        * -            Backwards lines, to first non-white character.
+        */
+       case '-':
+               wdot = dot - cnt;
+               vmoving = 0;
+               wcursor = 0;
+               break;
+
+       /*
+        * ^P           To previous line same column.  Ridiculous on the
+        *              console of the VAX since it puts console in LSI mode.
+        */
+       case 'k':
+       case CTRL('p'):
+               wdot = dot - cnt;
+               if (vmoving == 0)
+                       vmoving = 1, vmovcol = lcolumn(cursor);
+               wcursor = 0;
+               break;
+
+       /*
+        * L            To last line on screen, or count'th line from the
+        *              bottom.
+        */
+       case 'L':
+               wdot = dot + vcnt - vcline - cnt;
+               if (opf == vmove)
+                       markit(wdot);
+               vmoving = 0;
+               wcursor = 0;
+               break;
+
+       /*
+        * M            To the middle of the screen.
+        */
+       case 'M':
+               wdot = dot + ((vcnt + 1) / 2) - vcline - 1;
+               if (opf == vmove)
+                       markit(wdot);
+               vmoving = 0;
+               wcursor = 0;
+               break;
+
+       /*
+        * +            Forward line, to first non-white.
+        *
+        * CR           Convenient synonym for +.
+        */
+       case '+':
+       case CR:
+               wdot = dot + cnt;
+               vmoving = 0;
+               wcursor = 0;
+               break;
+
+       /*
+        * ^N           To next line, same column if possible.
+        *
+        * LF           Linefeed is a convenient synonym for ^N.
+        */
+       case CTRL('n'):
+       case 'j':
+       case NL:
+               wdot = dot + cnt;
+               if (vmoving == 0)
+                       vmoving = 1, vmovcol = lcolumn(cursor);
+               wcursor = 0;
+               break;
+
+       /*
+        * n            Search to next match of current pattern.
+        */
+       case 'n':
+               vglobp = vscandir;
+               c = *vglobp++;
+               goto nocount;
+
+       /*
+        * N            Like n but in reverse direction.
+        */
+       case 'N':
+               vglobp = vscandir[0] == '/' ? qmarkcell : slashcell;
+               c = *vglobp++;
+               goto nocount;
+
+       /*
+        * '            Return to line specified by following mark,
+        *              first white position on line.
+        *
+        * `            Return to marked line at remembered column.
+        */
+       case '\'':
+       case '`':
+               d = c;
+               c = getesc();
+               if (c == 0)
+                       return;
+               c = markreg(c);
+               forbid (c == 0);
+               wdot = getmark(c);
+               forbid (wdot == NOLINE);
+               forbid (Xhadcnt);
+               vmoving = 0;
+               wcursor = d == '`' ? ncols[c - 'a'] : 0;
+               if (opf == vmove && (wdot != dot || (d == '`' && wcursor != cursor)))
+                       markDOT();
+               if (wcursor) {
+                       vsave();
+                       getline(*wdot);
+                       if (wcursor > strend(linebuf))
+                               wcursor = 0;
+                       getDOT();
+               }
+               if (ospeed > B300)
+                       hold |= HOLDWIG;
+               break;
+
+       /*
+        * G            Goto count'th line, or last line if no count
+        *              given.
+        */
+       case 'G':
+               if (!Xhadcnt)
+                       cnt = lineDOL();
+               wdot = zero + cnt;
+               forbid (wdot < one || wdot > dol);
+               if (opf == vmove)
+                       markit(wdot);
+               vmoving = 0;
+               wcursor = 0;
+               break;
+
+       /*
+        * /            Scan forward for following re.
+        * ?            Scan backward for following re.
+        */
+       case '/':
+       case '?':
+               forbid (Xhadcnt);
+               vsave();
+               ocurs = cursor;
+               odot = dot;
+               wcursor = 0;
+               if (readecho(c))
+                       return;
+               if (!vglobp)
+                       vscandir[0] = genbuf[0];
+               oglobp = globp;
+               CP(vutmp, genbuf);
+               globp = vutmp;
+               d = peekc;
+fromsemi:
+               ungetchar(0);
+               fixech();
+               CATCH
+                       addr = address(cursor);
+               ONERR
+slerr:
+                       globp = oglobp;
+                       dot = odot;
+                       cursor = ocurs;
+                       ungetchar(d);
+                       splitw = 0;
+                       vclean();
+                       vjumpto(dot, ocurs, 0);
+                       return;
+               ENDCATCH
+               if (globp == 0)
+                       globp = "";
+               else if (peekc)
+                       --globp;
+               if (*globp == ';') {
+                       /* /foo/;/bar/ */
+                       globp++;
+                       dot = addr;
+                       cursor = loc1;
+                       goto fromsemi;
+               }
+               dot = odot;
+               ungetchar(d);
+               c = 0;
+               if (*globp == 'z')
+                       globp++, c = '\n';
+               if (any(*globp, "^+-."))
+                       c = *globp++;
+               i = 0;
+               while (xisdigit(*globp&TRIM))
+                       i = i * 10 + *globp++ - '0';
+               if (any(*globp, "^+-."))
+                       c = *globp++;
+               if (*globp) {
+                       /* random junk after the pattern */
+                       beep();
+                       goto slerr;
+               }
+               globp = oglobp;
+               splitw = 0;
+               vmoving = 0;
+               wcursor = loc1;
+               if (i != 0)
+                       vsetsiz(i);
+               if (opf == vmove) {
+                       if (state == ONEOPEN || state == HARDOPEN)
+                               outline = destline = WBOT;
+                       if (addr != dot || loc1 != cursor)
+                               markDOT();
+                       if (loc1 > linebuf && *loc1 == 0)
+                               loc1--;
+                       if (c)
+                               vjumpto(addr, loc1, c);
+                       else {
+                               vmoving = 0;
+                               if (loc1) {
+                                       vmoving++;
+                                       vmovcol = column(loc1);
+                               }
+                               getDOT();
+                               if (state == CRTOPEN && addr != dot)
+                                       vup1();
+                               vupdown(addr - dot, NOSTR);
+                       }
+                       return;
+               }
+               lastcp[-1] = 'n';
+               getDOT();
+               wdot = addr;
+               break;
+       }
+       /*
+        * Apply.
+        */
+       if (vreg && wdot == 0)
+               wdot = dot;
+       (*opf)(c);
+       wdot = NOLINE;
+}
+
+/*
+ * Find single character c, in direction dir from cursor.
+ */
+int 
+find(int c)
+{
+
+       for(;;) {
+               if (edge())
+                       return (0);
+               wcursor += dir>0 ? skipright(linebuf, wcursor) :
+                       skipleft(linebuf, wcursor-1);
+               if (samechar(wcursor, c))
+                       return (1);
+       }
+}
+
+/*
+ * Do a word motion with operator op, and cnt more words
+ * to go after this.
+ */
+int 
+word(register void (*op)(int), int cnt)
+{
+       register int which = 0, i;
+       register char *iwc;
+       register line *iwdot = wdot;
+
+       if (dir == 1) {
+               iwc = wcursor;
+               which = wordch(wcursor);
+               while (wordof(which, wcursor)) {
+                       if (cnt == 1 && op != vmove &&
+                                       wcursor[i = skipright(linebuf, wcursor)]
+                                       == 0) {
+                               wcursor += i;
+                               break;
+                       }
+                       if (!lnext())
+                               return (0);
+                       if (wcursor == linebuf)
+                               break;
+               }
+               /* Unless last segment of a change skip blanks */
+               if (op != vchange || cnt > 1)
+                       while (!margin() && blank())
+                               wcursor += skipright(linebuf, wcursor);
+               else
+                       if (wcursor == iwc && iwdot == wdot && *iwc)
+                               wcursor += skipright(linebuf, wcursor);
+               if (op == vmove && margin()) {
+                       if (wcursor == linebuf)
+                               wcursor--;
+                       else if (!lnext())
+                               return (0);
+               }
+       } else {
+               if (!lnext())
+                       return (0);
+               while (blank())
+                       if (!lnext())
+                               return (0);
+               if (!margin()) {
+                       which = wordch(wcursor);
+                       while (!margin() && wordof(which, wcursor))
+                               wcursor--;
+               }
+               if (wcursor < linebuf || !wordof(which, wcursor))
+                       wcursor += skipright(linebuf, wcursor);
+       }
+       return (1);
+}
+
+/*
+ * To end of word, with operator op and cnt more motions
+ * remaining after this.
+ */
+void 
+eend(register void (*op)(int))
+{
+       register int which;
+
+       if (!lnext())
+               return;
+       while (blank())
+               if (!lnext())
+                       return;
+       which = wordch(wcursor);
+       while (wordof(which, wcursor)) {
+               if (wcursor[1] == 0) {
+                       wcursor++;
+                       break;
+               }
+               if (!lnext())
+                       return;
+       }
+       if (op != vchange && op != vdelete && wcursor > linebuf)
+               wcursor--;
+}
+
+/*
+ * Wordof tells whether the character at *wc is in a word of
+ * kind which (blank/nonblank words are 0, conservative words 1).
+ */
+int 
+wordof(int which, register char *wc)
+{
+
+       if (cblank(wc))
+               return (0);
+       return (!wdkind || wordch(wc) == which);
+}
+
+/*
+ * Wordch tells whether character at *wc is a word character
+ * i.e. an alfa, digit, or underscore.
+ */
+int 
+wordch(char *wc)
+{
+       int c;
+
+#ifdef MB
+       if (mb_cur_max > 0 && *wc & 0200) {
+               mbtowi(&c, wc, mb_cur_max);
+               if (c & INVBIT)
+                       return 1;
+       } else
+#endif
+               c = wc[0]&0377;
+       return (xisalnum(c) || c == '_'
+#ifdef BIT8
+#ifdef ISO8859_1
+       /*
+        * We consider all ISO 8859-1 characters except for
+        * no-break-space as word characters.
+        */
+                       || c&0200 && (!(c&QUOTE) && (c&TRIM) != 0240)
+#endif
+#endif
+                       );
+}
+
+/*
+ * Edge tells when we hit the last character in the current line.
+ */
+int 
+edge(void)
+{
+
+       if (linebuf[0] == 0)
+               return (1);
+       if (dir == 1)
+               return (wcursor[skipright(linebuf, wcursor)] == 0);
+       else
+               return (wcursor == linebuf);
+}
+
+/*
+ * Margin tells us when we have fallen off the end of the line.
+ */
+int 
+margin(void)
+{
+
+       return (wcursor < linebuf || wcursor[0] == 0);
+}
diff --git a/ex_vops.c b/ex_vops.c
new file mode 100644 (file)
index 0000000..4d28fac
--- /dev/null
+++ b/ex_vops.c
@@ -0,0 +1,1068 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_vops.c  1.26 (gritter) 1/13/05";
+#endif
+#endif
+
+/* from ex_vops.c      7.7 (Berkeley) 6/7/85 */
+
+#include "ex.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+
+/*
+ * This file defines the operation sequences which interface the
+ * logical changes to the file buffer with the internal and external
+ * display representations.
+ */
+
+/*
+ * Undo.
+ *
+ * Undo is accomplished in two ways.  We often for small changes in the
+ * current line know how (in terms of a change operator) how the change
+ * occurred.  Thus on an intelligent terminal we can undo the operation
+ * by another such operation, using insert and delete character
+ * stuff.  The pointers vU[AD][12] index the buffer vutmp when this
+ * is possible and provide the necessary information.
+ *
+ * The other case is that the change involved multiple lines or that
+ * we have moved away from the line or forgotten how the change was
+ * accomplished.  In this case we do a redisplay and hope that the
+ * low level optimization routines (which don't look for winning
+ * via insert/delete character) will not lose too badly.
+ */
+char   *vUA1, *vUA2;
+char   *vUD1, *vUD2;
+
+void 
+vUndo(void)
+{
+
+       /*
+        * Avoid UU which clobbers ability to do u.
+        */
+       if (vundkind == VCAPU || vUNDdot != dot) {
+               beep();
+               return;
+       }
+       CP(vutmp, linebuf);
+       vUD1 = linebuf; vUD2 = strend(linebuf);
+       putmk1(dot, vUNDsav);
+       getDOT();
+       vUA1 = linebuf; vUA2 = strend(linebuf);
+       vundkind = VCAPU;
+       if (state == ONEOPEN || state == HARDOPEN) {
+               vjumpto(dot, vUNDcurs, 0);
+               return;
+       }
+       vdirty(vcline, 1);
+       vsyncCL();
+       cursor = linebuf;
+       vfixcurs();
+}
+
+void 
+vundo (
+    int show   /* if true update the screen */
+)
+{
+       register int cnt;
+       register line *addr;
+       register char *cp;
+       char temp[LBSIZE];
+       bool savenote;
+       int (*OO)(int);
+       short oldhold = hold;
+
+       switch (vundkind) {
+
+       case VMANYINS:
+               wcursor = 0;
+               addr1 = undap1;
+               addr2 = undap2 - 1;
+               vsave();
+               YANKreg('1');
+               notecnt = 0;
+               /* fall into ... */
+
+       case VMANY:
+       case VMCHNG:
+               vsave();
+               addr = dot - vcline;
+               notecnt = 1;
+               if (undkind == UNDPUT && undap1 == undap2) {
+                       beep();
+                       break;
+               }
+               /*
+                * Undo() call below basically replaces undap1 to undap2-1
+                * with dol through unddol-1.  Hack screen image to
+                * reflect this replacement.
+                */
+               if (show)
+                       if (undkind == UNDMOVE)
+                               vdirty(0, TLINES);
+                       else
+                               vreplace(undap1 - addr, undap2 - undap1,
+                                   undkind == UNDPUT ? 0 : unddol - dol);
+               savenote = notecnt;
+               undo(1);
+               if (show && (vundkind != VMCHNG || addr != dot))
+                       killU();
+               vundkind = VMANY;
+               cnt = dot - addr;
+               if (cnt < 0 || cnt > vcnt || state != VISUAL) {
+                       if (show)
+                               vjumpto(dot, NOSTR, '.');
+                       break;
+               }
+               if (!savenote)
+                       notecnt = 0;
+               if (show) {
+                       vcline = cnt;
+                       vrepaint(vmcurs);
+               }
+               vmcurs = 0;
+               break;
+
+       case VCHNG:
+       case VCAPU:
+               vundkind = VCHNG;
+               CP(temp, vutmp);
+               CP(vutmp, linebuf);
+               doomed = column(vUA2 - 1) - column(vUA1 - 1);
+               strcLIN(temp);
+               cp = vUA1; vUA1 = vUD1; vUD1 = cp;
+               cp = vUA2; vUA2 = vUD2; vUD2 = cp;
+               if (!show)
+                       break;
+               cursor = vUD1;
+               if (state == HARDOPEN) {
+                       doomed = 0;
+                       vsave();
+                       vopen(dot, WBOT);
+                       vnline(cursor);
+                       break;
+               }
+               /*
+                * Pseudo insert command.
+                */
+               vcursat(cursor);
+               OO = Outchar; Outchar = vinschar; hold |= HOLDQIK;
+               vprepins();
+               temp[vUA2 - linebuf] = 0;
+               for (cp = &temp[vUA1 - linebuf]; *cp;) {
+                       int     c, n;
+                       nextc(c, cp, n);
+                       cp += n;
+                       putchar(c);
+               }
+               Outchar = OO; hold = oldhold;
+               endim();
+               physdc(cindent(), cindent() + doomed);
+               doomed = 0;
+               vdirty(vcline, 1);
+               vsyncCL();
+               if (cursor > linebuf && cursor >= strend(linebuf))
+                       cursor += skipleft(linebuf, cursor);
+               vfixcurs();
+               break;
+
+       case VNONE:
+               beep();
+               break;
+       }
+}
+
+/*
+ * Routine to handle a change inside a macro.
+ * Fromvis is true if we were called from a visual command (as
+ * opposed to an ex command).  This has nothing to do with being
+ * in open/visual mode as :s/foo/bar is not fromvis.
+ */
+void 
+vmacchng(int fromvis)
+{
+       line *savedot, *savedol;
+       char *savecursor;
+       char savelb[LBSIZE];
+       int nlines, more;
+       /* register line *a1, *a2; */
+       /* char ch; */  /* DEBUG */
+
+       if (!inopen)
+               return;
+       if (!vmacp)
+               vch_mac = VC_NOTINMAC;
+#ifdef TRACE
+       if (trace)
+               fprintf(trace, "vmacchng, vch_mac=%d, linebuf='%s', *dot=%o\n", vch_mac, linebuf, *dot);
+#endif
+       if (vmacp && fromvis)
+               vsave();
+#ifdef TRACE
+       if (trace)
+               fprintf(trace, "after vsave, linebuf='%s', *dot=%o\n", linebuf, *dot);
+#endif
+       switch(vch_mac) {
+       case VC_NOCHANGE:
+               vch_mac = VC_ONECHANGE;
+               break;
+       case VC_ONECHANGE:
+               /* Save current state somewhere */
+#ifdef TRACE
+               vudump("before vmacchng hairy case");
+#endif
+               savedot = dot; savedol = dol; savecursor = cursor;
+               CP(savelb, linebuf);
+               nlines = dol - zero;
+               while ((line *) endcore - truedol < nlines)
+                       morelines();
+               copyw(truedol+1, zero+1, nlines);
+               truedol += nlines;
+
+#ifdef TRACE
+               visdump("before vundo");
+#endif
+               /* Restore state as it was at beginning of macro */
+               vundo(0);
+#ifdef TRACE
+               visdump("after vundo");
+               vudump("after vundo");
+#endif
+
+               /* Do the saveall we should have done then */
+               saveall();
+#ifdef TRACE
+               vudump("after saveall");
+#endif
+
+               /* Restore current state from where saved */
+               more = savedol - dol; /* amount we shift everything by */
+               if (more)
+                       (*(more>0 ? copywR : copyw))(savedol+1, dol+1, truedol-dol);
+               unddol += more; truedol += more; undap2 += more;
+
+               truedol -= nlines;
+               copyw(zero+1, truedol+1, nlines);
+               dot = savedot; dol = savedol ; cursor = savecursor;
+               CP(linebuf, savelb);
+               vch_mac = VC_MANYCHANGE;
+
+               /* Arrange that no further undo saving happens within macro */
+               otchng = tchng; /* Copied this line blindly - bug? */
+               inopen = -1;    /* no need to save since it had to be 1 or -1 before */
+               vundkind = VMANY;
+#ifdef TRACE
+               vudump("after vmacchng");
+#endif
+               break;
+       case VC_NOTINMAC:
+       case VC_MANYCHANGE:
+               /* Nothing to do for various reasons. */
+               break;
+       }
+}
+
+/*
+ * Initialize undo information before an append.
+ */
+void 
+vnoapp(void)
+{
+
+       vUD1 = vUD2 = cursor;
+}
+
+/*
+ * All the rest of the motion sequences have one or more
+ * cases to deal with.  In the case wdot == 0, operation
+ * is totally within current line, from cursor to wcursor.
+ * If wdot is given, but wcursor is 0, then operation affects
+ * the inclusive line range.  The hardest case is when both wdot
+ * and wcursor are given, then operation affects from line dot at
+ * cursor to line wdot at wcursor.
+ */
+
+/*
+ * Move is simple, except for moving onto new lines in hardcopy open mode.
+ */
+/*ARGSUSED*/
+void 
+vmove(int unused)
+{
+       register int cnt;
+
+       if (wdot) {
+               if (wdot < one || wdot > dol) {
+                       beep();
+                       return;
+               }
+               cnt = wdot - dot;
+               wdot = NOLINE;
+               if (cnt)
+                       killU();
+               vupdown(cnt, wcursor);
+               return;
+       }
+
+       /*
+        * When we move onto a new line, save information for U undo.
+        */
+       if (vUNDdot != dot) {
+               vUNDsav = *dot;
+               vUNDcurs = wcursor;
+               vUNDdot = dot;
+       }
+
+       /*
+        * In hardcopy open, type characters to left of cursor
+        * on new line, or back cursor up if its to left of where we are.
+        * In any case if the current line is ``rubbled'' i.e. has trashy
+        * looking overstrikes on it or \'s from deletes, we reprint
+        * so it is more comprehensible (and also because we can't work
+        * if we let it get more out of sync since column() won't work right.
+        */
+       if (state == HARDOPEN) {
+               register char *cp;
+               if (rubble) {
+                       register int c;
+                       int oldhold = hold;
+
+                       sethard();
+                       cp = wcursor;
+                       c = *cp;
+                       *cp = 0;
+                       hold |= HOLDDOL;
+                       vreopen(WTOP, lineDOT(), vcline);
+                       hold = oldhold;
+                       *cp = c;
+               } else if (wcursor > cursor) {
+                       vfixcurs();
+                       for (cp = cursor; *cp && cp < wcursor;) {
+                               int     c, n;
+                               nextc(c, cp, n);
+                               cp += n;
+                               c &= TRIM;
+                               putchar(c ? c : ' ');
+                       }
+               }
+       }
+       vsetcurs(wcursor);
+}
+
+/*
+ * Delete operator.
+ *
+ * Hard case of deleting a range where both wcursor and wdot
+ * are specified is treated as a special case of change and handled
+ * by vchange (although vchange may pass it back if it degenerates
+ * to a full line range delete.)
+ */
+void 
+vdelete(int c)
+{
+       register char *cp;
+       register int i;
+
+       if (wdot) {
+               if (wcursor) {
+                       vchange(EOF);
+                       return;
+               }
+               if ((i = xdw()) < 0)
+                       return;
+               if (state != VISUAL) {
+                       vgoto(LINE(0), 0);
+                       vputchar('@');
+               }
+               wdot = dot;
+               vremote(i, delete, 0);
+               notenam = "delete";
+               DEL[0] = 0;
+               killU();
+               vreplace(vcline, i, 0);
+               if (wdot > dol)
+                       vcline--;
+               vrepaint(NOSTR);
+               return;
+       }
+       if (wcursor < linebuf)
+               wcursor = linebuf;
+       if (cursor == wcursor) {
+               beep();
+               return;
+       }
+       i = vdcMID();
+       cp = cursor;
+       setDEL();
+       CP(cp, wcursor);
+       if (cp > linebuf && (cp[0] == 0 || c == '#'))
+               cp--;
+       if (state == HARDOPEN) {
+               bleep(i, cp);
+               cursor = cp;
+               return;
+       }
+       physdc(column(cursor + skipleft(linebuf, cursor)), i);
+       DEPTH(vcline) = 0;
+       vreopen(LINE(vcline), lineDOT(), vcline);
+       vsyncCL();
+       vsetcurs(cp);
+}
+
+/*
+ * Change operator.
+ *
+ * In a single line we mark the end of the changed area with '$'.
+ * On multiple whole lines, we clear the lines first.
+ * Across lines with both wcursor and wdot given, we delete
+ * and sync then append (but one operation for undo).
+ */
+void 
+vchange(int c)
+{
+       register char *cp;
+       register int i, ind, cnt;
+       line *addr;
+
+       if (wdot) {
+               /*
+                * Change/delete of lines or across line boundaries.
+                */
+               if ((cnt = xdw()) < 0)
+                       return;
+               getDOT();
+               if (wcursor && cnt == 1) {
+                       /*
+                        * Not really.
+                        */
+                       wdot = 0;
+                       if (c == EOF) {
+                               vdelete(c);
+                               return;
+                       }
+                       goto smallchange;
+               }
+               if (cursor && wcursor) {
+                       /*
+                        * Across line boundaries, but not
+                        * necessarily whole lines.
+                        * Construct what will be left.
+                        */
+                       *cursor = 0;
+                       strcpy(genbuf, linebuf);
+                       getline(*wdot);
+                       if (strlen(genbuf) + strlen(wcursor) > LBSIZE - 2) {
+                               getDOT();
+                               beep();
+                               return;
+                       }
+                       strcat(genbuf, wcursor);
+                       if (c == EOF && *vpastwh(genbuf) == 0) {
+                               /*
+                                * Although this is a delete
+                                * spanning line boundaries, what
+                                * would be left is all white space,
+                                * so take it all away.
+                                */
+                               wcursor = 0;
+                               getDOT();
+                               op = 0;
+                               notpart(lastreg);
+                               notpart('1');
+                               vdelete(c);
+                               return;
+                       }
+                       ind = -1;
+               } else if (c == EOF && wcursor == 0) {
+                       vdelete(c);
+                       return;
+               } else
+#ifdef LISPCODE
+                       /*
+                        * We are just substituting text for whole lines,
+                        * so determine the first autoindent.
+                        */
+                       if (value(LISP) && value(AUTOINDENT))
+                               ind = lindent(dot);
+                       else
+#endif
+                               ind = whitecnt(linebuf);
+               i = vcline >= 0 ? LINE(vcline) : WTOP;
+
+               /*
+                * Delete the lines from the buffer,
+                * and remember how the partial stuff came about in
+                * case we are told to put.
+                */
+               addr = dot;
+               vremote(cnt, delete, 0);
+               setpk();
+               notenam = "delete";
+               if (c != EOF)
+                       notenam = "change";
+               /*
+                * If DEL[0] were nonzero, put would put it back
+                * rather than the deleted lines.
+                */
+               DEL[0] = 0;
+               if (cnt > 1)
+                       killU();
+
+               /*
+                * Now hack the screen image coordination.
+                */
+               vreplace(vcline, cnt, 0);
+               wdot = NOLINE;
+               noteit(0);
+               vcline--;
+               if (addr <= dol)
+                       dot--;
+
+               /*
+                * If this is a across line delete/change,
+                * cursor stays where it is; just splice together the pieces
+                * of the new line.  Otherwise generate a autoindent
+                * after a S command.
+                */
+               if (ind >= 0) {
+                       *genindent(ind) = 0;
+                       vdoappend(genbuf);
+               } else {
+                       vmcurs = cursor;
+                       strcLIN(genbuf);
+                       vdoappend(linebuf);
+               }
+
+               /*
+                * Indicate a change on hardcopies by
+                * erasing the current line.
+                */
+               if (c != EOF && state != VISUAL && state != HARDOPEN) {
+                       int oldhold = hold;
+
+                       hold |= HOLDAT, vclrlin(i, dot), hold = oldhold;
+               }
+
+               /*
+                * Open the line (logically) on the screen, and 
+                * update the screen tail.  Unless we are really a delete
+                * go off and gather up inserted characters.
+                */
+               vcline++;
+               if (vcline < 0)
+                       vcline = 0;
+               vopen(dot, i);
+               vsyncCL();
+               noteit(1);
+               if (c != EOF) {
+                       if (ind >= 0) {
+                               cursor = linebuf;
+                               linebuf[0] = 0;
+                               vfixcurs();
+                       } else {
+                               ind = 0;
+                               vcursat(cursor);
+                       }
+                       vappend('x', 1, ind);
+                       return;
+               }
+               if (*cursor == 0 && cursor > linebuf)
+                       cursor += skipleft(linebuf, cursor);
+               vrepaint(cursor);
+               return;
+       }
+
+smallchange:
+       /*
+        * The rest of this is just low level hacking on changes
+        * of small numbers of characters.
+        */
+       if (wcursor < linebuf)
+               wcursor = linebuf;
+       if (cursor == wcursor) {
+               beep();
+               return;
+       }
+       i = vdcMID();
+       cp = cursor;
+       if (state != HARDOPEN)
+               vfixcurs();
+
+       /*
+        * Put out the \\'s indicating changed text in hardcopy,
+        * or mark the end of the change with $ if not hardcopy.
+        */
+       if (state == HARDOPEN) 
+               bleep(i, cp);
+       else {
+               int     c, d, n;
+               vcursbef(wcursor);
+               d = skipleft(linebuf, wcursor);
+               nextc(c, &wcursor[d], n);
+               if (colsc(c) > 1)
+                       putchar(' ');
+               putchar('$');
+               i = cindent();
+       }
+
+       /*
+        * Remember the deleted text for possible put,
+        * and then prepare and execute the input portion of the change.
+        */
+       cursor = cp;
+       setDEL();
+       CP(cursor, wcursor);
+       if (state != HARDOPEN) {
+               vcursaft(cursor - 1);
+               doomed = i - cindent();
+       } else {
+/*
+               sethard();
+               wcursor = cursor;
+               cursor = linebuf;
+               vgoto(outline, value(NUMBER) << 3);
+               vmove();
+*/
+               doomed = 0;
+       }
+       prepapp();
+       vappend('c', 1, 0);
+}
+
+/*
+ * Open new lines.
+ *
+ * Tricky thing here is slowopen.  This causes display updating
+ * to be held off so that 300 baud dumb terminals don't lose badly.
+ * This also suppressed counts, which otherwise say how many blank
+ * space to open up.  Counts are also suppressed on intelligent terminals.
+ * Actually counts are obsoleted, since if your terminal is slow
+ * you are better off with slowopen.
+ */
+void 
+voOpen (
+    int c,     /* mjm: char --> int */
+    register int cnt
+)
+{
+       register int ind = 0, i;
+       short oldhold = hold;
+#ifdef SIGWINCH
+       sigset_t set, oset;
+#endif
+
+       if (value(SLOWOPEN) || value(REDRAW) && AL && DL)
+               cnt = 1;
+#ifdef SIGWINCH
+       sigemptyset(&set);
+       sigaddset(&set, SIGWINCH);
+       sigprocmask(SIG_BLOCK, &set, &oset);
+#endif
+       vsave();
+       setLAST();
+       if (value(AUTOINDENT))
+               ind = whitecnt(linebuf);
+       if (c == 'O') {
+               vcline--;
+               dot--;
+               if (dot > zero)
+                       getDOT();
+       }
+       if (value(AUTOINDENT)) {
+#ifdef LISPCODE
+               if (value(LISP))
+                       ind = lindent(dot + 1);
+#endif
+       }
+       killU();
+       prepapp();
+       if (FIXUNDO)
+               vundkind = VMANY;
+       if (state != VISUAL)
+               c = WBOT + 1;
+       else {
+               c = vcline < 0 ? WTOP - cnt : LINE(vcline) + DEPTH(vcline);
+               if (c < ZERO)
+                       c = ZERO;
+               i = LINE(vcline + 1) - c;
+               if (i < cnt && c <= WBOT && (!AL || !DL))
+                       vinslin(c, cnt - i, vcline);
+       }
+       *genindent(ind) = 0;
+       vdoappend(genbuf);
+       vcline++;
+       oldhold = hold;
+       hold |= HOLDROL;
+       vopen(dot, c);
+       hold = oldhold;
+       if (value(SLOWOPEN))
+               /*
+                * Oh, so lazy!
+                */
+               vscrap();
+       else
+               vsync1(LINE(vcline));
+       cursor = linebuf;
+       linebuf[0] = 0;
+       vappend('o', 1, ind);
+#ifdef SIGWINCH
+       sigprocmask(SIG_SETMASK, &oset, NULL);
+#endif
+}
+
+/*
+ * > < and = shift operators.
+ *
+ * Note that =, which aligns lisp, is just a ragged sort of shift,
+ * since it never distributes text between lines.
+ */
+char   vshnam[2] = { 'x', 0 };
+
+/*ARGSUSED*/
+void 
+vshftop(int unused)
+{
+       register line *addr;
+       register int cnt;
+
+       if ((cnt = xdw()) < 0)
+               return;
+       addr = dot;
+       vremote(cnt, vshift, 0);
+       vshnam[0] = op;
+       notenam = vshnam;
+       dot = addr;
+       vreplace(vcline, cnt, cnt);
+       if (state == HARDOPEN)
+               vcnt = 0;
+       vrepaint(NOSTR);
+}
+
+/*
+ * !.
+ *
+ * Filter portions of the buffer through unix commands.
+ */
+/*ARGSUSED*/
+void 
+vfilter(int unused)
+{
+       register line *addr;
+       register int cnt;
+       char *oglobp;
+       short d;
+#ifdef BIT8
+       cell cuxb[UXBSIZE + 2];
+#endif
+
+       if ((cnt = xdw()) < 0)
+               return;
+       if (vglobp)
+#ifdef BIT8
+               vglobp = cuxb;
+#else
+               vglobp = uxb;
+#endif
+       if (readecho('!'))
+               return;
+       oglobp = globp; globp = genbuf + 1;
+       d = peekc; ungetchar(0);
+       CATCH
+               fixech();
+               unix0(0);
+#ifdef BIT8
+               str2cell(cuxb, uxb);
+#endif
+       ONERR
+               splitw = 0;
+               ungetchar(d);
+               vrepaint(cursor);
+               globp = oglobp;
+               return;
+       ENDCATCH
+       ungetchar(d); globp = oglobp;
+       addr = dot;
+       CATCH
+               vgoto(WECHO, 0); flusho();
+               vremote(cnt, filter, 2);
+       ONERR
+               vdirty(0, TLINES);
+       ENDCATCH
+       if (dot == zero && dol > zero)
+               dot = one;
+       splitw = 0;
+       notenam = "";
+       /*
+        * BUG: we shouldn't be depending on what undap2 and undap1 are,
+        * since we may be inside a macro.  What's really wanted is the
+        * number of lines we read from the filter.  However, the mistake
+        * will be an overestimate so it only results in extra work,
+        * it shouldn't cause any real screwups.
+        */
+       vreplace(vcline, cnt, undap2 - undap1);
+       dot = addr;
+       if (dot > dol) {
+               dot--;
+               vcline--;
+       }
+       vrepaint(NOSTR);
+}
+
+/*
+ * Xdw exchanges dot and wdot if appropriate and also checks
+ * that wdot is reasonable.  Its name comes from
+ *     xchange dotand wdot
+ */
+int 
+xdw(void)
+{
+       register char *cp;
+       register int cnt;
+/*
+       register int notp = 0;
+ */
+
+       if (wdot == NOLINE || wdot < one || wdot > dol) {
+               beep();
+               return (-1);
+       }
+       vsave();
+       setLAST();
+       if (dot > wdot || (dot == wdot && wcursor != 0 && cursor > wcursor)) {
+               register line *addr;
+
+               vcline -= dot - wdot;
+               addr = dot; dot = wdot; wdot = addr;
+               cp = cursor; cursor = wcursor; wcursor = cp;
+       }
+       /*
+        * If a region is specified but wcursor is at the begining
+        * of the last line, then we move it to be the end of the
+        * previous line (actually off the end).
+        */
+       if (cursor && wcursor == linebuf && wdot > dot) {
+               wdot--;
+               getDOT();
+               if (vpastwh(linebuf) >= cursor)
+                       wcursor = 0;
+               else {
+                       getline(*wdot);
+                       wcursor = strend(linebuf);
+                       getDOT();
+               }
+               /*
+                * Should prepare in caller for possible dot == wdot.
+                */
+       }
+       cnt = wdot - dot + 1;
+       if (vreg) {
+               vremote(cnt, YANKreg, vreg);
+/*
+               if (notp)
+                       notpart(vreg);
+ */
+       }
+
+       /*
+        * Kill buffer code.  If delete operator is c or d, then save
+        * the region in numbered buffers.
+        *
+        * BUG:                 This may be somewhat inefficient due
+        *                      to the way named buffer are implemented,
+        *                      necessitating some optimization.
+        */
+       vreg = 0;
+       if (any(op, "cd")) {
+               vremote(cnt, YANKreg, '1');
+/*
+               if (notp)
+                       notpart('1');
+ */
+       }
+       return (cnt);
+}
+
+/*
+ * Routine for vremote to call to implement shifts.
+ */
+/*ARGSUSED*/
+void 
+vshift(int unused)
+{
+
+       shift(op, 1);
+}
+
+/*
+ * Replace a single character with the next input character.
+ * A funny kind of insert.
+ */
+void 
+vrep(register int cnt)
+{
+       register int i, c;
+
+       if (cnt > strlen(cursor)) {
+               beep();
+               return;
+       }
+       showmode('r');
+       i = column(cursor + cnt - 1);
+       vcursat(cursor);
+       doomed = i - cindent();
+       if (!vglobp) {
+               c = getesc();
+               if (c == 0) {
+                       showmode(0);
+                       vfixcurs();
+                       return;
+               }
+               ungetkey(c);
+       }
+       CP(vutmp, linebuf);
+       if (FIXUNDO)
+               vundkind = VCHNG;
+       wcursor = cursor;
+       for (i = 0; i < cnt; i++)
+               wcursor += skipright(cursor, wcursor);
+       vUD1 = cursor; vUD2 = wcursor;
+       CP(cursor, wcursor);
+       prepapp();
+       vappend('r', cnt, 0);
+       *lastcp++ = INS[0];
+       setLAST();
+}
+
+/*
+ * Yank.
+ *
+ * Yanking to string registers occurs for free (essentially)
+ * in the routine xdw().
+ */
+/*ARGSUSED*/
+void 
+vyankit(int unused)
+{
+       register int cnt;
+
+       if (wdot) {
+               if ((cnt = xdw()) < 0)
+                       return;
+               vremote(cnt, yank, 0);
+               setpk();
+               notenam = "yank";
+               if (FIXUNDO)
+                       vundkind = VNONE;
+               DEL[0] = 0;
+               wdot = NOLINE;
+               if (notecnt <= vcnt - vcline && notecnt < value(REPORT))
+                       notecnt = 0;
+               vrepaint(cursor);
+               return;
+       }
+       takeout(DEL);
+}
+
+/*
+ * Set pkill variables so a put can
+ * know how to put back partial text.
+ * This is necessary because undo needs the complete
+ * line images to be saved, while a put wants to trim
+ * the first and last lines.  The compromise
+ * is for put to be more clever.
+ */
+void 
+setpk(void)
+{
+
+       if (wcursor) {
+               pkill[0] = cursor;
+               pkill[1] = wcursor;
+       }
+}
+
+/*
+ * Kill the last deleted part of a line so that "p" does not put it back.
+ * This is to be called from ex commands that delete some text.
+ */
+void
+vkillDEL(void)
+{
+       DEL[0] = 0;
+}
diff --git a/ex_vops2.c b/ex_vops2.c
new file mode 100644 (file)
index 0000000..d7cd3fb
--- /dev/null
@@ -0,0 +1,1097 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_vops2.c 1.34 (gritter) 1/12/05";
+#endif
+#endif
+
+/* from ex_vops2.c     6.8 (Berkeley) 6/7/85 */
+
+#include "ex.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+
+/*
+ * Low level routines for operations sequences,
+ * and mostly, insert mode (and a subroutine
+ * to read an input line, including in the echo area.)
+ */
+extern char    *vUA1, *vUA2;           /* mjm: extern; also in ex_vops.c */
+extern char    *vUD1, *vUD2;           /* mjm: extern; also in ex_vops.c */
+
+/*
+ * Obleeperate characters in hardcopy
+ * open with \'s.
+ */
+void 
+bleep(register int i, char *cp)
+{
+
+       i -= column(cp);
+       do
+               putchar('\\' | QUOTE);
+       while (--i >= 0);
+       rubble = 1;
+}
+
+/*
+ * Common code for middle part of delete
+ * and change operating on parts of lines.
+ */
+int 
+vdcMID(void)
+{
+       register char *cp;
+
+       squish();
+       setLAST();
+       if (FIXUNDO)
+               vundkind = VCHNG, CP(vutmp, linebuf);
+       if (wcursor < cursor)
+               cp = wcursor, wcursor = cursor, cursor = cp;
+       vUD1 = vUA1 = vUA2 = cursor; vUD2 = wcursor;
+       return (column(wcursor + skipleft(linebuf, wcursor)));
+}
+
+/*
+ * Take text from linebuf and stick it
+ * in the VBSIZE buffer BUF.  Used to save
+ * deleted text of part of line.
+ */
+void 
+takeout(cell *BUF)
+{
+       register char *cp;
+
+       if (wcursor < linebuf)
+               wcursor = linebuf;
+       if (cursor == wcursor) {
+               beep();
+               return;
+       }
+       if (wcursor < cursor) {
+               cp = wcursor;
+               wcursor = cursor;
+               cursor = cp;
+       }
+       setBUF(BUF);
+       if ((BUF[0] & (QUOTE|TRIM)) == OVERBUF)
+               beep();
+}
+
+/*
+ * Are we at the end of the printed representation of the
+ * line?  Used internally in hardcopy open.
+ */
+int 
+ateopr(void)
+{
+       register int i, c;
+       register cell *cp = vtube[destline] + destcol;
+
+       for (i = WCOLS - destcol; i > 0; i--) {
+               c = *cp++;
+               if (c == 0)
+                       return (1);
+               if (c != ' ' && (c & QUOTE) == 0)
+                       return (0);
+       }
+       return (1);
+}
+
+void 
+showmode(int mode)
+{
+       int sdc = destcol, sdl = destline;
+       char *ocurs, *str;
+
+       if (value(SHOWMODE) == 0 || TCOLUMNS <= 20 || state == ONEOPEN
+                       || state == HARDOPEN || vmacp != NULL)
+               return;
+       ocurs = cursor;
+       fixech();
+       vgoto(WECHO, TCOLUMNS - 20);
+       switch (mode) {
+       case 0:         str = catgets(catd, 1, 227,
+                                       "               ");
+                       break;
+       case 'A':       /*FALLTHROUGH*/
+       case 'a':       str = catgets(catd, 1, 228,
+                                       "AAPPEND MODE");
+                       break;
+       case 'C':       /*FALLTHROUGH*/
+       case 'c':       str = catgets(catd, 1, 229,
+                                       "CCHANGE MODE");
+                       break;
+       case 'O':       /*FALLTHROUGH*/
+       case 'o':       str = catgets(catd, 1, 230,
+                                       "OOPEN MODE");
+                       break;
+       case 'R':       str = catgets(catd, 1, 231,
+                                       "RREPLACE MODE");
+                       break;
+       case 'r':       str = catgets(catd, 1, 232,
+                                       "rREPLACE 1 CHAR");
+                       break;
+       default:        str = catgets(catd, 1, 233,
+                                       "IINSERT MODE");
+       }
+       if (value(TERSE))
+               putchar(str[0]);
+       else
+               printf(&str[1]);
+       vgoto(sdl, sdc);
+       cursor = ocurs;
+       splitw = 0;
+}
+
+/*
+ * Append.
+ *
+ * This routine handles the top level append, doing work
+ * as each new line comes in, and arranging repeatability.
+ * It also handles append with repeat counts, and calculation
+ * of autoindents for new lines.
+ */
+bool   vaifirst;
+bool   gobbled;
+char   *ogcursor;
+
+/*
+ * The addtext() and addto() routines combined, accepting a single
+ * cell character.
+ */
+void
+addc(cell c)
+{
+       register cell *cp = INS;
+
+       if (vglobp)
+               return;
+       if ((cp[0] & (QUOTE|TRIM)) != OVERBUF) {
+               if (cellen(cp) + 2 >= VBSIZE) {
+                       cp[0] = OVERBUF;
+                       lastcmd[0] = 0;
+               } else {
+                       while (*cp)
+                               cp++;
+                       *cp++ = c;
+                       *cp++ = 0;
+               }
+       }
+}
+
+void
+vappend(int ch, int cnt, int indent)
+/*     int ch;         /\* mjm: char --> int */
+{
+       register int i = 0;
+       register char *gcursor;
+       bool escape;
+       int repcnt, savedoomed;
+       short oldhold = hold;
+#ifdef SIGWINCH
+       sigset_t set, oset;
+#endif
+
+       /*
+        * Before a move in hardopen when the line is dirty
+        * or we are in the middle of the printed representation,
+        * we retype the line to the left of the cursor so the
+        * insert looks clean.
+        */
+       if (ch != 'o' && state == HARDOPEN && (rubble || !ateopr())) {
+               rubble = 1;
+               gcursor = cursor;
+               i = *gcursor;
+               *gcursor = ' ';
+               wcursor = gcursor;
+               vmove(0);
+               *gcursor = i;
+       }
+       vaifirst = indent == 0;
+
+       showmode(ch);
+
+       /*
+        * Handle replace character by (eventually)
+        * limiting the number of input characters allowed
+        * in the vgetline routine.
+        */
+       if (ch == 'r')
+               repcnt = 2;
+       else
+               repcnt = 0;
+
+       /*
+        * If an autoindent is specified, then
+        * generate a mixture of blanks to tabs to implement
+        * it and place the cursor after the indent.
+        * Text read by the vgetline routine will be placed in genbuf,
+        * so the indent is generated there.
+        */
+       if (value(AUTOINDENT) && indent != 0) {
+               gcursor = genindent(indent);
+               *gcursor = 0;
+               vgotoCL(qcolumn(cursor + skipright(cursor, linebuf), genbuf));
+       } else {
+               gcursor = genbuf;
+               *gcursor = 0;
+               if (ch == 'o')
+                       vfixcurs();
+       }
+
+       /*
+        * Prepare for undo.  Pointers delimit inserted portion of line.
+        */
+       vUA1 = vUA2 = cursor;
+
+       /*
+        * If we are not in a repeated command and a ^@ comes in
+        * then this means the previous inserted text.
+        * If there is none or it was too long to be saved,
+        * then beep() and also arrange to undo any damage done
+        * so far (e.g. if we are a change.)
+        */
+       if ((vglobp && *vglobp == 0) || peekbr()) {
+               if ((INS[0] & (QUOTE|TRIM)) == OVERBUF) {
+                       beep();
+                       if (!splitw)
+                               ungetkey('u');
+                       doomed = 0;
+                       hold = oldhold;
+                       showmode(0);
+                       return;
+               }
+               /*
+                * Unread input from INS.
+                * An escape will be generated at end of string.
+                * Hold off n^^2 type update on dumb terminals.
+                */
+               vglobp = INS;
+               hold |= HOLDQIK;
+       } else if (vglobp == 0)
+               /*
+                * Not a repeated command, get
+                * a new inserted text for repeat.
+                */
+               INS[0] = 0;
+
+       /*
+        * For wrapmargin to hack away second space after a '.'
+        * when the first space caused a line break we keep
+        * track that this happened in gobblebl, which says
+        * to gobble up a blank silently.
+        */
+       gobblebl = 0;
+
+#ifdef SIGWINCH
+       sigemptyset(&set);
+       sigaddset(&set, SIGWINCH);
+       sigprocmask(SIG_BLOCK, &set, &oset);
+#endif
+       /*
+        * Text gathering loop.
+        * New text goes into genbuf starting at gcursor.
+        * cursor preserves place in linebuf where text will eventually go.
+        */
+       if (*cursor == 0 || state == CRTOPEN)
+               hold |= HOLDROL;
+       for (;;) {
+               if (ch == 'r' && repcnt == 0)
+                       escape = 0;
+               else {
+                       gcursor = vgetline(repcnt, gcursor, &escape, ch);
+
+                       /*
+                        * After an append, stick information
+                        * about the ^D's and ^^D's and 0^D's in
+                        * the repeated text buffer so repeated
+                        * inserts of stuff indented with ^D as backtab's
+                        * can work.
+                        */
+                       if (HADUP)
+                               addc('^');
+                       else if (HADZERO)
+                               addc('0');
+                       while (CDCNT > 0)
+#ifndef        BIT8
+                               addc('\204'), CDCNT--;
+#else
+                               addc(OVERBUF), CDCNT--;
+#endif
+                       
+                       if (gobbled)
+                               addc(' ');
+                       addtext(ogcursor);
+               }
+               repcnt = 0;
+
+               /*
+                * Smash the generated and preexisting indents together
+                * and generate one cleanly made out of tabs and spaces
+                * if we are using autoindent.
+                */
+               if (!vaifirst && value(AUTOINDENT)) {
+                       i = fixindent(indent);
+                       if (!HADUP)
+                               indent = i;
+                       gcursor = strend(genbuf);
+               }
+
+               /*
+                * Limit the repetition count based on maximum
+                * possible line length; do output implied
+                * by further count (> 1) and cons up the new line
+                * in linebuf.
+                */
+               cnt = vmaxrep(ch, cnt);
+               CP(gcursor + skipright(ogcursor, gcursor), cursor);
+               do {
+                       CP(cursor, genbuf);
+                       if (cnt > 1) {
+                               int oldhold = hold;
+
+                               Outchar = vinschar;
+                               hold |= HOLDQIK;
+                               printf("%s", genbuf);
+                               hold = oldhold;
+                               Outchar = vputchar;
+                       }
+                       cursor += gcursor - genbuf;
+               } while (--cnt > 0);
+               endim();
+               vUA2 = cursor;
+               if (escape != '\n')
+                       CP(cursor, gcursor + skipright(ogcursor, gcursor));
+
+               /*
+                * If doomed characters remain, clobber them,
+                * and reopen the line to get the display exact.
+                */
+               if (state != HARDOPEN) {
+                       DEPTH(vcline) = 0;
+                       savedoomed = doomed;
+                       if (doomed > 0) {
+                               register int cind = cindent();
+
+                               physdc(cind, cind + doomed);
+                               doomed = 0;
+                       }
+                       i = vreopen(LINE(vcline), lineDOT(), vcline);
+#ifdef TRACE
+                       if (trace)
+                               fprintf(trace, "restoring doomed from %d to %d\n", doomed, savedoomed);
+#endif
+                       if (ch == 'R')
+                               doomed = savedoomed;
+               }
+
+               /*
+                * All done unless we are continuing on to another line.
+                */
+               if (escape != '\n')
+                       break;
+
+               /*
+                * Set up for the new line.
+                * First save the current line, then construct a new
+                * first image for the continuation line consisting
+                * of any new autoindent plus the pushed ahead text.
+                */
+               showmode(0);
+               killU();
+               addc(gobblebl ? ' ' : '\n');
+               vsave();
+               cnt = 1;
+               if (value(AUTOINDENT)) {
+#ifdef LISPCODE
+                       if (value(LISP))
+                               indent = lindent(dot + 1);
+                       else
+#endif
+                            if (!HADUP && vaifirst)
+                               indent = whitecnt(linebuf);
+                       vaifirst = 0;
+                       strcLIN(vpastwh(gcursor + 1));
+                       gcursor = genindent(indent);
+                       *gcursor = 0;
+                       if (gcursor + strlen(linebuf) > &genbuf[LBSIZE - 2])
+                               gcursor = genbuf;
+                       CP(gcursor, linebuf);
+               } else {
+                       CP(genbuf, gcursor + skipright(ogcursor, gcursor));
+                       gcursor = genbuf;
+               }
+
+               /*
+                * If we started out as a single line operation and are now
+                * turning into a multi-line change, then we had better yank
+                * out dot before it changes so that undo will work
+                * correctly later.
+                */
+               if (FIXUNDO && vundkind == VCHNG) {
+                       vremote(1, yank, 0);
+                       undap1--;
+               }
+
+               /*
+                * Now do the append of the new line in the buffer,
+                * and update the display.  If slowopen
+                * we don't do very much.
+                */
+               vdoappend(genbuf);
+               vundkind = VMANYINS;
+               vcline++;
+               if (state != VISUAL)
+                       vshow(dot, NOLINE);
+               else {
+                       i += LINE(vcline - 1);
+                       vopen(dot, i);
+                       if (value(SLOWOPEN))
+                               vscrap();
+                       else
+                               vsync1(LINE(vcline));
+               }
+               strcLIN(gcursor);
+               *gcursor = 0;
+               cursor = linebuf;
+               vgotoCL(qcolumn(cursor + skipleft(ogcursor, cursor), genbuf));
+               showmode(ch);
+       }
+
+       /*
+        * All done with insertion, position the cursor
+        * and sync the screen.
+        */
+       showmode(0);
+       hold = oldhold;
+       if (cursor > linebuf)
+               cursor += skipleft(linebuf, cursor);
+       if (state != HARDOPEN)
+               vsyncCL();
+       else if (cursor > linebuf)
+               back1();
+       doomed = 0;
+       wcursor = cursor;
+       vmove(0);
+#ifdef SIGWINCH
+       sigprocmask(SIG_SETMASK, &oset, NULL);
+#endif
+}
+
+/*
+ * Subroutine for vgetline to back up a single character position,
+ * backwards around end of lines (vgoto can't hack columns which are
+ * less than 0 in general).
+ */
+void
+back1(void)
+{
+
+       vgoto(destline - 1, WCOLS + destcol - 1);
+}
+
+#define        gappend(c) { \
+               int     _c = c; \
+               xgappend(_c, &gcursor); \
+       }
+
+static void
+xgappend(int c, char **gp)
+{
+       if (*gp >= &genbuf[MAXBSIZE-mb_cur_max-1]) {
+               beep();
+               return;
+       }
+#ifdef MB
+       if (mb_cur_max > 1 && !(c & INVBIT)) {
+               char    mb[MB_LEN_MAX];
+               int     i, n;
+               n = wctomb(mb, c);
+               for (i = 0; i < n; i++)
+                       *(*gp)++ = mb[i];
+       } else
+#endif /* MB */
+               *(*gp)++ = c & 0377;
+}
+
+/*
+ * Get a line into genbuf after gcursor.
+ * Cnt limits the number of input characters
+ * accepted and is used for handling the replace
+ * single character command.  Aescaped is the location
+ * where we stick a termination indicator (whether we
+ * ended with an ESCAPE or a newline/return.
+ *
+ * We do erase-kill type processing here and also
+ * are careful about the way we do this so that it is
+ * repeatable.  (I.e. so that your kill doesn't happen,
+ * when you repeat an insert if it was escaped with \ the
+ * first time you did it.  commch is the command character
+ * involved, including the prompt for readline.
+ */
+char *
+vgetline(int cnt, char *gcursor, bool *aescaped, int commch)
+{
+       register int c, ch;
+       register char *cp;
+       int x, y, iwhite, backsl=0;
+       cell *iglobp;
+       char cstr[2];
+       int (*OO)() = Outchar;
+
+       /*
+        * Clear the output state and counters
+        * for autoindent backwards motion (counts of ^D, etc.)
+        * Remember how much white space at beginning of line so
+        * as not to allow backspace over autoindent.
+        */
+       *aescaped = 0;
+       ogcursor = gcursor;
+       flusho();
+       CDCNT = 0;
+       HADUP = 0;
+       HADZERO = 0;
+       gobbled = 0;
+       iwhite = whitecnt(genbuf);
+       iglobp = vglobp;
+
+       /*
+        * Carefully avoid using vinschar in the echo area.
+        */
+       if (splitw)
+               Outchar = vputchar;
+       else {
+               Outchar = vinschar;
+               vprepins();
+       }
+       for (;;) {
+               backsl = 0;
+               if (gobblebl)
+                       gobblebl--;
+               if (cnt != 0) {
+                       cnt--;
+                       if (cnt == 0)
+                               goto vadone;
+               }
+               c = getkey();
+               if (c != ATTN)
+                       c &= (QUOTE|TRIM);
+               ch = c;
+               maphopcnt = 0;
+               if (vglobp == 0 && Peekkey == 0 && commch != 'r')
+                       while ((ch = map(c, immacs)) != c) {
+                               c = ch;
+                               if (!value(REMAP))
+                                       break;
+                               if (++maphopcnt > 256)
+                                       error(catgets(catd, 1, 234,
+                                               "Infinite macro loop"));
+                       }
+               if (!iglobp) {
+
+                       /*
+                        * Erase-kill type processing.
+                        * Only happens if we were not reading
+                        * from untyped input when we started.
+                        * Map users erase to ^H, kill to -1 for switch.
+                        */
+                       if (c == tty.c_cc[VERASE])
+                               c = CTRL('h');
+                       else if (c == tty.c_cc[VKILL])
+                               c = -1;
+                       if (c == ATTN)
+                               goto case_ATTN;
+                       switch (c) {
+
+                       /*
+                        * ^?           Interrupt drops you back to visual
+                        *              command mode with an unread interrupt
+                        *              still in the input buffer.
+                        *
+                        * ^\           Quit does the same as interrupt.
+                        *              If you are a ex command rather than
+                        *              a vi command this will drop you
+                        *              back to command mode for sure.
+                        */
+                       case QUIT:
+case_ATTN:
+                               ungetkey(c);
+                               goto vadone;
+
+                       /*
+                        * ^H           Backs up a character in the input.
+                        *
+                        * BUG:         Can't back around line boundaries.
+                        *              This is hard because stuff has
+                        *              already been saved for repeat.
+                        */
+                       case CTRL('h'):
+bakchar:
+                               cp = gcursor + skipleft(ogcursor, gcursor);
+                               if (cp < ogcursor) {
+                                       if (splitw) {
+                                               /*
+                                                * Backspacing over readecho
+                                                * prompt. Pretend delete but
+                                                * don't beep.
+                                                */
+                                               ungetkey(c);
+                                               goto vadone;
+                                       }
+                                       beep();
+                                       continue;
+                               }
+                               goto vbackup;
+
+                       /*
+                        * ^W           Back up a white/non-white word.
+                        */
+                       case CTRL('w'):
+                               wdkind = 1;
+                               for (cp = gcursor; cp > ogcursor
+                                               && isspace(cp[-1]&0377); cp--)
+                                       continue;
+                               for (c = wordch(cp - 1);
+                                   cp > ogcursor && wordof(c, cp - 1); cp--)
+                                       continue;
+                               goto vbackup;
+
+                       /*
+                        * users kill   Kill input on this line, back to
+                        *              the autoindent.
+                        */
+                       case -1:
+                               cp = ogcursor;
+vbackup:
+                               if (cp == gcursor) {
+                                       beep();
+                                       continue;
+                               }
+                               endim();
+                               *cp = 0;
+                               c = cindent();
+                               vgotoCL(qcolumn(cursor +
+                                       skipleft(linebuf, cursor), genbuf));
+                               if (doomed >= 0)
+                                       doomed += c - cindent();
+                               gcursor = cp;
+                               continue;
+
+                       /*
+                        * \            Followed by erase or kill
+                        *              maps to just the erase or kill.
+                        */
+                       case '\\':
+                               x = destcol, y = destline;
+                               putchar('\\');
+                               vcsync();
+                               c = getkey();
+                               if (c == tty.c_cc[VERASE]
+                                   || c == tty.c_cc[VKILL])
+                               {
+                                       vgoto(y, x);
+                                       if (doomed >= 0)
+                                               doomed++;
+                                       goto def;
+                               }
+                               ungetkey(c), c = '\\';
+                               backsl = 1;
+                               break;
+
+                       /*
+                        * ^Q           Super quote following character
+                        *              Only ^@ is verboten (trapped at
+                        *              a lower level) and \n forces a line
+                        *              split so doesn't really go in.
+                        *
+                        * ^V           Synonym for ^Q
+                        */
+                       case CTRL('q'):
+                       case CTRL('v'):
+                               x = destcol, y = destline;
+                               putchar('^');
+                               vgoto(y, x);
+                               c = getkey();
+                               if (c != NL) {
+                                       if (doomed >= 0)
+                                               doomed++;
+                                       goto def;
+                               }
+                               break;
+                       }
+               }
+
+               /*
+                * If we get a blank not in the echo area
+                * consider splitting the window in the wrapmargin.
+                */
+               if (c != NL && !splitw) {
+                       if (c == ' ' && gobblebl) {
+                               gobbled = 1;
+                               continue;
+                       }
+                       if (value(WRAPMARGIN) &&
+                               (outcol >= OCOLUMNS - value(WRAPMARGIN) ||
+                                backsl && outcol==0) &&
+                               commch != 'r') {
+                               /*
+                                * At end of word and hit wrapmargin.
+                                * Move the word to next line and keep going.
+                                */
+                               wdkind = 1;
+                               gappend(c);
+                               if (backsl)
+                                       gappend(getkey());
+                               *gcursor = 0;
+                               /*
+                                * Find end of previous word if we are past it.
+                                */
+                               for (cp=gcursor; cp>ogcursor
+                                               && isspace(cp[-1]&0377); cp--)
+                                       ;
+                               if (outcol+(backsl?OCOLUMNS:0) - (gcursor-cp) >= OCOLUMNS - value(WRAPMARGIN)) {
+                                       /*
+                                        * Find beginning of previous word.
+                                        */
+                                       for (; cp>ogcursor && !isspace(cp[-1]&0377); cp--)
+                                               ;
+                                       if (cp <= ogcursor) {
+                                               /*
+                                                * There is a single word that
+                                                * is too long to fit.  Just
+                                                * let it pass, but beep for
+                                                * each new letter to warn
+                                                * the luser.
+                                                */
+                                               c = *--gcursor;
+                                               *gcursor = 0;
+                                               beep();
+                                               goto dontbreak;
+                                       }
+                                       /*
+                                        * Save it for next line.
+                                        */
+                                       macpush(cp, 0);
+                                       cp--;
+                               }
+                               macpush("\n", 0);
+                               /*
+                                * Erase white space before the word.
+                                */
+                               while (cp > ogcursor && isspace(cp[-1]&0377))
+                                       cp--;   /* skip blank */
+                               gobblebl = 3;
+                               goto vbackup;
+                       }
+               dontbreak:;
+               }
+
+               /*
+                * Word abbreviation mode.
+                */
+               cstr[0] = c;
+               if (anyabbrs && gcursor > ogcursor && !wordch(cstr) && wordch(gcursor-1)) {
+                               int wdtype, abno;
+
+                               cstr[1] = 0;
+                               wdkind = 1;
+                               cp = gcursor + skipleft(ogcursor, gcursor);
+                               for (wdtype = wordch(cp - 1);
+                                   cp > ogcursor && wordof(wdtype, cp - 1); cp--)
+                                       ;
+                               *gcursor = 0;
+                               for (abno=0; abbrevs[abno].mapto; abno++) {
+                                       if (!abbrevs[abno].hadthis &&
+                                               eq(cp, abbrevs[abno].cap)) {
+                                               abbrevs[abno].hadthis++;
+                                               macpush(cstr, 0);
+                                               macpush(abbrevs[abno].mapto, 0);
+                                               goto vbackup;
+                                       }
+                               }
+               }
+
+#ifdef BIT8
+               if (c == OVERBUF)
+                       goto btrp;
+#endif
+               switch (c) {
+
+               /*
+                * ^M           Except in repeat maps to \n.
+                */
+               case CR:
+                       if (vglobp)
+                               goto def;
+                       c = '\n';
+                       /* presto chango ... */
+
+               /*
+                * \n           Start new line.
+                */
+               case NL:
+                       *aescaped = c;
+                       goto vadone;
+
+               /*
+                * escape       End insert unless repeat and more to repeat.
+                */
+               case ESCAPE:
+                       if (lastvgk)
+                               goto def;
+                       goto vadone;
+
+               /*
+                * ^D           Backtab.
+                * ^T           Software forward tab.
+                *
+                *              Unless in repeat where this means these
+                *              were superquoted in.
+                */
+               case CTRL('d'):
+               case CTRL('t'):
+                       if (vglobp)
+                               goto def;
+                       /* fall into ... */
+
+               /*
+                * ^D|QUOTE     Is a backtab (in a repeated command).
+                */
+#ifndef        BIT8
+               case CTRL('d') | QUOTE:
+#else
+btrp:
+#endif
+                       *gcursor = 0;
+                       cp = vpastwh(genbuf);
+                       c = whitecnt(genbuf);
+                       if (ch == CTRL('t')) {
+                               /*
+                                * ^t just generates new indent replacing
+                                * current white space rounded up to soft
+                                * tab stop increment.
+                                */
+                               if (cp != gcursor)
+                                       /*
+                                        * BUG:         Don't hack ^T except
+                                        *              right after initial
+                                        *              white space.
+                                        */
+                                       continue;
+                               cp = genindent(iwhite = backtab(c + value(SHIFTWIDTH) + 1));
+                               ogcursor = cp;
+                               goto vbackup;
+                       }
+                       /*
+                        * ^D works only if we are at the (end of) the
+                        * generated autoindent.  We count the ^D for repeat
+                        * purposes.
+                        */
+                       if (c == iwhite && c != 0)
+                               if (cp == gcursor) {
+                                       iwhite = backtab(c);
+                                       CDCNT++;
+                                       ogcursor = cp = genindent(iwhite);
+                                       goto vbackup;
+                               } else if (&cp[1] == gcursor &&
+                                   (*cp == '^' || *cp == '0')) {
+                                       /*
+                                        * ^^D moves to margin, then back
+                                        * to current indent on next line.
+                                        *
+                                        * 0^D moves to margin and then
+                                        * stays there.
+                                        */
+                                       HADZERO = *cp == '0';
+                                       ogcursor = cp = genbuf;
+                                       HADUP = 1 - HADZERO;
+                                       CDCNT = 1;
+                                       endim();
+                                       back1();
+                                       vputchar(' ');
+                                       goto vbackup;
+                               }
+                       if (vglobp && vglobp - iglobp >= 2 &&
+                           (vglobp[-2] == '^' || vglobp[-2] == '0')
+                           && gcursor == ogcursor + 1)
+                               goto bakchar;
+                       continue;
+
+               default:
+                       /*
+                        * Possibly discard control inputs.
+                        */
+                       if (!vglobp && junk(c)) {
+                               beep();
+                               continue;
+                       }
+def:
+                       if (!backsl) {
+                               /* int cnt; */
+                               putchar(c);
+                               flush();
+                       }
+                       if (gcursor > &genbuf[LBSIZE - 2])
+                               error(catgets(catd, 1, 235, "Line too long"));
+                       gappend(c & TRIM);
+                       vcsync();
+                       if (value(SHOWMATCH) && !iglobp)
+                               if (c == ')' || c == '}')
+                                       lsmatch(gcursor);
+                       continue;
+               }
+       }
+vadone:
+       *gcursor = 0;
+       if (Outchar != termchar)
+               Outchar = OO;
+       endim();
+       return (gcursor);
+}
+
+char   *vsplitpt;
+
+/*
+ * Append the line in buffer at lp
+ * to the buffer after dot.
+ */
+void
+vdoappend(char *lp)
+{
+       register int oing = inglobal;
+
+       vsplitpt = lp;
+       inglobal = 1;
+       ignore(append(vgetsplit, dot));
+       inglobal = oing;
+}
+
+/*
+ * Subroutine for vdoappend to pass to append.
+ */
+int
+vgetsplit(void)
+{
+
+       if (vsplitpt == 0)
+               return (EOF);
+       strcLIN(vsplitpt);
+       vsplitpt = 0;
+       return (0);
+}
+
+/*
+ * Vmaxrep determines the maximum repetitition factor
+ * allowed that will yield total line length less than
+ * LBSIZE characters and also does hacks for the R command.
+ */
+int
+vmaxrep(int ch, register int cnt)
+{
+       register int len, replen;
+
+       if (cnt > LBSIZE - 2)
+               cnt = LBSIZE - 2;
+       replen = strlen(genbuf);
+       if (ch == 'R') {
+               len = strlen(cursor);
+               if (replen < len)
+                       len = replen;
+#ifdef MB
+               if (mb_cur_max > 1) {
+                       char    *cp, *gp;
+                       int     c, g;
+                       for (gp = genbuf, g = 0; *gp; g++)
+                               gp += wskipright(genbuf, gp);
+                       for (cp = cursor, c = 0; c < g; c++)
+                               cp += wskipright(cursor, cp);
+                       CP(cursor, cp);
+               } else
+#endif /* MB */
+                       CP(cursor, cursor + len);
+               vUD2 += len;
+       }
+       len = strlen(linebuf);
+       if (len + cnt * replen <= LBSIZE - 2)
+               return (cnt);
+       cnt = (LBSIZE - 2 - len) / replen;
+       if (cnt == 0) {
+               vsave();
+               error(catgets(catd, 1, 236, "Line too long"));
+       }
+       return (cnt);
+}
diff --git a/ex_vops3.c b/ex_vops3.c
new file mode 100644 (file)
index 0000000..57cdebf
--- /dev/null
@@ -0,0 +1,730 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_vops3.c 1.19 (gritter) 1/2/05";
+#endif
+#endif
+
+/* from ex_vops3.c     7.3 (Berkeley) 6/7/85 */
+
+#include "ex.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+
+/*
+ * Routines to handle structure.
+ * Operations supported are:
+ *     ( ) { } [ ]
+ *
+ * These cover:                LISP            TEXT
+ *     ( )             s-exprs         sentences
+ *     { }             list at same    paragraphs
+ *     [ ]             defuns          sections
+ *
+ * { and } for C used to attempt to do something with matching {}'s, but
+ * I couldn't find definitions which worked intuitively very well, so I
+ * scrapped this.
+ *
+ * The code here is very hard to understand.
+ */
+line   *llimit;
+void   (*lf)(int);
+
+bool   wasend;
+
+/*
+ * Find over structure, repeated count times.
+ * Don't go past line limit.  F is the operation to
+ * be performed eventually.  If pastatom then the user said {}
+ * rather than (), implying past atoms in a list (or a paragraph
+ * rather than a sentence.
+ */
+int
+llfind(bool pastatom, int cnt, void (*f)(int), line *limit)
+{
+#ifdef LISPCODE
+       register int c;
+#endif
+       register int rc = 0;
+       char save[LBSIZE];
+
+       /*
+        * Initialize, saving the current line buffer state
+        * and computing the limit; a 0 argument means
+        * directional end of file.
+        */
+       wasend = 0;
+       lf = f;
+       strcpy(save, linebuf);
+       if (limit == 0)
+               limit = dir < 0 ? one : dol;
+       llimit = limit;
+       wdot = dot;
+       wcursor = cursor;
+
+       if (pastatom >= 2) {
+               while (cnt > 0 && word(f, cnt))
+                       cnt--;
+               if (pastatom == 3)
+                       eend(f);
+               if (dot == wdot) {
+                       wdot = 0;
+                       if (cursor == wcursor)
+                               rc = -1;
+               }
+       }
+#ifdef LISPCODE
+       else if (!value(LISP)) {
+#else
+       else {
+#endif
+               char *icurs;
+               line *idot;
+
+               if (linebuf[0] == 0) {
+                       do
+                               if (!lnext())
+                                       goto ret;
+                       while (linebuf[0] == 0);
+                       if (dir > 0) {
+                               wdot--;
+                               linebuf[0] = 0;
+                               wcursor = linebuf;
+                               /*
+                                * If looking for sentence, next line
+                                * starts one.
+                                */
+                               if (!pastatom) {
+                                       icurs = wcursor;
+                                       idot = wdot;
+                                       goto begin;
+                               }
+                       }
+               }
+               icurs = wcursor;
+               idot = wdot;
+
+               /*
+                * Advance so as to not find same thing again.
+                */
+               if (dir > 0) {
+                       if (!lnext()) {
+                               rc = -1;
+                               goto ret;
+                       }
+               } else
+                       ignore(lskipa1(""));
+
+               /*
+                * Count times find end of sentence/paragraph.
+                */
+begin:
+               for (;;) {
+                       while (!endsent(pastatom))
+                               if (!lnext())
+                                       goto ret;
+                       if (!pastatom || wcursor == linebuf && endPS())
+                               if (--cnt <= 0)
+                                       break;
+                       if (linebuf[0] == 0) {
+                               do
+                                       if (!lnext())
+                                               goto ret;
+                               while (linebuf[0] == 0);
+                       } else
+                               if (!lnext())
+                                       goto ret;
+               }
+
+               /*
+                * If going backwards, and didn't hit the end of the buffer,
+                * then reverse direction.
+                */
+               if (dir < 0 && (wdot != llimit || wcursor != linebuf)) {
+                       dir = 1;
+                       llimit = dot;
+                       /*
+                        * Empty line needs special treatement.
+                        * If moved to it from other than begining of next line,
+                        * then a sentence starts on next line.
+                        */
+                       if (linebuf[0] == 0 && !pastatom && 
+                          (wdot != dot - 1 || cursor != linebuf)) {
+                               lnext();
+                               goto ret;
+                       }
+               }
+
+               /*
+                * If we are not at a section/paragraph division,
+                * advance to next.
+                */
+               if (wcursor == icurs && wdot == idot || wcursor != linebuf || !endPS())
+                       ignore(lskipa1(""));
+       }
+#ifdef LISPCODE
+       else {
+               c = *wcursor;
+               /*
+                * Startup by skipping if at a ( going left or a ) going
+                * right to keep from getting stuck immediately.
+                */
+               if (dir < 0 && c == '(' || dir > 0 && c == ')') {
+                       if (!lnext()) {
+                               rc = -1;
+                               goto ret;
+                       }
+               }
+               /*
+                * Now chew up repitition count.  Each time around
+                * if at the beginning of an s-exp (going forwards)
+                * or the end of an s-exp (going backwards)
+                * skip the s-exp.  If not at beg/end resp, then stop
+                * if we hit a higher level paren, else skip an atom,
+                * counting it unless pastatom.
+                */
+               while (cnt > 0) {
+                       c = *wcursor;
+                       if (dir < 0 && c == ')' || dir > 0 && c == '(') {
+                               if (!lskipbal("()"))
+                                       goto ret;
+                               /*
+                                * Unless this is the last time going
+                                * backwards, skip past the matching paren
+                                * so we don't think it is a higher level paren.
+                                */
+                               if (dir < 0 && cnt == 1)
+                                       goto ret;
+                               if (!lnext() || !ltosolid())
+                                       goto ret;
+                               --cnt;
+                       } else if (dir < 0 && c == '(' || dir > 0 && c == ')')
+                               /* Found a higher level paren */
+                               goto ret;
+                       else {
+                               if (!lskipatom())
+                                       goto ret;
+                               if (!pastatom)
+                                       --cnt;
+                       }
+               }
+       }
+#endif
+ret:
+       strcLIN(save);
+       return (rc);
+}
+
+/*
+ * Is this the end of a sentence?
+ */
+int
+endsent(bool pastatom)
+{
+       register char *cp = wcursor;
+       register int c, d;
+
+       /*
+        * If this is the beginning of a line, then
+        * check for the end of a paragraph or section.
+        */
+       if (cp == linebuf)
+               return (endPS());
+
+       /*
+        * Sentences end with . ! ? not at the beginning
+        * of the line, and must be either at the end of the line,
+        * or followed by 2 spaces.  Any number of intervening ) ] ' "
+        * characters are allowed.
+        */
+       if (!any(c = *cp, ".!?"))
+               goto tryps;
+       do
+               if ((d = *++cp) == 0)
+                       return (1);
+       while (any(d, ")]'"));
+       if (*cp == 0 || *cp++ == ' ' && *cp == ' ')
+               return (1);
+tryps:
+       if (cp[1] == 0)
+               return (endPS());
+       return (0);
+}
+
+/*
+ * End of paragraphs/sections are respective
+ * macros as well as blank lines and form feeds.
+ */
+int
+endPS(void)
+{
+
+       return (linebuf[0] == 0 ||
+               isa(svalue(PARAGRAPHS)) || isa(svalue(SECTIONS)));
+           
+}
+
+#ifdef LISPCODE
+int
+lindent(line *addr)
+{
+       register int i;
+       char *swcurs = wcursor;
+       line *swdot = wdot;
+
+again:
+       if (addr > one) {
+               register char *cp;
+               register int cnt = 0;
+
+               addr--;
+               getline(*addr);
+               for (cp = linebuf; *cp; cp++)
+                       if (*cp == '(')
+                               cnt++;
+                       else if (*cp == ')')
+                               cnt--;
+               cp = vpastwh(linebuf);
+               if (*cp == 0)
+                       goto again;
+               if (cnt == 0)
+                       return (whitecnt(linebuf));
+               addr++;
+       }
+       wcursor = linebuf;
+       linebuf[0] = 0;
+       wdot = addr;
+       dir = -1;
+       llimit = one;
+       lf = (void (*)(int))lindent;
+       if (!lskipbal("()"))
+               i = 0;
+       else if (wcursor == linebuf)
+               i = 2;
+       else {
+               register char *wp = wcursor;
+
+               dir = 1;
+               llimit = wdot;
+               if (!lnext() || !ltosolid() || !lskipatom()) {
+                       wcursor = wp;
+                       i = 1;
+               } else
+                       i = 0;
+               i += column(wcursor) - 1;
+               if (!inopen)
+                       i--;
+       }
+       wdot = swdot;
+       wcursor = swcurs;
+       return (i);
+}
+#endif
+
+int
+lmatchp(line *addr)
+{
+       register int i;
+       register char *parens, *cp;
+
+       for (cp = cursor; !any(*cp, "({[)}]");)
+               if (*cp++ == 0)
+                       return (0);
+       lf = 0;
+       parens = any(*cp, "()") ? "()" : any(*cp, "[]") ? "[]" : "{}";
+       if (*cp == parens[1]) {
+               dir = -1;
+               llimit = one;
+       } else {
+               dir = 1;
+               llimit = dol;
+       }
+       if (addr)
+               llimit = addr;
+       if (splitw)
+               llimit = dot;
+       wcursor = cp;
+       wdot = dot;
+       i = lskipbal(parens);
+       return (i);
+}
+
+void
+lsmatch(char *cp)
+{
+       char save[LBSIZE];
+       register char *sp = save;
+       register char *scurs = cursor;
+
+       wcursor = cp;
+       strcpy(sp, linebuf);
+       *wcursor = 0;
+       strcpy(cursor, genbuf);
+       cursor = strend(linebuf) - 1;
+       if (lmatchp(dot - vcline)) {
+               register int i = insmode;
+               register int c = outcol;
+               register int l = outline;
+
+               if (!MI)
+                       endim();
+               vgoto(splitw ? WECHO : LINE(wdot - llimit), column(wcursor) - 1);
+               flush();
+               sleep(1);
+               vgoto(l, c);
+               if (i)
+                       goim();
+       }
+       else {
+               strcLIN(sp);
+               strcpy(scurs, genbuf);
+               if (!lmatchp((line *) 0))
+                       beep();
+       }
+       strcLIN(sp);
+       wdot = 0;
+       wcursor = 0;
+       cursor = scurs;
+}
+
+int
+ltosolid(void)
+{
+
+       return (ltosol1("()"));
+}
+
+int
+ltosol1(register char *parens)
+{
+       register char *cp;
+
+       if (*parens && !*wcursor && !lnext())
+               return (0);
+       while (isspace(*wcursor&0377) || (*wcursor == 0 && *parens))
+               if (!lnext())
+                       return (0);
+       if (any(*wcursor, parens) || dir > 0)
+               return (1);
+       for (cp = wcursor; cp > linebuf; cp--)
+               if (isspace(cp[-1]&0377) || any(cp[-1], parens))
+                       break;
+       wcursor = cp;
+       return (1);
+}
+
+int
+lskipbal(register char *parens)
+{
+       register int level = dir;
+       register int c;
+
+       do {
+               if (!lnext()) {
+                       wdot = NOLINE;
+                       return (0);
+               }
+               c = *wcursor;
+               if (c == parens[1])
+                       level--;
+               else if (c == parens[0])
+                       level++;
+       } while (level);
+       return (1);
+}
+
+int
+lskipatom(void)
+{
+
+       return (lskipa1("()"));
+}
+
+int
+lskipa1(register char *parens)
+{
+       register int c;
+
+       for (;;) {
+               if (dir < 0 && wcursor == linebuf) {
+                       if (!lnext())
+                               return (0);
+                       break;
+               }
+               c = *wcursor;
+               if (c && (isspace(c) || any(c, parens)))
+                       break;
+               if (!lnext())
+                       return (0);
+               if (dir > 0 && wcursor == linebuf)
+                       break;
+       }
+       return (ltosol1(parens));
+}
+
+int
+lnext(void)
+{
+
+       if (dir > 0) {
+               if (*wcursor)
+                       wcursor += skipright(linebuf, wcursor);
+               if (*wcursor)
+                       return (1);
+               if (wdot >= llimit) {
+                       if (lf == vmove && wcursor > linebuf)
+                               wcursor += skipleft(linebuf, wcursor);
+                       return (0);
+               }
+               wdot++;
+               getline(*wdot);
+               wcursor = linebuf;
+               return (1);
+       } else {
+               wcursor += skipleft(linebuf, wcursor);
+               if (wcursor >= linebuf)
+                       return (1);
+#ifdef LISPCODE
+               if (lf == (void (*)(int))lindent && linebuf[0] == '(')
+                       llimit = wdot;
+#endif
+               if (wdot <= llimit) {
+                       wcursor = linebuf;
+                       return (0);
+               }
+               wdot--;
+               getline(*wdot);
+               wcursor = linebuf[0] == 0 ? linebuf : strend(linebuf) - 1;
+               return (1);
+       }
+}
+
+int
+lbrack(register int c, void (*f)(int))
+{
+       register line *addr;
+
+       addr = dot;
+       for (;;) {
+               addr += dir;
+               if (addr < one || addr > dol) {
+                       addr -= dir;
+                       break;
+               }
+               getline(*addr);
+               if (linebuf[0] == '{' ||
+#ifdef LISPCODE
+                   value(LISP) && linebuf[0] == '(' ||
+#endif
+                   isa(svalue(SECTIONS))) {
+                       if (c == ']' && f != vmove) {
+                               addr--;
+                               getline(*addr);
+                       }
+                       break;
+               }
+               if (c == ']' && f != vmove && linebuf[0] == '}')
+                       break;
+       }
+       if (addr == dot)
+               return (0);
+       if (f != vmove)
+               wcursor = c == ']' ? strend(linebuf) : linebuf;
+       else
+               wcursor = 0;
+       wdot = addr;
+       vmoving = 0;
+       return (1);
+}
+
+int
+isa(register char *cp)
+{
+
+       if (linebuf[0] != '.')
+               return (0);
+       for (; cp[0] && cp[1]; cp += 2)
+               if (linebuf[1] == cp[0]) {
+                       if (linebuf[2] == cp[1])
+                               return (1);
+                       if (linebuf[2] == 0 && cp[1] == ' ')
+                               return (1);
+               }
+       return (0);
+}
+
+static void
+cswitch(char *dst, int *dn, const char *src, int *sn)
+{
+       int     c;
+
+#ifdef MB
+       if (mb_cur_max > 1) {
+               nextc(c, src, *sn);
+               if (c & INVBIT) {
+                       *dst = *src;
+                       *dn = *sn = 1;
+               } else {
+                       if (iswupper(c))
+                               c = towlower(c);
+                       else if (iswlower(c))
+                               c = towupper(c);
+                       if ((*dn = wctomb(dst, c)) > *sn) {
+                               *dst = *src;
+                               *dn = *sn = 1;
+                       }
+               }
+       } else
+#endif /* MB */
+       {
+               c = *src & 0377;
+               if (isupper(c))
+                       *dst = tolower(c);
+               else if (islower(c))
+                       *dst = toupper(c);
+               else
+                       *dst = c;
+               *dn = *sn = 1;
+       }
+}
+
+void
+vswitch(int cnt)
+{
+       if (cnt <= 1) {
+               char mbuf[MB_LEN_MAX+4];
+               int     n0, n1;
+               setLAST();
+               mbuf[0] = 'r';
+               cswitch(&mbuf[1], &n1, cursor, &n0);
+               if (cursor[n1] != '\0')
+                       mbuf[1+n1++] = ' ';
+               mbuf[1+n1] = '\0';
+               macpush(mbuf, 1);
+       } else {        /* cnt > 1 */
+               char *mbuf = malloc(MAXDIGS + cnt*(mb_cur_max+1) + 5);
+               register char *p = &mbuf[MAXDIGS + 1];
+               int num, n0, n1, m;
+
+               setLAST();
+               *p++ = 's';
+               for (num = 0, m = 0; num < cnt && cursor[m] != '\0'; num++) {
+                       *p++ = CTRL('v');
+                       cswitch(p, &n1, &cursor[m], &n0);
+                       p += n1;
+                       m += n0;
+               }
+               *p++ = ESCAPE;
+               if (cursor[m])
+                       *p++ = ' ';
+               *p++ = '\0';
+               macpush(p_dconv((long)num, mbuf), 1);
+               lastvgk = 0;
+               free(mbuf);
+       }
+}
+
+#ifdef MB
+int
+wskipleft(char *lp, char *pos)
+{
+       int     c, n;
+
+       do {
+               nextc(c, lp, n);
+               lp += n;
+       } while (lp < pos);
+       return -n;
+}
+
+int
+wskipright(char *line, char *pos)
+{
+       int     c, n;
+
+       nextc(c, pos, n);
+       return n;
+}
+
+int
+wsamechar(char *cp, int d)
+{
+       int     c;
+
+       if (mbtowi(&c, cp, mb_cur_max) >= 0 && c == d)
+               return 1;
+       return 0;
+}
+#endif /* MB */
diff --git a/ex_vput.c b/ex_vput.c
new file mode 100644 (file)
index 0000000..f2c9c3f
--- /dev/null
+++ b/ex_vput.c
@@ -0,0 +1,1625 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_vput.c  1.49 (gritter) 2/15/05";
+#endif
+#endif
+
+/* from ex_vput.c      7.4.1 (2.11BSD GTE) 12/9/94 */
+
+#include "ex.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+
+/*
+ * Deal with the screen, clearing, cursor positioning, putting characters
+ * into the screen image, and deleting characters.
+ * Really hard stuff here is utilizing insert character operations
+ * on intelligent terminals which differs widely from terminal to terminal.
+ */
+void 
+vclear(void)
+{
+
+#ifdef ADEBUG
+       if (trace)
+               tfixnl(), fprintf(trace, "------\nvclear\n");
+#endif
+       tputs(CL, TLINES, putch);
+       destcol = 0;
+       outcol = 0;
+       destline = 0;
+       outline = 0;
+       if (inopen)
+               vclrcell(vtube0, WCOLS * (WECHO - ZERO + 1));
+}
+
+/*
+ * Clear memory.
+ */
+void 
+vclrcell(register cell *cp, register int i)
+{
+       if (i > 0)
+               do
+                       *cp++ = 0;
+               while (--i != 0);
+}
+
+/*
+ * Clear a physical display line, high level.
+ */
+void 
+vclrlin(int l, line *tp)
+{
+
+       vigoto(l, 0);
+       if ((hold & HOLDAT) == 0)
+#ifndef        UCVISUAL
+               putchar(tp > dol ? '~' : '@');
+#else
+               putchar(tp > dol ? ((UPPERCASE || xHZ) ? '^' : '~') : '@');
+#endif
+       if (state == HARDOPEN)
+               sethard();
+       vclreol();
+}
+
+/*
+ * Clear to the end of the current physical line
+ */
+void
+vclreol(void)
+{
+       register int i, j;
+       register cell *tp;
+
+       if (destcol == WCOLS)
+               return;
+       destline += destcol / WCOLS;
+       destcol %= WCOLS;
+       if (destline < 0 || destline > WECHO)
+               error(catgets(catd, 1, 237, "Internal error: vclreol"));
+       i = WCOLS - destcol;
+       tp = vtube[destline] + destcol;
+       if (CE) {
+               if (IN && *tp || !ateopr()) {
+                       vcsync();
+                       vputp(CE, 1);
+               }
+               vclrcell(tp, i);
+               return;
+       }
+       if (*tp == 0)
+               return;
+       while (i > 0 && (j = *tp & (QUOTE|TRIM|MULTICOL))) {
+               if ((j != ' ' && (j & QUOTE) == 0)) {
+                       destcol = WCOLS - i;
+                       vputchar(' ');
+               }
+               --i, *tp++ = 0;
+       }
+}
+
+/*
+ * Clear the echo line.
+ * If didphys then its been cleared physically (as
+ * a side effect of a clear to end of display, e.g.)
+ * so just do it logically.
+ * If work here is being held off, just remember, in
+ * heldech, if work needs to be done, don't do anything.
+ */
+void
+vclrech(bool didphys)
+{
+
+       if (Peekkey == ATTN)
+               return;
+       if (hold & HOLDECH) {
+               heldech = !didphys;
+               return;
+       }
+       if (!didphys && (CD || CE)) {
+               splitw++;
+               /*
+                * If display is retained below, then MUST use CD or CE
+                * since we don't really know whats out there.
+                * Vigoto might decide (incorrectly) to do nothing.
+                */
+               if (DB) {
+                       vgoto(WECHO, 0);
+                       vputp(CD ? CD : CE, 1);
+               } else {
+                       if (XT) {
+                               /*
+                                * This code basically handles the t1061
+                                * where positioning at (0, 0) won't work
+                                * because the terminal won't let you put
+                                * the cursor on it's magic cookie.
+                                *
+                                * Should probably be XS above, or even a
+                                * new X? glitch, but right now t1061 is the
+                                * only terminal with XT.
+                                */
+                               vgoto(WECHO, 0);
+                               vputp(DL, 1);
+                       } else {
+                               vigoto(WECHO, 0);
+                               vclreol();
+                       }
+               }
+               splitw = 0;
+               didphys = 1;
+       }
+       if (didphys)
+               vclrcell(vtube[WECHO], WCOLS);
+       heldech = 0;
+}
+
+/*
+ * Fix the echo area for use, setting
+ * the state variable splitw so we wont rollup
+ * when we move the cursor there.
+ */
+void
+fixech(void)
+{
+
+       splitw++;
+       if (state != VISUAL && state != CRTOPEN) {
+               vclean();
+               vcnt = 0;
+       }
+       vgoto(WECHO, 0); flusho();
+}
+
+/*
+ * Put the cursor ``before'' cp.
+ */
+void
+vcursbef(register char *cp)
+{
+
+       if (cp <= linebuf)
+               vgotoCL(value(NUMBER) << 3);
+       else
+               vgotoCL(column(cp - 1) - 1);
+}
+
+/*
+ * Put the cursor ``at'' cp.
+ */
+void
+vcursat(register char *cp)
+{
+
+       if (cp <= linebuf && linebuf[0] == 0)
+               vgotoCL(value(NUMBER) << 3);
+       else
+               vgotoCL(column(cp + skipleft(linebuf, cp)));
+}
+
+/*
+ * Put the cursor ``after'' cp.
+ */
+void
+vcursaft(register char *cp)
+{
+
+       vgotoCL(column(cp));
+}
+
+/*
+ * Fix the cursor to be positioned in the correct place
+ * to accept a command.
+ */
+void
+vfixcurs(void)
+{
+
+       vsetcurs(cursor);
+}
+
+/*
+ * Compute the column position implied by the cursor at ``nc'',
+ * and move the cursor there.
+ */
+void
+vsetcurs(register char *nc)
+{
+       register int col;
+
+       col = column(nc);
+       if (linebuf[0])
+               col--;
+       vgotoCL(col);
+       cursor = vcolbp;
+}
+
+/*
+ * Move the cursor invisibly, i.e. only remember to do it.
+ */
+void
+vigoto(int y, int x)
+{
+
+       destline = y;
+       destcol = x;
+}
+
+/*
+ * Move the cursor to the position implied by any previous
+ * vigoto (or low level hacking with destcol/destline as in readecho).
+ */
+void
+vcsync(void)
+{
+
+       vgoto(destline, destcol);
+}
+
+/*
+ * Goto column x of the current line.
+ */
+void
+vgotoCL(register int x)
+{
+
+       if (splitw)
+               vgoto(WECHO, x);
+       else
+               vgoto(LINE(vcline), x);
+}
+
+/*
+ * Invisible goto column x of current line.
+ */
+void
+vigotoCL(register int x)
+{
+
+       if (splitw)
+               vigoto(WECHO, x);
+       else
+               vigoto(LINE(vcline), x);
+}
+
+/*
+ * Move cursor to line y, column x, handling wraparound and scrolling.
+ */
+void
+vgoto(register int y, register int x)
+{
+       register cell *tp;
+       register int c;
+
+       /*
+        * Fold the possibly too large value of x.
+        */
+       if (x >= WCOLS) {
+               y += x / WCOLS;
+               x %= WCOLS;
+       }
+#ifdef MB
+       if (y >= 0 && y <= WLINES && mb_cur_max > 1 && !insmode) {
+               while (x > 0 && (vtube[y][x]&(MULTICOL|TRIM)) == MULTICOL &&
+                               vtube[y][x-1] & MULTICOL &&
+                               (vtube[y][x-1]&(MULTICOL|TRIM)) != MULTICOL)
+                       x--;
+       }
+#endif /* MB */
+       if (y < 0)
+               error(catgets(catd, 1, 238, "Internal error: vgoto"));
+       if (outcol >= WCOLS) {
+               if (AM) {
+                       outline += outcol / WCOLS;
+                       outcol %= WCOLS;
+               } else
+                       outcol = WCOLS - 1;
+       }
+
+       /*
+        * In a hardcopy or glass crt open, print the stuff
+        * implied by a motion, or backspace.
+        */
+       if (state == HARDOPEN || state == ONEOPEN) {
+               if (y != outline)
+                       error(catgets(catd, 1, 239, "Line too long for open"));
+               if (x + 1 < outcol - x || (outcol > x && !BS))
+                       destcol = 0, fgoto();
+               tp = vtube[WBOT] + outcol;
+               while (outcol != x)
+                       if (outcol < x) {
+                               if (*tp == 0)
+                                       *tp = ' ';
+                               c = *tp++ & TRIM;
+                               vputc(c && (!OS || EO) ? c : ' ');
+                               outcol++;
+                       } else {
+                               if (BC)
+                                       vputp(BC, 0);
+                               else
+                                       vputc('\b');
+                               outcol--;
+                       }
+               destcol = outcol = x;
+               destline = outline;
+               return;
+       }
+
+       /*
+        * If the destination position implies a scroll, do it.
+        */
+       destline = y;
+       if (destline > WBOT && (!splitw || destline > WECHO)) {
+               endim();
+               vrollup(destline);
+       }
+
+       /*
+        * If there really is a motion involved, do it.
+        * The check here is an optimization based on profiling.
+        */
+       destcol = x;
+       if ((destline - outline) * WCOLS != destcol - outcol) {
+               if (!MI)
+                       endim();
+               fgoto();
+       }
+}
+
+/*
+ * This is the hardest code in the editor, and deals with insert modes
+ * on different kinds of intelligent terminals.  The complexity is due
+ * to the cross product of three factors:
+ *
+ *     1. Lines may display as more than one segment on the screen.
+ *     2. There are 2 kinds of intelligent terminal insert modes.
+ *     3. Tabs squash when you insert characters in front of them,
+ *        in a way in which current intelligent terminals don't handle.
+ *
+ * The two kinds of terminals are typified by the DM2500 or HP2645 for
+ * one and the CONCEPT-100 or the FOX for the other.
+ *
+ * The first (HP2645) kind has an insert mode where the characters
+ * fall off the end of the line and the screen is shifted rigidly
+ * no matter how the display came about.
+ *
+ * The second (CONCEPT-100) kind comes from terminals which are designed
+ * for forms editing and which distinguish between blanks and ``spaces''
+ * on the screen, spaces being like blank, but never having had
+ * and data typed into that screen position (since, e.g. a clear operation
+ * like clear screen).  On these terminals, when you insert a character,
+ * the characters from where you are to the end of the screen shift
+ * over till a ``space'' is found, and the null character there gets
+ * eaten up.
+ *
+ *
+ * The code here considers the line as consisting of several parts
+ * the first part is the ``doomed'' part, i.e. a part of the line
+ * which is being typed over.  Next comes some text up to the first
+ * following tab.  The tab is the next segment of the line, and finally
+ * text after the tab.
+ *
+ * We have to consider each of these segments and the effect of the
+ * insertion of a character on them.  On terminals like HP2645's we
+ * must simulate a multi-line insert mode using the primitive one
+ * line insert mode.  If we are inserting in front of a tab, we have
+ * to either delete characters from the tab or insert white space
+ * (when the tab reaches a new spot where it gets larger) before we
+ * insert the new character.
+ *
+ * On a terminal like a CONCEPT our strategy is to make all
+ * blanks be displayed, while trying to keep the screen having ``spaces''
+ * for portions of tabs.  In this way the terminal hardward does some
+ * of the hacking for compression of tabs, although this tends to
+ * disappear as you work on the line and spaces change into blanks.
+ *
+ * There are a number of boundary conditions (like typing just before
+ * the first following tab) where we can avoid a lot of work.  Most
+ * of them have to be dealt with explicitly because performance is
+ * much, much worse if we don't.
+ *
+ * A final thing which is hacked here is two flavors of insert mode.
+ * Datamedia's do this by an insert mode which you enter and leave
+ * and by having normal motion character operate differently in this
+ * mode, notably by having a newline insert a line on the screen in
+ * this mode.  This generally means it is unsafe to move around
+ * the screen ignoring the fact that we are in this mode.
+ * This is possible on some terminals, and wins big (e.g. HP), so
+ * we encode this as a ``can move in insert capability'' mi,
+ * and terminals which have it can do insert mode with much less
+ * work when tabs are present following the cursor on the current line.
+ */
+
+/*
+ * Routine to expand a tab, calling the normal Outchar routine
+ * to put out each implied character.  Note that we call outchar
+ * with a QUOTE.  We use QUOTE internally to represent a position
+ * which is part of the expansion of a tab.
+ */
+void
+vgotab(void)
+{
+       register int i = tabcol(destcol, value(TABSTOP)) - destcol;
+
+       do
+               (*Outchar)(QUOTE);
+       while (--i);
+}
+
+/*
+ * Variables for insert mode.
+ */
+int    linend;                 /* The column position of end of line */
+int    tabstart;               /* Column of start of first following tab */
+int    tabend;                 /* Column of end of following tabs */
+int    tabsize;                /* Size of the following tabs */
+int    tabslack;               /* Number of ``spaces'' in following tabs */
+int    inssiz;                 /* Number of characters to be inserted */
+int    inscol;                 /* Column where insertion is taking place */
+int    insmc0;                 /* Multi-column character before insertion */
+int    insmc1;                 /* Multi-column character at insertion */
+int    shft;                   /* Amount tab expansion shifted rest of line */
+int    slakused;               /* This much of tabslack will be used up */
+
+/*
+ * This routine MUST be called before insert mode is run,
+ * and brings all segments of the current line to the top
+ * of the screen image buffer so it is easier for us to
+ * maniuplate them.
+ */
+void
+vprepins(void)
+{
+       register int i;
+       register cell *cp = vtube0;
+
+       for (i = 0; i < DEPTH(vcline); i++) {
+               vmaktop(LINE(vcline) + i, cp);
+               cp += WCOLS;
+       }
+}
+
+void
+vmaktop(register int p, cell *cp)
+{
+       register int i;
+       cell temp[TUBECOLS];
+
+       if (p < 0 || vtube[p] == cp)
+               return;
+       for (i = ZERO; i <= WECHO; i++)
+               if (vtube[i] == cp) {
+                       copy(temp, vtube[i], WCOLS * sizeof *temp);
+                       copy(vtube[i], vtube[p], WCOLS * sizeof *temp);
+                       copy(vtube[p], temp, WCOLS * sizeof *temp);
+                       vtube[i] = vtube[p];
+                       vtube[p] = cp;
+                       return;
+               }
+       error(catgets(catd, 1, 240, "Line too long"));
+}
+
+/*
+ * Insert character c at current cursor position.
+ * Multi-character inserts occur only as a result
+ * of expansion of tabs (i.e. inssize == 1 except
+ * for tabs) and code assumes this in several place
+ * to make life simpler.
+ */
+int
+vinschar(int c)
+/*     int c;          /\* mjm: char --> int */
+{
+       register int i;
+       register cell *tp;
+       char    *OIM;
+       bool    OXN;
+       int     noim, filler = 0;
+
+       insmc1 = colsc(c) - 1;
+       if ((!IM || !EI) && ((hold & HOLDQIK) || !value(REDRAW) || value(SLOWOPEN))) {
+               /*
+                * Don't want to try to use terminal
+                * insert mode, or to try to fake it.
+                * Just put the character out; the screen
+                * will probably be wrong but we will fix it later.
+                */
+               if (c == '\t') {
+                       vgotab();
+                       return c;
+               }
+               vputchar(c);
+#ifdef MB
+               if (insmc1 == 0 && (vtube0[destcol]&(TRIM|MULTICOL))==MULTICOL)
+                       vtube0[destcol] = INVBIT;
+#endif /* MB */
+               if (DEPTH(vcline) * WCOLS + !value(REDRAW) >
+                   (destline - LINE(vcline)) * WCOLS + destcol)
+                       return c;
+               /*
+                * The next line is about to be clobbered
+                * make space for another segment of this line
+                * (on an intelligent terminal) or just remember
+                * that next line was clobbered (on a dumb one
+                * if we don't care to redraw the tail.
+                */
+               if (AL) {
+                       vnpins(0);
+               } else {
+                       c = LINE(vcline) + DEPTH(vcline);
+                       if (c < LINE(vcline + 1) || c > WBOT)
+                               return c;
+                       i = destcol;
+                       vinslin(c, 1, vcline);
+                       DEPTH(vcline)++;
+                       vigoto(c, i);
+                       vprepins();
+               }
+               return c;
+       }
+       /*
+        * Compute the number of positions in the line image of the
+        * current line.  This is done from the physical image
+        * since that is faster.  Note that we have no memory
+        * from insertion to insertion so that routines which use
+        * us don't have to worry about moving the cursor around.
+        */
+       if (*vtube0 == 0)
+               linend = 0;
+       else {
+               /*
+                * Search backwards for a non-null character
+                * from the end of the displayed line.
+                */
+               i = WCOLS * DEPTH(vcline);
+               if (i == 0)
+                       i = WCOLS;
+               tp = vtube0 + i;
+               while (*--tp == 0)
+                       if (--i == 0)
+                               break;
+               linend = i + insmc1;
+       }
+
+       /*
+        * We insert at a position based on the physical location
+        * of the output cursor.
+        */
+       inscol = destcol + (destline - LINE(vcline)) * WCOLS;
+       insmc0 = 0;
+#ifdef MB
+       i = 0;
+       while (inscol+i < LBSIZE && vtube0[inscol+i]&MULTICOL &&
+                       (vtube0[inscol+insmc0+i]&(MULTICOL|TRIM)) != MULTICOL)
+               i++;
+       while (inscol+insmc0+i < LBSIZE &&
+                       (vtube0[inscol+insmc0+i]&(MULTICOL|TRIM)) == MULTICOL)
+               insmc0++;
+#endif /* MB */
+       if (c == '\t') {
+               /*
+                * Characters inserted from a tab must be
+                * remembered as being part of a tab, but we can't
+                * use QUOTE here since we really need to print blanks.
+                * QUOTE|' ' is the representation of this.
+                */
+               inssiz = tabcol(inscol+insmc0, value(TABSTOP)) - inscol - insmc0;
+               c = ' ' | QUOTE;
+       } else
+               inssiz = 1;
+
+       /*
+        * If the text to be inserted is less than the number
+        * of doomed positions, then we don't need insert mode,
+        * rather we can just typeover.
+        */
+       if (inssiz + insmc1 <= doomed) {
+               endim();
+               if (inscol + insmc0 != linend)
+                       doomed -= inssiz + insmc1;
+#ifdef MB
+               if (insmc1 == 0 && c != '\t' &&
+                               vtube0[inscol+insmc0] & MULTICOL)
+                       vtube0[inscol+insmc0] = INVBIT;
+#endif /* MB */
+               do
+                       vputchar(c);
+               while (--inssiz);
+               return c;
+       }
+
+       /*
+        * Have to really do some insertion, thus
+        * stake out the bounds of the first following
+        * group of tabs, computing starting position,
+        * ending position, and the number of ``spaces'' therein
+        * so we can tell how much it will squish.
+        */
+       tp = vtube0 + inscol + insmc0;
+       for (i = inscol + insmc0; i < linend; i++) {
+               if (*tp++ & QUOTE) {
+                       --tp;
+                       break;
+               }
+       }
+       tabstart = tabend = i;
+       tabslack = 0;
+       while (tabend < linend) {
+               i = *tp++;
+               if ((i & QUOTE) == 0)
+                       break;
+               if ((i & (TRIM|MULTICOL)) == 0)
+                       tabslack++;
+               tabsize++;
+               tabend++;
+       }
+       tabsize = tabend - tabstart;
+
+       /*
+        * For HP's and DM's, e.g. tabslack has no meaning.
+        */
+       if (!IN)
+               tabslack = 0;
+#ifdef IDEBUG
+       if (trace) {
+               fprintf(trace, "inscol %d, inssiz %d, tabstart %d, ",
+                       inscol, inssiz, tabstart);
+               fprintf(trace, "tabend %d, tabslack %d, linend %d\n",
+                       tabend, tabslack, linend);
+       }
+#endif
+       OIM = IM;
+       OXN = XN;
+       noim = 0;
+#ifdef MB
+       if (mb_cur_max > 1) {
+               if (destcol + 1 + insmc1 == WCOLS + 1) {
+                       noim = 1;
+                       if (insmc1 == 1 && insmc0 == 0)
+                               filler = 1;
+               }
+               for (i = inscol; vtube0[i]; i++)
+                       if (i + 1 >= WCOLS && vtube0[i] & MULTICOL) {
+                               noim = 1;
+                               break;
+                       }
+       }
+#endif /* MB */
+       if (noim) {
+               endim();
+               IM = 0;
+               XN = 0;
+       }
+
+       /*
+        * The real work begins.
+        */
+       slakused = 0;
+       shft = 0;
+       if (tabsize) {
+               /*
+                * There are tabs on this line.
+                * If they need to expand, then the rest of the line
+                * will have to be shifted over.  In this case,
+                * we will need to make sure there are no ``spaces''
+                * in the rest of the line (on e.g. CONCEPT-100)
+                * and then grab another segment on the screen if this
+                * line is now deeper.  We then do the shift
+                * implied by the insertion.
+                */
+               if (inssiz >= doomed + tabcol(tabstart, value(TABSTOP)) - tabstart) {
+                       if (IN)
+                               vrigid();
+                       vneedpos(value(TABSTOP));
+                       vishft();
+               }
+       } else if (inssiz + insmc1 > doomed)
+               /*
+                * No tabs, but line may still get deeper.
+                */
+               vneedpos(inssiz + insmc1 - doomed);
+       /*
+        * Now put in the inserted characters.
+        */
+       viin(c);
+
+       /*
+        * Now put the cursor in its final resting place.
+        */
+       destline = LINE(vcline);
+       destcol = inscol + inssiz + insmc1 + filler;
+       vcsync();
+       if (IM != OIM) {
+               IM = OIM;
+               XN = OXN;
+       }
+       return c;
+}
+
+/*
+ * Rigidify the rest of the line after the first
+ * group of following tabs, typing blanks over ``spaces''.
+ */
+void
+vrigid(void)
+{
+       register int col;
+       register cell *tp = vtube0 + tabend;
+
+       for (col = tabend; col < linend; col++) {
+               if ((*tp++ & TRIM) == 0) {
+                       endim();
+                       vgotoCL(col);
+                       vputchar(' ' | QUOTE);
+               }
+       }
+}
+
+/*
+ * We need cnt more positions on this line.
+ * Open up new space on the screen (this may in fact be a
+ * screen rollup).
+ *
+ * On a dumb terminal we may infact redisplay the rest of the
+ * screen here brute force to keep it pretty.
+ */
+void
+vneedpos(int npcnt)
+{
+       register int d = DEPTH(vcline);
+       register int rmdr = d * WCOLS - linend;
+
+       /*
+        * Delete the showmode string on wraparound to last line. Cannot use
+        * vclrech() since the mode string is printed on the echo area, but
+        * not actually a part of it.
+        */
+       if (value(SHOWMODE) && (value(REDRAW) || (IM && EI)) &&
+                       npcnt == rmdr - IN && LINE(vcline) + d == WECHO) {
+               int sdc, sdl;
+               char *ocurs;
+
+               endim();
+               sdc = destcol, sdl = destline, ocurs = cursor;
+               splitw++;
+               vgoto(WECHO, 0);
+               if (CD) {
+                       vputp(CD, 1);
+               } else if (CE) {
+                       vputp(CE, 1);
+               } else {
+                       int i;
+
+                       for (i = 1; i < WCOLS; i++)
+                               vputchar(' ');
+               }
+               destcol = sdc, destline = sdl; cursor = ocurs;
+               splitw = 0;
+       }
+       if (npcnt <= rmdr - IN)
+               return;
+       endim();
+       vnpins(1);
+}
+
+void
+vnpins(int dosync)
+{
+       register int d = DEPTH(vcline);
+       register int e;
+
+       e = LINE(vcline) + DEPTH(vcline);
+       if (e < LINE(vcline + 1)) {
+               vigoto(e, 0);
+               vclreol();
+               return;
+       }
+       DEPTH(vcline)++;
+       if (e < WECHO) {
+               e = vglitchup(vcline, d);
+               vigoto(e, 0); vclreol();
+               if (dosync) {
+                       int (*Ooutchar)() = Outchar;
+                       Outchar = vputchar;
+                       vsync(e + 1);
+                       Outchar = Ooutchar;
+               }
+       } else {
+               vup1();
+               vigoto(WBOT, 0);
+               vclreol();
+       }
+       vprepins();
+}
+
+/*
+ * Do the shift of the next tabstop implied by
+ * insertion so it expands.
+ */
+void
+vishft(void)
+{
+       int tshft = 0;
+       int j;
+       register int i;
+       register cell *tp = vtube0;
+       register cell *up;
+       short oldhold = hold;
+
+       shft = value(TABSTOP);
+       hold |= HOLDPUPD;
+       if (!IM && !EI) {
+               /*
+                * Dumb terminals are easy, we just have
+                * to retype the text.
+                */
+               vigotoCL(tabend + shft);
+               up = tp + tabend;
+               for (i = tabend; i < linend; i++)
+                       vputchar(*up++);
+       } else if (IN) {
+               /*
+                * CONCEPT-like terminals do most of the work for us,
+                * we don't have to muck with simulation of multi-line
+                * insert mode.  Some of the shifting may come for free
+                * also if the tabs don't have enough slack to take up
+                * all the inserted characters.
+                */
+               i = shft;
+               slakused = inssiz - doomed;
+               if (slakused > tabslack) {
+                       i -= slakused - tabslack;
+                       slakused -= tabslack;
+               }
+               if (i > 0 && tabend != linend) {
+                       tshft = i;
+                       vgotoCL(tabend);
+                       goim();
+                       do
+                               vputchar(' ' | QUOTE);
+                       while (--i);
+               }
+       } else {
+               /*
+                * HP and Datamedia type terminals have to have multi-line
+                * insert faked.  Hack each segment after where we are
+                * (going backwards to where we are.)  We then can
+                * hack the segment where the end of the first following
+                * tab group is.
+                */
+               for (j = DEPTH(vcline) - 1; j > (tabend + shft) / WCOLS; j--) {
+                       vgotoCL(j * WCOLS);
+                       goim();
+                       up = tp + j * WCOLS - shft;
+                       i = shft;
+                       do {
+                               if (*up)
+                                       vputchar(*up++);
+                               else
+                                       break;
+                       } while (--i);
+               }
+               vigotoCL(tabstart);
+               i = shft - (inssiz - doomed);
+               if (i > 0) {
+                       tabslack = inssiz - doomed;
+                       vcsync();
+                       goim();
+                       do
+                               vputchar(' ');
+                       while (--i);
+               }
+       }
+       /*
+        * Now do the data moving in the internal screen
+        * image which is common to all three cases.
+        */
+       tp += linend;
+       up = tp + shft;
+       i = linend - tabend;
+       if (i > 0)
+               do
+                       *--up = *--tp;
+               while (--i);
+       if (IN && tshft) {
+               i = tshft;
+               do
+                       *--up = ' ' | QUOTE;
+               while (--i);
+       }
+       hold = oldhold;
+}
+
+/*
+ * Now do the insert of the characters (finally).
+ */
+void
+viin(int c)
+/*     int c;          /\* mjm: char --> int */
+{
+       register cell *tp, *up;
+       register int i, j;
+       register bool noim = 0;
+       int remdoom;
+       short oldhold = hold;
+
+       hold |= HOLDPUPD;
+       if (tabsize && (IM && EI) && inssiz - doomed > tabslack)
+               /*
+                * There is a tab out there which will be affected
+                * by the insertion since there aren't enough doomed
+                * characters to take up all the insertion and we do
+                * have insert mode capability.
+                */
+               if (inscol + insmc0 + doomed == tabstart) {
+                       /*
+                        * The end of the doomed characters sits right at the
+                        * start of the tabs, then we don't need to use insert
+                        * mode; unless the tab has already been expanded
+                        * in which case we MUST use insert mode.
+                        */
+                       slakused = 0;
+                       noim = !shft;
+               } else {
+                       /*
+                        * The last really special case to handle is case
+                        * where the tab is just sitting there and doesn't
+                        * have enough slack to let the insertion take
+                        * place without shifting the rest of the line
+                        * over.  In this case we have to go out and
+                        * delete some characters of the tab before we start
+                        * or the answer will be wrong, as the rest of the
+                        * line will have been shifted.  This code means
+                        * that terminals with only insert chracter (no
+                        * delete character) won't work correctly.
+                        */
+                       i = inssiz - doomed - tabslack - slakused;
+                       i %= value(TABSTOP);
+                       if (i > 0) {
+                               vgotoCL(tabstart);
+                               godm();
+                               for (i = inssiz - doomed - tabslack; i > 0; i--)
+                                       vputp(DC, DEPTH(vcline));
+                               enddm();
+                       }
+               }
+
+       /* 
+        * Now put out the characters of the actual insertion.
+        */
+       vigotoCL(inscol);
+       remdoom = doomed;
+       for (i = inssiz; i > 0; i--) {
+               if (remdoom > insmc1) {
+                       remdoom--;
+                       endim();
+               } else if (noim || insmc1 && remdoom == insmc1)
+                       endim();
+               else if (IM && EI) {
+                       vcsync();
+                       goim();
+               }
+               vputchar(c);
+       }
+
+       if (!IM || !EI || remdoom && remdoom == insmc1) {
+               /*
+                * We are a dumb terminal; brute force update
+                * the rest of the line; this is very much an n^^2 process,
+                * and totally unreasonable at low speed.
+                *
+                * You asked for it, you get it.
+                */
+               tp = vtube0 + inscol + doomed;
+               for (i = inscol + doomed; i < tabstart; i++)
+                       vputchar(*tp++);
+               hold = oldhold;
+               vigotoCL(tabstart + inssiz + insmc0 - doomed);
+               for (i = tabsize - (inssiz - insmc0 - doomed) + shft;
+                               i > 0; i--)
+                       vputchar(' ' | QUOTE);
+       } else {
+               if (!IN) {
+                       /*
+                        * On terminals without multi-line
+                        * insert in the hardware, we must go fix the segments
+                        * between the inserted text and the following
+                        * tabs, if they are on different lines.
+                        *
+                        * Aaargh.
+                        */
+                       tp = vtube0;
+                       for (j = (inscol + insmc0 + inssiz - 1) / WCOLS + 1;
+                           j <= (tabstart + inssiz - doomed - 1) / WCOLS; j++) {
+                               vgotoCL(j * WCOLS);
+                               i = inssiz - doomed + insmc1;
+                               up = tp + j * WCOLS - i;
+                               goim();
+                               do
+                                       vputchar(*up++);
+                               while (--i && *up);
+                       }
+               } else {
+                       /*
+                        * On terminals with multi line inserts,
+                        * life is simpler, just reflect eating of
+                        * the slack.
+                        */
+                       tp = vtube0 + tabend;
+                       for (i = tabsize - (inssiz + insmc1 - doomed); i >= 0; i--) {
+                               if ((*--tp & (QUOTE|TRIM)) == QUOTE) {
+                                       --tabslack;
+                                       if (tabslack >= slakused)
+                                               continue;
+                               }
+                               *tp = ' ' | QUOTE;
+                       }
+               }
+               /*
+                * Blank out the shifted positions to be tab positions.
+                */
+               if (shft) {
+                       tp = vtube0 + tabend + shft;
+                       for (i = tabsize - (inssiz - doomed) + shft; i > 0; i--)
+                               if ((*--tp & QUOTE) == 0)
+                                       *tp = ' ' | QUOTE;
+               }
+       }
+
+       /*
+        * Finally, complete the screen image update
+        * to reflect the insertion.
+        */
+       hold = oldhold;
+       tp = vtube0 + tabstart;
+       up = tp + insmc1 + inssiz - doomed;
+       for (i = tabstart; i > inscol + doomed; i--)
+               *--up = *--tp;
+#ifdef MB
+       for (i = insmc1; i > 0; i--)
+               *--up = MULTICOL;
+#endif
+       for (i = inssiz; i > 0; i--)
+               *--up = c | (insmc1 ? MULTICOL : 0);
+       doomed = 0;
+}
+
+/*
+ * Go into ``delete mode''.  If the
+ * sequence which goes into delete mode
+ * is the same as that which goes into insert
+ * mode, then we are in delete mode already.
+ */
+void
+godm(void)
+{
+
+       if (insmode) {
+               if (eq(DM, IM))
+                       return;
+               endim();
+       }
+       vputp(DM, 0);
+}
+
+/*
+ * If we are coming out of delete mode, but
+ * delete and insert mode end with the same sequence,
+ * it wins to pretend we are now in insert mode,
+ * since we will likely want to be there again soon
+ * if we just moved over to delete space from part of
+ * a tab (above).
+ */
+void
+enddm(void)
+{
+
+       if (eq(DM, IM)) {
+               insmode = 1;
+               return;
+       }
+       vputp(ED, 0);
+}
+
+/*
+ * In and out of insert mode.
+ * Note that the code here demands that there be
+ * a string for insert mode (the null string) even
+ * if the terminal does all insertions a single character
+ * at a time, since it branches based on whether IM is null.
+ */
+void
+goim(void)
+{
+
+       if (!insmode)
+               vputp(IM, 0);
+       insmode = 1;
+}
+
+void
+endim(void)
+{
+
+       if (insmode) {
+               vputp(EI, 0);
+               insmode = 0;
+       }
+}
+
+/*
+ * Put the character c on the screen at the current cursor position.
+ * This routine handles wraparound and scrolling and understands not
+ * to roll when splitw is set, i.e. we are working in the echo area.
+ * There is a bunch of hacking here dealing with the difference between
+ * QUOTE, QUOTE|' ', and ' ' for CONCEPT-100 like terminals, and also
+ * code to deal with terminals which overstrike, including CRT's where
+ * you can erase overstrikes with some work.  CRT's which do underlining
+ * implicitly which has to be erased (like CONCEPTS) are also handled.
+ */
+int
+vputchar(register int c)
+{
+       register cell *tp;
+       register int d, m, n;
+
+#ifndef        BIT8
+       c &= (QUOTE|TRIM);
+#endif
+#ifdef TRACE
+       if (trace)
+               tracec(c);
+#endif
+       /* Fix problem of >79 chars on echo line. */
+       if (destcol >= WCOLS-1 && splitw && destline == WECHO)
+               pofix();
+#ifdef MB
+       if (mb_cur_max > 1) {
+               if (c == MULTICOL)
+                       return c;
+               /*
+                * If a multicolumn character extends beyond the screen
+                * width, it must be put on the next line. A tilde is
+                * printed as an indicator but must disappear when the
+                * text is moved at a later time.
+                */
+               if (c == ('~'|INVBIT|QUOTE))
+                       c = '~'|INVBIT;
+               else if (c == ('~'|INVBIT))
+                       return c;
+               else if (destcol < WCOLS && destcol +
+                               colsc(c==QUOTE ? ' ' : c&TRIM&~MULTICOL) - 1
+                               >= WCOLS)
+                       vputchar('~'|INVBIT|QUOTE);
+       }
+#endif /* MB */
+       if (destcol >= WCOLS) {
+               destline += destcol / WCOLS;
+               destcol %= WCOLS;
+       }
+       if (destline > WBOT && (!splitw || destline > WECHO))
+               vrollup(destline);
+       tp = vtube[destline] + destcol;
+       if (c == QUOTE) {
+               if (insmode) {
+                       /*
+                        * When in insert mode, tabs have to expand
+                        * to real, printed blanks.
+                        */
+                       c = ' ' | QUOTE;
+                       goto def;
+               }
+               if (*tp == 0) {
+                       /*
+                        * A ``space''.
+                        */
+                       if ((hold & HOLDPUPD) == 0)
+                               *tp = QUOTE;
+                       destcol++;
+                       return c;
+               }
+               /*
+                * A ``space'' ontop of a part of a tab.
+                */
+               if (*tp & QUOTE) {
+                       destcol++;
+                       return c;
+               }
+               c = ' ' | QUOTE;
+               goto def;
+       }
+
+#ifdef notdef
+#ifdef BIT8
+       if (c == ' ' | QUOTE) {
+               c = ' ';
+               goto def;
+       }
+#endif
+#endif
+       switch (c) {
+
+       case '\t':
+               vgotab();
+               return c;
+
+       case ' ':
+               /*
+                * We can get away without printing a space in a number
+                * of cases, but not always.  We get away with doing nothing
+                * if we are not in insert mode, and not on a CONCEPT-100
+                * like terminal, and either not in hardcopy open or in hardcopy
+                * open on a terminal with no overstriking, provided,
+                * in all cases, that nothing has ever been displayed
+                * at this position.  Ugh.
+                */
+               if (!insmode && !IN && (state != HARDOPEN || OS)
+               && (*tp&QUOTE)) {
+                       *tp = ' ';
+                       destcol++;
+                       return c;
+               }
+               goto def;
+
+def:
+       default:
+               d = *tp & TRIM;
+               /*
+                * Now get away with doing nothing if the characters
+                * are the same, provided we are not in insert mode
+                * and if we are in hardopen, that the terminal has overstrike.
+                */
+               if ((d & ~MULTICOL) == (c & TRIM & ~MULTICOL) && !insmode &&
+                               (state != HARDOPEN || OS) && c != MULTICOL) {
+                       n = colsc(d);
+                       for (m = 1; m < n; m++)
+                               if ((tp[m] & (MULTICOL|TRIM)) != MULTICOL)
+                                       break;
+                       if (m == n) {
+                               if ((hold & HOLDPUPD) == 0)
+                                       *tp = c | (n > 1 ? MULTICOL : 0);
+                               destcol += n;
+                               return c;
+                       }
+               }
+               /*
+                * Backwards looking optimization.
+                * The low level cursor motion routines will use
+                * a cursor motion right sequence to step 1 character
+                * right.  On, e.g., a DM3025A this is 2 characters
+                * and printing is noticeably slower at 300 baud.
+                * Since the low level routines are not allowed to use
+                * spaces for positioning, we discover the common
+                * case of a single space here and force a space
+                * to be printed.
+                */
+               if (destcol == outcol + 1 && tp[-1] == ' ' && outline == destline) {
+                       vputc(' ');
+                       outcol++;
+               }
+
+               /*
+                * This is an inline expansion a call to vcsync() dictated
+                * by high frequency in a profile.
+                */
+               if (outcol != destcol || outline != destline)
+                       vgoto(destline, destcol);
+
+               /*
+                * Deal with terminals which have overstrike.
+                * We handle erasing general overstrikes, erasing
+                * underlines on terminals (such as CONCEPTS) which
+                * do underlining correctly automatically (e.g. on nroff
+                * output), and remembering, in hardcopy mode,
+                * that we have overstruct something.
+                */
+               if (!insmode && d && d != ' ' && d != (c & TRIM)) {
+                       if (EO && (OS || UL && (c == '_' || d == '_'))) {
+                               vputc(' ');
+                               outcol++, destcol++;
+                               back1();
+                       } else
+                               rubble = 1;
+               }
+
+               /*
+                * Unless we are just bashing characters around for
+                * inner working of insert mode, update the display.
+                */
+               if ((hold & HOLDPUPD) == 0)
+                       *tp = c;
+
+               /*
+                * In insert mode, put out the IC sequence, padded
+                * based on the depth of the current line.
+                * A terminal which had no real insert mode, rather
+                * opening a character position at a time could do this.
+                * Actually should use depth to end of current line
+                * but this rarely matters.
+                */
+#ifdef notdef
+               if (insmode)
+#else
+               /*
+                * It seems today's termcap writers consider this
+                * an either-or situation; if both im and ic
+                * are used vi puts out additional spaces.
+                *
+                * SVR4 ex does not include this change. If it hits
+                * your terminal, change back to the old way and
+                * mail me a description.
+                *
+                * GR July 2000
+                */
+               if (insmode && (!IM || !*IM))
+#endif /* !notdef */
+               {
+                       n = colsc(c&TRIM);
+                       for (m = 0; m < n; m++)
+                               vputp(IC, DEPTH(vcline));
+               }
+               vputc(c & TRIM);
+
+               /*
+                * In insert mode, IP is a post insert pad.
+                */
+               if (insmode)
+                       vputp(IP, DEPTH(vcline));
+               destcol++, outcol++;
+
+               /*
+                * CONCEPT braindamage in early models:  after a wraparound
+                * the next newline is eaten.  It's hungry so we just
+                * feed it now rather than worrying about it.
+                * Fixed to use return linefeed to work right
+                * on vt100/tab132 as well as concept.
+                */
+               if (XN && outcol % WCOLS == 0) {
+                       vputc('\r');
+                       vputc('\n');
+               }
+       }
+#ifdef MB
+       if (mb_cur_max > 1 && (d = colsc(c&TRIM&~MULTICOL)) > 1) {
+               if ((hold & HOLDPUPD) == 0)
+                       *tp |= MULTICOL;
+               while (--d) {
+                       if ((hold & HOLDPUPD) == 0)
+                               *++tp = MULTICOL;
+                       destcol++;
+                       outcol++;
+               }
+       }
+#endif /* MB */
+       return c;
+}
+
+/*
+ * Delete display positions stcol through endcol.
+ * Amount of use of special terminal features here is limited.
+ */
+void
+physdc(int stcol, int endcol)
+{
+       register cell *tp, *up;
+       cell *tpe = NULL;
+       register int i;
+       register int nc = endcol - stcol;
+
+#ifdef IDEBUG
+       if (trace)
+               tfixnl(), fprintf(trace, "physdc(%d, %d)\n", stcol, endcol);
+#endif
+       if (!DC || nc <= 0)
+               return;
+       if (IN) {
+               /*
+                * CONCEPT-100 like terminal.
+                * If there are any ``spaces'' in the material to be
+                * deleted, then this is too hard, just retype.
+                */
+               vprepins();
+               up = vtube0 + stcol;
+               i = nc;
+               do {
+                       if ((*up++ & (QUOTE|TRIM)) == QUOTE)
+                               return;
+               } while (--i);
+               i = 2 * nc;
+               do {
+                       if (*up == 0 || (*up++ & QUOTE) == QUOTE)
+                               return;
+               } while (--i);
+               vgotoCL(stcol);
+       } else {
+               /*
+                * HP like delete mode.
+                * Compute how much text we are moving over by deleting.
+                * If it appears to be faster to just retype
+                * the line, do nothing and that will be done later.
+                * We are assuming 2 output characters per deleted
+                * characters and that clear to end of line is available.
+                */
+               i = stcol / WCOLS;
+               if (i != endcol / WCOLS)
+                       return;
+               i += LINE(vcline);
+               stcol %= WCOLS;
+               endcol %= WCOLS;
+               up = vtube[i]; tp = up + endcol; tpe = up + WCOLS;
+               while (tp < tpe && *tp)
+                       tp++;
+               if (tp - (up + stcol) < 2 * nc)
+                       return;
+               vgoto(i, stcol);
+       }
+
+       /*
+        * Go into delete mode and do the actual delete.
+        * Padding is on DC itself.
+        */
+       godm();
+       for (i = nc; i > 0; i--)
+               vputp(DC, DEPTH(vcline));
+       vputp(ED, 0);
+
+       /*
+        * Straighten up.
+        * With CONCEPT like terminals, characters are pulled left
+        * from first following null.  HP like terminals shift rest of
+        * this (single physical) line rigidly.
+        */
+       if (IN) {
+               up = vtube0 + stcol;
+               tp = vtube0 + endcol;
+               while (i = *tp++) {
+                       if ((i & (QUOTE|TRIM)) == QUOTE)
+                               break;
+                       *up++ = i;
+               }
+               do
+                       *up++ = i;
+               while (--nc);
+       } else {
+               copy(up + stcol, up + endcol,
+                               (WCOLS - endcol) * sizeof *up);
+               vclrcell(tpe - nc, nc);
+       }
+}
+
+#ifdef TRACE
+void
+tfixnl(void)
+{
+
+       if (trubble || techoin)
+               fprintf(trace, "\n");
+       trubble = 0, techoin = 0;
+}
+
+void
+tvliny(void)
+{
+       register int i;
+
+       if (!trace)
+               return;
+       tfixnl();
+       fprintf(trace, "vcnt = %d, vcline = %d, vliny = ", vcnt, vcline);
+       for (i = 0; i <= vcnt; i++) {
+               fprintf(trace, "%d", LINE(i));
+               if (FLAGS(i) & VDIRT)
+                       fprintf(trace, "*");
+               if (DEPTH(i) != 1)
+                       fprintf(trace, "<%d>", DEPTH(i));
+               if (i < vcnt)
+                       fprintf(trace, " ");
+       }
+       fprintf(trace, "\n");
+}
+
+void
+tracec(int c)
+/*     int c;          /\* mjm: char --> int */
+{
+
+       if (!techoin)
+               trubble = 1;
+       if (c == ESCAPE)
+               fprintf(trace, "$");
+       else if (c & QUOTE)     /* mjm: for 3B (no sign extension) */
+               fprintf(trace, "~%c", ctlof(c&TRIM));
+       else if (c < ' ' || c == DELETE)
+               fprintf(trace, "^%c", ctlof(c));
+       else
+               fprintf(trace, "%c", c);
+}
+#endif
+
+/*
+ * Put a character with possible tracing.
+ */
+int
+vputch(int c)
+{
+
+#ifdef TRACE
+       if (trace)
+               tracec(c);
+#endif
+       return vputc(c);
+}
diff --git a/ex_vwind.c b/ex_vwind.c
new file mode 100644 (file)
index 0000000..751c991
--- /dev/null
@@ -0,0 +1,500 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_vwind.c 1.9 (gritter) 11/23/04";
+#endif
+#endif
+
+/* from ex_vwind.c     7.3 (Berkeley) 6/7/85 */
+
+#include "ex.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+
+/*
+ * Routines to adjust the window, showing specified lines
+ * in certain positions on the screen, and scrolling in both
+ * directions.  Code here is very dependent on mode (open versus visual).
+ */
+
+/*
+ * Move in a nonlocal way to line addr.
+ * If it isn't on screen put it in specified context.
+ * New position for cursor is curs.
+ * Like most routines here, we vsave().
+ */
+void 
+vmoveto(register line *addr, char *curs, int context)
+{
+
+       markit(addr);
+       vsave();
+       vjumpto(addr, curs, context);
+}
+
+/*
+ * Vjumpto is like vmoveto, but doesn't mark previous
+ * context or save linebuf as current line.
+ */
+void 
+vjumpto(register line *addr, char *curs, int context)
+{
+
+       noteit(0);
+       if (context != 0)
+               vcontext(addr, context);
+       else
+               vshow(addr, NOLINE);
+       noteit(1);
+       vnline(curs);
+}
+
+/*
+ * Go up or down cnt (negative is up) to new position curs.
+ */
+void 
+vupdown(register int cnt, char *curs)
+{
+
+       if (cnt > 0)
+               vdown(cnt, 0, 0);
+       else if (cnt < 0)
+               vup(-cnt, 0, 0);
+       if (vcnt == 0)
+               vrepaint(curs);
+       else
+               vnline(curs);
+}
+
+/*
+ * Go up cnt lines, afterwards preferring to be ind
+ * logical lines from the top of the screen.
+ * If scroll, then we MUST use a scroll.
+ * Otherwise clear and redraw if motion is far.
+ */
+void 
+vup(register int cnt, register int ind, int scroll)
+{
+       register int i, tot;
+
+       if (dot == one) {
+               beep();
+               return;
+       }
+       vsave();
+       i = lineDOT() - 1;
+       if (cnt > i) {
+               ind -= cnt - i;
+               if (ind < 0)
+                       ind = 0;
+               cnt = i;
+       }
+       if (!scroll && cnt <= vcline) {
+               vshow(dot - cnt, NOLINE);
+               return;
+       }
+       cnt -= vcline, dot -= vcline, vcline = 0;
+       if (hold & HOLDWIG)
+               goto contxt;
+       if (state == VISUAL && !AL && !SR &&
+           cnt <= WTOP - ZERO && vfit(dot - cnt, cnt) <= WTOP - ZERO)
+               goto okr;
+       tot = WECHO - ZERO;
+       if (state != VISUAL || (!AL && !SR) || (!scroll && (cnt > tot || vfit(dot - cnt, cnt) > tot / 3 + 1))) {
+               if (ind > basWLINES / 2)
+                       ind = basWLINES / 3;
+contxt:
+               vcontext(dot + ind - cnt, '.');
+               return;
+       }
+okr:
+       vrollR(cnt);
+       if (scroll) {
+               vcline += ind, dot += ind;
+               if (vcline >= vcnt)
+                       dot -= vcline - vcnt + 1, vcline = vcnt - 1;
+               getDOT();
+       }
+}
+
+/*
+ * Like vup, but scrolling down.
+ */
+void 
+vdown(register int cnt, register int ind, int scroll)
+{
+       register int i, tot;
+
+       if (dot == dol) {
+               beep();
+               return;
+       }
+       vsave();
+       i = dol - dot;
+       if (cnt > i) {
+               ind -= cnt - i;
+               if (ind < 0)
+                       ind = 0;
+               cnt = i;
+       }
+       i = vcnt - vcline - 1;
+       if (!scroll && cnt <= i) {
+               vshow(dot + cnt, NOLINE);
+               return;
+       }
+       cnt -= i, dot += i, vcline += i;
+       if (hold & HOLDWIG)
+               goto dcontxt;
+       if (!scroll) {
+               tot = WECHO - ZERO;
+               if (state != VISUAL || cnt - tot > 0 || vfit(dot, cnt) > tot / 3 + 1) {
+dcontxt:
+                       vcontext(dot + cnt, '.');
+                       return;
+               }
+       }
+       if (cnt > 0)
+               vroll(cnt);
+       if (state == VISUAL && scroll) {
+               vcline -= ind, dot -= ind;
+               if (vcline < 0)
+                       dot -= vcline, vcline = 0;
+               getDOT();
+       }
+}
+
+/*
+ * Show line addr in context where on the screen.
+ * Work here is in determining new top line implied by
+ * this placement of line addr, since we always draw from the top.
+ */
+void 
+vcontext(register line *addr, int where)
+{
+       register line *top;
+
+       getline(*addr);
+       if (state != VISUAL)
+               top = addr;
+       else switch (where) {
+
+       case '^':
+               addr = vback(addr, basWLINES - vdepth());
+               getline(*addr);
+               /* fall into ... */
+
+       case '-':
+               top = vback(addr, basWLINES - vdepth());
+               getline(*addr);
+               break;
+
+       case '.':
+               top = vback(addr, basWLINES / 2 - vdepth());
+               getline(*addr);
+               break;
+
+       default:
+               top = addr;
+               break;
+       }
+       if (state == ONEOPEN && LINE(0) == WBOT)
+               vup1();
+       vcnt = vcline = 0;
+       vclean();
+       if (state == CRTOPEN)
+               vup1();
+       vshow(addr, top);
+}
+
+/*
+ * Get a clean line.  If we are in a hard open
+ * we may be able to reuse the line we are on
+ * if it is blank.  This is a real win.
+ */
+void 
+vclean(void)
+{
+
+       if (state != VISUAL && state != CRTOPEN) {
+               destcol = 0;
+               if (!ateopr())
+                       vup1();
+               vcnt = 0;
+       }
+}
+
+/*
+ * Show line addr with the specified top line on the screen.
+ * Top may be 0; in this case have vcontext compute the top
+ * (and call us recursively).  Eventually, we clear the screen
+ * (or its open mode equivalent) and redraw.
+ */
+void 
+vshow(line *addr, line *top)
+{
+       register int cnt = addr - dot;
+       register int i = vcline + cnt;
+       short oldhold = hold;
+
+       if (state != HARDOPEN && state != ONEOPEN && i >= 0 && i < vcnt) {
+               dot = addr;
+               getDOT();
+               vcline = i;
+               return;
+       }
+       if (state != VISUAL) {
+               dot = addr;
+               vopen(dot, WBOT);
+               return;
+       }
+       if (top == 0) {
+               vcontext(addr, '.');
+               return;
+       }
+       dot = top;
+       oldhold = hold;
+       hold |= HOLDAT;
+       vclear();
+       vreset(0);
+       vredraw(WTOP);
+       /* error if vcline >= vcnt ! */
+       vcline = addr - top;
+       dot = addr;
+       getDOT();
+       hold = oldhold;
+       vsync(LASTLINE);
+}
+
+/*
+ * reset the state.
+ * If inecho then leave us at the beginning of the echo
+ * area;  we are called this way in the middle of a :e escape
+ * from visual, e.g.
+ */
+void 
+vreset(int inecho)
+{
+
+       vcnt = vcline = 0;
+       WTOP = basWTOP;
+       WLINES = basWLINES;
+       if (inecho)
+               splitw = 1, vgoto(WECHO, 0);
+}
+
+/*
+ * Starting from which line preceding tp uses almost (but not more
+ * than) cnt physical lines?
+ */
+line *
+vback(register line *tp, register int cnt)
+{
+       register int d;
+
+       if (cnt > 0)
+               for (; tp > one; tp--) {
+                       getline(tp[-1]);
+                       d = vdepth();
+                       if (d > cnt)
+                               break;
+                       cnt -= d;
+               }
+       return (tp);
+}
+
+/*
+ * How much scrolling will it take to roll cnt lines starting at tp?
+ */
+int 
+vfit(register line *tp, int cnt)
+{
+       register int j;
+
+       j = 0;
+       while (cnt > 0) {
+               cnt--;
+               getline(tp[cnt]);
+               j += vdepth();
+       }
+       if (tp > dot)
+               j -= WBOT - LASTLINE;
+       return (j);
+}
+
+/*
+ * Roll cnt lines onto the screen.
+ */
+void 
+vroll(register int cnt)
+{
+       short oldhold = hold;
+
+#ifdef ADEBUG
+       if (trace)
+               tfixnl(), fprintf(trace, "vroll(%d)\n", cnt);
+#endif
+       if (state != VISUAL)
+               hold |= HOLDAT|HOLDROL;
+       if (WBOT == WECHO) {
+               vcnt = 0;
+               if (state == ONEOPEN)
+                       vup1();
+       }
+       for (; cnt > 0 && Peekkey != ATTN; cnt--) {
+               dot++, vcline++;
+               vopen(dot, LASTLINE);
+               vscrap();
+       }
+       hold = oldhold;
+       if (state == HARDOPEN)
+               sethard();
+       vsyncCL();
+}
+
+/*
+ * Roll backwards (scroll up).
+ */
+void 
+vrollR(register int cnt)
+{
+       short oldhold = hold;
+
+#ifdef ADEBUG
+       if (trace)
+               tfixnl(), fprintf(trace, "vrollR(%d), dot=%d\n", cnt, lineDOT());
+#endif
+       if (WBOT == WECHO)
+               vcnt = 0;
+       heldech = 0;
+       hold |= HOLDAT|HOLDECH;
+       for (; cnt > 0 && Peekkey != ATTN; cnt--) {
+               dot--;
+               vopen(dot, WTOP);
+               vscrap();
+       }
+       hold = oldhold;
+       if (heldech)
+               vclrech(0);
+       vsync(LINE(vcnt-1));
+}
+
+/*
+ * Go into cooked mode (allow interrupts) during
+ * a scroll if we are at less than 1200 baud and not
+ * a 'vi' command, of if we are in a 'vi' command and the
+ * scroll is more than 2 full screens.
+ *
+ * BUG:                An interrupt during a scroll in this way
+ *             dumps to command mode.
+ */
+int 
+vcookit(register int cnt)
+{
+
+       return (cnt > 1 && (ospeed < B1200 && !initev || cnt > TLINES * 2));
+}
+
+/*
+ * Determine displayed depth of current line.
+ */
+int 
+vdepth(void)
+{
+       register int d;
+
+       d = (column(NOSTR) + WCOLS - 1 + (Putchar == listchar) + IN) / WCOLS;
+#ifdef ADEBUG
+       if (trace)
+               tfixnl(), fprintf(trace, "vdepth returns %d\n", d == 0 ? 1 : d);
+#endif
+       return (d == 0 ? 1 : d);
+}
+
+/*
+ * Move onto a new line, with cursor at position curs.
+ */
+void 
+vnline(char *curs)
+{
+
+       if (curs)
+               wcursor = curs;
+       else if (vmoving)
+               wcursor = vfindcol(vmovcol);
+       else
+               wcursor = vskipwh(linebuf);
+       cursor = linebuf;
+       vmove(0);
+}
diff --git a/expreserve.c b/expreserve.c
new file mode 100644 (file)
index 0000000..e0e49a8
--- /dev/null
@@ -0,0 +1,555 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+ */
+
+#ifdef __GNUC__
+#define        UNUSED  __attribute__ ((unused))
+#else
+#define        UNUSED
+#endif
+
+#ifndef        lint
+#ifdef DOSCCS
+char *copyright =
+"@(#) Copyright (c) 1980 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif
+static char sccsid[] UNUSED = "@(#)expreserve.c        1.23 (gritter) 11/27/04";
+#endif
+
+/* from expreserve.c   7.13.2 (2.11BSD GTE) 1996/10/26 */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <dirent.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <time.h>
+
+#include "config.h"
+
+#ifdef LANGMSG
+#include <nl_types.h>
+#include <locale.h>
+nl_catd        catd;
+#else
+#define        catgets(a, b, c, d)     (d)
+#endif
+
+#ifdef BUFSIZ
+#undef BUFSIZ
+#endif
+#ifdef LINE_MAX
+#define        BUFSIZ  LINE_MAX        /* POSIX line size */
+#else  /* !LINE_MAX */
+#ifdef VMUNIX
+#define        BUFSIZ  1024
+#else  /* !VMUNIX */
+#ifdef u370
+#define        BUFSIZ  4096
+#else  /* !u370 */
+#define        BUFSIZ  512
+#endif /* !u370 */
+#endif
+#endif /* !VMUNIX */
+
+#ifdef LARGEF
+typedef        off_t   bloc;
+#else
+typedef        short   bloc;
+#endif
+
+#ifdef VMUNIX
+#ifdef LARGEF
+typedef        off_t   bbloc;
+#else
+typedef        int     bbloc;
+#endif
+#else
+typedef        short   bbloc;
+#endif
+
+#ifdef notdef
+#define TMP    "/tmp"
+#else
+#define        TMP     "/var/tmp"
+#endif
+
+#ifndef VMUNIX
+#define        LBLKS   125
+#else
+#ifdef LARGEF
+#define        LBLKS   20000
+#else
+#define        LBLKS   900
+#endif
+#endif
+#ifdef _POSIX_PATH_MAX
+#define        FNSIZE  _POSIX_PATH_MAX
+#else
+#define        FNSIZE  128
+#endif
+
+#ifdef VMUNIX
+#define        HBLKS   (1 + (FNSIZE + LBLKS * sizeof(bloc)) / BUFSIZ)
+#else
+#define HBLKS  1
+#endif
+
+char xstr[1];                  /* make loader happy */
+
+extern void notify(uid_t, char *, int, time_t);
+extern int copyout(char *);
+extern void mkdigits(char *);
+extern void mknext(char *);
+
+/*
+ * Expreserve - preserve a file in /usr/preserve
+ * Bill Joy UCB November 13, 1977
+ *
+ * This routine is very naive - it doesn't remove anything from
+ * /usr/preserve... this may mean that we leave
+ * stuff there... the danger in doing anything with /usr/preserve
+ * is that the clock may be screwed up and we may get confused.
+ *
+ * We are called in two ways - first from the editor with no argumentss
+ * and the standard input open on the temp file. Second with an argument
+ * to preserve the entire contents of /tmp (root only).
+ *
+ * BUG: should do something about preserving Rx... (register contents)
+ *      temporaries.
+ */
+
+struct         header {
+       time_t  Time;                   /* Time temp file last updated */
+       uid_t   Uid;
+       bbloc   Flines;                 /* Number of lines in file */
+       char    Savedfile[FNSIZE];      /* The current file name */
+       bloc    Blocks[LBLKS];          /* Blocks where line pointers stashed */
+} H;
+
+#define        ignore(a)       a
+#define        ignorl(a)       a
+
+#define eq(a, b) (strcmp(a, b) == 0)
+
+int 
+main(int argc, char **argv)
+{
+       register DIR *tf;
+       struct dirent *dirent;
+       struct stat stbuf;
+
+#ifdef LANGMSG
+       setlocale(LC_MESSAGES, "");
+       catd = catopen(CATNAME, NL_CAT_LOCALE);
+#endif
+       /*
+        * If only one argument, then preserve the standard input.
+        */
+       if (argc == 1) {
+               if (copyout((char *) 0))
+                       exit(1);
+               exit(0);
+       }
+
+       /*
+        * If not super user, then can only preserve standard input.
+        */
+       if (getuid()) {
+               fprintf(stderr, catgets(catd, 3, 1, "NOT super user\n"));
+               exit(1);
+       }
+
+       /*
+        * ... else preserve all the stuff in /tmp, removing
+        * it as we go.
+        */
+       if (chdir(TMP) < 0) {
+               perror(TMP);
+               exit(1);
+       }
+
+       tf = opendir(".");
+       if (tf == NULL) {
+               perror(TMP);
+               exit(1);
+       }
+       while ((dirent = readdir(tf)) != NULL) {
+               /* Ex temporaries must begin with Ex. */
+               if (dirent->d_name[0] != 'E' || dirent->d_name[1] != 'x')
+                       continue;
+               if (stat(dirent->d_name, &stbuf))
+                       continue;
+               if ((stbuf.st_mode & S_IFMT) != S_IFREG)
+                       continue;
+               /*
+                * Save the bastard.
+                */
+               ignore(copyout(dirent->d_name));
+       }
+       closedir(tf);
+       return 0;
+}
+
+#ifdef notdef
+char   pattern[] =     "/usr/preserve/Exaa`XXXXX";
+#else
+char   pattern[] =     "/var/preserve/Exa`XXXXXXXXXX";
+#endif
+
+/*
+ * Notify user uid that his file fname has been saved.
+ */
+void
+notify(uid_t uid, char *fname, int flag, time_t time)
+{
+       struct passwd *pp = getpwuid(uid);
+       register FILE *mf;
+       char    cmd[BUFSIZ];
+       struct utsname ut;
+       char *hostname;
+       char    croak[128];
+       char    *timestamp;
+
+       if (pp == NULL)
+               return;
+       uname(&ut);
+       hostname = ut.nodename;
+       timestamp = ctime(&time);
+       timestamp[16] = 0;      /* blast from seconds on */
+       putenv("MAILRC=/dev/null");
+       sprintf(cmd, "/bin/mail %s", pp->pw_name);
+       setuid(getuid());
+       mf = popen(cmd, "w");
+       if (mf == NULL)
+               return;
+       setbuf(mf, cmd);
+       /*
+        *      flag says how the editor croaked:
+        * "the editor was killed" is perhaps still not an ideal
+        * error message.  Usually, either it was forcably terminated
+        * or the phone was hung up, but we don't know which.
+        */
+       sprintf(croak, flag
+               ? catgets(catd, 3, 2, "the system went down")
+               : catgets(catd, 3, 3, "the editor was killed"));
+       if (fname[0] == 0) {
+               fname = "LOST";
+               fprintf(mf, catgets(catd, 3, 4,
+                               "Subject: editor saved ``LOST''\n"));
+               fprintf(mf, catgets(catd, 3, 5,
+                               "You were editing a file without a name\n"));
+               fprintf(mf, catgets(catd, 3, 6,
+                       "at <%s> on the machine ``%s'' when %s.\n"),
+                       timestamp, hostname, croak);
+               fprintf(mf, catgets(catd, 3, 7,
+               "Since the file had no name, it has been named \"LOST\".\n"));
+       } else {
+               fprintf(mf, catgets(catd, 3, 8,
+                               "Subject: editor saved ``%s''\n"), fname);
+               fprintf(mf, catgets(catd, 3, 9,
+                       "You were editing the file \"%s\"\n"), fname);
+               fprintf(mf, catgets(catd, 3, 10,
+                       "at <%s> on the machine ``%s''\n"),
+                       timestamp, hostname);
+               fprintf(mf, catgets(catd, 3, 11, "when %s.\n"), croak);
+       }
+       fprintf(mf, catgets(catd, 3, 12,
+               "\nYou can retrieve most of your changes to this file\n"));
+       fprintf(mf, catgets(catd, 3, 13,
+                       "using the \"recover\" command of the editor.\n"));
+       fprintf(mf, catgets(catd, 3, 14,
+"An easy way to do this is to give the command \"vi -r %s\".\n"), fname);
+       fprintf(mf, catgets(catd, 3, 15,
+               "This method also works using \"ex\" and \"edit\".\n"));
+       pclose(mf);
+}
+
+/*
+ * Copy file name into /usr/preserve/...
+ * If name is (char *) 0, then do the standard input.
+ * We make some checks on the input to make sure it is
+ * really an editor temporary, generate a name for the
+ * file (this is the slowest thing since we must stat
+ * to find a unique name), and finally copy the file.
+ */
+int
+copyout(char *name)
+{
+       int i;
+       char buf[BUFSIZ];
+       static int reenter;
+
+       /*
+        * The first time we put in the digits of our
+        * process number at the end of the pattern.
+        */
+       if (reenter == 0) {
+               mkdigits(pattern);
+               reenter++;
+       }
+
+       /*
+        * If a file name was given, make it the standard
+        * input if possible.
+        */
+       if (name != 0) {
+               ignore(close(0));
+               /*
+                * Need read/write access for arcane reasons
+                * (see below).
+                */
+               if (open(name, O_RDWR) < 0)
+                       return (-1);
+       }
+
+       /*
+        * Get the header block.
+        */
+       ignorl(lseek(0, (off_t) 0, SEEK_SET));
+       if (read(0, (char *) &H, sizeof H) != sizeof H) {
+format:
+               if (name == 0)
+                       fprintf(stderr, catgets(catd, 3, 16,
+                                               "Buffer format error\t"));
+               return (-1);
+       }
+
+       /*
+        * Consistency checsks so we don't copy out garbage.
+        */
+       if (H.Flines < 0) {
+#ifdef DEBUG
+               fprintf(stderr, "Negative number of lines\n");
+#endif
+               goto format;
+       }
+       if (H.Blocks[0] != HBLKS || H.Blocks[1] != HBLKS+1) {
+#ifdef DEBUG
+               fprintf(stderr, "Blocks %d %d\n", H.Blocks[0], H.Blocks[1]);
+#endif
+               goto format;
+       }
+       if (name == 0 && H.Uid != getuid()) {
+#ifdef DEBUG
+               fprintf(stderr, "Wrong user-id\n");
+#endif
+               goto format;
+       }
+       if (lseek(0, (off_t) 0, SEEK_SET)) {
+#ifdef DEBUG
+               fprintf(stderr, "Negative number of lines\n");
+#endif
+               goto format;
+       }
+
+       /*
+        * If no name was assigned to the file, then give it the name
+        * LOST, by putting this in the header.
+        */
+       if (H.Savedfile[0] == 0) {
+               strcpy(H.Savedfile, "LOST");
+               ignore(write(0, (char *) &H, sizeof H));
+               H.Savedfile[0] = 0;
+               lseek(0, (off_t) 0, SEEK_SET);
+       }
+
+       /*
+        * File is good.  Get a name and create a file for the copy.
+        */
+       mknext(pattern);
+       ignore(close(1));
+       if (open(pattern, O_CREAT|O_EXCL|O_WRONLY|O_TRUNC
+#ifdef O_NOFOLLOW
+                               |O_NOFOLLOW
+#endif /* O_NOFOLLOW */
+                               , 0600) < 0) {
+               if (name == 0)
+                       perror(pattern);
+               return (1);
+       }
+
+       /*
+        * Make the target be owned by the owner of the file.
+        */
+       ignore(chown(pattern, H.Uid, 0));
+
+       /*
+        * Copy the file.
+        */
+       for (;;) {
+               i = read(0, buf, BUFSIZ);
+               if (i < 0) {
+                       if (name)
+                               perror(catgets(catd, 3, 17,
+                                               "Buffer read error"));
+                       ignore(unlink(pattern));
+                       return (-1);
+               }
+               if (i == 0) {
+                       if (name)
+                               ignore(unlink(name));
+                       notify(H.Uid, H.Savedfile, name != 0, H.Time);
+                       return (0);
+               }
+               if (write(1, buf, i) != i) {
+                       if (name == 0)
+                               perror(pattern);
+                       unlink(pattern);
+                       return (-1);
+               }
+       }
+}
+
+/*
+ * Blast the last 5 characters of cp to be the process number.
+ */
+void
+mkdigits(char *cp)
+{
+       register pid_t i;
+       register int j;
+
+#ifdef notdef
+       for (i = getpid(), j = 5, cp += strlen(cp); j > 0; i /= 10, j--)
+               *--cp = i % 10 | '0';
+#else
+       for (i = getpid(), j = 10, cp += strlen(cp); j > 0; i /= 10, j--)
+               *--cp = i % 10 | '0';
+#endif
+}
+
+/*
+ * Make the name in cp be unique by clobbering up to
+ * three alphabetic characters into a sequence of the form 'aab', 'aac', etc.
+ * Mktemp gets weird names too quickly to be useful here.
+ */
+void
+mknext(char *cp)
+{
+       char *dcp;
+       struct stat stb;
+
+       dcp = cp + strlen(cp) - 1;
+       while (isdigit(*dcp & 0377))
+               dcp--;
+whoops:
+       if (dcp[0] == 'z') {
+               dcp[0] = 'a';
+               if (dcp[-1] == 'z') {
+#ifdef notdef
+                       dcp[-1] = 'a';
+                       if (dcp[-2] == 'z')
+#endif
+                               fprintf(stderr, catgets(catd, 3, 18,
+                                               "Can't find a name\t"));
+#ifdef notdef
+                       dcp[-2]++;
+#endif
+               } else
+                       dcp[-1]++;
+       } else
+               dcp[0]++;
+       if (stat(cp, &stb) == 0)
+               goto whoops;
+}
+
+/*
+ *     people making love
+ *     never exactly the same
+ *     just like a snowflake 
+ */
+
+#ifdef lint
+void
+Ignore(int a)
+{
+
+       a = a;
+}
+
+void
+Ignorl(long a)
+{
+
+       a = a;
+}
+#endif
diff --git a/exrecover.c b/exrecover.c
new file mode 100644 (file)
index 0000000..cca2250
--- /dev/null
@@ -0,0 +1,902 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+ */
+
+#ifdef __GNUC__
+#define        UNUSED  __attribute__ ((unused))
+#else
+#define        UNUSED
+#endif
+
+#ifndef        lint
+#ifdef DOSCCS
+char *copyright =
+"@(#) Copyright (c) 1980 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif
+static char sccsid[] UNUSED = "@(#)exrecover.c 1.21 (gritter) 11/27/04";
+#endif
+
+/* from exrecover.c    7.9.2 (2.11BSD) 1996/10/26 */
+
+#include <stdarg.h>
+#ifdef notdef  /* GR */
+#include <stdio.h>     /* mjm: BUFSIZ: stdio = 512, VMUNIX = 1024 */
+#undef BUFSIZ          /* mjm: BUFSIZ different */
+#undef EOF             /* mjm: EOF and NULL effectively the same */
+#undef NULL
+#else
+#define        xstderr (int*)0
+typedef        int     xFILE;
+extern void    perror(const char *);
+extern int     vsprintf(char *, const char *, va_list);
+#endif
+
+#define        var
+
+#include "ex.h"
+#include "ex_temp.h"
+#include "ex_tty.h"
+#include <dirent.h>
+#include <time.h>
+
+#ifndef        MAXNAMLEN
+#ifdef FNSIZE
+#define        MAXNAMLEN       FNSIZE
+#else
+#ifdef NAME_MAX
+#define        MAXNAMLEN       NAME_MAX
+#else
+#define        MAXNAMLEN       255
+#endif
+#endif
+#endif
+
+#define        TMP             "/var/tmp"
+
+#ifdef LANGMSG
+nl_catd        catd;
+#endif
+
+char xstr[1];          /* make loader happy */
+int tfile = -1;        /* ditto */
+
+/*
+ *
+ * This program searches through the specified directory and then
+ * the directory /usr/preserve looking for an instance of the specified
+ * file from a crashed editor or a crashed system.
+ * If this file is found, it is unscrambled and written to
+ * the standard output.
+ *
+ * If this program terminates without a "broken pipe" diagnostic
+ * (i.e. the editor doesn't die right away) then the buffer we are
+ * writing from is removed when we finish.  This is potentially a mistake
+ * as there is not enough handshaking to guarantee that the file has actually
+ * been recovered, but should suffice for most cases.
+ */
+
+/*
+ * Here we save the information about files, when
+ * you ask us what files we have saved for you.
+ * We buffer file name, number of lines, and the time
+ * at which the file was saved.
+ */
+struct svfile {
+       char    sf_name[FNSIZE + 1];
+       int     sf_lines;
+       char    sf_entry[MAXNAMLEN + 1];
+       time_t  sf_time;
+};
+
+#define        ignorl(a)       a
+
+/*
+ * This directory definition also appears (obviously) in expreserve.c.
+ * Change both if you change either.
+ */
+#ifdef notdef
+char   mydir[] =       "/usr/preserve";
+#else
+char   mydir[] =       "/var/preserve";
+#endif
+
+/*
+ * Limit on the number of printed entries
+ * when an, e.g. ``ex -r'' command is given.
+ */
+#define        NENTRY  50
+
+char   nb[BUFSIZ];
+int    vercnt;                 /* Count number of versions of file found */
+
+extern void error(char *, ...);
+extern void listfiles(char *);
+extern void enter(struct svfile *, char *, int);
+extern int qucmp(struct svfile *, struct svfile *);
+extern void findtmp(char *);
+extern void searchdir(char *);
+extern int yeah(char *);
+extern int preserve(void);
+extern void scrapbad(void);
+extern void putfile(int);
+extern void wrerror(void);
+extern void clrstats(void);
+extern void getline(line);
+extern char *getblock(line, int);
+extern void blkio(bloc, char *, ssize_t (*)(int, void *, size_t));
+extern void syserror(void);
+extern void xvfprintf(xFILE *, char *, va_list);
+extern void xfprintf(xFILE *, char *, ...);
+
+int 
+main(int argc, char *argv[])
+{
+       register char *cp;
+       register int b, i;
+
+       /*
+        * Initialize the built-in memory allocator.
+        */
+#ifdef VMUNIX
+       poolsbrk(0);
+#endif
+#ifdef LANGMSG
+       setlocale(LC_MESSAGES, "");
+       catd = catopen(CATNAME, NL_CAT_LOCALE);
+#endif
+
+       /*
+        * Initialize as though the editor had just started.
+        */
+       fendcore = (line *) sbrk(0);
+       dot = zero = dol = fendcore;
+       one = zero + 1;
+       endcore = fendcore - 2;
+       iblock = oblock = -1;
+
+       /*
+        * If given only a -r argument, then list the saved files.
+        */
+       if (argc == 2 && strcmp(argv[1], "-r") == 0) {
+               listfiles(mydir);
+               listfiles(TMP);
+               exit(0);
+       }
+       if (argc != 3)
+               error(catgets(catd, 2, 1,
+                       " Wrong number of arguments to exrecover"), 0);
+
+       strcpy(file, argv[2]);
+
+       /*
+        * Search for this file.
+        */
+       findtmp(argv[1]);
+
+       /*
+        * Got (one of the versions of) it, write it back to the editor.
+        */
+       cp = ctime(&H.Time);
+       cp[19] = 0;
+       xfprintf(xstderr, catgets(catd, 2, 2, " [Dated: %s"), cp);
+       xfprintf(xstderr, vercnt > 1 ? catgets(catd, 2, 3,
+                       ", newest of %d saved]")
+               : catgets(catd, 2, 4, "]"), vercnt);
+       H.Flines++;
+
+       /*
+        * Allocate space for the line pointers from the temp file.
+        */
+       if ((char *) sbrk(H.Flines * sizeof (line)) == (char *) -1)
+               /*
+                * Good grief.
+                */
+               error(catgets(catd, 1, 5, " Not enough core for lines"), 0);
+#ifdef DEBUG
+       xfprintf(xstderr, "%d lines\n", H.Flines);
+#endif
+
+       /*
+        * Now go get the blocks of seek pointers which are scattered
+        * throughout the temp file, reconstructing the incore
+        * line pointers at point of crash.
+        */
+       b = 0;
+       while (H.Flines > 0) {
+               ignorl(lseek(tfile, (off_t) ((blocks[b] & BLKMSK) * BUFSIZ),
+                                       SEEK_SET));
+               i = H.Flines < BUFSIZ / sizeof (line) ?
+                       H.Flines * sizeof (line) : BUFSIZ;
+               if (read(tfile, (char *) dot, i) != i) {
+                       perror(nb);
+                       exit(1);
+               }
+               dot += i / sizeof (line);
+               H.Flines -= i / sizeof (line);
+               b++;
+       }
+       dot--; dol = dot;
+
+       /*
+        * Sigh... due to sandbagging some lines may really not be there.
+        * Find and discard such.  This shouldn't happen much.
+        */
+       scrapbad();
+
+       /*
+        * Now if there were any lines in the recovered file
+        * write them to the standard output.
+        */
+       if (dol > zero) {
+               addr1 = one; addr2 = dol; io = 1;
+               putfile(0);
+       }
+
+       /*
+        * Trash the saved buffer.
+        * Hopefully the system won't crash before the editor
+        * syncs the new recovered buffer; i.e. for an instant here
+        * you may lose if the system crashes because this file
+        * is gone, but the editor hasn't completed reading the recovered
+        * file from the pipe from us to it.
+        *
+        * This doesn't work if we are coming from an non-absolute path
+        * name since we may have chdir'ed but what the hay, noone really
+        * ever edits with temporaries in "." anyways.
+        */
+       if (nb[0] == '/')
+               ignore(unlink(nb));
+
+       /*
+        * Adieu.
+        */
+       exit(0);
+}
+
+/*
+ * Print an error message (notably not in error
+ * message file).  If terminal is in RAW mode, then
+ * we should be writing output for "vi", so don't print
+ * a newline which would screw up the screen.
+ */
+/*VARARGS2*/
+void
+error(char *str, ...)
+{
+       va_list ap;
+
+       va_start(ap, str);
+       xvfprintf(xstderr, str, ap);
+       va_end(ap);
+       tcgetattr(2, &tty);
+       if (tty.c_lflag & ICANON)
+               xfprintf(xstderr, "\n");
+       exit(1);
+}
+
+void
+listfiles(char *dirname)
+{
+       register DIR *dir;
+       struct dirent *dirent;
+       int ecount;
+       register int f;
+       char *cp;
+       struct svfile *fp, svbuf[NENTRY];
+
+       /*
+        * Open /usr/preserve, and go there to make things quick.
+        */
+       dir = opendir(dirname);
+       if (dir == NULL) {
+               perror(dirname);
+               return;
+       }
+       if (chdir(dirname) < 0) {
+               perror(dirname);
+               return;
+       }
+       xfprintf(xstderr, "%s:\n", dirname);
+
+       /*
+        * Look at the candidate files in /usr/preserve.
+        */
+       fp = &svbuf[0];
+       ecount = 0;
+       while ((dirent = readdir(dir)) != NULL) {
+               if (dirent->d_name[0] != 'E')
+                       continue;
+#ifdef DEBUG
+               xfprintf(xstderr, "considering %s\n", dirent->d_name);
+#endif
+               /*
+                * Name begins with E; open it and
+                * make sure the uid in the header is our uid.
+                * If not, then don't bother with this file, it can't
+                * be ours.
+                */
+               f = open(dirent->d_name, O_RDONLY);
+               if (f < 0) {
+#ifdef DEBUG
+                       xfprintf(xstderr, "open failed\n");
+#endif
+                       continue;
+               }
+               if (read(f, (char *) &H, sizeof H) != sizeof H) {
+#ifdef DEBUG
+                       xfprintf(xstderr, "culdnt read hedr\n");
+#endif
+                       ignore(close(f));
+                       continue;
+               }
+               ignore(close(f));
+               if (getuid() != H.Uid) {
+#ifdef DEBUG
+                       xfprintf(xstderr, "uid wrong\n");
+#endif
+                       continue;
+               }
+
+               /*
+                * Saved the day!
+                */
+               enter(fp++, dirent->d_name, ecount);
+               ecount++;
+#ifdef DEBUG
+               xfprintf(xstderr, "entered file %s\n", dirent->d_name);
+#endif
+       }
+       ignore(closedir(dir));
+
+       /*
+        * If any files were saved, then sort them and print
+        * them out.
+        */
+       if (ecount == 0) {
+               xfprintf(xstderr, catgets(catd, 2, 6, "No files saved.\n"));
+               return;
+       }
+       qsort(&svbuf[0], ecount, sizeof svbuf[0], (int(*)()) qucmp);
+       for (fp = &svbuf[0]; fp < &svbuf[ecount]; fp++) {
+               cp = ctime(&fp->sf_time);
+               cp[10] = 0;
+               xfprintf(xstderr, catgets(catd, 2, 7, "On %s at "), cp);
+               cp[16] = 0;
+               xfprintf(xstderr, &cp[11]);
+               xfprintf(xstderr, catgets(catd, 2, 8,
+                                       " saved %d lines of file \"%s\"\n"),
+                   fp->sf_lines, fp->sf_name);
+       }
+}
+
+/*
+ * Enter a new file into the saved file information.
+ */
+void
+enter(struct svfile *fp, char *fname, int count)
+{
+       register char *cp, *cp2;
+       register struct svfile *f, *fl;
+       time_t curtime, itol();
+
+       f = 0;
+       if (count >= NENTRY) {
+               /*
+                * My god, a huge number of saved files.
+                * Would you work on a system that crashed this
+                * often?  Hope not.  So lets trash the oldest
+                * as the most useless.
+                *
+                * (I wonder if this code has ever run?)
+                */
+               fl = fp - count + NENTRY - 1;
+               curtime = fl->sf_time;
+               for (f = fl; --f > fp-count; )
+                       if (f->sf_time < curtime)
+                               curtime = f->sf_time;
+               for (f = fl; --f > fp-count; )
+                       if (f->sf_time == curtime)
+                               break;
+               fp = f;
+       }
+
+       /*
+        * Gotcha.
+        */
+       fp->sf_time = H.Time;
+       fp->sf_lines = H.Flines;
+       cp2 = fp->sf_name, cp = savedfile;
+       while (*cp2++ = *cp++);
+       for (cp2 = fp->sf_entry, cp = fname; *cp && cp-fname < 14;)
+               *cp2++ = *cp++;
+       *cp2++ = 0;
+}
+
+/*
+ * Do the qsort compare to sort the entries first by file name,
+ * then by modify time.
+ */
+int
+qucmp(struct svfile *p1, struct svfile *p2)
+{
+       register int t;
+
+       if (t = strcmp(p1->sf_name, p2->sf_name))
+               return(t);
+       if (p1->sf_time > p2->sf_time)
+               return(-1);
+       return(p1->sf_time < p2->sf_time);
+}
+
+/*
+ * Scratch for search.
+ */
+char   bestnb[BUFSIZ];         /* Name of the best one */
+long   besttime;               /* Time at which the best file was saved */
+int    bestfd;                 /* Keep best file open so it dont vanish */
+
+/*
+ * Look for a file, both in the users directory option value
+ * (i.e. usually /tmp) and in /usr/preserve.
+ * Want to find the newest so we search on and on.
+ */
+void
+findtmp(char *dir)
+{
+
+       /*
+        * No name or file so far.
+        */
+       bestnb[0] = 0;
+       bestfd = -1;
+
+       /*
+        * Search /usr/preserve and, if we can get there, /tmp
+        * (actually the users "directory" option).
+        */
+       searchdir(dir);
+       if (chdir(mydir) == 0)
+               searchdir(mydir);
+       if (bestfd != -1) {
+               /*
+                * Gotcha.
+                * Put the file (which is already open) in the file
+                * used by the temp file routines, and save its
+                * name for later unlinking.
+                */
+               tfile = bestfd;
+               strcpy(nb, bestnb);
+               ignorl(lseek(tfile, (off_t) 0, SEEK_SET));
+
+               /*
+                * Gotta be able to read the header or fall through
+                * to lossage.
+                */
+               if (read(tfile, (char *) &H, sizeof H) == sizeof H)
+                       return;
+       }
+
+       /*
+        * Extreme lossage...
+        */
+       error(catgets(catd, 2, 9, " File not found"), 0);
+}
+
+/*
+ * Search for the file in directory dirname.
+ *
+ * Don't chdir here, because the users directory
+ * may be ".", and we would move away before we searched it.
+ * Note that we actually chdir elsewhere (because it is too slow
+ * to look around in /usr/preserve without chdir'ing there) so we
+ * can't win, because we don't know the name of '.' and if the path
+ * name of the file we want to unlink is relative, rather than absolute
+ * we won't be able to find it again.
+ */
+void
+searchdir(char *dirname)
+{
+       struct dirent *dirent;
+       register DIR *dir;
+       /* char dbuf[BUFSIZ]; */
+
+       dir = opendir(dirname);
+       if (dir == NULL)
+               return;
+       while ((dirent = readdir(dir)) != NULL) {
+               if (dirent->d_name[0] != 'E')
+                       continue;
+               /*
+                * Got a file in the directory starting with E...
+                * Save a consed up name for the file to unlink
+                * later, and check that this is really a file
+                * we are looking for.
+                */
+               ignore(strcat(strcat(strcpy(nb, dirname), "/"), dirent->d_name));
+               if (yeah(nb)) {
+                       /*
+                        * Well, it is the file we are looking for.
+                        * Is it more recent than any version we found before?
+                        */
+                       if (H.Time > besttime) {
+                               /*
+                                * A winner.
+                                */
+                               ignore(close(bestfd));
+                               bestfd = dup(tfile);
+                               besttime = H.Time;
+                               strcpy(bestnb, nb);
+                       }
+                       /*
+                        * Count versions so user can be told there are
+                        * ``yet more pages to be turned''.
+                        */
+                       vercnt++;
+               }
+               ignore(close(tfile));
+       }
+       ignore(closedir(dir));
+}
+
+/*
+ * Given a candidate file to be recovered, see
+ * if its really an editor temporary and of this
+ * user and the file specified.
+ */
+int
+yeah(char *name)
+{
+
+       tfile = open(name, O_RDWR);
+       if (tfile < 0)
+               return (0);
+       if (read(tfile, (char *) &H, sizeof H) != sizeof H) {
+nope:
+               ignore(close(tfile));
+               return (0);
+       }
+       if (strcmp(savedfile, file))
+               goto nope;
+       if (getuid() != H.Uid)
+               goto nope;
+       /*
+        * This is old and stupid code, which
+        * puts a word LOST in the header block, so that lost lines
+        * can be made to point at it.
+        */
+       ignorl(lseek(tfile, (off_t) (BUFSIZ*HBLKS-8), SEEK_SET));
+       ignore(write(tfile, "LOST", 5));
+       return (1);
+}
+
+int
+preserve(void)
+{
+       return 0;
+}
+
+/*
+ * Find the true end of the scratch file, and ``LOSE''
+ * lines which point into thin air.  This lossage occurs
+ * due to the sandbagging of i/o which can cause blocks to
+ * be written in a non-obvious order, different from the order
+ * in which the editor tried to write them.
+ *
+ * Lines which are lost are replaced with the text LOST so
+ * they are easy to find.  We work hard at pretty formatting here
+ * as lines tend to be lost in blocks.
+ *
+ * This only seems to happen on very heavily loaded systems, and
+ * not very often.
+ */
+void
+scrapbad(void)
+{
+       register line *ip;
+       struct stat stbuf;
+       off_t size, maxt;
+       bbloc bno, cnt = 0, bad, was;
+       char bk[BUFSIZ];
+
+       ignore(fstat(tfile, &stbuf));
+       size = stbuf.st_size;
+       maxt = (size >> SHFT) | (BNDRY-1);
+       bno = (maxt >> OFFBTS) & BLKMSK;
+#ifdef DEBUG
+       xfprintf(xstderr, "size %ld, maxt %o, bno %d\n", size, maxt, bno);
+#endif
+
+       /*
+        * Look for a null separating two lines in the temp file;
+        * if last line was split across blocks, then it is lost
+        * if the last block is.
+        */
+       while (bno > 0) {
+               ignorl(lseek(tfile, (off_t) (BUFSIZ * (bno & BLKMSK)),
+                                       SEEK_SET));
+               cnt = read(tfile, (char *) bk, BUFSIZ);
+               while (cnt > 0)
+                       if (bk[--cnt] == 0)
+                               goto null;
+               bno--;
+       }
+null:
+
+       /*
+        * Magically calculate the largest valid pointer in the temp file,
+        * consing it up from the block number and the count.
+        */
+       maxt = ((bno << OFFBTS) | (cnt >> SHFT)) & ~1;
+#ifdef DEBUG
+       xfprintf(xstderr, "bno %d, cnt %d, maxt %o\n", bno, cnt, maxt);
+#endif
+
+       /*
+        * Now cycle through the line pointers,
+        * trashing the Lusers.
+        */
+       was = bad = 0;
+       for (ip = one; ip <= dol; ip++)
+               if (*ip > maxt) {
+#ifdef DEBUG
+                       xfprintf(xstderr, "%d bad, %o > %o\n", ip - zero, *ip, maxt);
+#endif
+                       if (was == 0)
+                               was = ip - zero;
+                       *ip = ((HBLKS*BUFSIZ)-8) >> SHFT;
+               } else if (was) {
+                       if (bad == 0)
+                               xfprintf(xstderr, catgets(catd, 2, 10,
+                                               " [Lost line(s):"));
+                       xfprintf(xstderr, catgets(catd, 2, 11,
+                                               " %d"), was);
+                       if ((ip - 1) - zero > was)
+                               xfprintf(xstderr, catgets(catd, 2, 12, "-%d"),
+                                               (int) ((ip - 1) - zero));
+                       bad++;
+                       was = 0;
+               }
+       if (was != 0) {
+               if (bad == 0)
+                       xfprintf(xstderr, catgets(catd, 2, 13,
+                                               " [Lost line(s):"));
+               xfprintf(xstderr, catgets(catd, 2, 14, " %d"), was);
+               if (dol - zero != was)
+                       xfprintf(xstderr, catgets(catd, 2, 15,
+                                               "-%d"), (int) (dol - zero));
+               bad++;
+       }
+       if (bad)
+               xfprintf(xstderr, catgets(catd, 2, 16, "]"));
+}
+
+int    cntch, cntln, cntodd, cntnull;
+
+/*
+ * Following routines stolen mercilessly from ex.
+ */
+void
+putfile(int unused)
+{
+       line *a1;
+       register char *fp, *lp;
+       register int nib;
+
+       a1 = addr1;
+       clrstats();
+       cntln = addr2 - a1 + 1;
+       if (cntln == 0)
+               return;
+       nib = BUFSIZ;
+       fp = genbuf;
+       do {
+               getline(*a1++);
+               lp = linebuf;
+               for (;;) {
+                       if (--nib < 0) {
+                               nib = fp - genbuf;
+                               if (write(io, genbuf, nib) != nib)
+                                       wrerror();
+                               cntch += nib;
+                               nib = MAXBSIZE - 1 /* 511 */;
+                               fp = genbuf;
+                       }
+                       if ((*fp++ = *lp++) == 0) {
+                               fp[-1] = '\n';
+                               break;
+                       }
+               }
+       } while (a1 <= addr2);
+       nib = fp - genbuf;
+       if (write(io, genbuf, nib) != nib)
+               wrerror();
+       cntch += nib;
+}
+
+void
+wrerror(void)
+{
+
+       syserror();
+}
+
+void
+clrstats(void)
+{
+
+       ninbuf = 0;
+       cntch = 0;
+       cntln = 0;
+       cntnull = 0;
+       cntodd = 0;
+}
+
+#define        READ    0
+#define        WRITE   1
+
+void
+getline(line tl)
+{
+       register char *bp, *lp;
+       register int nl;
+
+       lp = linebuf;
+       bp = getblock(tl, READ);
+       nl = nleft;
+       tl &= ~OFFMSK;
+       while (*lp++ = *bp++)
+               if (--nl == 0) {
+                       bp = getblock(tl += INCRMT, READ);
+                       nl = nleft;
+               }
+}
+
+char *
+getblock(line atl, int iof)
+{
+       register bbloc bno, off;
+       
+       bno = (atl >> OFFBTS) & BLKMSK;
+       off = (atl << SHFT) & LBTMSK;
+       if (bno >= NMBLKS)
+               error(catgets(catd, 2, 17, " Tmp file too large"));
+       nleft = BUFSIZ - off;
+       if (bno == iblock) {
+               ichanged |= iof;
+               return (ibuff + off);
+       }
+       if (bno == oblock)
+               return (obuff + off);
+       if (iof == READ) {
+               if (ichanged)
+                       blkio(iblock, ibuff, (ssize_t(*)())write);
+               ichanged = 0;
+               iblock = bno;
+               blkio(bno, ibuff, (ssize_t(*)())read);
+               return (ibuff + off);
+       }
+       if (oblock >= 0)
+               blkio(oblock, obuff, (ssize_t(*)())write);
+       oblock = bno;
+       return (obuff + off);
+}
+
+void
+blkio(bloc b, char *buf, ssize_t (*iofcn)(int, void *, size_t))
+{
+
+       lseek(tfile, (off_t) ((b & BLKMSK) * BUFSIZ), SEEK_SET);
+       if ((*iofcn)(tfile, buf, BUFSIZ) != BUFSIZ)
+               syserror();
+}
+
+void
+syserror(void)
+{
+
+       dirtcnt = 0;
+       write(2, " ", 1);
+       error("%s", strerror(errno));
+       exit(1);
+}
+
+/*
+ * Must avoid stdio because expreserve uses sbrk to do memory
+ * allocation and stdio uses malloc.
+ */
+/*
+ * I do not know whether vsprintf() uses malloc() or not.
+ * So this may be fail, too.
+ */
+void
+xvfprintf(xFILE *fp, char *fmt, va_list ap)
+{
+       char buf[BUFSIZ];
+
+       if (fp != xstderr)
+               return;
+       vsprintf(buf, fmt, ap);
+       write(2, buf, strlen(buf));
+}
+
+void
+xfprintf(xFILE *fp, char *fmt, ...)
+{
+       va_list ap;
+
+       if (fp != xstderr)
+               return;
+       va_start(ap, fmt);
+       xvfprintf(fp, fmt, ap);
+       va_end(ap);
+}
diff --git a/libterm/Makefile b/libterm/Makefile
new file mode 100644 (file)
index 0000000..b3432b1
--- /dev/null
@@ -0,0 +1,64 @@
+#
+# This code contains changes by
+#       Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+#
+# Conditions 1, 2, and 4 and the no-warranty notice below apply
+# to these changes.
+#
+#
+# Copyright (c) 1980, 1993
+#      The Regents of the University of California.  All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+#    must display the following acknowledgement:
+#      This product includes software developed by the University of
+#      California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+#    may be used to endorse or promote products derived from this software
+#    without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#      from Makefile   5.1 (Berkeley) 6/5/85
+#
+#      Sccsid @(#)Makefile     1.2 (gritter) 2/4/02
+#
+DEFS   = -DCM_N -DCM_GT -DCM_B -DCM_D
+# COPT comes from ex.
+CFLAGS = $(DEFS) $(COPT)
+SRCS   = termcap.c tgoto.c tputs.c
+OBJS   = termcap.o tgoto.o tputs.o
+
+.c.o: ; $(CC) $(CFLAGS) -c $<
+
+all: libtermlib.a
+
+libtermlib.a: $(OBJS)
+       ar cr libtermlib.a $(OBJS)
+
+clean:
+       rm -f libtermlib.a $(OBJS) core
+
+# DO NOT DELETE
+
+termcap.o: libterm.h
+tgoto.o: libterm.h
+tputs.o: libterm.h
diff --git a/libterm/libterm.h b/libterm/libterm.h
new file mode 100644 (file)
index 0000000..8629224
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Header for termcap routines derived from 2.11 BSD.
+ *
+ *     Sccsid @(#)libterm.h    1.4 (gritter) 11/23/04
+ */
+
+/*
+ * Size of the capability buffer string.
+ */
+#define        TCBUFSIZE       2048
+
+int tgetent(char *, const char *);
+int tgetnum(char *);
+int tgetflag(char *);
+char *tgetstr(char *, char **);
+char *tgoto(char *, int, int);
+int tputs(const char *, int, int (*)(int));
diff --git a/libterm/termcap.c b/libterm/termcap.c
new file mode 100644 (file)
index 0000000..6cee780
--- /dev/null
@@ -0,0 +1,410 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char *sccsid = "@(#)termcap.c   1.7 (gritter) 11/23/04";
+#endif
+#endif
+
+/* from termcap.c      5.1 (Berkeley) 6/5/85 */
+
+#if 0  /* GR */
+#define        TCBUFSIZE               1024
+#else
+#include "libterm.h"
+#endif
+#define        E_TERMCAP       "/etc/termcap"
+#define MAXHOP         32      /* max number of tc= indirections */
+
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+/*
+ * termcap - routines for dealing with the terminal capability data base
+ *
+ * BUG:                Should use a "last" pointer in tbuf, so that searching
+ *             for capabilities alphabetically would not be a n**2/2
+ *             process when large numbers of capabilities are given.
+ * Note:       If we add a last pointer now we will screw up the
+ *             tc capability. We really should compile termcap.
+ *
+ * Essentially all the work here is scanning and decoding escapes
+ * in string capabilities.  We don't use stdio because the editor
+ * doesn't, and because living w/o it is not hard.
+ */
+
+static char *tbuf;
+static int hopcount;   /* detect infinite loops in termcap, init 0 */
+
+static int tnamatch(const char *);
+static int tnchktc(void);
+static char *tskip(register const char *);
+static char *tdecode(register char *, char **);
+
+/*
+ * Tnamatch deals with name matching.  The first field of the termcap
+ * entry is a sequence of names separated by |'s, so we compare
+ * against each such name.  The normal : terminator after the last
+ * name (before the first field) stops us.
+ */
+static int
+tnamatch(const char *np)
+{
+       register const char *Np;
+       register char *Bp;
+
+       Bp = tbuf;
+       if (*Bp == '#')
+               return(0);
+       for (;;) {
+               for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
+                       continue;
+               if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
+                       return (1);
+               while (*Bp && *Bp != ':' && *Bp != '|')
+                       Bp++;
+               if (*Bp == 0 || *Bp == ':')
+                       return (0);
+               Bp++;
+       }
+}
+
+/*
+ * tnchktc: check the last entry, see if it's tc=xxx. If so,
+ * recursively find xxx and append that entry (minus the names)
+ * to take the place of the tc=xxx entry. This allows termcap
+ * entries to say "like an HP2621 but doesn't turn on the labels".
+ * Note that this works because of the left to right scan.
+ */
+static int
+tnchktc(void)
+{
+       register char *p, *q;
+       char tcname[16];        /* name of similar terminal */
+       char tcbuf[TCBUFSIZE];
+       char rmbuf[TCBUFSIZE];
+       char *holdtbuf = tbuf, *holdtc;
+       int l;
+
+       p = tbuf;
+       while (*p) {
+               holdtc = p = tskip(p);
+               if (!*p)
+                       break;
+               if (*p++ != 't' || *p == 0 || *p++ != 'c')
+                       continue;
+               if (*p++ != '=') {
+               bad:    write(2, "Bad termcap entry\n", 18);
+                       return (0);
+               }
+               for (q = tcname; *p && *p != ':'; p++) {
+                       if (q >= &tcname[sizeof tcname - 1])
+                               goto bad;
+                       *q++ = *p;
+               }
+               *q = '\0';
+               if (++hopcount > MAXHOP) {
+                       write(2, "Infinite tc= loop\n", 18);
+                       return (0);
+               }
+               if (tgetent(tcbuf, tcname) != 1) {
+                       hopcount = 0;           /* unwind recursion */
+                       return(0);
+               }
+               hopcount--;
+               tbuf = holdtbuf;
+               strcpy(rmbuf, &p[1]);
+               for (q=tcbuf; *q != ':'; q++)
+                       ;
+               l = holdtc - holdtbuf + strlen(rmbuf) + strlen(q);
+               if (l > TCBUFSIZE) {
+                       write(2, "Termcap entry too long\n", 23);
+                       break;
+               }
+               q++;
+               for (p = holdtc; *q; q++)
+                       *p++ = *q;
+               strcpy(p, rmbuf);
+               p = holdtc;
+       }
+       return(1);
+}
+
+/*
+ * Get an entry for terminal name in buffer bp,
+ * from the termcap file.  Parse is very rudimentary;
+ * we just notice escaped newlines.
+ */
+int
+tgetent(char *bp, const char *name)
+{
+       register char *cp;
+       register int c;
+       register int i = 0, cnt = 0;
+       char ibuf[TCBUFSIZE];
+       int tf;
+
+       tbuf = bp;
+       tf = -1;
+#ifndef V6
+       cp = getenv("TERMCAP");
+       /*
+        * TERMCAP can have one of two things in it. It can be the
+        * name of a file to use instead of /etc/termcap. In this
+        * case it better start with a "/". Or it can be an entry to
+        * use so we don't have to read the file. In this case it
+        * has to already have the newlines crunched out.
+        */
+       if (cp && *cp) {
+               if (*cp == '/') {
+                       tf = open(cp, 0);
+               } else {
+                       tbuf = cp;
+                       c = tnamatch(name);
+                       tbuf = bp;
+                       if (c) {
+                               strcpy(bp,cp);
+                               return(tnchktc());
+                       }
+               }
+       }
+       if (tf < 0)
+               tf = open(E_TERMCAP, 0);
+#else
+       tf = open(E_TERMCAP, 0);
+#endif
+       if (tf < 0)
+               return (-1);
+       for (;;) {
+               cp = bp;
+               for (;;) {
+                       if (i == cnt) {
+                               cnt = read(tf, ibuf, TCBUFSIZE);
+                               if (cnt <= 0) {
+                                       close(tf);
+                                       return (0);
+                               }
+                               i = 0;
+                       }
+                       c = ibuf[i++];
+                       if (c == '\n') {
+                               if (cp > bp && cp[-1] == '\\'){
+                                       cp--;
+                                       continue;
+                               }
+                               break;
+                       }
+                       if (cp >= bp+TCBUFSIZE) {
+                               write(2,"Termcap entry too long\n", 23);
+                               break;
+                       } else
+                               *cp++ = c;
+               }
+               *cp = 0;
+
+               /*
+                * The real work for the match.
+                */
+               if (tnamatch(name)) {
+                       close(tf);
+                       return(tnchktc());
+               }
+       }
+}
+
+/*
+ * Skip to the next field.  Notice that this is very dumb, not
+ * knowing about \: escapes or any such.  If necessary, :'s can be put
+ * into the termcap file in octal.
+ */
+static char *
+tskip(register const char *bp)
+{
+
+       while (*bp && *bp != ':')
+               bp++;
+       if (*bp == ':')
+               bp++;
+       return (char *)bp;
+}
+
+/*
+ * Return the (numeric) option id.
+ * Numeric options look like
+ *     li#80
+ * i.e. the option string is separated from the numeric value by
+ * a # character.  If the option is not found we return -1.
+ * Note that we handle octal numbers beginning with 0.
+ */
+int
+tgetnum(char *id)
+{
+       register int i, base;
+       register char *bp = tbuf;
+
+       for (;;) {
+               bp = tskip(bp);
+               if (*bp == 0)
+                       return (-1);
+               if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
+                       continue;
+               if (*bp == '@')
+                       return(-1);
+               if (*bp != '#')
+                       continue;
+               bp++;
+               base = 10;
+               if (*bp == '0')
+                       base = 8;
+               i = 0;
+               while (isdigit((*bp & 0377)))
+                       i *= base, i += *bp++ - '0';
+               return (i);
+       }
+}
+
+/*
+ * Handle a flag option.
+ * Flag options are given "naked", i.e. followed by a : or the end
+ * of the buffer.  Return 1 if we find the option, or 0 if it is
+ * not given.
+ */
+int
+tgetflag(char *id)
+{
+       register char *bp = tbuf;
+
+       for (;;) {
+               bp = tskip(bp);
+               if (!*bp)
+                       return (0);
+               if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
+                       if (!*bp || *bp == ':')
+                               return (1);
+                       else if (*bp == '@')
+                               return(0);
+               }
+       }
+}
+
+/*
+ * Get a string valued option.
+ * These are given as
+ *     cl=^Z
+ * Much decoding is done on the strings, and the strings are
+ * placed in area, which is a ref parameter which is updated.
+ * No checking on area overflow.
+ */
+char *
+tgetstr(char *id, char **area)
+{
+       register char *bp = tbuf;
+
+       for (;;) {
+               bp = tskip(bp);
+               if (!*bp)
+                       return (0);
+               if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
+                       continue;
+               if (*bp == '@')
+                       return(0);
+               if (*bp != '=')
+                       continue;
+               bp++;
+               return (tdecode(bp, area));
+       }
+}
+
+/*
+ * Tdecode does the grung work to decode the
+ * string capability escapes.
+ */
+static char *
+tdecode(register char *str, char **area)
+{
+       register char *cp;
+       register int c;
+       register char *dp;
+       int i;
+
+       cp = *area;
+       while ((c = *str++) && c != ':') {
+               switch (c) {
+
+               case '^':
+                       c = *str++ & 037;
+                       break;
+
+               case '\\':
+                       dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
+                       c = *str++;
+nextc:
+                       if (*dp++ == c) {
+                               c = *dp++;
+                               break;
+                       }
+                       dp++;
+                       if (*dp)
+                               goto nextc;
+                       if (isdigit(c)) {
+                               c -= '0', i = 2;
+                               do
+                                       c <<= 3, c |= *str++ - '0';
+                               while (--i && isdigit(*str & 0377));
+                       }
+                       break;
+               }
+               *cp++ = c;
+       }
+       *cp++ = 0;
+       str = *area;
+       *area = cp;
+       return (str);
+}
+
+/*
+*/
+static const char sccssl[] = "@(#)libterm.sl   1.7 (gritter) 11/23/04";
diff --git a/libterm/tgoto.c b/libterm/tgoto.c
new file mode 100644 (file)
index 0000000..07316db
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char *sccsid = "@(#)tgoto.c     1.3 (gritter) 11/23/04";
+#endif
+#endif
+
+/* from tgoto.c        5.1 (Berkeley) 6/5/85 */
+
+#include "libterm.h"
+
+#define        CTRL(c) (c & 037)
+
+#define MAXRETURNSIZE 64
+
+#ifdef __STDC__
+#include <string.h>
+#endif
+
+char   *UP;
+char   *BC;
+
+/*
+ * Routine to perform cursor addressing.
+ * CM is a string containing printf type escapes to allow
+ * cursor addressing.  We start out ready to print the destination
+ * line, and switch each time we print row or column.
+ * The following escapes are defined for substituting row/column:
+ *
+ *     %d      as in printf
+ *     %2      like %2d
+ *     %3      like %3d
+ *     %.      gives %c hacking special case characters
+ *     %+x     like %c but adding x first
+ *
+ *     The codes below affect the state but don't use up a value.
+ *
+ *     %>xy    if value > x add y
+ *     %r      reverses row/column
+ *     %i      increments row/column (for one origin indexing)
+ *     %%      gives %
+ *     %B      BCD (2 decimal digits encoded in one byte)
+ *     %D      Delta Data (backwards bcd)
+ *
+ * all other characters are ``self-inserting''.
+ */
+char *
+tgoto(char *CM, int destcol, int destline)
+{
+       static char result[MAXRETURNSIZE];
+       static char added[10];
+       char *cp = CM;
+       register char *dp = result;
+       register int c;
+       int oncol = 0;
+       register int which = destline;
+
+       if (cp == 0) {
+toohard:
+               /*
+                * ``We don't do that under BOZO's big top''
+                */
+               return ("OOPS");
+       }
+       added[0] = 0;
+       while (c = *cp++) {
+               if (c != '%') {
+                       *dp++ = c;
+                       continue;
+               }
+               switch (c = *cp++) {
+
+#ifdef CM_N
+               case 'n':
+                       destcol ^= 0140;
+                       destline ^= 0140;
+                       goto setwhich;
+#endif
+
+               case 'd':
+                       if (which < 10)
+                               goto one;
+                       if (which < 100)
+                               goto two;
+                       /* fall into... */
+
+               case '3':
+                       *dp++ = (which / 100) | '0';
+                       which %= 100;
+                       /* fall into... */
+
+               case '2':
+two:   
+                       *dp++ = which / 10 | '0';
+one:
+                       *dp++ = which % 10 | '0';
+swap:
+                       oncol = 1 - oncol;
+setwhich:
+                       which = oncol ? destcol : destline;
+                       continue;
+
+#ifdef CM_GT
+               case '>':
+                       if (which > *cp++)
+                               which += *cp++;
+                       else
+                               cp++;
+                       continue;
+#endif
+
+               case '+':
+                       which += *cp++;
+                       /* fall into... */
+
+               case '.':
+/* casedot: */
+                       /*
+                        * This code is worth scratching your head at for a
+                        * while.  The idea is that various weird things can
+                        * happen to nulls, EOT's, tabs, and newlines by the
+                        * tty driver, arpanet, and so on, so we don't send
+                        * them if we can help it.
+                        *
+                        * Tab is taken out to get Ann Arbors to work, otherwise
+                        * when they go to column 9 we increment which is wrong
+                        * because bcd isn't continuous.  We should take out
+                        * the rest too, or run the thing through more than
+                        * once until it doesn't make any of these, but that
+                        * would make termlib (and hence pdp-11 ex) bigger,
+                        * and also somewhat slower.  This requires all
+                        * programs which use termlib to stty tabs so they
+                        * don't get expanded.  They should do this anyway
+                        * because some terminals use ^I for other things,
+                        * like nondestructive space.
+                        */
+                       if (which == 0 || which == CTRL('d') || /* which == '\t' || */ which == '\n') {
+                               if (oncol || UP) /* Assumption: backspace works */
+                                       /*
+                                        * Loop needed because newline happens
+                                        * to be the successor of tab.
+                                        */
+                                       do {
+                                               strcat(added, oncol ? (BC ? BC : "\b") : UP);
+                                               which++;
+                                       } while (which == '\n');
+                       }
+                       *dp++ = which;
+                       goto swap;
+
+               case 'r':
+                       oncol = 1;
+                       goto setwhich;
+
+               case 'i':
+                       destcol++;
+                       destline++;
+                       which++;
+                       continue;
+
+               case '%':
+                       *dp++ = c;
+                       continue;
+
+#ifdef CM_B
+               case 'B':
+                       which = (which/10 << 4) + which%10;
+                       continue;
+#endif
+
+#ifdef CM_D
+               case 'D':
+                       which = which - 2 * (which%16);
+                       continue;
+#endif
+
+               default:
+                       goto toohard;
+               }
+       }
+       strcpy(dp, added);
+       return (result);
+}
diff --git a/libterm/tputs.c b/libterm/tputs.c
new file mode 100644 (file)
index 0000000..58e0665
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char *sccsid = "@(#)tputs.c     1.4 (gritter) 11/23/04";
+#endif
+#endif
+
+/* from tputs.c        5.1 (Berkeley) 6/5/85 */
+
+#include <ctype.h>
+
+#include "libterm.h"
+
+/*
+ * The following array gives the number of tens of milliseconds per
+ * character for each speed as returned by gtty.  Thus since 300
+ * baud returns a 7, there are 33.3 milliseconds per char at 300 baud.
+ */
+static
+short  tmspc10[] = {
+       0, 2000, 1333, 909, 743, 666, 500, 333, 166, 83, 55, 41, 20, 10, 5
+};
+
+short  ospeed;
+char   PC;
+
+/*
+ * Put the character string cp out, with padding.
+ * The number of affected lines is affcnt, and the routine
+ * used to output one character is outc.
+ */
+int
+tputs(const char *cp, int affcnt, int (*outc)(int))
+{
+       register int i = 0;
+       register int mspc10;
+
+       if (cp == 0)
+               return 1;
+
+       /*
+        * Convert the number representing the delay.
+        */
+       if (isdigit(*cp & 0377)) {
+               do
+                       i = i * 10 + *cp++ - '0';
+               while (isdigit(*cp & 0377));
+       }
+       i *= 10;
+       if (*cp == '.') {
+               cp++;
+               if (isdigit(*cp & 0377))
+                       i += *cp - '0';
+               /*
+                * Only one digit to the right of the decimal point.
+                */
+               while (isdigit(*cp & 0377))
+                       cp++;
+       }
+
+       /*
+        * If the delay is followed by a `*', then
+        * multiply by the affected lines count.
+        */
+       if (*cp == '*')
+               cp++, i *= affcnt;
+
+       /*
+        * The guts of the string.
+        */
+       while (*cp)
+               (*outc)(*cp++);
+
+       /*
+        * If no delay needed, or output speed is
+        * not comprehensible, then don't try to delay.
+        */
+       if (i == 0)
+               return 1;
+       if (ospeed <= 0 || ospeed >= (sizeof tmspc10 / sizeof tmspc10[0]))
+               return 1;
+
+       /*
+        * Round up by a half a character frame,
+        * and then do the delay.
+        * Too bad there are no user program accessible programmed delays.
+        * Transmitting pad characters slows many
+        * terminals down and also loads the system.
+        */
+       mspc10 = tmspc10[ospeed];
+       i += mspc10 / 2;
+       for (i /= mspc10; i > 0; i--)
+               (*outc)(PC);
+       return 1;
+}
diff --git a/libuxre/COPYING.LGPL b/libuxre/COPYING.LGPL
new file mode 100644 (file)
index 0000000..b1e3f5a
--- /dev/null
@@ -0,0 +1,504 @@
+                 GNU LESSER GENERAL PUBLIC LICENSE
+                      Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+                 GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/libuxre/Makefile b/libuxre/Makefile
new file mode 100644 (file)
index 0000000..46d7320
--- /dev/null
@@ -0,0 +1,12 @@
+CFLAGS = $(COPT) $(RPMCFLAGS) -I.
+OBJS = bracket.o _collelem.o _collmult.o regcomp.o regdfa.o regerror.o regexec.o regfree.o regnfa.o regparse.o stubs.o
+
+.c.o: ; $(CC) $(CFLAGS) -c $<
+
+all: libuxre.a
+
+libuxre.a: $(OBJS)
+       ar cr libuxre.a $(OBJS)
+
+clean:
+       rm -f libuxre.a $(OBJS) core
diff --git a/libuxre/NOTES b/libuxre/NOTES
new file mode 100644 (file)
index 0000000..19aedf1
--- /dev/null
@@ -0,0 +1,14 @@
+Notes for the modified 'UNIX(R) Regular Expression Library'
+============================================================
+
+The code this is based on was released by Caldera as 'osutils-0.1a'
+and is available at <http://unixtools.sourceforge.net/>. Notable
+changes include:
+
+- Support for multibyte characters was enabled again.
+- Support for traditional extended regular expression syntax was added.
+- Fix: With REG_ICASE, [B-z] matches 'A', 'a', and '[' according to
+  POSIX.2.
+- Some speed improvements.
+
+       Gunnar Ritter                                           9/22/03
diff --git a/libuxre/_collelem.c b/libuxre/_collelem.c
new file mode 100644 (file)
index 0000000..c5dbb05
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Changes by Gunnar Ritter, Freiburg i. Br., Germany, November 2002.
+ *
+ * Sccsid @(#)_collelem.c      1.4 (gritter) 10/18/03
+ */
+/*  UNIX(R) Regular Expresssion Library
+ *
+ *  Note: Code is released under the GNU LGPL
+ *
+ *  Copyright (C) 2001 Caldera International, Inc.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to:
+ *        Free Software Foundation, Inc.
+ *        59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*     #include "synonyms.h"   */
+#include "colldata.h"
+#include <stddef.h>
+
+#define CCE(p) ((const CollElem *)(p))
+#define CCM(p) ((const CollMult *)(p))
+
+LIBUXRE_STATIC const CollElem *
+libuxre_collelem(struct lc_collate *col, CollElem *spare, wchar_t wc)
+{
+       const char *tbl;
+       size_t hi, lo, cur;
+       const CollMult *cmp;
+       const CollElem *cep;
+       long diff;
+       int sz;
+
+       /*
+       * ELEM_ENCODED is returned when the collation is entirely
+       * based on the encoded value of the character.
+       */
+       if (col == 0 || col->flags & CHF_ENCODED
+               || (tbl = (const char *)col->maintbl) == 0)
+       {
+               return ELEM_ENCODED;
+       }
+       if ((wuchar_type)wc <= UCHAR_MAX)
+       {
+       indexed:;
+               cep = CCE(&tbl[(wuchar_type)wc * col->elemsize]);
+               if (cep->weight[0] == WGHT_SPECIAL)
+                       return ELEM_BADCHAR;
+               return cep;
+       }
+       if (col->flags & CHF_INDEXED)
+       {
+               if ((wuchar_type)wc >= col->nmain)
+                       return ELEM_BADCHAR;
+               goto indexed;
+       }
+       /*
+       * Binary search for a match.  Could speed up the search if
+       * some interpolation was used, but keep it simple for now.
+       * Note that this is actually a table of CollMult's.
+       *
+       * To save space in the file, sequences of similar elements
+       * are sometimes compressed into a single CollMult that
+       * describes many entries.  This is denoted by a subnbeg
+       * with the SUBN_SPECIAL bit set.  The rest of the bits give
+       * the range covered by this entry.
+       */
+       sz = col->elemsize + (sizeof(CollMult) - sizeof(CollElem));
+       tbl += (1 + UCHAR_MAX) * col->elemsize;
+       lo = 0;
+       hi = col->nmain - UCHAR_MAX;
+       while (lo < hi)
+       {
+               if ((cur = (hi + lo) >> 1) < lo)   /* hi+lo overflowed */
+                       cur |= ~(~(size_t)0 >> 1); /* lost high order bit */
+               cmp = CCM(&tbl[cur * sz]);
+               if ((diff = wc - cmp->ch) < 0)
+                       hi = cur;
+               else if (cmp->elem.subnbeg & SUBN_SPECIAL)
+               {
+                       if (diff > (long)(cmp->elem.subnbeg & ~SUBN_SPECIAL))
+                               lo = cur + 1;
+                       else /* create an entry from the sequence in spare */
+                       {
+                               spare->multbeg = cmp->elem.multbeg;
+                               spare->subnbeg = 0;
+                               spare->weight[0] = cmp->elem.weight[0] + diff;
+                               for (lo = 1; lo < col->nweight; lo++)
+                               {
+                                       wuchar_type w;
+
+                                       if ((w = cmp->elem.weight[lo])
+                                               == WGHT_SPECIAL)
+                                       {
+                                               w = spare->weight[0];
+                                       }
+                                       spare->weight[lo] = w;
+                               }
+                               return spare;
+                       }
+               }
+               else if (diff == 0)
+                       return &cmp->elem;
+               else
+                       lo = cur + 1;
+       }
+       return ELEM_BADCHAR;
+}
diff --git a/libuxre/_collmult.c b/libuxre/_collmult.c
new file mode 100644 (file)
index 0000000..7a199b3
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Changes by Gunnar Ritter, Freiburg i. Br., Germany, November 2002.
+ *
+ * Sccsid @(#)_collmult.c      1.4 (gritter) 9/22/03
+ */
+/*  UNIX(R) Regular Expresssion Library
+ *
+ *  Note: Code is released under the GNU LGPL
+ *
+ *  Copyright (C) 2001 Caldera International, Inc.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to:
+ *        Free Software Foundation, Inc.
+ *        59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*     #include "synonyms.h"   */
+#include "colldata.h"
+#include <stddef.h>
+
+#define CCM(p) ((const CollMult *)(p))
+
+LIBUXRE_STATIC const CollElem *
+libuxre_collmult(struct lc_collate *col, const CollElem *cep, wchar_t wc)
+{
+       const char *tbl;
+       size_t sz;
+       w_type ch;
+
+       if (col == 0 || cep->multbeg == 0
+               || (tbl = (const char *)col->multtbl) == 0)
+       {
+               return ELEM_BADCHAR;
+       }
+       sz = col->elemsize + (sizeof(CollMult) - sizeof(CollElem));
+       tbl += sz * cep->multbeg;
+       while ((ch = CCM(tbl)->ch) != wc)
+       {
+               if (ch == 0)
+                       return ELEM_BADCHAR;    /* end of list */
+               tbl += sz;
+       }
+       return &CCM(tbl)->elem;
+}
diff --git a/libuxre/bracket.c b/libuxre/bracket.c
new file mode 100644 (file)
index 0000000..bc31b23
--- /dev/null
@@ -0,0 +1,829 @@
+/*
+ * Changes by Gunnar Ritter, Freiburg i. Br., Germany, November 2002.
+ *
+ * Sccsid @(#)bracket.c        1.14 (gritter) 10/18/03
+ */
+/*  UNIX(R) Regular Expresssion Library
+ *
+ *  Note: Code is released under the GNU LGPL
+ *
+ *  Copyright (C) 2001 Caldera International, Inc.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to:
+ *        Free Software Foundation, Inc.
+ *        59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*     #include "synonyms.h"   */
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include "re.h"
+
+/*
+* Build and match the [...] part of REs.
+*
+* In general, each compiled bracket construct holds a set of mapped
+* wide character values and a set of character classifications.
+* The mapping applied (when the current LC_COLLATE is not CHF_ENCODED)
+* is the "basic" weight (cep->weight[0]); otherwise the actual wide
+* character is used.
+*
+* To support simplified range handling, this code assumes that a w_type,
+* a signed integer type, can hold all valid basic weight values (as well
+* as all wide character values for CHF_ENCODED locales) and that these
+* are all positive.  Negative values indicate error conditions (BKT_*);
+* zero (which must be the same as WGHT_IGNORE) indicates success, but
+* that the item installed is not a range endpoint.
+*/
+
+static int
+addwide(Bracket *bp, wchar_t ord)
+{
+       unsigned int nw;
+
+       if ((nw = bp->nwide) < NWIDE)
+               bp->wide[nw] = ord;
+       else
+       {
+               if (nw % NWIDE == 0 && (bp->exwide =
+                       realloc(bp->exwide, nw * sizeof(wchar_t))) == 0)
+               {
+                       return BKT_ESPACE;
+               }
+               nw -= NWIDE;
+               bp->exwide[nw] = ord;
+       }
+       bp->nwide++;
+       return 0;
+}
+
+#if USHRT_MAX == 65535 /* have 16 bits */
+#define PLIND(n)       ((n) >> 4)
+#define PLBIT(n)       (1 << ((n) & 0xf))
+#else
+#define PLIND(n)       ((n) / CHAR_BIT)
+#define PLBIT(n)       (1 << ((n) % CHAR_BIT))
+#endif
+
+#define RANGE  ((wchar_t)'-')  /* separates wide chars in ranges */
+
+static int
+addrange(Bracket *bp, wchar_t ord, w_type prev)
+{
+       int ret;
+
+       if (prev > 0 && prev != ord) /* try for range */
+       {
+               if (prev > ord)
+               {
+                       if (bp->flags & BKT_ODDRANGE)   /* prev only - done */
+                               return 0;
+                       else if ((bp->flags & BKT_BADRANGE) == 0)
+                               return BKT_ERANGE;
+               }
+               else
+               {
+                       if (++prev <= UCHAR_MAX) /* "prev" already there */
+                       {
+                               do
+                               {
+                                       bp->byte[PLIND(prev)] |= PLBIT(prev);
+                                       if (prev == ord)
+                                               return 0;
+                               } while (++prev <= UCHAR_MAX);
+                       }
+                       if ((ret = addwide(bp, prev)) != 0)
+                               return ret;
+                       if (++prev > ord)
+                               return 0;
+                       if (prev < ord && (ret = addwide(bp, RANGE)) != 0)
+                               return ret;
+                       return addwide(bp, ord);
+               }
+       }
+       if (ord <= UCHAR_MAX)
+       {
+               bp->byte[PLIND(ord)] |= PLBIT(ord);
+               return 0;
+       }
+       if (prev == ord) /* don't bother */
+               return 0;
+       return addwide(bp, ord);
+}
+
+static w_type
+place(Bracket *bp, wchar_t wc, w_type prev, int mb_cur_max)
+{
+       const CollElem *cep;
+       CollElem spare;
+       int ret;
+
+       if ((cep = libuxre_collelem(bp->col, &spare, wc)) != ELEM_ENCODED)
+       {
+               if (cep == ELEM_BADCHAR)
+                       return BKT_BADCHAR;
+               wc = cep->weight[0];
+       }
+       if ((ret = addrange(bp, wc, prev)) != 0)
+               return ret;
+       return wc;
+}
+
+#ifndef CHARCLASS_NAME_MAX
+#   define CHARCLASS_NAME_MAX  127
+#endif
+
+static w_type
+chcls(Bracket *bp, const unsigned char *s, int n)
+{
+       char clsstr[CHARCLASS_NAME_MAX + 1];
+       unsigned int nt;
+       wctype_t wct;
+
+       if (n > CHARCLASS_NAME_MAX)
+               return BKT_ECTYPE;
+       (void)memcpy(clsstr, s, n);
+       clsstr[n] = '\0';
+       if ((wct = wctype(clsstr)) == 0)
+               return BKT_ECTYPE;
+       if ((nt = bp->ntype) < NTYPE)
+               bp->type[nt] = wct;
+       else
+       {
+               if (nt % NTYPE == 0 && (bp->extype =
+                       realloc(bp->extype, nt * sizeof(wctype_t))) == 0)
+               {
+                       return BKT_ESPACE;
+               }
+               nt -= NTYPE;
+               bp->extype[nt] = wct;
+       }
+       bp->ntype++;
+       return 0; /* cannot be end point of a range */
+}
+
+       /*
+       * The purpose of mcce() and its Mcce structure is to locate
+       * the next full collation element from "wc" and "s".  It is
+       * called both at compile and execute time.  These two differ
+       * primarily in that at compile time there is an exact number
+       * of bytes to be consumed, while at execute time the longest
+       * valid collation element is to be found.
+       *
+       * When BKT_ONECASE is set, MCCEs become particularly messy.
+       * There is no guarantee that all possible combinations of
+       * upper/lower case are defined as MCCEs.  Thus, this code
+       * tries both lower- and uppercase (in that order) for each
+       * character than might be part of an MCCE.
+       */
+
+typedef struct
+{
+       const unsigned char     *max;   /* restriction by caller */
+       const unsigned char     *aft;   /* longest successful */
+       Bracket                 *bp;    /* readonly */
+       struct lc_collate       *col;   /* readonly */
+       const CollElem          *cep;   /* entry matching longest */
+       wchar_t                 ch;     /* initial character (if any) */
+       w_type                  wc;     /* character matching "aft" */
+} Mcce;
+
+static int
+mcce(Mcce *mcp, const CollElem *cep, const unsigned char *s, int mb_cur_max,
+               int compile_time)
+{
+       const CollElem *nxt;
+       CollElem spare;
+       w_type ch, wc;
+       int i;
+
+       /*
+       * Get next character.
+       */
+       if ((wc = mcp->ch) != '\0')
+       {
+               mcp->ch = '\0';
+       }
+       else if (ISONEBYTE(wc = *s++))
+       {
+               if (wc == '\0')
+                       return 0;
+       }
+       else if ((i = libuxre_mb2wc(&wc, s)) > 0)
+       {
+               s += i;
+               if (mcp->max != 0 && s > mcp->max)
+                       return 0;
+       }
+       else if (i < 0)
+               return BKT_ILLSEQ;
+       /*
+       * Try out the this character as part of an MCCE.
+       * If BKT_ONECASE is set, this code tries both the lower- and
+       * uppercase version, continuing if it matches so far.
+       */
+       ch = wc;
+       if (mcp->bp->flags & BKT_ONECASE)
+       {
+               if ((wc = to_lower(wc)) == ch)
+                       ch = to_upper(wc);
+       }
+       for (;;) /* at most twice */
+       {
+               if (cep == ELEM_BADCHAR) /* first character */
+               {
+                       if ((nxt = libuxre_collelem(mcp->col, &spare, wc))
+                               == ELEM_ENCODED
+                               || (mcp->col->flags & CHF_MULTICH) == 0
+                               || s == mcp->max)
+                       {
+                               mcp->aft = s;
+                               mcp->cep = nxt;
+                               mcp->wc = wc;
+                               break;
+                       }
+               }
+               else
+               {
+                       nxt = libuxre_collmult(mcp->col, cep, wc);
+               } 
+               if (nxt != ELEM_BADCHAR)
+               {
+                       /*
+                       * Okay so far.  Record this collating element
+                       * if it's really one (not WGHT_IGNORE) and
+                       * we've reached a new high point or it's the
+                       * first match.
+                       *
+                       * If there's a possibility for more, call mcce()
+                       * recursively for the subsequent characters.
+                       */
+                       if (nxt->weight[0] != WGHT_IGNORE
+                               && (mcp->aft < s || mcp->cep == ELEM_BADCHAR))
+                       {
+                               mcp->aft = s;
+                               mcp->cep = nxt;
+                               mcp->wc = wc;
+                       }
+                       if (nxt->multbeg != 0
+                               && (mcp->max == 0 || s < mcp->max))
+                       {
+                               if ((i = mcce(mcp, nxt, s, mb_cur_max,
+                                               compile_time)) != 0)
+                                       return i;
+                       }
+               }
+               if (wc == ch)
+                       break;
+               wc = ch;
+       }
+       return 0;
+}
+
+static w_type
+eqcls(Bracket *bp, const unsigned char *s, int n, w_type prev, int mb_cur_max)
+{
+       w_type last;
+       Mcce mcbuf;
+       int err;
+
+       mcbuf.max = &s[n];
+       mcbuf.aft = &s[0];
+       mcbuf.bp = bp;
+       mcbuf.col = bp->col;
+       mcbuf.cep = ELEM_BADCHAR;
+       mcbuf.ch = '\0';
+       if ((err = mcce(&mcbuf, ELEM_BADCHAR, s, mb_cur_max, 1)) != 0)
+               return err;
+       if (mcbuf.cep == ELEM_BADCHAR || mcbuf.aft != mcbuf.max)
+               return BKT_EEQUIV;
+       last = mcbuf.wc;
+       if (mcbuf.cep != ELEM_ENCODED && mcbuf.col->nweight > 1)
+       {
+               const CollElem *cep;
+
+               /*
+               * The first and last weight[0] values for equivalence
+               * classes are stuffed into the terminator for the
+               * multiple character lists.  If these values are
+               * scattered (elements that are not part of this
+               * equivalence class have weight[0] values between the
+               * two end points), then SUBN_SPECIAL is placed in
+               * this terminator.  Note that weight[1] of the
+               * terminator must be other than WGHT_IGNORE, too.
+               */
+               last = mcbuf.cep->weight[0];
+               if ((cep = libuxre_collmult(bp->col, mcbuf.cep, 0))
+                               != ELEM_BADCHAR
+                       && cep->weight[1] != WGHT_IGNORE)
+               {
+                       last = cep->weight[1];
+                       if (cep->subnbeg == SUBN_SPECIAL)
+                       {
+                               unsigned int nq;
+
+                               /*
+                               * Permit ranges up to the first and
+                               * after the last.
+                               */
+                               if (prev > 0 && prev != cep->weight[0]
+                                       && (prev = addrange(bp,
+                                               cep->weight[0], prev)) != 0)
+                               {
+                                       return prev;
+                               }
+                               /*
+                               * Record the equivalence class by storing
+                               * the primary weight.
+                               */
+                               if ((nq = bp->nquiv) < NQUIV)
+                                       bp->quiv[nq] = mcbuf.cep->weight[1];
+                               else
+                               {
+                                       if (nq % NQUIV == 0 && (bp->exquiv = 
+                                               realloc(bp->exquiv,
+                                                       nq * sizeof(wuchar_type)))
+                                               == 0)
+                                       {
+                                               return REG_ESPACE;
+                                       }
+                                       nq -= NQUIV;
+                                       bp->exquiv[nq] = mcbuf.cep->weight[1];
+                               }
+                               bp->nquiv++;
+                               return last;
+                       }
+                       mcbuf.cep = cep;
+               }
+               mcbuf.wc = mcbuf.cep->weight[0];
+       }
+       /*
+       * Determine range, if any, to install.
+       *
+       * If there's a pending low (prev > 0), then try to use it.
+       *
+       * Otherwise, try to use mcbuf.wc as the low end of the range.
+       * Since addrange() assumes that the low point has already been
+       * placed, we try to fool it by using a prev of one less than
+       * mcbuf.wc.  But, if that value would not look like a valid
+       * low point of a range, we have to explicitly place mcbuf.wc.
+       */
+       if (prev <= 0 && (prev = mcbuf.wc - 1) <= 0)
+       {
+               if ((prev = addrange(bp, mcbuf.wc, 0)) != 0)
+                       return prev;
+       }
+       if ((mcbuf.wc = addrange(bp, last, prev)) != 0)
+               return mcbuf.wc;
+       return last;
+}
+
+static w_type
+clsym(Bracket *bp, const unsigned char *s, int n, w_type prev, int mb_cur_max)
+{
+       Mcce mcbuf;
+       int err;
+
+       mcbuf.max = &s[n];
+       mcbuf.aft = &s[0];
+       mcbuf.bp = bp;
+       mcbuf.col = bp->col;
+       mcbuf.cep = ELEM_BADCHAR;
+       mcbuf.ch = '\0';
+       if ((err = mcce(&mcbuf, ELEM_BADCHAR, s, mb_cur_max, 1)) != 0)
+               return err;
+       if (mcbuf.cep == ELEM_BADCHAR || mcbuf.aft != mcbuf.max)
+               return BKT_ECOLLATE;
+       if (mcbuf.cep != ELEM_ENCODED)
+               mcbuf.wc = mcbuf.cep->weight[0];
+       if ((err = addrange(bp, mcbuf.wc, prev)) != 0)
+               return err;
+       return mcbuf.wc;
+}
+
+       /*
+       * Scans the rest of a bracket construction within a regular
+       * expression and fills in a description for it.
+       * The leading [ and the optional set complement indicator
+       * were handled already by the caller.
+       * Returns:
+       *       <0 error (a BKT_* value)
+       *       >0 success; equals how many bytes were scanned.
+       */
+LIBUXRE_STATIC int
+libuxre_bktmbcomp(Bracket *bp, const unsigned char *pat0,
+               int flags, int mb_cur_max)
+{
+       static const Bracket zero = {0};
+       const unsigned char *pat = pat0;
+       struct lc_collate *savecol;
+       w_type n, wc, prev = 0;
+
+       /*
+       * Set represented set to empty.  Easiest to copy an empty
+       * version over the caller's, (re)setting col and flags.
+       */
+       savecol = bp->col;
+       *bp = zero;
+       bp->col = savecol;
+       bp->flags = flags
+               & (BKT_NEGATED | BKT_ONECASE | BKT_NOTNL | BKT_BADRANGE |
+                               BKT_ODDRANGE);
+       /*
+       * Handle optional "empty" brackets; typically only used
+       * in combination with BKT_QUOTE or BKT_ESCAPE.
+       */
+       if ((wc = *pat) == ']' && (flags & BKT_EMPTY) != 0)
+               return 1;
+       /*
+       * Populate *bp.
+       */
+       for (;; prev = n)
+       {
+               switch (wc)
+               {
+               case '\0':
+               ebrack:;
+                       n = BKT_EBRACK;
+                       goto err;
+               case '\n':
+                       if (flags & BKT_NLBAD)
+                               goto ebrack;
+                       goto regular;
+               case '/':
+                       if (flags & BKT_SLASHBAD)
+                               goto ebrack;
+                       goto regular;
+               case '\\':
+                       if ((flags & (BKT_ESCAPE | BKT_QUOTE
+                               | BKT_ESCNL | BKT_ESCSEQ)) == 0)
+                       {
+                               goto regular;
+                       }
+                       switch (wc = *++pat)
+                       {
+                       default:
+                       noesc:;
+                               if ((flags & BKT_ESCAPE) == 0)
+                               {
+                                       wc = '\\';
+                                       pat--;
+                               }
+                               break;
+                       case '\\':
+                       case ']':
+                       case '-':
+                       case '^':
+                               if ((flags & BKT_QUOTE) == 0)
+                                       goto noesc;
+                               break;
+                       case 'a':
+                               if ((flags & BKT_ESCSEQ) == 0 ||
+                                               (flags & BKT_OLDESC))
+                                       goto noesc;
+                               wc = '\a';
+                               break;
+                       case 'b':
+                               if ((flags & BKT_ESCSEQ) == 0)
+                                       goto noesc;
+                               wc = '\b';
+                               break;
+                       case 'f':
+                               if ((flags & BKT_ESCSEQ) == 0)
+                                       goto noesc;
+                               wc = '\f';
+                               break;
+                       case 'n':
+                               if ((flags & (BKT_ESCSEQ | BKT_ESCNL)) == 0)
+                                       goto noesc;
+                               wc = '\n';
+                               break;
+                       case 'r':
+                               if ((flags & BKT_ESCSEQ) == 0)
+                                       goto noesc;
+                               wc = '\r';
+                               break;
+                       case 't':
+                               if ((flags & BKT_ESCSEQ) == 0)
+                                       goto noesc;
+                               wc = '\t';
+                               break;
+                       case 'v':
+                               if ((flags & BKT_ESCSEQ) == 0 ||
+                                               (flags & BKT_OLDESC))
+                                       goto noesc;
+                               wc = '\v';
+                               break;
+                       case 'x':
+                               if ((flags & BKT_ESCSEQ) == 0 ||
+                                               (flags & BKT_OLDESC))
+                                       goto noesc;
+                               if (!isxdigit(wc = *++pat))
+                               {
+                                       pat--;
+                                       goto noesc;
+                               }
+                               /*
+                               * Take as many hex digits as possible,
+                               * ignoring overflows.
+                               * Any positive result is okay.
+                               */
+                               n = 0;
+                               do
+                               {
+                                       if (isdigit(wc))
+                                               wc -= '0';
+                                       else if (isupper(wc))
+                                               wc -= 'A' + 10;
+                                       else
+                                               wc -= 'a' + 10;
+                                       n <<= 4;
+                                       n |= wc;
+                               } while (isxdigit(wc = *++pat));
+                               pat--;
+                               if ((wc = n) <= 0)
+                               {
+                                       n = BKT_BADESC;
+                                       goto err;
+                               }
+                               break;
+                       case '0':
+                       case '1':
+                       case '2':
+                       case '3':
+                       case '4':
+                       case '5':
+                       case '6':
+                       case '7':
+                       case '8':
+                       case '9':
+                               if ((flags & BKT_ESCSEQ) == 0 ||
+                                               (flags & BKT_OLDESC))
+                                       goto noesc;
+                               /*
+                               * For compatibility (w/awk),
+                               * permit "octal" 8 and 9.
+                               */
+                               n = wc - '0';
+                               if ((wc = *++pat) >= '0' && wc <= '9')
+                               {
+                                       n <<= 3;
+                                       n += wc - '0';
+                                       if ((wc = *++pat) >= '0' && wc <= '9')
+                                       {
+                                               n <<= 3;
+                                               n += wc - '0';
+                                       }
+                               }
+                               pat--;
+                               if ((wc = n) <= 0)
+                               {
+                                       n = BKT_BADESC;
+                                       goto err;
+                               }
+                               break;
+                       }
+                       goto regular;
+               case '[':
+                       if (((wc = *++pat) == ':' || wc == '=' || wc == '.') &&
+                                       (flags & BKT_NOI18N) == 0)
+                       {
+                               n = 0;
+                               while (*++pat != wc || pat[1] != ']')
+                               {
+                                       if (*pat == '\0')
+                                       {
+                                       badpat:;
+                                               n = BKT_BADPAT;
+                                               goto err;
+                                       }
+                                       else if (*pat == '/')
+                                       {
+                                               if (flags & BKT_SLASHBAD)
+                                                       goto badpat;
+                                       }
+                                       else if (*pat == '\n')
+                                       {
+                                               if (flags & BKT_NLBAD)
+                                                       goto badpat;
+                                       }
+                                       n++;
+                               }
+                               if (n == 0)
+                               {
+                                       n = BKT_EMPTYSUBBKT;
+                                       goto err;
+                               }
+                               if (wc == ':')
+                                       n = chcls(bp, &pat[-n], n);
+                               else if (wc == '=')
+                                       n = eqcls(bp, &pat[-n], n, prev,
+                                                       mb_cur_max);
+                               else /* wc == '.' */
+                                       n = clsym(bp, &pat[-n], n, prev,
+                                                       mb_cur_max);
+                               pat++;
+                               break;
+                       }
+                       wc = '[';
+                       pat--;
+                       goto regular;
+               default:
+                       if (!ISONEBYTE(wc) &&
+                                       (n = libuxre_mb2wc(&wc, pat + 1)) > 0)
+                               pat += n;
+               regular:;
+                       n = place(bp, wc, prev, mb_cur_max);
+                       break;
+               }
+               if (n < 0) {
+                       n = BKT_ILLSEQ;
+                       goto err;
+               }
+               if ((wc = *++pat) == ']')
+                       break;
+               if (wc == '-' && n != 0)
+               {
+                       if (prev == 0 || (flags & BKT_SEPRANGE) == 0)
+                       {
+                               if ((wc = *++pat) != ']')
+                                       continue; /* valid range */
+                               wc = '-';
+                               pat--;
+                       }
+               }
+               n = 0;  /* no range this time */
+       }
+       return pat - pat0 + 1;
+err:;
+       libuxre_bktfree(bp);
+       return n;
+}
+
+LIBUXRE_STATIC void
+libuxre_bktfree(Bracket *bp)
+{
+       if (bp->extype != 0)
+               free(bp->extype);
+       if (bp->exquiv != 0)
+               free(bp->exquiv);
+       if (bp->exwide != 0)
+               free(bp->exwide);
+}
+
+LIBUXRE_STATIC int
+libuxre_bktmbexec(Bracket *bp, wchar_t wc,
+               const unsigned char *str, int mb_cur_max)
+{
+       unsigned int i;
+       wchar_t lc, uc;
+       Mcce mcbuf;
+
+       mcbuf.aft = str; /* in case of match in character classes */
+       mcbuf.ch = wc;
+       /*
+       * First: check the single wc against any character classes.
+       * Since multiple character collating elements are not part
+       * of this world, they don't apply here.
+       */
+       if ((i = bp->ntype) != 0)
+       {
+               wctype_t *wctp = &bp->type[0];
+
+               if (bp->flags & BKT_ONECASE)
+               {
+                       if ((wc = to_lower(wc)) == mcbuf.ch)
+                               mcbuf.ch = to_upper(wc);
+               }
+               for (;;)
+               {
+                       if (iswctype(mb_cur_max==1?btowc(wc):wc, *wctp))
+                               goto match;
+                       if (wc != mcbuf.ch &&
+                           iswctype(mb_cur_max==1?btowc(mcbuf.ch):mcbuf.ch,
+                                   *wctp))
+                               goto match;
+                       if (--i == 0)
+                               break;
+                       if (++wctp == &bp->type[NTYPE])
+                               wctp = &bp->extype[0];
+               }
+       }
+       /*
+       * The main match is determined by the weight[0] value
+       * of the character (or characters, if the input can be
+       * taken as a multiple character collating element).
+       */
+       mcbuf.max = 0;
+       mcbuf.bp = bp;
+       mcbuf.col = bp->col;
+       mcbuf.cep = ELEM_BADCHAR;
+       mcce(&mcbuf, ELEM_BADCHAR, str, mb_cur_max, 0);
+       if (mcbuf.cep == ELEM_BADCHAR)
+               return -1;      /* never matches */
+       if (mcbuf.cep != ELEM_ENCODED)
+               mcbuf.wc = mcbuf.cep->weight[0];
+       /*
+       * POSIX.2 demands that both a character and its case counterpart
+       * can match if REG_ICASE is set. This means that [B-z] matches
+       * 'A', 'a', and '['.
+       */
+       if (bp->flags & BKT_ONECASE)
+       {
+               lc = to_lower(mcbuf.wc);
+               uc = to_upper(mcbuf.wc);
+       }
+       else
+               lc = uc = mcbuf.wc;
+       /*
+       * See if it's in the set.  Note that the list of true wide
+       * character values has explicit ranges.
+       */
+       if (mcbuf.wc <= UCHAR_MAX)
+       {
+               if (bp->byte[PLIND(lc)] & PLBIT(lc))
+                       goto match;
+               if (lc != uc && (bp->byte[PLIND(uc)] & PLBIT(uc)))
+                       goto match;
+       }
+       else if ((i = bp->nwide) != 0)
+       {
+               wchar_t *wcp = &bp->wide[0];
+               long lcmp, ucmp;
+
+               for (;;)
+               {
+                       if ((lcmp = lc - *wcp) == 0)
+                               goto match;
+                       ucmp = uc - *wcp;
+                       if (lc != uc && ucmp == 0)
+                               goto match;
+                       if (--i == 0)
+                               break;
+                       if (++wcp == &bp->wide[NWIDE])
+                               wcp = &bp->exwide[0];
+                       if (*wcp == RANGE)
+                       {
+                               if (++wcp == &bp->wide[NWIDE])
+                                       wcp = &bp->exwide[0];
+                               if (lcmp > 0 && lc <= *wcp)
+                                       goto match;
+                               if (lc != uc && ucmp > 0 && uc < *wcp)
+                                       goto match;
+                               if ((i -= 2) == 0)
+                                       break;
+                               if (++wcp == &bp->wide[NWIDE])
+                                       wcp = &bp->exwide[0];
+                       }
+               }
+       }
+       /*
+       * The last chance for a match is if an equivalence class
+       * was specified for which the primary weights are scattered
+       * through the weight[0]s.
+       */
+       if ((i = bp->nquiv) != 0 && mcbuf.cep != ELEM_ENCODED)
+       {
+               wuchar_type *wucp = &bp->quiv[0];
+
+               mcbuf.wc = mcbuf.cep->weight[1];
+               for (;;)
+               {
+                       if (mcbuf.wc == *wucp)
+                               goto match;
+                       if (--i == 0)
+                               break;
+                       if (++wucp == &bp->quiv[NQUIV])
+                               wucp = &bp->exquiv[0];
+               }
+       }
+       /*
+       * Only here when no match against the set was found.
+       * One final special case w/r/t newline.
+       */
+       if (bp->flags & BKT_NEGATED)
+       {
+               if (wc != '\n' || (bp->flags & BKT_NOTNL) == 0)
+                       return mcbuf.aft - str;
+       }
+       return -1;
+match:;
+       /*
+       * Only here when a match against the described set is found.
+       */
+       if (bp->flags & BKT_NEGATED)
+               return -1;
+       return mcbuf.aft - str;
+}
diff --git a/libuxre/colldata.h b/libuxre/colldata.h
new file mode 100644 (file)
index 0000000..e3a3784
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * Changes by Gunnar Ritter, Freiburg i. Br., Germany, November 2002.
+ *
+ * Sccsid @(#)colldata.h       1.5 (gritter) 5/1/04
+ */
+/*  UNIX(R) Regular Expresssion Library
+ *
+ *  Note: Code is released under the GNU LGPL
+ *
+ *  Copyright (C) 2001 Caldera International, Inc.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to:
+ *        Free Software Foundation, Inc.
+ *        59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef        LIBUXRE_COLLDATA_H
+#define        LIBUXRE_COLLDATA_H
+
+typedef struct
+{
+       long    coll_offst;     /* offset to xnd table */
+       long    sub_cnt;        /* length of subnd table */
+       long    sub_offst;      /* offset to subnd table */
+       long    str_offst;      /* offset to strings for subnd table */
+       long    flags;          /* nonzero if reg.exp. used */
+} hd;
+
+typedef struct
+{
+       unsigned char   ch;     /* character or number of followers */
+       unsigned char   pwt;    /* primary weight */
+       unsigned char   swt;    /* secondary weight */
+       unsigned char   ns;     /* index of follower state list */
+} xnd;
+
+typedef struct
+{
+       char    *exp;   /* expression to be replaced */
+       long    explen; /* length of expression */
+       char    *repl;  /* replacement string */
+} subnd;
+
+/*----------------------------------*/
+
+#include <wcharm.h>
+#include <limits.h>
+/*     #include <stdlock.h>    */
+
+/*
+* Structure of a collation file:
+*  1. CollHead (maintbl is 0 if CHF_ENCODED)
+*   if !CHF_ENCODED then
+*    2. CollElem[bytes] (256 for 8 bit bytes)
+*    3. if CHF_INDEXED then
+*       CollElem[wides] (nmain-256 for 8 bit bytes)
+*      else
+*       CollMult[wides]
+*    4. CollMult[*] (none if multtbl is 0)
+*    5. wuchar_type[*] (none if repltbl is 0)
+*    6. CollSubn[*] (none if subntbl is 0)
+*    7. strings (first is pathname for .so if CHF_DYNAMIC)
+*
+* The actual location of parts 2 through 7 is not important.
+*
+* The main table is in encoded value order.
+*
+* All indeces/offsets must be nonzero to be effective; zero is reserved
+* to indicate no-such-entry.  This implies either that an unused initial
+* entry is placed in each of (4) through (7), or that the "start offset"
+* given by the header is artificially pushed back by an entry size.
+*
+* Note that if CHF_ENCODED is not set, then nweight must be positive.
+*
+* If an element can begin a multiple character element, it contains a
+* nonzero multbeg which is the initial index into (4) for its list;
+* the list is terminated by a CollMult with a ch of zero.
+*
+* If there are elements with the same primary weight (weight[1]), then
+* for each such element, it must have a CollMult list.  The CollMult
+* that terminates the list (ch==0) notes the lowest and highest basic
+* weights for those elements with that same primary weight value
+* respectively in weight[0] and weight[1].  If there are some basic
+* weights between these values that do not have the same primary
+* weight--are not in the equivalence class--then the terminator also
+* has a SUBN_SPECIAL mark.  Note that this list terminator should be
+* shared when the elements are not multiple character collating
+* elements because they wouldn't otherwise have a CollMult list.
+*
+* WGHT_IGNORE is used to denote ignored collating elements for a
+* particular collation ordering pass.  All main table entries other
+* than for '\0' will have a non-WGHT_IGNORE weight[0].  However, it is
+* possible for a CollMult entries from (4) to have a WGHT_IGNORE
+* weight[0]:  If, for example, "xyz" is a multiple character collating
+* element, but "xy" is not, then the CollMult for "y" will have a
+* WGHT_IGNORE weight[0].  Also, WGHT_IGNORE is used to terminate each
+* list of replacement weights.
+*
+* Within (3), it is possible to describe a sequence of unremarkable
+* collating elements with a single CollMult entry.  If the SUBN_SPECIAL
+* bit is set, the rest of subnbeg represents the number of collating
+* elements covered by this entry.  The weight[0] values are determined
+* by adding the difference between the encoded value and the entry's ch
+* value to the entry's weight[0].  This value is then substituted for
+* any weight[n], n>0 that has only the WGHT_SPECIAL bit set. libuxre_collelem()
+* hides any match to such an entry by filling in a "spare" CollElem.
+*
+* If there are substitution strings, then for each character that begins
+* a string, it has a nonzero subnbeg which is similarly the initial
+* index into (6).  The indeces in (6) refer to offsets within (7).
+*/
+
+#define TOPBIT(t)      (((t)1) << (sizeof(t) * CHAR_BIT - 1))
+
+#define CHF_ENCODED    0x1     /* collation by encoded values only */
+#define CHF_INDEXED    0x2     /* main table indexed by encoded values */
+#define CHF_MULTICH    0x4     /* a multiple char. coll. elem. exists */
+#define CHF_DYNAMIC    0x8     /* shared object has collation functions */
+
+#define CWF_BACKWARD   0x1     /* reversed ordering for this weight */
+#define CWF_POSITION   0x2     /* weight takes position into account */
+
+#define CLVERS         1       /* most recent version */
+
+#define WGHT_IGNORE    0       /* ignore this collating element */
+#define WGHT_SPECIAL   TOPBIT(wuchar_type)
+#define SUBN_SPECIAL   TOPBIT(unsigned short)
+
+#ifndef        COLL_WEIGHTS_MAX
+#define        COLL_WEIGHTS_MAX        1
+#endif
+
+typedef struct
+{
+       unsigned long   maintbl;        /* start of main table */
+       unsigned long   multtbl;        /* start of multi-char table */
+       unsigned long   repltbl;        /* start of replacement weights */
+       unsigned long   subntbl;        /* start of substitutions */
+       unsigned long   strstbl;        /* start of sub. strings */
+       unsigned long   nmain;          /* # entries in main table */
+       unsigned short  flags;          /* CHF_* bits */
+       unsigned short  version;        /* handle future changes */
+       unsigned char   elemsize;       /* # bytes/element (w/padding) */
+       unsigned char   nweight;        /* # weights/element */
+       unsigned char   order[COLL_WEIGHTS_MAX]; /* CWF_* bits/weight */
+} CollHead;
+
+typedef struct
+{
+       unsigned short  multbeg;        /* start of multi-chars */
+       unsigned short  subnbeg;        /* start of substitutions */
+       wuchar_type     weight[COLL_WEIGHTS_MAX];
+} CollElem;
+
+typedef struct
+{
+       wchar_t         ch;     /* "this" character (of sequence) */
+       CollElem        elem;   /* its full information */
+} CollMult;
+
+typedef struct
+{
+       unsigned short  strbeg;         /* start of match string */
+       unsigned short  length;         /* length of match string */
+       unsigned short  repbeg;         /* start of replacement */
+} CollSubn;
+
+struct lc_collate
+{
+       const unsigned char     *strstbl;
+       const wuchar_type       *repltbl;
+       const CollElem          *maintbl;
+       const CollMult          *multtbl;
+       const CollSubn          *subntbl;
+#ifdef DSHLIB
+       void    *handle;
+       void    (*done)(struct lc_collate *);
+       int     (*strc)(struct lc_collate *, const char *, const char *);
+       int     (*wcsc)(struct lc_collate *, const wchar_t *, const wchar_t *);
+       size_t  (*strx)(struct lc_collate *, char *, const char *, size_t);
+       size_t  (*wcsx)(struct lc_collate *, wchar_t *, const wchar_t *, size_t);
+#endif
+       const char              *mapobj;
+       size_t                  mapsize;
+       unsigned long           nmain;
+       short                   nuse;
+       unsigned short          flags;
+       unsigned char           elemsize;
+       unsigned char           nweight;
+       unsigned char           order[COLL_WEIGHTS_MAX];
+};
+
+#define ELEM_BADCHAR   ((CollElem *)0)
+#define ELEM_ENCODED   ((CollElem *)-1)
+
+/*
+LIBUXRE_STATIC int     libuxre_old_collate(struct lc_collate *);
+LIBUXRE_STATIC int     libuxre_strqcoll(struct lc_collate *, const char *,
+                               const char *);
+LIBUXRE_STATIC int     libuxre_wcsqcoll(struct lc_collate *, const wchar_t *,
+                               const wchar_t *);
+*/
+extern struct lc_collate *libuxre_lc_collate(struct lc_collate *);
+LIBUXRE_STATIC const CollElem  *libuxre_collelem(struct lc_collate *,
+                                       CollElem *, wchar_t);
+LIBUXRE_STATIC const CollElem  *libuxre_collmult(struct lc_collate *,
+                                       const CollElem *, wchar_t);
+/*
+LIBUXRE_STATIC const CollElem  *libuxre_collmbs(struct lc_collate *,
+                                       CollElem *, const unsigned char **);
+LIBUXRE_STATIC const CollElem  *libuxre_collwcs(struct lc_collate *,
+                                       CollElem *, const wchar_t **);
+*/
+
+#endif /* !LIBUXRE_COLLDATA_H */
diff --git a/libuxre/onefile.c b/libuxre/onefile.c
new file mode 100644 (file)
index 0000000..78f22a0
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Changes by Gunnar Ritter, Freiburg i. Br., Germany, November 2002.
+ *
+ * Sccsid @(#)onefile.c        1.1 (gritter) 9/22/03
+ */
+/*  UNIX(R) Regular Expresssion Library
+ *
+ *  Note: Code is released under the GNU LGPL
+ *
+ *  Copyright (C) 2001 Caldera International, Inc.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to:
+ *        Free Software Foundation, Inc.
+ *        59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define        LIBUXRE_STATIC  static
+
+#include "_collelem.c"
+#include "_collmult.c"
+#include "stubs.c"
+#include "bracket.c"
+#include "regdfa.c"
+#include "regnfa.c"
+#include "regparse.c"
+#include "regcomp.c"
+#include "regexec.c"
diff --git a/libuxre/re.h b/libuxre/re.h
new file mode 100644 (file)
index 0000000..2738a05
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * Changes by Gunnar Ritter, Freiburg i. Br., Germany, November 2002.
+ *
+ * Sccsid @(#)re.h     1.15 (gritter) 2/6/05
+ */
+/*  UNIX(R) Regular Expresssion Library
+ *
+ *  Note: Code is released under the GNU LGPL
+ *
+ *  Copyright (C) 2001 Caldera International, Inc.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to:
+ *        Free Software Foundation, Inc.
+ *        59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef        LIBUXRE_RE_H
+#define        LIBUXRE_RE_H
+
+       /*
+       * Maps safe external tag to internal one
+       */
+#define re_coll_       lc_collate      /* <regex.h> */
+/*     #define __fnm_collate   lc_collate      */      /* <fnmatch.h> */
+
+#include <limits.h>
+#include <regex.h>
+/*     #include <fnmatch.h>    */
+#include <colldata.h>
+
+#define NBSHT  (sizeof(unsigned short) * CHAR_BIT)
+#define NBYTE  (((1 << CHAR_BIT) + NBSHT - 1) / NBSHT)
+#define NTYPE  4
+#define NWIDE  32
+#define NQUIV  4
+
+typedef struct
+{
+       struct lc_collate       *col;   /* only member set by caller */
+       wctype_t                *extype;
+       wuchar_type             *exquiv;
+       wchar_t                 *exwide;
+       wctype_t                type[NTYPE];
+       wuchar_type             quiv[NQUIV];
+       wchar_t                 wide[NWIDE];
+       unsigned short          byte[NBYTE];
+       unsigned short          ntype;
+       unsigned short          nquiv;
+       unsigned short          nwide;
+       unsigned int            flags;
+} Bracket;
+
+#define BKT_NEGATED    0x001   /* complemented set */
+#define BKT_ONECASE    0x002   /* uppercase same as lowercase */
+#define BKT_NOTNL      0x004   /* do not match newline when BKT_NEGATED */
+#define BKT_BADRANGE   0x008   /* accept [m-a] ranges as [ma] */
+#define BKT_SEPRANGE   0x010   /* disallow [a-m-z] style ranges */
+#define BKT_NLBAD      0x020   /* newline disallowed */
+#define BKT_SLASHBAD   0x040   /* slash disallowed (for pathnames) */
+#define BKT_EMPTY      0x080   /* take leading ] is end (empty set) */
+#define BKT_ESCAPE     0x100   /* allow \ as quote for next anything */
+#define BKT_QUOTE      0x200   /* allow \ as quote for \\, \^, \- or \] */
+#define BKT_ESCNL      0x400   /* take \n as the newline character */
+#define BKT_ESCSEQ     0x800   /* otherwise, take \ as in C escapes */
+#define        BKT_ODDRANGE    0x1000  /* oawk oddity: [m-a] means [m] */
+#define        BKT_NOI18N      0x2000  /* disable [::] [==] [..] */
+#define        BKT_OLDESC      0x4000  /* enable \b \f \n \r \t only */
+
+       /*
+       * These error returns for libuxre_bktmbcomp() are directly tied to
+       * the error returns for regcomp() for convenience.
+       */
+#define BKT_BADPAT     (-REG_BADPAT)
+#define BKT_ECOLLATE   (-REG_ECOLLATE)
+#define BKT_ECTYPE     (-REG_ECTYPE)
+#define BKT_EEQUIV     (-REG_EEQUIV)
+#define BKT_BADCHAR    (-REG_EBKTCHAR)
+#define BKT_EBRACK     (-REG_EBRACK)
+#define BKT_EMPTYSUBBKT        (-REG_EMPTYSUBBKT)
+#define BKT_ERANGE     (-REG_ERANGE)
+#define BKT_ESPACE     (-REG_ESPACE)
+#define BKT_BADESC     (-REG_BADESC)
+#define        BKT_ILLSEQ      (-REG_ILLSEQ)
+
+       /*
+       * These must be distinct from the flags in <fnmatch.h>.
+       */
+#define FNM_COLLATE    0x2000  /* have collation information */
+#define FNM_CURRENT    0x4000  /* have full-sized fnm_t structure */
+
+       /*
+       * These must be distinct from the flags in <regex.h>.
+       */
+#define REG_NFA                0x20000000
+#define REG_DFA                0x40000000
+#define REG_GOTBKT     0x80000000
+
+#define BRACE_INF      USHRT_MAX
+#define BRACE_MAX      5100    /* arbitrary number < SHRT_MAX */
+#define BRACE_DFAMAX   255     /* max amount for r.e. duplication */
+
+typedef union  /* extra info always kept for some tokens/nodes */
+{
+       Bracket         *bkt;   /* ROP_BKT */
+       size_t          sub;    /* ROP_LP (ROP_RP), ROP_REF */
+       unsigned short  num[2]; /* ROP_BRACE: num[0]=low, num[1]=high */
+} Info;
+
+typedef struct /* lexical context while parsing */
+{
+       Info                    info;
+       const unsigned char     *pat;
+       unsigned char           *clist;
+       struct lc_collate       *col;
+       unsigned long           flags;
+       w_type                  tok;
+       size_t                  maxref;
+       size_t                  nleft;
+       size_t                  nright;
+       size_t                  nclist;
+       int                     bktflags;
+       int                     err;
+       int                     mb_cur_max;
+} Lex;
+
+typedef struct t_tree  Tree;   /* RE parse tree node */
+struct t_tree
+{
+       union
+       {
+               Tree    *ptr;   /* unary & binary nodes */
+               size_t  pos;    /* position for DFA leaves */
+       } left;
+       union
+       {
+               Tree    *ptr;   /* binary nodes */
+               Info    info;
+       } right;
+       Tree            *parent;
+       w_type          op;     /* positive => char. to match */
+};
+
+typedef struct re_dfa_ Dfa;    /* DFA engine description */
+typedef struct re_nfa_ Nfa;    /* NFA engine description */
+
+typedef struct
+{
+       const unsigned char     *str;
+       regmatch_t              *match;
+       size_t                  nmatch;
+       unsigned long           flags;
+       int                     mb_cur_max;
+} Exec;
+
+       /*
+       * Regular expression operators.  Some only used internally.
+       * All are negative, to distinguish them from the regular
+       * "match this particular wide character" operation.
+       */
+#define BINARY_ROP     0x02
+#define UNARY_ROP      0x01
+#define LEAF_ROP       0x00
+
+#define MAKE_ROP(k, v) (-((v) | ((k) << 4)))
+#define KIND_ROP(v)    ((-(v)) >> 4)
+
+#define ROP_OR         MAKE_ROP(BINARY_ROP, 1)
+#define ROP_CAT                MAKE_ROP(BINARY_ROP, 2)
+
+#define ROP_STAR       MAKE_ROP(UNARY_ROP, 1)
+#define ROP_PLUS       MAKE_ROP(UNARY_ROP, 2)
+#define ROP_QUEST      MAKE_ROP(UNARY_ROP, 3)
+#define ROP_BRACE      MAKE_ROP(UNARY_ROP, 4)
+#define ROP_LP         MAKE_ROP(UNARY_ROP, 5)
+#define ROP_RP         MAKE_ROP(UNARY_ROP, 6)
+
+#define ROP_NOP                MAKE_ROP(LEAF_ROP, 1)   /* temporary */
+#define ROP_BOL                MAKE_ROP(LEAF_ROP, 2)   /* ^ anchor */
+#define ROP_EOL                MAKE_ROP(LEAF_ROP, 3)   /* $ anchor */
+#define ROP_ALL                MAKE_ROP(LEAF_ROP, 4)   /* anything (added) */
+#define ROP_ANYCH      MAKE_ROP(LEAF_ROP, 5)   /* . w/\n */
+#define ROP_NOTNL      MAKE_ROP(LEAF_ROP, 6)   /* . w/out \n */
+#define ROP_EMPTY      MAKE_ROP(LEAF_ROP, 7)   /* empty string */
+#define ROP_NONE       MAKE_ROP(LEAF_ROP, 8)   /* match failure */
+#define ROP_BKT                MAKE_ROP(LEAF_ROP, 9)   /* [...] */
+#define ROP_BKTCOPY    MAKE_ROP(LEAF_ROP, 10)  /* [...] (duplicated) */
+#define ROP_LT         MAKE_ROP(LEAF_ROP, 11)  /* \< word begin */
+#define ROP_GT         MAKE_ROP(LEAF_ROP, 12)  /* \> word end */
+#define ROP_REF                MAKE_ROP(LEAF_ROP, 13)  /* \digit */
+#define ROP_END                MAKE_ROP(LEAF_ROP, 14)  /* final (added) */
+
+       /*
+       * Return values:
+       *  libuxre_bktmbcomp()
+       *       <0 error (see BKT_* above); >0 #bytes scanned
+       *  libuxre_bktmbexec()
+       *       <0 doesn't match; >=0 matches, #extra bytes scanned
+       */
+LIBUXRE_STATIC void    libuxre_bktfree(Bracket *);
+LIBUXRE_STATIC int     libuxre_bktmbcomp(Bracket *, const unsigned char *,
+                               int, int);
+LIBUXRE_STATIC int     libuxre_bktmbexec(Bracket *, wchar_t,
+                               const unsigned char *, int);
+
+LIBUXRE_STATIC void    libuxre_regdeltree(Tree *, int);
+LIBUXRE_STATIC Tree    *libuxre_reg1tree(w_type, Tree *);
+LIBUXRE_STATIC Tree    *libuxre_reg2tree(w_type, Tree *, Tree *);
+LIBUXRE_STATIC Tree    *libuxre_regparse(Lex *, const unsigned char *, int);
+
+extern void            libuxre_regdeldfa(Dfa *);
+LIBUXRE_STATIC int     libuxre_regdfacomp(regex_t *, Tree *, Lex *);
+LIBUXRE_STATIC int     libuxre_regdfaexec(Dfa *, Exec *);
+
+extern void            libuxre_regdelnfa(Nfa *);
+LIBUXRE_STATIC int     libuxre_regnfacomp(regex_t *, Tree *, Lex *);
+LIBUXRE_STATIC int     libuxre_regnfaexec(Nfa *, Exec *);
+#endif /* !LIBUXRE_RE_H */
diff --git a/libuxre/regcomp.c b/libuxre/regcomp.c
new file mode 100644 (file)
index 0000000..20a197d
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Changes by Gunnar Ritter, Freiburg i. Br., Germany, November 2002.
+ *
+ * Sccsid @(#)regcomp.c        1.6 (gritter) 9/22/03
+ */
+/*  UNIX(R) Regular Expresssion Library
+ *
+ *  Note: Code is released under the GNU LGPL
+ *
+ *  Copyright (C) 2001 Caldera International, Inc.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to:
+ *        Free Software Foundation, Inc.
+ *        59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*     #include "synonyms.h"   */
+#include "re.h"
+
+/*     #pragma weak regcomp = _regcomp */
+
+int
+regcomp(regex_t *ep, const char *pat, int flags)
+{
+       Tree *tp;
+       Lex lex;
+
+       if ((tp=libuxre_regparse(&lex, (const unsigned char *)pat, flags)) == 0)
+               goto out;
+       ep->re_nsub = lex.nleft;
+       ep->re_flags = lex.flags & ~(REG_NOTBOL | REG_NOTEOL | REG_NONEMPTY);
+       ep->re_col = lex.col;
+       ep->re_mb_cur_max = lex.mb_cur_max;
+       /*
+       * Build the engine(s).  The factors determining which are built:
+       *  1. If the pattern built insists on an NFA, then only build NFA.
+       *  2. If flags include REG_NOSUB or REG_ONESUB and not (1),
+       *     then only build DFA.
+       *  3. Otherwise, build both.
+       * Since libuxre_regdfacomp() modifies the tree and libuxre_regnfacomp()
+       * doesn't, libuxre_regnfacomp() must be called first, if both are to
+       * be called.
+       */
+       if (ep->re_nsub != 0 && (flags & (REG_NOSUB | REG_ONESUB)) == 0
+               || lex.flags & REG_NFA)
+       {
+               ep->re_flags |= REG_NFA;
+               if ((lex.err = libuxre_regnfacomp(ep, tp, &lex)) != 0)
+                       goto out;
+       }
+       if ((lex.flags & REG_NFA) == 0)
+       {
+               ep->re_flags |= REG_DFA;
+               if ((lex.err = libuxre_regdfacomp(ep, tp, &lex)) != 0)
+               {
+                       if (ep->re_flags & REG_NFA)
+                               libuxre_regdelnfa(ep->re_nfa);
+               }
+       }
+out:;
+       if (lex.err != 0 && lex.col != 0)
+               (void)libuxre_lc_collate(lex.col);
+       if (tp != 0)
+               libuxre_regdeltree(tp, lex.err);
+       return lex.err;
+}
diff --git a/libuxre/regdfa.c b/libuxre/regdfa.c
new file mode 100644 (file)
index 0000000..8142e8d
--- /dev/null
@@ -0,0 +1,877 @@
+/*
+ * Changes by Gunnar Ritter, Freiburg i. Br., Germany, November 2002.
+ *
+ * Sccsid @(#)regdfa.c 1.9 (gritter) 9/22/03
+ */
+/*  UNIX(R) Regular Expresssion Library
+ *
+ *  Note: Code is released under the GNU LGPL
+ *
+ *  Copyright (C) 2001 Caldera International, Inc.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to:
+ *        Free Software Foundation, Inc.
+ *        59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*     #include "synonyms.h"   */
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "regdfa.h"
+
+/*
+* Deterministic Finite Automata.
+*/
+
+       /*
+       * Postorder traversal that returns a copy of the subtree,
+       * except that ROP_BKT becomes ROP_BKTCOPY (since they
+       * share the same pointed to Bracket object).
+       */
+static Tree *
+copy(regex_t *ep, Tree *tp)
+{
+       Tree *np;
+
+       if ((np = malloc(sizeof(Tree))) == 0)
+               return 0;
+       switch (np->op = tp->op) /* almost always correct */
+       {
+       case ROP_BKT:
+               np->op = ROP_BKTCOPY;
+               /*FALLTHROUGH*/
+       case ROP_BKTCOPY:
+               np->right.info.bkt = tp->right.info.bkt;
+               /*FALLTHROUGH*/
+       default:
+               np->left.pos = ep->re_dfa->nposn++;
+               /*FALLTHROUGH*/
+       case ROP_EMPTY:
+               return np;
+       case ROP_CAT:
+       case ROP_OR:
+               if ((np->right.ptr = copy(ep, tp->right.ptr)) == 0)
+               {
+                       free(np);
+                       return 0;
+               }
+               np->right.ptr->parent = np;
+               /*FALLTHROUGH*/
+       case ROP_STAR:
+       case ROP_PLUS:
+       case ROP_QUEST:
+       case ROP_LP:
+               if ((np->left.ptr = copy(ep, tp->left.ptr)) == 0)
+                       break;
+               np->left.ptr->parent = np;
+               return np;
+       }
+       libuxre_regdeltree(np, 1);
+       return 0;
+}
+
+       /*
+       * Postorder traversal.
+       * Assign unique ascending integer values to the leaves.
+       * Since the right child is traversed before the left,
+       * the position for ROP_END is guaranteed to be zero.
+       * The parse tree is rewritten in two cases:
+       * - Each ROP_BRACE is replaced by an equivalent--sometimes
+       *   large--subtree using only ROP_CAT, ROP_QUEST, and
+       *   ROP_PLUS.
+       * - If REG_ICASE, replace each simple character that has
+       *   an uppercase equivalent with a ROP_OR subtree over the
+       *   two versions.
+       * Since these rewrites occur bottom up, they have already
+       * been applied before any subtrees passed to copy().
+       */
+static Tree *
+findposn(regex_t *ep, Tree *tp, int mb_cur_max)
+{
+       unsigned int lo, hi;
+       Tree *ptr, *par;
+       w_type wc;
+
+       switch (tp->op)
+       {
+       default:
+               if (ep->re_flags & REG_ICASE
+                       && (wc = to_upper(tp->op)) != tp->op)
+               {
+                       if ((ptr = libuxre_reg1tree(tp->op, 0)) == 0)
+                               return 0;
+                       ptr->parent = tp;
+                       ptr->left.pos = ep->re_dfa->nposn++;
+                       tp->op = ROP_OR;
+                       tp->left.ptr = ptr;
+                       ptr = libuxre_reg1tree(wc, 0);
+                       if ((tp->right.ptr = ptr) == 0)
+                               return 0;
+                       ptr->parent = tp;
+                       ptr->left.pos = ep->re_dfa->nposn++;
+                       return tp;
+               }
+               /*FALLTHROUGH*/
+       case ROP_BOL:
+       case ROP_EOL:
+       case ROP_ALL:
+       case ROP_ANYCH:
+       case ROP_NOTNL:
+       case ROP_NONE:
+       case ROP_BKT:
+       case ROP_BKTCOPY:
+       case ROP_END:
+               tp->left.pos = ep->re_dfa->nposn++;
+               return tp;
+       case ROP_EMPTY:
+               return tp;
+       case ROP_OR:
+       case ROP_CAT:
+               if ((tp->right.ptr = findposn(ep, tp->right.ptr,
+                                               mb_cur_max)) == 0)
+                       return 0;
+               /*FALLTHROUGH*/
+       case ROP_STAR:
+       case ROP_PLUS:
+       case ROP_QUEST:
+       case ROP_LP:
+               if ((tp->left.ptr = findposn(ep, tp->left.ptr,
+                                               mb_cur_max)) == 0)
+                       return 0;
+               return tp;
+       case ROP_BRACE:
+               if ((tp->left.ptr = findposn(ep, tp->left.ptr,
+                                               mb_cur_max)) == 0)
+                       return 0;
+               break;
+       }
+       /*
+       * ROP_BRACE as is cannot be handled in a DFA.  This code
+       * duplicates the ROP_BRACE subtree as a left-towering
+       * series of ROP_CAT nodes, the first "lo" of which are
+       * direct copies of the original subtree.  The tail of
+       * the series are either some number of ROP_QUESTs over
+       * copies of the original subtree, or a single ROP_PLUS
+       * over a copy (when "hi" is infinity).
+       *
+       * All interesting cases {lo,hi}:
+       *  {0,0} -> ROP_EMPTY, parsing, temporary
+       *  {0,1} -> ROP_QUEST, parsing
+       *  {0,2} -> CAT(QUEST(left), QUEST(copy))
+       *  {0,n} -> CAT({0,n-1}, QUEST(copy))
+       *  {0,}  -> ROP_STAR, parsing
+       *
+       *  {1,1} -> ROP_NOP, parsing, temporary
+       *  {1,2} -> CAT(left, QUEST(copy))
+       *  {1,n} -> CAT({1,n-1}, QUEST(copy))
+       *  {1,}  -> ROP_PLUS, parsing
+       *
+       *  {2,2} -> CAT(left, copy)
+       *  {2,n} -> CAT({2,n-1}, QUEST(copy))
+       *  {2,}  -> CAT(left, PLUS(copy))
+       *
+       *  {3,3} -> CAT({2,2}, copy)
+       *  {3,n} -> CAT({3,n-1}, QUEST(copy))
+       *  {3,}  -> CAT({2,2}, PLUS(copy))
+       *
+       *  {n,}  -> CAT({n-1,n-1}, PLUS(copy))
+       *
+       * In all cases, the ROP_BRACE node is turned into the
+       * left-most ROP_CAT, and a copy of its original subtree
+       * is connected as the right child.  Note that the bottom-
+       * up nature of this duplication guarantees that copy()
+       * never sees a ROP_BRACE node.
+       */
+       par = tp->parent;
+       lo = tp->right.info.num[0];
+       hi = tp->right.info.num[1];
+       if ((ptr = copy(ep, tp->left.ptr)) == 0)
+               return 0;
+       ptr->parent = tp;
+       tp->op = ROP_CAT;
+       tp->right.ptr = ptr;
+       if (lo == 0)
+       {
+               if ((tp->left.ptr = libuxre_reg1tree(ROP_QUEST, tp->left.ptr))
+                               == 0)
+                       return 0;
+               tp->left.ptr->parent = tp;
+       }
+       else
+       {
+               if (hi == BRACE_INF || (hi -= lo) == 0)
+                       lo--;   /* lo > 1; no extra needed */
+               while (--lo != 0)
+               {
+                       if ((tp = libuxre_reg2tree(ROP_CAT, tp, copy(ep, ptr)))
+                                       == 0)
+                               return 0;
+               }
+       }
+       if (hi == BRACE_INF)
+       {
+               if ((tp->right.ptr = libuxre_reg1tree(ROP_PLUS, tp->right.ptr))
+                               == 0)
+                       return 0;
+               tp->right.ptr->parent = tp;
+       }
+       else if (hi != 0)
+       {
+               if ((tp->right.ptr = libuxre_reg1tree(ROP_QUEST, tp->right.ptr))
+                               == 0)
+                       return 0;
+               ptr = tp->right.ptr;
+               ptr->parent = tp;
+               while (--hi != 0)
+               {
+                       if ((tp = libuxre_reg2tree(ROP_CAT, tp, copy(ep, ptr)))
+                                       == 0)
+                               return 0;
+               }
+       }
+       tp->parent = par;
+       return tp;
+}
+
+       /*
+       * Postorder traversal, but not always entire subtree.
+       * For each leaf reachable by the empty string, add it
+       * to the set.  Return 0 if the subtree can match empty.
+       */
+static int
+first(Dfa *dp, Tree *tp)
+{
+       switch (tp->op)
+       {
+       case ROP_BOL:
+               if (dp->flags & REG_NOTBOL)
+                       return 0;
+               break;
+       case ROP_EOL:
+               if (dp->flags & REG_NOTEOL)
+                       return 0;
+               break;
+       case ROP_EMPTY:
+               return 0;
+       case ROP_OR:
+               return first(dp, tp->left.ptr) & first(dp, tp->right.ptr);
+       case ROP_CAT:
+               if (first(dp, tp->left.ptr) != 0)
+                       return 1;
+               return first(dp, tp->right.ptr);
+       case ROP_BRACE:
+               if (tp->right.info.num[0] != 0 && first(dp, tp->left.ptr) != 0)
+                       return 1;
+               /*FALLTHROUGH*/
+       case ROP_STAR:
+       case ROP_QUEST:
+               first(dp, tp->left.ptr);
+               return 0;
+       case ROP_LP:
+       case ROP_PLUS:
+               return first(dp, tp->left.ptr);
+       }
+       if (dp->posset[tp->left.pos] == 0)
+       {
+               dp->posset[tp->left.pos] = 1;
+               dp->nset++;
+       }
+       return 1;
+}
+
+       /*
+       * Walk from leaf up (most likely not to root).
+       * Determine follow set for the leaf by filling
+       * set[] with the positions reachable.
+       */
+static void
+follow(Dfa *dp, Tree *tp)
+{
+       Tree *pp;
+
+       switch ((pp = tp->parent)->op)
+       {
+       case ROP_CAT:
+               if (pp->left.ptr == tp && first(dp, pp->right.ptr) != 0)
+                       break;
+               /*FALLTHROUGH*/
+       case ROP_OR:
+       case ROP_QUEST:
+       case ROP_LP:
+               follow(dp, pp);
+               break;
+       case ROP_STAR:
+       case ROP_PLUS:
+       case ROP_BRACE:
+               first(dp, tp);
+               follow(dp, pp);
+               break;
+       }
+}
+
+       /*
+       * Postorder traversal.
+       * At each leaf, copy it into posn[] and assign its follow set.
+       * Because the left-most subtree is ROP_ALL under ROP_STAR, the
+       * follow set for its leaf (position dp->nposn-1) is the same
+       * as the initial state's signature (prior to any ROP_BOL).
+       */
+static int
+posnfoll(Dfa *dp, Tree *tp)
+{
+       unsigned char *s;
+       size_t i, n;
+       size_t *fp;
+       Posn *p;
+       int ret;
+
+       switch (tp->op)
+       {
+       case ROP_OR:
+       case ROP_CAT:
+               if ((ret = posnfoll(dp, tp->right.ptr)) != 0)
+                       return ret;
+               /*FALLTHROUGH*/
+       case ROP_STAR:
+       case ROP_PLUS:
+       case ROP_QUEST:
+       case ROP_LP:
+               if ((ret = posnfoll(dp, tp->left.ptr)) != 0)
+                       return ret;
+               return 0;
+       case ROP_END:   /* keeps follow() from walking above the root */
+               p = &dp->posn[tp->left.pos];
+               p->op = tp->op;
+               p->seti = 0;
+               p->nset = 0;
+               return 0;
+       case ROP_BKT:
+       case ROP_BKTCOPY:
+               p = &dp->posn[tp->left.pos];
+               p->bkt = tp->right.info.bkt;
+               goto skip;
+       case ROP_BOL:
+               dp->flags |= REG_NOTBOL; /* adjacent ROP_BOLs match empty */
+               break;
+       case ROP_EOL:
+               dp->flags |= REG_NOTEOL; /* adjacent ROP_EOLs match empty */
+               break;
+       }
+       p = &dp->posn[tp->left.pos];
+skip:;
+       p->op = tp->op;
+       memset(dp->posset, 0, dp->nposn);
+       dp->nset = 0;
+       follow(dp, tp);
+       dp->flags &= ~(REG_NOTBOL | REG_NOTEOL);
+       fp = dp->posfoll;
+       if ((p->nset = dp->nset) > dp->avail) /* need more */
+       {
+               if ((n = p->nset << 1) < dp->nposn)
+                       n = dp->nposn;  
+               dp->avail += n;
+               if ((fp = realloc(dp->posfoll,
+                       sizeof(size_t) * (dp->avail + dp->used))) == 0)
+               {
+                       return REG_ESPACE;
+               }
+               dp->posfoll = fp;
+       }
+       p->seti = dp->used;
+       if ((i = dp->nset) != 0)
+       {
+               dp->used += i;
+               dp->avail -= i;
+               fp += p->seti;
+               s = dp->posset;
+               n = 0;
+               do
+               {
+                       if (*s++ != 0)
+                       {
+                               *fp++ = n;
+                               if (--i == 0)
+                                       break;
+                       }
+               } while (++n != dp->nposn);
+       }
+       return 0;
+}
+
+static int
+addstate(Dfa *dp) /* install state if unique; return its index */
+{
+       size_t *sp, *fp;
+       size_t t, n, i;
+       int flushed;
+
+       /*
+       * Compare dp->nset/dp->cursig[] against remembered states.
+       */
+       t = dp->top;
+       do
+       {
+               if (dp->nsig[--t] != dp->nset)
+                       continue;
+               if ((n = dp->nset) != 0)
+               {
+                       fp = &dp->sigfoll[dp->sigi[t]];
+                       sp = &dp->cursig[0];
+               loop:;
+                       if (*fp++ != *sp++)
+                               continue; /* to the do-while */
+                       if (--n != 0)
+                               goto loop;
+               }
+               return t + 1;
+       } while (t != 0);
+       /*
+       * Not in currently cached states; add it.
+       */
+       flushed = 0;
+       if ((t = dp->top) >= CACHESZ)   /* need to flush the cache */
+       {
+               flushed = 1;
+               n = dp->anybol;
+               n = dp->sigi[n] + dp->nsig[n];  /* past invariant states */
+               dp->avail += dp->used - n;
+               dp->used = n;
+               dp->top = n = dp->nfix;
+               memset((void *)&dp->trans, 0, sizeof(dp->trans));
+               memset((void *)&dp->acc[n], 0, CACHESZ - n);
+               t = n;
+       }
+       dp->top++;
+       fp = dp->sigfoll;
+       if ((n = dp->nset) > dp->avail) /* grow strip */
+       {
+               i = dp->avail + n << 1;
+               if ((fp = realloc(fp, sizeof(size_t) * (i + dp->used))) == 0)
+                       return 0;
+               dp->avail = i;
+               dp->sigfoll = fp;
+       }
+       dp->acc[t] = 0;
+       if ((dp->nsig[t] = n) != 0)
+       {
+               sp = dp->cursig;
+               if (sp[0] == 0)
+                       dp->acc[t] = 1;
+               dp->sigi[t] = i = dp->used;
+               dp->used += n;
+               dp->avail -= n;
+               fp += i;
+               do
+                       *fp++ = *sp++;
+               while (--n != 0);
+       }
+       t++;
+       if (flushed)
+               return -t;
+       return t;
+}
+
+void
+libuxre_regdeldfa(Dfa *dp)
+{
+       Posn *pp;
+       size_t np;
+
+       if (dp->posfoll != 0)
+               free(dp->posfoll);
+       if (dp->sigfoll != 0)
+               free(dp->sigfoll);
+       if (dp->cursig != 0)
+               free(dp->cursig);
+       if ((pp = dp->posn) != 0)
+       {
+               /*
+               * Need to walk the positions list to free any
+               * space used for ROP_BKTs.
+               */
+               np = dp->nposn;
+               do
+               {
+                       if (pp->op == ROP_BKT)
+                       {
+                               libuxre_bktfree(pp->bkt);
+                               free(pp->bkt);
+                       }
+               } while (++pp, --np != 0);
+               free(dp->posn);
+       }
+       free(dp);
+}
+
+int
+regtrans(Dfa *dp, int st, w_type wc, int mb_cur_max)
+{
+       const unsigned char *s;
+       size_t *fp, *sp;
+       size_t i, n;
+       Posn *pp;
+       int nst;
+
+       if ((n = dp->nsig[st]) == 0)    /* dead state */
+               return st + 1;          /* stay here */
+       memset(dp->posset, 0, dp->nposn);
+       dp->nset = 0;
+       fp = &dp->sigfoll[dp->sigi[st]];
+       do
+       {
+               pp = &dp->posn[*fp];
+               switch (pp->op)
+               {
+               case ROP_EOL:
+                       if (wc == '\0' && (dp->flags & REG_NOTEOL) == 0)
+                               break;
+                       /*FALLTHROUGH*/
+               case ROP_BOL:
+               default:
+                       if (pp->op == wc)
+                               break;
+                       /*FALLTHROUGH*/
+               case ROP_END:
+               case ROP_NONE:
+                       continue;
+               case ROP_NOTNL:
+                       if (wc == '\n')
+                               continue;
+                       /*FALLTHROUGH*/
+               case ROP_ANYCH:
+                       if (wc <= '\0')
+                               continue;
+                       break;
+               case ROP_ALL:
+                       if (wc == '\0')
+                               continue;
+                       break;
+               case ROP_BKT:
+               case ROP_BKTCOPY:
+                       /*
+                       * Note that multiple character bracket matches
+                       * are precluded from DFAs.  (See regparse.c and
+                       * regcomp.c.)  Thus, the continuation string
+                       * argument is not used in libuxre_bktmbexec().
+                       */
+                       if (wc > '\0' &&
+                           libuxre_bktmbexec(pp->bkt, wc, 0, mb_cur_max) == 0)
+                               break;
+                       continue;
+               }
+               /*
+               * Current character matches this position.
+               * For each position in its follow list,
+               * add that position to the new state's signature.
+               */
+               i = pp->nset;
+               sp = &dp->posfoll[pp->seti];
+               do
+               {
+                       if (dp->posset[*sp] == 0)
+                       {
+                               dp->posset[*sp] = 1;
+                               dp->nset++;
+                       }
+               } while (++sp, --i != 0);
+       } while (++fp, --n != 0);
+       /*
+       * Move the signature (if any) into cursig[] and install it.
+       */
+       if ((i = dp->nset) != 0)
+       {
+               fp = dp->cursig;
+               s = dp->posset;
+               for (n = 0;; n++)
+               {
+                       if (*s++ != 0)
+                       {
+                               *fp++ = n;
+                               if (--i == 0)
+                                       break;
+                       }
+               }
+       }
+       if ((nst = addstate(dp)) < 0) /* flushed cache */
+               nst = -nst;
+       else if (nst > 0 && (wc & ~(long)(NCHAR - 1)) == 0)
+               dp->trans[st][wc] = nst;
+       return nst;
+}
+
+LIBUXRE_STATIC int
+libuxre_regdfacomp(regex_t *ep, Tree *tp, Lex *lxp)
+{
+       Tree *lp;
+       Dfa *dp;
+       Posn *p;
+       int st;
+
+       /*
+       * It's convenient to insert an STAR(ALL) subtree to the
+       * immediate left of the current tree.  This makes the
+       * "any match" libuxre_regdfaexec() not a special case,
+       * and the initial state signature will fall out when
+       * building the follow sets for all the leaves.
+       */
+       if ((lp = libuxre_reg1tree(ROP_ALL, 0)) == 0
+               || (lp = libuxre_reg1tree(ROP_STAR, lp)) == 0
+               || (tp->left.ptr = lp
+                       = libuxre_reg2tree(ROP_CAT, lp, tp->left.ptr)) == 0)
+       {
+               return REG_ESPACE;
+       }
+       lp->parent = tp;
+       if ((dp = calloc(1, sizeof(Dfa))) == 0)
+               return REG_ESPACE;
+       ep->re_dfa = dp;
+       /*
+       * Just in case null pointers aren't just all bits zero...
+       */
+       dp->posfoll = 0;
+       dp->sigfoll = 0;
+       dp->cursig = 0;
+       dp->posn = 0;
+       /*
+       * Assign position values to each of the tree's leaves
+       * (the important parts), meanwhile potentially rewriting
+       * the parse tree so that it fits within the restrictions
+       * of our DFA.
+       */
+       if ((tp = findposn(ep, tp, lxp->mb_cur_max)) == 0)
+               goto err;
+       /*
+       * Get space for the array of positions and current set,
+       * now that the number of positions is known.
+       */
+       if ((dp->posn = malloc(sizeof(Posn) * dp->nposn + dp->nposn)) == 0)
+               goto err;
+       dp->posset = (unsigned char *)&dp->posn[dp->nposn];
+       /*
+       * Get follow sets for each position.
+       */
+       if (posnfoll(dp, tp) != 0)
+               goto err;
+       /*
+       * Set up the special invariant states:
+       *  - dead state (no valid transitions); index 0.
+       *  - initial state for any match [STAR(ALL) follow set]; index 1.
+       *  - initial state for any match after ROP_BOL.
+       *  - initial state for left-most longest if REG_NOTBOL.
+       *  - initial state for left-most longest after ROP_BOL.
+       * The final two are not allocated if leftmost() cannot be called.
+       * The pairs of initial states are the same if there is no
+       * explicit ROP_BOL transition.
+       */
+       dp->avail += dp->used;
+       dp->used = 0;
+       if ((dp->sigfoll = malloc(sizeof(size_t) * dp->avail)) == 0)
+               goto err;
+       p = &dp->posn[dp->nposn - 1];   /* same as first(root) */
+       dp->cursig = &dp->posfoll[p->seti];
+       dp->nset = p->nset;
+       dp->top = 1;    /* index 0 is dead state */
+       addstate(dp);   /* must be state index 1 (returns 2) */
+       if ((dp->cursig = malloc(sizeof(size_t) * dp->nposn)) == 0)
+               goto err;
+       dp->nfix = 2;
+       if ((st = regtrans(dp, 1, ROP_BOL, lxp->mb_cur_max)) == 0)
+               goto err;
+       if ((dp->anybol = st - 1) == 2) /* new state */
+               dp->nfix = 3;
+       if ((ep->re_flags & REG_NOSUB) == 0) /* leftmost() might be called */
+       {
+               /*
+               * leftmost() initial states are the same as the
+               * "any match" ones without the STAR(ALL) position.
+               */
+               dp->sigi[dp->nfix] = 0;
+               dp->nsig[dp->nfix] = dp->nsig[1] - 1;
+               dp->acc[dp->nfix] = dp->acc[1];
+               dp->leftbol = dp->leftmost = dp->nfix;
+               dp->nfix++;
+               if (dp->anybol != 1)    /* distinct state w/BOL */
+               {
+                       dp->sigi[dp->nfix] = dp->sigi[2];
+                       dp->nsig[dp->nfix] = dp->nsig[2] - 1;
+                       dp->acc[dp->nfix] = dp->acc[2];
+                       dp->leftbol = dp->nfix;
+                       dp->nfix++;
+               }
+               dp->top = dp->nfix;
+       }
+       return 0;
+err:;
+       libuxre_regdeldfa(dp);
+       return REG_ESPACE;
+}
+
+static int
+leftmost(Dfa *dp, Exec *xp)
+{
+       const unsigned char *s, *beg, *end;
+       int i, nst, st, mb_cur_max;
+       w_type wc;
+
+       mb_cur_max = xp->mb_cur_max;
+       beg = s = xp->str;
+       end = 0;
+       st = dp->leftbol;
+       if (xp->flags & REG_NOTBOL)
+               st = dp->leftmost;
+       if (dp->acc[st] && (xp->flags & REG_NONEMPTY) == 0)
+               end = s;        /* initial empty match allowed */
+       for (;;)
+       {
+               if ((wc = *s++) == '\n')
+               {
+                       if (xp->flags & REG_NEWLINE)
+                               wc = ROP_EOL;
+               }
+               else if (!ISONEBYTE(wc) && (i = libuxre_mb2wc(&wc, s)) > 0)
+                       s += i;
+               if ((wc & ~(long)(NCHAR - 1)) != 0
+                       || (nst = dp->trans[st][wc]) == 0)
+               {
+                       if ((nst=regtrans(dp, st, wc, mb_cur_max)) == 0)
+                               return REG_ESPACE;
+                       if (wc == ROP_EOL) /* REG_NEWLINE only */
+                       {
+                               if (dp->acc[nst - 1])
+                               {
+                                       if (end == 0 || end < s)
+                                               end = s;
+                                       break;
+                               }
+                               beg = s;
+                               st = dp->leftbol;
+                               goto newst;
+                       }
+               }
+               if ((st = nst - 1) == 0) /* dead state */
+               {
+                       if (end != 0)
+                               break;
+                       if ((wc = *beg++) == '\0')
+                               return REG_NOMATCH;
+                       else if (!ISONEBYTE(wc) &&
+                                       (i = libuxre_mb2wc(&wc, beg)) > 0)
+                               beg += i;
+                       s = beg;
+                       st = dp->leftmost;
+                       goto newst;
+               }
+               if (wc == '\0')
+               {
+                       if (dp->acc[st])
+                       {
+                               s--;    /* don't include \0 */
+                               if (end == 0 || end < s)
+                                       end = s;
+                               break;
+                       }
+                       if (end != 0)
+                               break;
+                       return REG_NOMATCH;
+               }
+       newst:;
+               if (dp->acc[st])
+               {
+                       if (end == 0 || end < s)
+                               end = s;
+               }
+       }
+       xp->match[0].rm_so = beg - xp->str;
+       xp->match[0].rm_eo = end - xp->str;
+       return 0;
+}
+
+/*
+* Optimization by simplification: singlebyte locale and REG_NEWLINE not set.
+* Performance gain for grep is 25% so it's worth the hack.
+*/
+static int
+regdfaexec_opt(Dfa *dp, Exec *xp)
+{
+       const unsigned char *s;
+       int nst, st;
+
+       s = xp->str;
+       st = dp->anybol;
+       if (xp->flags & REG_NOTBOL)
+               st = 1;
+       if (dp->acc[st] && (xp->flags & REG_NONEMPTY) == 0)
+               return 0;       /* initial empty match allowed */
+       do
+       {
+               if ((nst = dp->trans[st][*s]) == 0)
+               {
+                       if ((nst = regtrans(dp, st, *s, 1)) == 0)
+                               return REG_ESPACE;
+               }
+               if (dp->acc[st = nst - 1])
+                       return 0;
+       } while (*s++ != '\0'); /* st != 0 */
+       return REG_NOMATCH;
+}
+
+LIBUXRE_STATIC int
+libuxre_regdfaexec(Dfa *dp, Exec *xp)
+{
+       const unsigned char *s;
+       int i, nst, st, mb_cur_max;
+       w_type wc;
+
+       dp->flags = xp->flags & REG_NOTEOL;     /* for regtrans() */
+       mb_cur_max = xp->mb_cur_max;
+       if (xp->nmatch != 0)
+               return leftmost(dp, xp);
+       if (mb_cur_max == 1 && (xp->flags & REG_NEWLINE) == 0)
+               return regdfaexec_opt(dp, xp);
+       s = xp->str;
+       st = dp->anybol;
+       if (xp->flags & REG_NOTBOL)
+               st = 1;
+       if (dp->acc[st] && (xp->flags & REG_NONEMPTY) == 0)
+               return 0;       /* initial empty match allowed */
+       for (;;)
+       {
+               if ((wc = *s++) == '\n')
+               {
+                       if (xp->flags & REG_NEWLINE)
+                               wc = ROP_EOL;
+               }
+               else if (!ISONEBYTE(wc) && (i = libuxre_mb2wc(&wc, s)) > 0)
+                       s += i;
+               if ((wc & ~(long)(NCHAR - 1)) != 0
+                       || (nst = dp->trans[st][wc]) == 0)
+               {
+                       if ((nst=regtrans(dp, st, wc, mb_cur_max)) == 0)
+                               return REG_ESPACE;
+                       if (wc == ROP_EOL) /* REG_NEWLINE only */
+                       {
+                               if (dp->acc[nst - 1])
+                                       return 0;
+                               if (dp->acc[st = dp->anybol])
+                                       return 0;
+                               continue;
+                       }
+               }
+               if (dp->acc[st = nst - 1])
+                       return 0;
+               if (wc == '\0') /* st == 0 */
+                       return REG_NOMATCH;
+       }
+}
diff --git a/libuxre/regdfa.h b/libuxre/regdfa.h
new file mode 100644 (file)
index 0000000..8cb0d48
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Changes by Gunnar Ritter, Freiburg i. Br., Germany, November 2002.
+ *
+ * Sccsid @(#)regdfa.h 1.3 (gritter) 9/22/03
+ */
+/*  UNIX(R) Regular Expresssion Library
+ *
+ *  Note: Code is released under the GNU LGPL
+ *
+ *  Copyright (C) 2001 Caldera International, Inc.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to:
+ *        Free Software Foundation, Inc.
+ *        59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*     #include "synonyms.h"   */
+
+/*
+* Deterministic Finite Automata.
+*/
+
+#ifndef        LIBUXRE_REGDFA_H
+#define        LIBUXRE_REGDFA_H
+
+#include <re.h>
+
+typedef struct
+{
+       Bracket *bkt;   /* extra info for ROP_BKT */
+       size_t  nset;   /* number of items in the follow set */
+       size_t  seti;   /* index into the follow set strip */
+       w_type  op;     /* the leaf match operation */
+} Posn;
+
+#define CACHESZ        32      /* max. states to remember (must fit in uchar) */
+#define NCHAR  (1 << CHAR_BIT)
+
+struct re_dfa_ /*Dfa*/
+{
+       unsigned char   *posset;        /* signatures built here */
+       size_t          *posfoll;       /* follow strip for posn[] */
+       size_t          *sigfoll;       /* follow strip for sigi[] */
+       size_t          *cursig;        /* current state's signature */
+       Posn            *posn;          /* important positions */
+       size_t          nposn;          /* length of posn,cursig,posset */
+       size_t          used;           /* used portion of follow strip */
+       size_t          avail;          /* unused part of follow strip */
+       size_t          nset;           /* # items nonzero in posset[] */
+       size_t          nsig[CACHESZ];  /* number of items in signature */
+       size_t          sigi[CACHESZ];  /* index into sigfoll[] */
+       unsigned char   acc[CACHESZ];   /* nonzero for accepting states */
+       unsigned char   leftmost;       /* leftmost() start, not BOL */
+       unsigned char   leftbol;        /* leftmost() start, w/BOL */
+       unsigned char   anybol;         /* any match start, w/BOL */
+       unsigned char   nfix;           /* number of invariant states */
+       unsigned char   top;            /* next state index available */
+       unsigned char   flags;          /* interesting flags */
+       unsigned char   trans[CACHESZ][NCHAR];  /* goto table */
+};
+
+extern int      regtrans(Dfa *, int, w_type, int);
+
+#endif /* !LIBUXRE_REGDFA_H */
diff --git a/libuxre/regerror.c b/libuxre/regerror.c
new file mode 100644 (file)
index 0000000..397e3e5
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Changes by Gunnar Ritter, Freiburg i. Br., Germany, November 2002.
+ *
+ * Sccsid @(#)regerror.c       1.4 (gritter) 3/29/03
+ */
+/*  UNIX(R) Regular Expresssion Library
+ *
+ *  Note: Code is released under the GNU LGPL
+ *
+ *  Copyright (C) 2001 Caldera International, Inc.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to:
+ *        Free Software Foundation, Inc.
+ *        59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*     #include "synonyms.h"   */
+#include <string.h>
+#include "re.h"
+/*     include "_locale.h"     */
+
+/*     #pragma weak regerror = _regerror       */
+
+size_t
+regerror(int err, const regex_t *ep, char *str, size_t max)
+{
+       const struct
+       {
+               int             index;
+               const char      *str;
+       } unk = 
+       {
+                       88,  "unknown regular expression error"
+       }, msgs[] =
+       {
+       /*ENOSYS*/      { 89,  "feature not implemented" },
+       /*0*/           { 0,   "" },
+       /*NOMATCH*/     { 90,  "regular expression failed to match" },
+       /*BADPAT*/      { 91,  "invalid regular expression" },
+       /*ECOLLATE*/    { 92,  "invalid collating element construct" },
+       /*ECTYPE*/      { 93,  "invalid character class construct" },
+       /*EEQUIV*/      { 94,  "invalid equivalence class construct" },
+       /*EBKTCHAR*/    { 95,  "invalid character in '[ ]' construct" },
+       /*EESCAPE*/     { 96,  "trailing \\ in pattern" },
+       /*ESUBREG*/     { 97,  "'\\digit' out of range" },
+       /*EBRACK*/      { 98,  "'[ ]' imbalance" },
+       /*EMPTYSUBBKT*/ { 99,  "empty nested '[ ]' construct" },
+       /*EMPTYPAREN*/  { 100, "empty '\\( \\)' or '( )'" },
+       /*NOPAT*/       { 101, "empty pattern" },
+       /*EPAREN*/      { 102, "'\\( \\)' or '( )' imbalance" },
+       /*EBRACE*/      { 103, "'\\{ \\} or '{ }' imbalance" },
+       /*BADBR*/       { 104, "invalid '\\{ \\}' or '{ }'" },
+       /*ERANGE*/      { 105, "invalid endpoint in range" },
+       /*ESPACE*/      { 106, "out of regular expression memory" },
+       /*BADRPT*/      { 107, "invalid *, +, ?, \\{\\} or {} operator" },
+       /*BADESC*/      { 108, "invalid escape sequence (e.g. \\0)" },
+       /*ILLSEQ*/      { 109, "illegal byte sequence"}
+       };
+       const char *p;
+       size_t len;
+       int i;
+
+       if (err < REG_ENOSYS || REG_ILLSEQ < err)
+       {
+               i = unk.index;
+               p = unk.str;
+       }
+       else
+       {
+               i = msgs[err - REG_ENOSYS].index;
+               p = msgs[err - REG_ENOSYS].str;
+       }
+/*     p = __gtxt(_str_uxlibc, i, p);  */
+       len = strlen(p) + 1;
+       if (max != 0)
+       {
+               if (max > len)
+                       max = len;
+               else if (max < len)
+                       str[--max] = '\0';
+               memcpy(str, p, max);
+       }
+       return len;
+}
diff --git a/libuxre/regex.h b/libuxre/regex.h
new file mode 100644 (file)
index 0000000..8dbd028
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Changes by Gunnar Ritter, Freiburg i. Br., Germany, November 2002.
+ *
+ * Sccsid @(#)regex.h  1.13 (gritter) 2/6/05
+ */
+/*  UNIX(R) Regular Expresssion Library
+ *
+ *  Note: Code is released under the GNU LGPL
+ *
+ *  Copyright (C) 2001 Caldera International, Inc.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to:
+ *        Free Software Foundation, Inc.
+ *        59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef LIBUXRE_REGEX_H
+#define LIBUXRE_REGEX_H
+/*     from unixsrc:usr/src/common/head/regex.h /main/uw7_nj/1 */
+
+#include <sys/types.h> /* really only want [s]size_t */
+
+       /*
+       * Official regexec() flags.
+       */
+#define REG_NOTBOL     0x000001 /* start of string does not match ^ */
+#define REG_NOTEOL     0x000002 /* end of string does not match $ */
+
+       /*
+       * Additional regexec() flags.
+       */
+#define REG_NONEMPTY   0x000004 /* do not match empty at start of string */
+
+       /*
+       * Extensions to provide individual control over each
+       * of the differences between basic and extended REs.
+       */
+#define REG_OR         0x0000001 /* enable | operator */
+#define REG_PLUS       0x0000002 /* enable + operator */
+#define REG_QUEST      0x0000004 /* enable ? operator */
+#define REG_BRACES     0x0000008 /* use {m,n} (instead of \{m,n\}) */
+#define REG_PARENS     0x0000010 /* use (...) [instead of \(...\)] */
+#define REG_ANCHORS    0x0000020 /* ^ and $ are anchors anywhere */
+#define REG_NOBACKREF  0x0000040 /* disable \digit */
+#define REG_NOAUTOQUOTE        0x0000080 /* no automatic quoting of REG_BADRPTs */
+
+       /*
+       * Official regcomp() flags.
+       */
+#define REG_EXTENDED   (REG_OR | REG_PLUS | REG_QUEST | REG_BRACES | \
+                               REG_PARENS | REG_ANCHORS | \
+                               REG_NOBACKREF | REG_NOAUTOQUOTE)
+#define REG_ICASE      0x0000100 /* ignore case */
+#define REG_NOSUB      0x0000200 /* only success/fail for regexec() */
+#define REG_NEWLINE    0x0000400 /* take \n as line separator for ^ and $ */
+
+       /*
+       * Additional regcomp() flags.
+       * Some of these assume that int is >16 bits!
+       * Beware: 0x20000000 and above are used in re.h.
+       */
+#define REG_ONESUB     0x0000800 /* regexec() only needs pmatch[0] */
+#define REG_MTPARENFAIL        0x0001000 /* take empty \(\) or () as match failure */
+#define REG_MTPARENBAD 0x0002000 /* disallow empty \(\) or () */
+#define REG_BADRANGE   0x0004000 /* accept [m-a] ranges as [ma] */
+#define        REG_ODDRANGE    0x0008000 /* oawk oddity: [m-a] means [m] */
+#define REG_SEPRANGE   0x0010000 /* disallow [a-m-z] style ranges */
+#define REG_BKTQUOTE   0x0020000 /* allow \ in []s to quote \, -, ^ or ] */
+#define REG_BKTEMPTY   0x0040000 /* allow empty []s (w/BKTQUOTE, BKTESCAPE) */
+#define REG_ANGLES     0x0080000 /* enable \<, \> operators */
+#define REG_ESCNL      0x0100000 /* take \n as newline character */
+#define REG_NLALT      0x0200000 /* take newline as alternation */
+#define REG_ESCSEQ     0x0400000 /* otherwise, take \ as start of C escapes */
+#define REG_BKTESCAPE  0x0800000 /* allow \ in []s to quote next anything */
+#define        REG_NOBRACES    0x1000000 /* disable {n,m} */
+#define        REG_ADDITIVE    0x2000000 /* a+*b means + and * additive, ^+ is valid */
+#define        REG_NOI18N      0x4000000 /* disable I18N features ([::] etc.) */
+#define        REG_OLDESC      0x8000000 /* recognize \b \f \n \r \t \123 only */
+#define        REG_AVOIDNULL   0x10000000/* avoid null subexpression matches */
+#define REG_OLDBRE     (REG_BADRANGE | REG_ANGLES | REG_ESCNL)
+#define REG_OLDERE     (REG_OR | REG_PLUS | REG_QUEST | REG_NOBRACES | \
+                               REG_PARENS | REG_ANCHORS | REG_ODDRANGE | \
+                               REG_NOBACKREF | REG_ADDITIVE | REG_NOAUTOQUOTE)
+
+       /*
+       * Error return values.
+       */
+#define REG_ENOSYS     (-1)    /* unsupported */
+#define        REG_NOMATCH     1       /* regexec() failed to match */
+#define        REG_BADPAT      2       /* invalid regular expression */
+#define        REG_ECOLLATE    3       /* invalid collating element construct */
+#define        REG_ECTYPE      4       /* invalid character class construct */
+#define REG_EEQUIV     5       /* invalid equivalence class construct */
+#define REG_EBKTCHAR   6       /* invalid character in [] construct */
+#define        REG_EESCAPE     7       /* trailing \ in pattern */
+#define        REG_ESUBREG     8       /* number in \digit invalid or in error */
+#define        REG_EBRACK      9       /* [] imbalance */
+#define REG_EMPTYSUBBKT        10      /* empty sub-bracket construct */
+#define REG_EMPTYPAREN 11      /* empty \(\) or () [REG_MTPARENBAD] */
+#define REG_NOPAT      12      /* no (empty) pattern */
+#define        REG_EPAREN      13      /* \(\) or () imbalance */
+#define        REG_EBRACE      14      /* \{\} or {} imbalance */
+#define        REG_BADBR       15      /* contents of \{\} or {} invalid */
+#define        REG_ERANGE      16      /* invalid endpoint in expression */
+#define        REG_ESPACE      17      /* out of memory */
+#define        REG_BADRPT      18      /* *,+,?,\{\} or {} not after r.e. */
+#define REG_BADESC     19      /* invalid escape sequence (e.g. \0) */
+#define        REG_ILLSEQ      20      /* illegal byte sequence */
+
+typedef struct
+{
+       size_t          re_nsub;        /* only advertised member */
+       unsigned long   re_flags;       /* augmented regcomp() flags */
+       struct re_dfa_  *re_dfa;        /* DFA engine */
+       struct re_nfa_  *re_nfa;        /* NFA engine */
+       struct re_coll_ *re_col;        /* current collation info */
+       int             re_mb_cur_max;  /* MB_CUR_MAX acceleration */
+       void            *re_more;       /* just in case... */
+} regex_t;
+
+typedef ssize_t regoff_t;
+
+typedef struct
+{
+       regoff_t        rm_so;
+       regoff_t        rm_eo;
+} regmatch_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int    regcomp(regex_t *, const char *, int);
+int    regexec(const regex_t *, const char *, size_t, regmatch_t *, int);
+size_t regerror(int, const regex_t *, char *, size_t);
+void   regfree(regex_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !LIBUXRE_REGEX_H */
diff --git a/libuxre/regexec.c b/libuxre/regexec.c
new file mode 100644 (file)
index 0000000..667868f
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Changes by Gunnar Ritter, Freiburg i. Br., Germany, November 2002.
+ *
+ * Sccsid @(#)regexec.c        1.7 (gritter) 2/6/05
+ */
+/*  UNIX(R) Regular Expresssion Library
+ *
+ *  Note: Code is released under the GNU LGPL
+ *
+ *  Copyright (C) 2001 Caldera International, Inc.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to:
+ *        Free Software Foundation, Inc.
+ *        59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*     #include "synonyms.h"   */
+#include "re.h"
+
+/*     #pragma weak regexec = _regexec */
+
+int
+regexec(const regex_t *ep, const char *s, size_t n, regmatch_t *mp, int flg)
+{
+       Exec ex;
+       int ret;
+
+       ex.flags = flg | (ep->re_flags & (REG_NEWLINE|REG_ICASE|REG_AVOIDNULL));
+       ex.str = (const unsigned char *)s;
+       ex.match = mp;
+       ex.mb_cur_max = ep->re_mb_cur_max;
+       if ((ex.nmatch = n) != 0) /* impose limits from compile flags */
+       {
+               if (ep->re_flags & REG_NOSUB)
+                       n = ex.nmatch = 0;
+               else if (ep->re_flags & REG_ONESUB)
+                       ex.nmatch = 1;
+               else if (n > ep->re_nsub + 1)
+                       ex.nmatch = ep->re_nsub + 1;
+       }
+       if (ep->re_flags & REG_DFA && ex.nmatch <= 1)
+               ret = libuxre_regdfaexec(ep->re_dfa, &ex);
+       else
+               ret = libuxre_regnfaexec(ep->re_nfa, &ex);
+       /*
+       * Fill unused part of mp[].
+       */
+       if (ret != 0)
+               ex.nmatch = 0;
+       while (n > ex.nmatch)
+       {
+               n--;
+               mp[n].rm_so = -1;
+               mp[n].rm_eo = -1;
+       }
+       return ret;
+}
diff --git a/libuxre/regfree.c b/libuxre/regfree.c
new file mode 100644 (file)
index 0000000..31180d7
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Changes by Gunnar Ritter, Freiburg i. Br., Germany, November 2002.
+ *
+ * Sccsid @(#)regfree.c        1.3 (gritter) 9/22/03
+ */
+/*  UNIX(R) Regular Expresssion Library
+ *
+ *  Note: Code is released under the GNU LGPL
+ *
+ *  Copyright (C) 2001 Caldera International, Inc.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to:
+ *        Free Software Foundation, Inc.
+ *        59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*     #include "synonyms.h"   */
+#include "re.h"
+
+/*     #pragma weak regfree = _regfree */
+
+void
+regfree(regex_t *ep)
+{
+       if (ep->re_flags & REG_DFA)
+               libuxre_regdeldfa(ep->re_dfa);
+       if (ep->re_flags & REG_NFA)
+               libuxre_regdelnfa(ep->re_nfa);
+       if (ep->re_col != 0)
+               (void)libuxre_lc_collate(ep->re_col);
+}
diff --git a/libuxre/regnfa.c b/libuxre/regnfa.c
new file mode 100644 (file)
index 0000000..6953f1f
--- /dev/null
@@ -0,0 +1,1070 @@
+/*
+ * Changes by Gunnar Ritter, Freiburg i. Br., Germany, November 2002.
+ *
+ * Sccsid @(#)regnfa.c 1.8 (gritter) 2/6/05
+ */
+/*  UNIX(R) Regular Expresssion Library
+ *
+ *  Note: Code is released under the GNU LGPL
+ *
+ *  Copyright (C) 2001 Caldera International, Inc.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to:
+ *        Free Software Foundation, Inc.
+ *        59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*     #include "synonyms.h"   */
+#include <string.h>
+#include <stdlib.h>
+#include "re.h"
+#include <stddef.h>
+#include <ctype.h>
+
+typedef unsigned char  Uchar;
+typedef unsigned short Ushort;
+
+/*
+* Nondeterministic Finite Automata.
+*/
+typedef struct t_graph Graph;
+struct t_graph
+{
+       union
+       {
+               Graph   *ptr;
+               Info    info;
+       } alt;
+       Graph           *next;
+       w_type          op;
+};
+
+typedef struct t_stack Stack;
+struct t_stack
+{
+       Stack           *link;  /* simplifies cleanup */
+       Stack           *prev;  /* covered states */
+       Graph           *wasgp; /* node associated with this state */
+       const Uchar     *str;   /* saved position in the string */
+       Ushort          cnt;    /* ROP_BRACE: traversal count */
+};
+
+       /*
+       * A Context holds all the information needed for each
+       * potential path through the NFA graph.
+       */
+typedef struct t_ctxt  Context;
+struct t_ctxt
+{
+       Context         *link;  /* simplifies cleanup */
+       Context         *next;  /* singly linked */
+       Stack           *sp;    /* nested counts */
+       Graph           *gp;    /* starting node */
+       Graph           *wasgp; /* node associated with this state */
+       const Uchar     *str;   /* saved position in the string */
+       Ushort          cnt;    /* ROP_BRACE: traversal count */
+       size_t          nset;   /* length of rm[] that is currently set */
+       regmatch_t      rm[1];  /* enough to cover re_nsub+1 (np->rmlen) */
+};
+
+struct re_nfa_ /*Nfa*/
+{
+       Graph   *gp;    /* entire NFA */
+       Stack   *sp;    /* unused Stacks */
+       Stack   *allsp; /* linked Stacks (for cleanup) */
+       Context *allcp; /* linked Contexts (for cleanup) */
+       Context *cur;   /* Contexts to be continued now */
+       Context *step;  /* Contexts waiting for a step of the NFA */
+       Context *avail; /* unused Contexts */
+       Context **ecur; /* ends cur list of Contexts */
+       Context **estp; /* ends step list of Contexts */
+       size_t  rmlen;  /* length of rm[] in each Context */
+       size_t  rmmin;  /* minimum length needed */
+       size_t  used;   /* length used for this libuxre_regnfaexec() */
+       w_type  beg;    /* nonzero for fixed char initial node NFAs */
+};
+
+#define ROP_MTOR       ROP_CAT /* ROP_OR, except might be empty loop */
+
+       /*
+       * Depth first traversal.
+       * Make a singly linked list (in alt.ptr) of the graph's nodes.
+       * Must toss any ROP_BKTs, too, since "alt" is overwritten.
+       */
+static void
+deltolist(Graph *gp, Graph **list)
+{
+       Graph *ptr;
+
+       if ((ptr = gp->next) != 0) /* first time */
+       {
+               gp->next = 0;
+               if (gp->op == ROP_OR || gp->op == ROP_MTOR)
+                       deltolist(gp->alt.ptr, list);
+               deltolist(ptr, list);
+               if (gp->op == ROP_BKT)
+               {
+                       libuxre_bktfree(gp->alt.info.bkt);
+                       free(gp->alt.info.bkt);
+               }
+       }
+       else if (gp->op == ROP_END)
+               gp->op = ROP_NOP;
+       else
+               return;
+       gp->alt.ptr = *list;
+       *list = gp;
+}
+
+       /*
+       * After the list is turned into a linked list,
+       * walk that list freeing the nodes.
+       */
+static void
+delgraph(Graph *gp)
+{
+       Graph *gp2, end;
+
+       gp2 = &end;
+       deltolist(gp, &gp2);
+       while ((gp = gp2) != &end)
+       {
+               gp2 = gp->alt.ptr;
+               free(gp);
+       }
+}
+
+       /*
+       * Depth first traversal.
+       * Look for ROP_NOPs and prune them from the graph.
+       * Chain them all together on *nop's list.
+       */
+static Graph *
+nopskip(Graph *gp, Graph **nop)
+{
+       Graph *ptr;
+
+       if ((ptr = gp->next) != 0) /* might have yet to do this subgraph */
+       {
+               if (gp->op == ROP_NOP)
+               {
+                       if (gp->alt.ptr != 0) /* touched */
+                               return gp->next; /* already did it */
+                       gp->alt.ptr = *nop;
+                       *nop = gp;
+               }
+               gp->next = 0; /* this subgraph's pending */
+               if (gp->op == ROP_OR || gp->op == ROP_MTOR)
+                       gp->alt.ptr = nopskip(gp->alt.ptr, nop);
+               gp->next = nopskip(ptr, nop);
+               if (gp->op == ROP_NOP)
+                       return gp->next;
+       }
+       return gp;
+}
+
+       /*
+       * Postorder traversal of the parse tree.
+       * Build a graph using "Thompson's" algorithm.
+       * The only significant modification is the
+       * ROP_BRACE->ROP_MTOR construction.
+       * Returns 1 => graph might match empty
+       *         0 => graph cannot match empty
+       *        -1 => error (in allocation)
+       */
+static int
+mkgraph(Tree *tp, Graph **first, Graph **last)
+{
+       Graph *new = 0, *nop, *lf, *ll, *rf, *rl;
+       int lmt, rmt = 0;
+
+       if (tp->op != ROP_CAT)
+       {
+               if ((new = malloc(sizeof(Graph))) == 0)
+                       return 0;
+               new->op = tp->op;       /* usually */
+       }
+       switch (tp->op)
+       {
+       case ROP_REF:
+               new->alt.info.sub = tp->right.info.sub;
+               *first = new;
+               *last = new;
+               return 1; /* safe--can't really tell */
+       case ROP_BKT:
+               tp->op = ROP_BKTCOPY;   /* now graph owns clean up */
+               /*FALLTHROUGH*/
+       case ROP_BKTCOPY:
+               new->alt.info.bkt = tp->right.info.bkt;
+               /*FALLTHROUGH*/
+       default:
+               *first = new;
+               *last = new;
+               return 0;
+       case ROP_EMPTY:
+               new->op = ROP_NOP;
+               new->alt.ptr = 0;       /* untouched */
+               *first = new;
+               *last = new;
+               return 1;
+       case ROP_OR:
+       case ROP_CAT:
+               lf = 0; /* in case of error */
+               if ((rmt = mkgraph(tp->right.ptr, &rf, &rl)) < 0)
+                       goto err;
+               /*FALLTHROUGH*/
+       case ROP_STAR:
+       case ROP_PLUS:
+       case ROP_QUEST:
+       case ROP_BRACE:
+       case ROP_LP:
+               if ((lmt = mkgraph(tp->left.ptr, &lf, &ll)) < 0)
+                       goto err;
+               break;
+       }
+       /*
+       * Note that ROP_NOP only serves as the node that reconnects
+       * the two choices of an incoming ROP_OR or ROP_QUEST.  To
+       * prevent rewalking portions of the graph in nopskip(),
+       * this code marks all ROP_NOP nodes as currently untouched.
+       */
+       switch (tp->op)
+       {
+       case ROP_OR:
+               if ((nop = malloc(sizeof(Graph))) == 0)
+                       goto err;
+               nop->op = ROP_NOP;
+               nop->alt.ptr = 0;       /* untouched */
+               ll->next = nop;
+               rl->next = nop;
+               new->next = lf;
+               new->alt.ptr = rf;
+               *first = new;
+               *last = nop;
+               return lmt | rmt;
+       case ROP_CAT:   /* no "new" */
+               ll->next = rf;
+               *first = lf;
+               *last = rl;
+               return lmt & rmt;
+       case ROP_QUEST:
+               if ((nop = malloc(sizeof(Graph))) == 0)
+                       goto err;
+               nop->op = ROP_NOP;
+               nop->alt.ptr = 0;       /* untouched */
+               new->op = ROP_OR;
+               new->next = lf;
+               new->alt.ptr = nop;
+               ll->next = nop;
+               *first = new;
+               *last = nop;
+               return 1;
+       case ROP_STAR:
+               *first = new;
+               rmt = 1;
+       star:;
+               new->op = lmt ? ROP_MTOR : ROP_OR;
+               new->alt.ptr = lf;
+               ll->next = new;
+               *last = new;
+               return rmt;
+       case ROP_PLUS:
+               *first = lf;
+               rmt = lmt;
+               goto star;
+       case ROP_BRACE:
+               if ((nop = malloc(sizeof(Graph))) == 0)
+                       goto err;
+               nop->op = ROP_MTOR; /* going to save state anyway... */
+               nop->alt.ptr = lf;
+               ll->next = new;
+               new->next = nop;
+               new->alt.info.num[1] = tp->right.info.num[1];
+               if ((new->alt.info.num[0] = tp->right.info.num[0]) == 0)
+               {
+                       lmt = 1;
+                       *first = new;
+               }
+               else
+               {
+                       new->alt.info.num[0]--; /* already done 1 */
+                       if (new->alt.info.num[1] != BRACE_INF)
+                               new->alt.info.num[1]--; /* likewise */
+                       *first = lf;
+               }
+               *last = nop;
+               return lmt;
+       case ROP_LP:
+               if ((nop = malloc(sizeof(Graph))) == 0)
+                       goto err;
+               nop->op = ROP_RP;
+               nop->alt.info.sub = tp->right.info.sub;
+               new->alt.info.sub = tp->right.info.sub;
+               new->next = lf;
+               ll->next = nop;
+               *first = new;
+               *last = nop;
+               return lmt;
+       }
+err:;
+       if (KIND_ROP(tp->op) == BINARY_ROP && rf != 0)
+               delgraph(rf);
+       if (lf != 0)
+               delgraph(lf);
+       if (tp->op != ROP_CAT)
+               free(new);
+       return -1;
+}
+
+       /*
+       * Semi-preorder traversal.
+       * Return zero if there's no simple first character
+       * (including the operation ROP_BOL) that must always
+       * be at the start of a matching string.
+       * This code doesn't attempt to get an answer if the
+       * first of the tree many be empty.
+       */
+static w_type
+firstop(Tree *tp)
+{
+       w_type op;
+
+       switch (tp->op)
+       {
+       case ROP_OR:
+               if ((op = firstop(tp->left.ptr)) == 0
+                       || op != firstop(tp->right.ptr))
+               {
+                       return 0;
+               }
+               return op;
+       case ROP_BRACE:
+               if (tp->right.info.num[0] == 0)
+                       return 0;
+               /*FALLTHROUGH*/
+       case ROP_CAT:
+       case ROP_PLUS:
+       case ROP_LP:
+               return firstop(tp->left.ptr);
+       default:
+               if (tp->op < 0)
+                       return 0;
+               /*FALLTHROUGH*/
+       case ROP_BOL:
+               return tp->op;
+       }
+}
+
+void
+libuxre_regdelnfa(Nfa *np)
+{
+       Context *cp, *cpn;
+       Stack *sp, *spn;
+
+       if (np->gp != 0)
+               delgraph(np->gp);
+       for (cp = np->allcp; cp != 0; cp = cpn)
+       {
+               cpn = cp->link;
+               free(cp);
+       }
+       for (sp = np->allsp; sp != 0; sp = spn)
+       {
+               spn = sp->link;
+               free(sp);
+       }
+       free(np);
+}
+
+LIBUXRE_STATIC int
+libuxre_regnfacomp(regex_t *ep, Tree *tp, Lex *lxp)
+{
+       Graph *gp, end;
+       Nfa *np;
+
+       if ((np = malloc(sizeof(Nfa))) == 0)
+               goto err;
+       np->gp = 0; /* in case of error */
+       if (mkgraph(tp, &np->gp, &gp) < 0)
+               goto err;
+       gp->next = 0;   /* nothing follows ROP_END */
+       np->rmlen = 0;
+       if ((ep->re_flags & REG_NOSUB) == 0)
+               np->rmlen = ep->re_nsub + 1;
+       np->rmmin = 0;
+       if (lxp->maxref != 0 && (np->rmmin = lxp->maxref + 1) > np->rmlen)
+               np->rmlen = np->rmmin;
+       /*
+       * Delete all ROP_NOPs from the graph.
+       * nopskip() disconnects them from the graph and
+       * links them together through their alt.ptr's.
+       */
+       gp = &end;
+       np->gp = nopskip(np->gp, &gp);
+       while (gp != &end)
+       {
+               Graph *gp2 = gp;
+
+               gp = gp->alt.ptr;
+               free(gp2);
+       }
+       np->sp = 0;
+       np->allsp = 0;
+       np->avail = 0;
+       np->allcp = 0;
+       ep->re_nfa = np;
+       np->beg = firstop(tp); 
+       return 0;
+err:;
+       if (np != 0)
+       {
+               if (np->gp != 0)
+                       delgraph(np->gp);
+               free(np);
+       }
+       return REG_ESPACE;
+}
+
+static Stack *
+newstck(Nfa *np)
+{
+       Stack *sp, **spp;
+       int i;
+
+       if ((sp = np->sp) == 0) /* get more */
+       {
+               spp = &np->sp;
+               i = 4;
+               while ((sp = malloc(sizeof(Stack))) != 0)
+               {
+                       sp->link = np->allsp;
+                       np->allsp = sp;
+                       *spp = sp;
+                       spp = &sp->prev;
+                       if (--i == 0)
+                               break;
+               }
+               *spp = 0;
+               if ((sp = np->sp) == 0) /* first malloc failed */
+                       return 0;
+       }
+       np->sp = sp->prev;
+       return sp;
+}
+
+static int
+mkstck(Nfa *np, Context *cp, Graph *gp)
+{
+       Stack *new, *sp;
+
+       if (gp == 0)    /* copy existing stack tail */
+       {
+               /*
+               * Hoist up top of stack.
+               */
+               new = cp->sp;
+               cp->wasgp = new->wasgp;
+               cp->str = new->str;
+               cp->cnt = new->cnt;
+               cp->sp = new->prev;
+               if ((sp = new->prev) == 0) /* only one below */
+               {
+                       new->prev = np->sp;
+                       np->sp = new;
+                       cp->sp = 0;
+                       return 0;
+               }
+               for (;;) /* copy the rest; reusing the old top */
+               {
+                       new->wasgp = sp->wasgp;
+                       new->str = sp->str;
+                       new->cnt = sp->cnt;
+                       if ((new->prev = sp->prev) == 0)
+                               break;
+                       if ((new->prev = newstck(np)) == 0)
+                               return REG_ESPACE;
+                       new = new->prev;
+                       sp = sp->prev;
+               }
+               return 0;
+       }
+       if (cp->wasgp != 0)     /* push current down */
+       {
+               if ((new = newstck(np)) == 0)
+                       return REG_ESPACE;
+               new->prev = cp->sp;
+               cp->sp = new;
+               new->wasgp = cp->wasgp;
+               new->str = cp->str;
+               new->cnt = cp->cnt;
+       }
+       cp->wasgp = gp;
+       cp->str = 0;
+       cp->cnt = 0;
+       return 0;
+}
+
+       /*
+       * Allocate a new Context (from np->avail)
+       * and add it to the end of the current list.
+       */
+static int
+newctxt(Nfa *np, Context *cp, Graph *gp)
+{
+       Context *new;
+       size_t n;
+
+       if ((new = np->avail) == 0)     /* need more */
+       {
+               Context *ncp, **cpp;
+               int i;
+
+               /*
+               * Can't easily allocate Contexts in one call because
+               * the alignments (given the varying length of rm[])
+               * are potentially nontrivial.
+               */
+               n = offsetof(Context, rm) + np->rmlen * sizeof(regmatch_t);
+               i = 4;
+               cpp = &np->avail;
+               while ((ncp = malloc(n)) != 0)
+               {
+                       ncp->link = np->allcp;
+                       np->allcp = ncp;
+                       *cpp = ncp;
+                       cpp = &ncp->next;
+                       if (--i == 0)
+                               break;
+               }
+               *cpp = 0;
+               if ((new = np->avail) == 0)     /* first malloc failed */
+                       return REG_ESPACE;
+       }
+       np->avail = new->next;
+       new->next = 0;
+       new->gp = gp;
+       new->sp = 0;
+       new->wasgp = 0;
+       new->nset = 0;
+       if (cp != 0) /* copy existing context information */
+       {
+               if (cp->sp != 0) /* copy tail of stack */
+               {
+                       new->sp = cp->sp;
+                       if (mkstck(np, new, 0) != 0)
+                               return REG_ESPACE;
+               }
+               new->wasgp = cp->wasgp;
+               new->str = cp->str;
+               new->cnt = cp->cnt;
+               /*
+               * Copy any valid subexpression match information
+               * from the existing context.
+               */
+               if (np->used != 0 && (n = cp->nset) != 0)
+               {
+                       regmatch_t *rmn = new->rm, *rmo = cp->rm;
+
+                       new->nset = n;
+                       for (;; ++rmn, ++rmo)
+                       {
+                               rmn->rm_so = rmo->rm_so;
+                               rmn->rm_eo = rmo->rm_eo;
+                               if (--n == 0)
+                                       break;
+                       }
+               }
+       }
+       /*
+       * Append it to the end of the current Context list.
+       */
+       *np->ecur = new;
+       np->ecur = &new->next;
+       return 0;
+}
+
+       /*
+       * Compare two byte string sequences for equality.
+       * If REG_ICASE, walk through the strings doing
+       * caseless comparisons of the wide characters.
+       */
+static int
+casecmp(const Uchar *s, Exec *xp, ssize_t i, ssize_t n, int mb_cur_max)
+{
+       const Uchar *p = &xp->str[i];
+       const Uchar *end;
+       w_type wc1, wc2;
+       int k;
+
+       if (strncmp((char *)s, (char *)p, n) == 0) /* try for exact match */
+               return 1;
+       if ((xp->flags & REG_ICASE) == 0)
+               return 0;
+       /*
+       * Walk through each testing for a match, ignoring case,
+       * of the resulting wide characters.
+       * Note that only "s" can run out of characters.
+       */
+       end = &p[n];
+       do
+       {
+               if ((wc1 = *s++) == '\0')
+                       return 0;
+               if (!ISONEBYTE(wc1) && (k = libuxre_mb2wc(&wc1, s)) > 0)
+                       s += k;
+               if (!ISONEBYTE(wc2 = *p++) && (k = libuxre_mb2wc(&wc2, p)) > 0)
+                       p += k;
+               if (wc1 != wc2)
+               {
+                       wc1 = to_lower(wc1);
+                       wc2 = to_lower(wc2);
+                       if (wc1 != wc2)
+                               return 0;
+               }
+       } while (p < end);
+       return 1;
+}
+
+LIBUXRE_STATIC int
+libuxre_regnfaexec(Nfa *np, Exec *xp)
+{
+       const Uchar *s, *s1, *s2;
+       Context *cp, *cpn;
+       Graph *gp, *brace;
+       Stack *sp, *spn;
+       ssize_t rmso, len;
+       int i, ret, mb_cur_max;
+       w_type wc;
+       size_t n;
+
+       ret = 0;        /* assume it matches */
+       rmso = -1;      /* but no match yet */
+       np->cur = 0;
+       np->step = 0;
+       np->ecur = &np->cur;
+       np->estp = &np->step;
+       if ((np->used = xp->nmatch) < np->rmmin)
+               np->used = np->rmmin;
+       s1 = 0;         /* one char back */
+       s = xp->str;    /* current high water in string */
+       mb_cur_max = xp->mb_cur_max;
+       for (;;)
+       {
+               /*
+               * Get next character from string.
+               * If the engine proper hasn't started and the engine
+               * requires a particular character to start and this
+               * character isn't it, try the next one.
+               */
+               for (;;)
+               {
+                       s2 = s1;
+                       s1 = s;
+                       if (!ISONEBYTE(wc = *s++) &&
+                                       (i = libuxre_mb2wc(&wc, s)) > 0)
+                               s += i;
+                       if (np->cur != 0 || np->beg == wc || np->beg == 0)
+                               break;
+                       if (np->beg == ROP_BOL)
+                       {
+                               if (s2 == 0 && (xp->flags & REG_NOTBOL) == 0)
+                                       break;
+                               if ((xp->flags & REG_NEWLINE) == 0)
+                                       goto nomatch;
+                               if (s2 != 0 && *s2 == '\n')
+                                       break;
+                       }
+                       if (wc == '\0')
+                               goto nomatch;
+               }
+               /*
+               * Start the engine by inserting a fresh initial context
+               * if there's no known match as yet.  (Once some match
+               * has been found, the end is near.)
+               */
+               if (rmso < 0 && newctxt(np, 0, np->gp) != 0)
+                       goto err;
+               /*
+               * Walk the current Contexts list, trying each.
+               * "loop" is when a new Context is to be tried,
+               * "again" is when the same Context continues,
+               * but wc was not yet matched.
+               */
+               cp = np->cur;
+       loop:;
+               gp = cp->gp;
+       again:;
+               switch (gp->op)
+               {
+               case ROP_BRACE: /* gp->next->op == ROP_MTOR */
+                       brace = gp;
+                       gp = gp->next;
+                       goto mtor;
+               case ROP_MTOR:
+                       brace = 0;
+               mtor:;
+                       if (cp->wasgp != gp) /* first time */
+                       {
+                               if (mkstck(np, cp, gp) != 0)
+                                       goto err;
+                       }
+                       else if (cp->str == s) /* spinning */
+                               goto poptonext;
+                       cp->str = s;
+                       if (brace != 0)
+                       {
+                               if (cp->cnt >= brace->alt.info.num[1])
+                                       goto poptonext;
+                               if (++cp->cnt <= brace->alt.info.num[0])
+                               {
+                                       gp = gp->alt.ptr;
+                                       goto again;
+                               }
+                               if (cp->cnt > BRACE_MAX)
+                                       cp->cnt = BRACE_MAX;
+                       }
+                       if (newctxt(np, cp, gp->alt.ptr) != 0)
+                               goto err;
+               poptonext:;
+                       cp->wasgp = 0;
+                       if ((sp = cp->sp) != 0) /* pop stack */
+                       {
+                               cp->sp = sp->prev;
+                               cp->wasgp = sp->wasgp;
+                               cp->str = sp->str;
+                               cp->cnt = sp->cnt;
+                               sp->prev = np->sp;
+                               np->sp = sp;
+                       }
+                       /*FALLTHROUGH*/
+               case ROP_EMPTY:
+               tonext:;
+                       gp = gp->next;
+                       goto again;
+               case ROP_OR:
+                       if (newctxt(np, cp, gp->alt.ptr) != 0)
+                               goto err;
+                       goto tonext;
+               case ROP_LP:
+                       if ((n = gp->alt.info.sub) < np->used)
+                       {
+                               size_t k;
+
+                               cp->rm[n].rm_so = s1 - xp->str;
+                               cp->rm[n].rm_eo = -1;
+                               /*
+                               * Mark any skipped subexpressions as
+                               * failing to participate in the match.
+                               */
+                               if ((k = cp->nset) < n)
+                               {
+                                       regmatch_t *rmp = &cp->rm[k];
+
+                                       for (;; rmp++)
+                                       {
+                                               rmp->rm_so = -1;
+                                               rmp->rm_eo = -1;
+                                               if (++k >= n)
+                                                       break;
+                                       }
+                               }
+                               cp->nset = n + 1;
+                       }
+                       goto tonext;
+               case ROP_RP:
+                       if ((n = gp->alt.info.sub) < np->used)
+                               cp->rm[n].rm_eo = s1 - xp->str;
+                       goto tonext;
+               case ROP_BOL:
+                       if (s2 == 0)
+                       {
+                               if (xp->flags & REG_NOTBOL)
+                                       goto failed;
+                       }
+                       else if ((xp->flags & REG_NEWLINE) == 0 || *s2 != '\n')
+                               goto failed;
+                       goto tonext;
+               case ROP_EOL:
+                       if (wc == '\0')
+                       {
+                               if (xp->flags & REG_NOTEOL)
+                                       goto failed;
+                       }
+                       else if ((xp->flags & REG_NEWLINE) == 0 || wc != '\n')
+                               goto failed;
+                       goto tonext;
+               default:        /* character match */
+                       if (gp->op != wc)
+                       {
+                               if ((xp->flags & REG_ICASE) == 0
+                                       || gp->op != to_lower(wc))
+                               {
+                                       goto failed;
+                               }
+                       }
+               nextwc:;
+                       cp->gp = gp->next;
+               tostep:;
+                       cpn = cp->next;
+                       cp->next = 0;
+                       *np->estp = cp;
+                       np->estp = &cp->next;
+                       if ((cp = cpn) == 0)
+                               break;
+                       goto loop;
+               case ROP_NOTNL:
+                       if (wc == '\n')
+                               goto failed;
+                       /*FALLTHROUGH*/
+               case ROP_ANYCH:
+                       if (wc > '\0')
+                               goto nextwc;
+                       /*FALLTHROUGH*/
+               case ROP_NONE:
+               failed:;
+                       cpn = cp->next;
+                       cp->next = np->avail;
+                       np->avail = cp;
+                       if ((cp = cpn) == 0)
+                               break;
+                       goto loop;
+               case ROP_LT:
+                       if (s2 == 0)
+                       {
+                               if (xp->flags & REG_NOTBOL)
+                                       goto failed;
+                       }
+                       else
+                       {
+                               w_type pwc;
+
+                               if (wc != '_' &&
+                                   !iswalnum(mb_cur_max == 1 ? btowc(wc) : wc))
+                                       goto failed;
+                               if (!ISONEBYTE(pwc = *s2))
+                                       libuxre_mb2wc(&pwc, &s2[1]);
+                               if (pwc == '_' ||
+                                   iswalnum(mb_cur_max== 1 ? btowc(pwc) : pwc))
+                                       goto failed;
+                       }
+                       goto tonext;
+               case ROP_GT:
+                       if (wc == '_' ||
+                           iswalnum(mb_cur_max == 1 ? btowc(wc) : wc))
+                               goto failed;
+                       goto tonext;
+               case ROP_BKT:
+               case ROP_BKTCOPY:
+                       if (cp->wasgp == gp) /* rest of MCCE */
+                       {
+                       checkspin:;
+                               if (s1 >= cp->str) /* got it all */
+                                       goto poptonext;
+                               goto tostep;
+                       }
+                       if ((i = libuxre_bktmbexec(gp->alt.info.bkt, wc, s,
+                                                       mb_cur_max)) < 0)
+                               goto failed;
+                       if ((n = i) == 0)       /* only matched wc */
+                               goto nextwc;
+               spin:;
+                       if (mkstck(np, cp, gp) != 0)
+                               goto err;
+                       cp->gp = gp;    /* stay here until reach past s+n */
+                       cp->str = s + n;
+                       goto tostep;
+               case ROP_REF:
+                       if (cp->wasgp == gp)    /* rest of matched string */
+                               goto checkspin;
+                       if ((n = gp->alt.info.sub) >= cp->nset)
+                               goto failed;
+                       if ((len = cp->rm[n].rm_eo) < 0)
+                               goto failed;
+                       if ((len -= n = cp->rm[n].rm_so) == 0)
+                               goto tonext;
+                       if (casecmp(s1, xp, n, len, mb_cur_max) == 0)
+                               goto failed;
+                       if ((n = s - s1) >= len)
+                               goto nextwc;
+                       n = len - n;
+                       goto spin;
+               case ROP_END:   /* success! */
+                       if (xp->flags & REG_NONEMPTY)
+                       {
+                               if (s2 == 0)
+                                       goto failed;
+                       }
+                       if (xp->nmatch == 0)
+                               goto match;
+                       /*
+                       * Mark any skipped subexpressions as failing to match.
+                       */
+                       if ((n = cp->nset) < xp->nmatch)
+                       {
+                               do
+                               {
+                                       cp->rm[n].rm_so = -1;
+                                       cp->rm[n].rm_eo = -1;
+                               } while (++n < xp->nmatch);
+                       }
+                       /*
+                       * Note the left-most match that's longest.
+                       */
+                       n = cp->rm[0].rm_so;
+                       if (rmso < 0 || n < rmso)
+                       {
+                               rmso = n;
+                       record:;
+                               memcpy(xp->match, cp->rm,
+                                       xp->nmatch * sizeof(regmatch_t));
+                               goto failed;
+                       }
+                       if (rmso < n || xp->match[0].rm_eo > cp->rm[0].rm_eo)
+                               goto failed;
+                       if (xp->match[0].rm_eo < cp->rm[0].rm_eo)
+                               goto record;
+#if 0 /* maximize the lengths of earlier LP...RPs */
+                       /*
+                       * If both are of the same length and start
+                       * at the same point, choose the one with
+                       * a "longest submatch from left to right"
+                       * where an empty string wins over a nonmatch.
+                       */
+                       for (n = 1; n < xp->nmatch; n++)
+                       {
+                               ssize_t nlen;
+
+                               /*
+                               * First, go with the choice that has any
+                               * match for subexpr n.
+                               */
+                               len = xp->match[n].rm_eo;
+                               nlen = cp->rm[n].rm_eo;
+                               if (nlen < 0)
+                               {
+                                       if (len >= 0)
+                                               break;
+                               }
+                               else if (len < 0)
+                                       goto record;
+                               /*
+                               * Both have a match; go with the longer.
+                               */
+                               len -= xp->match[n].rm_so;
+                               nlen -= cp->rm[n].rm_so;
+                               if (nlen < len)
+                                       break;
+                               if (nlen > len)
+                                       goto record;
+                       }
+#else /* take LP and RP as "fence posts" and maximize earlier gaps */
+                       /*
+                       * If both are of the same length and start
+                       * at the same point, choose the one with
+                       * the larger earlier subpatterns, in which
+                       * each rm_so and rm_eo serves as a separator.
+                       */
+                       for (n = 1; n < xp->nmatch; n++)
+                       {
+                               ssize_t nlen;
+                               int     use;
+
+                               if (xp->flags & REG_AVOIDNULL) {
+                                       /*
+                                       * This is to to satisfy POSIX.1-2001
+                                       * XBD pp. 172-173 ll. 6127-6129, whose
+                                       * translation is "do not match null
+                                       * expressions if there is a choice".
+                                       * See also POSIX.2 interpretation #43
+                                       * in which the question was raised.
+                                       *
+                                       * The first subexpression of "\(x*\)*"
+                                       * must thus match the string "xxx".
+                                       */
+                                       use = cp->rm[n].rm_eo -
+                                                       cp->rm[n].rm_so >=
+                                               xp->match[n].rm_eo -
+                                                       xp->match[n].rm_so ||
+                                               xp->match[n].rm_so < 0;
+                               } else
+                                       use = 1;
+                               /*
+                               * Choose the rightmost ROP_LP as that
+                               * maximizes the gap from before.
+                               */
+                               len = xp->match[n].rm_so;
+                               nlen = cp->rm[n].rm_so;
+                               if (len < nlen && use)
+                                       goto record;
+                               if (len > nlen)
+                                       break;
+                               /*
+                               * The ROP_LPs are at the same point:
+                               * Choose the rightmost ROP_RP.
+                               */
+                               len = xp->match[n].rm_eo;
+                               nlen = cp->rm[n].rm_eo;
+                               if (len < nlen && use)
+                                       goto record;
+                               if (len > nlen)
+                                       break;
+                       }
+#endif
+                       goto failed;
+               }
+               /*
+               * Finished the current Context list.  If the input string
+               * has been entirely scanned, we're done.  Otherwise, make
+               * the next step list current for the next character.
+               * If the next step list was empty and there's an existing
+               * match, that's the left-most longest.
+               */
+               if (wc == '\0')
+               {
+                       if (rmso >= 0)
+                               goto match;
+                       goto nomatch;
+               }
+               np->ecur = np->estp;
+               if ((np->cur = np->step) == 0)
+               {
+                       if (rmso >= 0)
+                               goto match;
+                       np->ecur = &np->cur; /* was pointing at step */
+               }
+               np->step = 0;
+               np->estp = &np->step;
+       }
+nomatch:;
+       ret = REG_NOMATCH;
+match:;
+       np->avail = 0;
+       for (cp = np->allcp; cp != 0; cp = cpn)
+       {
+               cpn = cp->link;
+               cp->next = np->avail;
+               np->avail = cp;
+       }
+       np->sp = 0;
+       for (sp = np->allsp; sp != 0; sp = spn)
+       {
+               spn = sp->link;
+               sp->prev = np->sp;
+               np->sp = sp;
+       }
+       return ret;
+err:;
+       ret = REG_ESPACE;
+       goto match;
+}
diff --git a/libuxre/regparse.c b/libuxre/regparse.c
new file mode 100644 (file)
index 0000000..0a5c6b2
--- /dev/null
@@ -0,0 +1,1091 @@
+/*
+ * Changes by Gunnar Ritter, Freiburg i. Br., Germany, November 2002.
+ *
+ * Sccsid @(#)regparse.c       1.12 (gritter) 9/22/03
+ */
+/*  UNIX(R) Regular Expresssion Library
+ *
+ *  Note: Code is released under the GNU LGPL
+ *
+ *  Copyright (C) 2001 Caldera International, Inc.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to:
+ *        Free Software Foundation, Inc.
+ *        59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*     #include "synonyms.h"   */
+#include <stdlib.h>
+#include <ctype.h>
+#include "re.h"
+
+LIBUXRE_STATIC void
+libuxre_regdeltree(Tree *tp, int all)
+{
+       if (tp == 0)
+               return;
+       if (tp->op < 0)
+       {
+               switch (KIND_ROP(tp->op))
+               {
+               case BINARY_ROP:
+                       libuxre_regdeltree(tp->right.ptr, all);
+                       /*FALLTHROUGH*/
+               case UNARY_ROP:
+                       libuxre_regdeltree(tp->left.ptr, all);
+                       break;
+               default:
+                       if (tp->op == ROP_BKT && all)
+                       {
+                               libuxre_bktfree(tp->right.info.bkt);
+                               free(tp->right.info.bkt);
+                       }
+                       break;
+               }
+       }
+       free(tp);
+}
+
+LIBUXRE_STATIC Tree *
+libuxre_reg1tree(w_type op, Tree *lp)
+{
+       Tree *tp;
+
+       if ((tp = malloc(sizeof(Tree))) == 0)
+       {
+               if (lp != 0)
+                       libuxre_regdeltree(lp, 1);
+               return 0;
+       }
+       tp->op = op;
+       tp->left.ptr = lp;
+       if (lp != 0)
+               lp->parent = tp;
+       return tp;
+}
+
+LIBUXRE_STATIC Tree *
+libuxre_reg2tree(w_type op, Tree *lp, Tree *rp)
+{
+       Tree *tp;
+
+       if ((tp = malloc(sizeof(Tree))) == 0)
+       {
+               libuxre_regdeltree(lp, 1);
+               libuxre_regdeltree(rp, 1);
+               return 0;
+       }
+       tp->op = op;
+       tp->left.ptr = lp;
+       lp->parent = tp;
+       tp->right.ptr = rp;
+       rp->parent = tp;
+       return tp;
+}
+
+static int
+lex(Lex *lxp)
+{
+       size_t num;
+       w_type wc;
+       int n, mb_cur_max;
+
+       mb_cur_max = lxp->mb_cur_max;
+nextc: switch (wc = *lxp->pat++) /* interesting ones are single bytes */
+       {
+       case '\0':
+               lxp->pat--;     /* continue to report ROP_END */
+               wc = ROP_END;
+               break;
+       case '(':
+               if (lxp->flags & REG_PARENS)
+               {
+               leftparen:;
+                       /*
+                       * Must keep track of the closed and
+                       * yet-to-be closed groups as a list.
+                       * Consider (()a(()b(()c(()d... in which
+                       * at each letter another even-numbered
+                       * group is made available, but no
+                       * odd-numbered ones are.
+                       */
+                       if ((lxp->flags & REG_NOBACKREF) == 0)
+                       {
+                               if (lxp->nleft >= lxp->nclist) /* grow it */
+                               {
+                                       unsigned char *p;
+
+                                       lxp->nclist += 8; /* arbitrary */
+                                       if ((p = realloc(lxp->clist,
+                                               lxp->nclist)) == 0)
+                                       {
+                                               lxp->err = REG_ESPACE;
+                                               return -1;
+                                       }
+                                       lxp->clist = p;
+                               }
+                               lxp->clist[lxp->nleft] = 0; /* unavailable */
+                       }
+                       lxp->nleft++;
+                       wc = ROP_LP;
+               }
+               break;
+       case ')':
+               /*
+               * For REG_PARENS, only take a right paren as a close
+               * if there is a matching left paren.
+               */
+               if (lxp->flags & REG_PARENS && lxp->nright < lxp->nleft)
+               {
+                       lxp->nright++;
+               rightparen:;
+                       /*
+                       * The group that is being closed is the highest
+                       * numbered as-yet-unclosed group.
+                       */
+                       if ((lxp->flags & REG_NOBACKREF) == 0)
+                       {
+                               num = lxp->nleft;
+                               while (lxp->clist[--num] != 0)
+                                       ;
+                               lxp->clist[num] = 1;
+                       }
+                       wc = ROP_RP;
+               }
+               break;
+       case '.':
+               wc = ROP_ANYCH;
+               if (lxp->flags & REG_NEWLINE)
+                       wc = ROP_NOTNL;
+               break;
+       case '*':
+               if (lxp->flags & REG_ADDITIVE)
+               {
+       nxtstar:        switch (*lxp->pat)
+                       {
+                       case '+':
+                               if ((lxp->flags & REG_PLUS) == 0)
+                                       break;
+                               lxp->pat++;
+                               goto nxtstar;
+                       case '?':
+                               if ((lxp->flags & REG_QUEST) == 0)
+                                       break;
+                               /*FALLTHRU*/
+                       case '*':
+                               lxp->pat++;
+                               goto nxtstar;
+                       }
+               }
+               wc = ROP_STAR;
+               break;
+       case '^':
+               /*
+               * Look "behind" to see if this is an anchor.
+               * Take it as an anchor if it follows an alternation
+               * operator.  (lxp->tok is initially set to ROP_OR.)
+               */
+               if (lxp->flags & REG_ANCHORS || lxp->tok == ROP_OR) {
+                       if (lxp->flags & REG_ADDITIVE)
+                       {
+                               int optional = 0;
+
+                       nxtcar: switch (*lxp->pat)
+                               {
+                               case '+':
+                                       if ((lxp->flags & REG_PLUS) == 0)
+                                               break;
+                                       lxp->pat++;
+                                       goto nxtcar;
+                               case '?':
+                                       if ((lxp->flags & REG_QUEST) == 0)
+                                               break;
+                                       /*FALLTHRU*/
+                               case '*':
+                                       optional = 1;
+                                       lxp->pat++;
+                                       goto nxtcar;
+                               }
+                               if (optional)
+                                       goto nextc;
+                       }
+                       wc = ROP_BOL;
+               }
+               break;
+       case '$':
+               /*
+               * Look ahead to see if this is an anchor,
+               * unless any '$' is an anchor.
+               * Take it as an anchor if it occurs just before
+               * the pattern end or an alternation operator.
+               */
+               if (lxp->flags & REG_ANCHORS || *lxp->pat == '\0'
+                       || (lxp->flags & REG_OR && *lxp->pat == '|')
+                       || (lxp->flags & REG_NLALT && *lxp->pat == '\n'))
+               {
+                       if (lxp->flags & REG_ADDITIVE)
+                       {
+                               int optional = 0;
+
+                       nxtdol: switch (*lxp->pat)
+                               {
+                               case '+':
+                                       if ((lxp->flags & REG_PLUS) == 0)
+                                               break;
+                                       lxp->pat++;
+                                       goto nxtdol;
+                               case '?':
+                                       if ((lxp->flags & REG_QUEST) == 0)
+                                               break;
+                                       /*FALLTHRU*/
+                               case '*':
+                                       optional = 1;
+                                       lxp->pat++;
+                                       goto nxtdol;
+                               }
+                               if (optional)
+                                       goto nextc;
+                       }
+                       wc = ROP_EOL;
+               }
+               break;
+       case '+':
+               if (lxp->flags & REG_PLUS)
+               {
+                       wc = ROP_PLUS;
+                       if (lxp->flags & REG_ADDITIVE)
+                       {
+               nxtplus:        switch (*lxp->pat)
+                               {
+                               case '?':
+                                       if ((lxp->flags & REG_QUEST) == 0)
+                                               break;
+                               case '*':
+                                       wc = ROP_STAR;
+                                       /*FALLTHRU*/
+                               case '+':
+                                       lxp->pat++;
+                                       goto nxtplus;
+                               }
+                       }
+               }
+               break;
+       case '?':
+               if (lxp->flags & REG_QUEST)
+               {
+                       wc = ROP_QUEST;
+                       if (lxp->flags & REG_ADDITIVE)
+                       {
+               nxtquest:       switch (*lxp->pat)
+                               {
+                               case '+':
+                                       if ((lxp->flags & REG_PLUS) == 0)
+                                               break;
+                               case '*':
+                                       wc = ROP_STAR;
+                                       /*FALLTHRU*/
+                               case '?':
+                                       lxp->pat++;
+                                       goto nxtquest;
+                               }
+                       }
+               }
+               break;
+       case '\n':
+               if (lxp->flags & REG_NLALT)
+               {
+                       /*
+                       * Even when newline is an alternative separator,
+                       * it doesn't permit parenthesized subexpressions
+                       * to include it.
+                       */
+                       if (lxp->nleft != lxp->nright)
+                       {
+                               lxp->err = REG_EPAREN;
+                               return -1;
+                       }
+                       wc = ROP_OR;
+               }
+               else if (lxp->flags & REG_NEWLINE)
+                       lxp->flags |= REG_NFA;
+               break;
+       case '|':
+               if (lxp->flags & REG_OR)
+                       wc = ROP_OR;
+               break;
+       case '[':
+               if ((lxp->info.bkt = malloc(sizeof(Bracket))) == 0)
+               {
+                       lxp->err = REG_ESPACE;
+                       return -1;
+               }
+               if ((lxp->flags & REG_GOTBKT) == 0)     /* first time */
+               {
+                       struct lc_collate *col;
+
+                       lxp->flags |= REG_GOTBKT;
+                       lxp->bktflags = 0;
+                       if (lxp->flags & REG_ICASE)
+                               lxp->bktflags |= BKT_ONECASE;
+                       if (lxp->flags & REG_NEWLINE)
+                               lxp->bktflags |= BKT_NOTNL;
+                       if (lxp->flags & REG_BADRANGE)
+                               lxp->bktflags |= BKT_BADRANGE;
+                       if (lxp->flags & REG_ODDRANGE)
+                               lxp->bktflags |= BKT_ODDRANGE;
+                       if (lxp->flags & REG_SEPRANGE)
+                               lxp->bktflags |= BKT_SEPRANGE;
+                       if (lxp->flags & REG_BKTQUOTE)
+                               lxp->bktflags |= BKT_QUOTE;
+                       if (lxp->flags & REG_BKTEMPTY)
+                               lxp->bktflags |= BKT_EMPTY;
+                       if (lxp->flags & REG_ESCNL)
+                               lxp->bktflags |= BKT_ESCNL;
+                       if (lxp->flags & REG_NLALT)
+                               lxp->bktflags |= BKT_NLBAD;
+                       if (lxp->flags & REG_ESCSEQ)
+                               lxp->bktflags |= BKT_ESCSEQ;
+                       if (lxp->flags & REG_BKTESCAPE)
+                               lxp->bktflags |= BKT_ESCAPE;
+                       if (lxp->flags & REG_NOI18N)
+                               lxp->bktflags |= BKT_NOI18N;
+                       if (lxp->flags & REG_OLDESC)
+                               lxp->bktflags |= BKT_OLDESC;
+                       if ((col = libuxre_lc_collate(0)) != 0)
+                       {
+                               if (col->maintbl == 0
+                                       || col->flags & CHF_ENCODED)
+                               {
+                                       (void)libuxre_lc_collate(col);
+                                       col = 0;
+                               }
+                               else if (col->flags & CHF_MULTICH)
+                                       lxp->flags |= REG_NFA;
+                       }
+                       lxp->col = col;
+               }
+               n = lxp->bktflags;
+               if (*lxp->pat == '^')
+               {
+                       n |= BKT_NEGATED;
+                       lxp->pat++;
+               }
+               lxp->info.bkt->col = lxp->col;
+               if ((n = libuxre_bktmbcomp(lxp->info.bkt, lxp->pat,
+                                               n, mb_cur_max)) < 0)
+               {
+                       free(lxp->info.bkt);
+                       lxp->err = -n;  /* convert to REG_* errors */
+                       return -1;
+               }
+               /*
+               * NFA forced if newline can be a match and REG_NEWLINE is set.
+               */
+               if ((lxp->flags & (REG_NFA | REG_NEWLINE)) == REG_NEWLINE
+                       && lxp->pat[-1] == '[' /* i.e., not BKT_NEGATED */
+                       && libuxre_bktmbexec(lxp->info.bkt, '\n', 0, 1) == 0)
+               {
+                       lxp->flags |= REG_NFA;
+               }
+               lxp->pat += n;
+               wc = ROP_BKT;
+               break;
+       case '{':
+               if (lxp->flags & REG_NOBRACES || (lxp->flags & REG_BRACES) == 0)
+                       break;
+       interval:;
+               if (!isdigit(num = *lxp->pat))
+               {
+               badbr:;
+                       lxp->err = REG_BADBR;
+                       if (*lxp->pat == '\0')
+                               lxp->err = REG_EBRACE; /* more accurate */
+                       return -1;
+               }
+               num -= '0';
+               while (isdigit(wc = *++lxp->pat))
+               {
+                       num *= 10;
+                       if ((num += wc - '0') > BRACE_MAX)
+                               goto badbr;
+               }
+               lxp->info.num[0] = num;
+               lxp->info.num[1] = num;
+               if (wc == ',')
+               {
+                       lxp->info.num[1] = BRACE_INF;
+                       if (isdigit(wc = *++lxp->pat))
+                       {
+                               num = wc - '0';
+                               while (isdigit(wc = *++lxp->pat))
+                               {
+                                       num *= 10;
+                                       if ((num += wc - '0') > BRACE_MAX)
+                                               goto badbr;
+                               }
+                               if (num < lxp->info.num[0])
+                                       goto badbr;
+                               lxp->info.num[1] = num;
+                       }
+               }
+               if ((lxp->flags & REG_BRACES) == 0)
+               {
+                       if (wc != '\\')
+                               goto badbr;
+                       wc = *++lxp->pat;
+               }
+               if (wc != '}')
+                       goto badbr;
+               lxp->pat++;
+               wc = ROP_BRACE;
+               /*
+               * Replace interval with simpler equivalents where possible,
+               * even when the operators are not otherwise available.
+               */
+               if (lxp->info.num[1] <= 1)
+               {
+                       if (lxp->info.num[0] == 1)
+                               wc = ROP_NOP;   /* {1,1} is noise */
+                       else if (lxp->info.num[1] == 0)
+                               wc = ROP_EMPTY; /* {0,0} is empty string */
+                       else
+                               wc = ROP_QUEST; /* {0,1} is ? */
+               }
+               else if (lxp->info.num[1] == BRACE_INF)
+               {
+                       if (lxp->info.num[0] == 0)
+                               wc = ROP_STAR;
+                       else if (lxp->info.num[0] == 1)
+                               wc = ROP_PLUS;
+                       else if (lxp->info.num[0] > BRACE_DFAMAX)
+                               lxp->flags |= REG_NFA;
+               }
+               else if (lxp->info.num[1] > BRACE_DFAMAX)
+               {
+                       lxp->flags |= REG_NFA;
+               }
+               break;
+       case '\\':
+               switch (wc = *lxp->pat++)
+               {
+               case '\0':
+                       lxp->err = REG_EESCAPE;
+                       return -1;
+               case '<':
+                       if (lxp->flags & REG_ANGLES)
+                       {
+                               lxp->flags |= REG_NFA;
+                               wc = ROP_LT;
+                       }
+                       goto out;
+               case '>':
+                       if (lxp->flags & REG_ANGLES)
+                       {
+                               lxp->flags |= REG_NFA;
+                               wc = ROP_GT;
+                       }
+                       goto out;
+               case '(':
+                       if ((lxp->flags & REG_PARENS) == 0)
+                               goto leftparen;
+                       goto out;
+               case ')':
+                       if ((lxp->flags & REG_PARENS) == 0)
+                       {
+                               if (++lxp->nright > lxp->nleft)
+                               {
+                                       lxp->err = REG_EPAREN;
+                                       return -1;
+                               }
+                               goto rightparen;
+                       }
+                       goto out;
+               case '{':
+                       if (lxp->flags & (REG_BRACES|REG_NOBRACES))
+                               goto out;
+                       goto interval;
+               case '1':
+               case '2':
+               case '3':
+               case '4':
+               case '5':
+               case '6':
+               case '7':
+               case '8':
+               case '9':
+                       num = wc - '0';
+                       if ((lxp->flags & REG_NOBACKREF) == 0)
+                       {
+                       backref:;
+                               if (num > lxp->nleft
+                                       || lxp->clist[num - 1] == 0)
+                               {
+                                       lxp->err = REG_ESUBREG;
+                                       return -1;
+                               }
+                               lxp->info.sub = num;
+                               if (lxp->maxref < num)
+                                       lxp->maxref = num;
+                               lxp->flags |= REG_NFA;
+                               wc = ROP_REF;
+                               goto out;
+                       }
+                       /*
+                       * For compatibility (w/awk), permit "octal" 8 and 9.
+                       * Already have the value of the first digit in num.
+                       *
+                       * If REG_OLDESC, exactly three digits must be present.
+                       */
+               tryoctal:;
+                       if ((lxp->flags & REG_ESCSEQ) == 0)
+                               goto out;
+                       if ((wc = *lxp->pat) >= '0' && wc <= '9')
+                       {
+                               num <<= 3;
+                               num += wc - '0';
+                               if ((wc = *++lxp->pat) >= '0' && wc <= '9')
+                               {
+                                       num <<= 3;
+                                       num += wc - '0';
+                                       lxp->pat++;
+                               }
+                               else if (lxp->flags & REG_OLDESC)
+                               {
+                                       lxp->pat--;
+                                       wc = lxp->pat[-1];
+                                       goto out;
+                               }
+                       }
+                       else if (lxp->flags & REG_OLDESC)
+                       {
+                               wc = lxp->pat[-1];
+                               goto out;
+                       }
+                       if ((wc = num) <= 0)
+                       {
+                               lxp->err = REG_BADESC;
+                               return -1;
+                       }
+                       goto out;
+               case '0':
+                       if ((lxp->flags & REG_NOBACKREF) == 0
+                               && (num = *lxp->pat) >= '0' && num <= '9')
+                       {
+                               num -= '0';
+                               /*
+                               * This loop ignores wraparounds.
+                               * Keep track of number of digits in n.
+                               */
+                               n = 1;
+                               while ((wc = *++lxp->pat) >= '0' && wc <= '9')
+                               {
+                                       num *= 10;
+                                       num += wc - '0';
+                                       n++;
+                               }
+                               if (num != 0)
+                                       goto backref;
+                               lxp->pat -= n;
+                       }
+                       num = 0;
+                       goto tryoctal;
+               case 'a':
+                       if ((lxp->flags&(REG_ESCSEQ|REG_OLDESC)) == REG_ESCSEQ)
+                               wc = '\a';
+                       goto out;
+               case 'b':
+                       if (lxp->flags & REG_ESCSEQ)
+                               wc = '\b';
+                       goto out;
+               case 'f':
+                       if (lxp->flags & REG_ESCSEQ)
+                               wc = '\f';
+                       goto out;
+               case 'n':
+                       if (lxp->flags & (REG_ESCSEQ | REG_ESCNL))
+                       {
+                               wc = '\n';
+                               if (lxp->flags & REG_NEWLINE)
+                                       lxp->flags |= REG_NFA;
+                       }
+                       goto out;
+               case 'r':
+                       if (lxp->flags & REG_ESCSEQ)
+                               wc = '\r';
+                       goto out;
+               case 't':
+                       if (lxp->flags & REG_ESCSEQ)
+                               wc = '\t';
+                       goto out;
+               case 'v':
+                       if ((lxp->flags&(REG_ESCSEQ|REG_OLDESC)) == REG_ESCSEQ)
+                               wc = '\v';
+                       goto out;
+               case 'x':
+                       if ((lxp->flags&(REG_ESCSEQ|REG_OLDESC)) == REG_ESCSEQ
+                               && isxdigit(num = *lxp->pat))
+                       {
+                               wc = num;
+                               num = 0;
+                               /*
+                               * Take as many hex digits as possible,
+                               * ignoring overflows.
+                               * If the result (squeezed into a w_type)
+                               * is positive, it's okay.
+                               */
+                               do
+                               {
+                                       if (isdigit(wc))
+                                               wc -= '0';
+                                       else if (isupper(wc))
+                                               wc -= 'A' + 10;
+                                       else
+                                               wc -= 'a' + 10;
+                                       num <<= 4;
+                                       num |= wc;
+                               } while (isxdigit(wc = *++lxp->pat));
+                               if ((wc = num) <= 0)
+                               {
+                                       lxp->err = REG_BADESC;
+                                       return -1;
+                               }
+                       }
+                       goto out;
+               }
+               /*FALLTHROUGH*/
+       default:
+               if (!ISONEBYTE(wc))
+               {
+                       if ((n = libuxre_mb2wc(&wc, lxp->pat)) > 0)
+                               lxp->pat += n;
+                       else if (n < 0)
+                       {
+                               lxp->err = REG_ILLSEQ;
+                               return -1;
+                       }
+               }
+               if (lxp->flags & REG_ICASE)
+                       wc = to_lower(wc);
+               break;
+       }
+out:;
+       lxp->tok = wc;
+       return 0;
+}
+
+static Tree    *alt(Lex *);
+
+static Tree *
+leaf(Lex *lxp)
+{
+       Tree *tp;
+
+       if ((tp = malloc(sizeof(Tree))) == 0)
+       {
+               lxp->err = REG_ESPACE;
+               return 0;
+       }
+       switch (tp->op = lxp->tok) /* covers most cases */
+       {
+       default:
+               if (tp->op < 0)
+               {
+                       lxp->err = REG_BADPAT;
+                       tp->right.ptr = 0;
+                       goto badunary;
+               }
+               break;
+       case ROP_STAR:
+       case ROP_PLUS:
+       case ROP_QUEST:
+               if ((lxp->flags & REG_NOAUTOQUOTE) == 0
+                       && lxp->pat[-1] != '}')
+               {
+                       tp->op = lxp->pat[-1];
+                       break;
+               }
+               /*FALLTHROUGH*/
+       case ROP_BRACE:
+       case ROP_EMPTY: /* was {0,0} ROP_BRACE */
+       case ROP_NOP:   /* was {1,1} ROP_BRACE */
+               lxp->err = REG_BADRPT;
+       badunary:;
+               tp->left.ptr = 0;
+               goto err;
+       case ROP_ANYCH:
+       case ROP_NOTNL:
+               break;
+       case ROP_BOL:
+       case ROP_EOL:
+       case ROP_LT:
+       case ROP_GT:
+               /*
+               * Look ahead for what would have been taken to be
+               * postfix operators.
+               */
+               if (lex(lxp) != 0)
+                       goto err;
+               switch (lxp->tok)
+               {
+               case ROP_STAR:
+               case ROP_PLUS:
+               case ROP_QUEST:
+                       if ((lxp->flags & REG_NOAUTOQUOTE) == 0
+                               && lxp->pat[-1] != '}')
+                       {
+                               lxp->tok = lxp->pat[-1];
+                               break;
+                       }
+                       /*FALLTHROUGH*/
+               case ROP_BRACE:
+               case ROP_EMPTY: /* was {0,0} ROP_BRACE */
+               case ROP_NOP:   /* was {1,1} ROP_BRACE */
+                       lxp->err = REG_BADRPT;
+                       goto err;
+               }
+               return tp;
+       case ROP_BKT:
+               tp->right.info.bkt = lxp->info.bkt;
+               break;
+       case ROP_REF:
+               tp->right.info.sub = lxp->info.sub;
+               break;
+       case ROP_LP:
+               tp->right.info.sub = lxp->nleft;
+               if (lex(lxp) != 0)
+                       goto badunary;
+               if (lxp->tok == ROP_RP) /* empty parens; choice of meaning */
+               {
+                       if (lxp->flags & REG_MTPARENBAD)
+                       {
+                               lxp->err = REG_EMPTYPAREN;
+                               goto badunary;
+                       }
+                       lxp->tok = ROP_EMPTY;
+                       if (lxp->flags & REG_MTPARENFAIL)
+                               lxp->tok = ROP_NONE;
+                       if ((tp->left.ptr = libuxre_reg1tree(lxp->tok, 0)) == 0)
+                               goto badunary;
+               }
+               else if ((tp->left.ptr = alt(lxp)) == 0)
+               {
+                       if (lxp->err == REG_BADPAT)
+                               goto parenerr;
+                       goto badunary;
+               }
+               else if (lxp->tok != ROP_RP)
+               {
+                       lxp->err = REG_BADPAT;
+               parenerr:;
+                       if (lxp->nleft != lxp->nright)
+                               lxp->err = REG_EPAREN;  /* better choice */
+                       goto badunary;
+               }
+               tp->left.ptr->parent = tp;
+               break;
+       }
+       if (lex(lxp) != 0)
+       {
+       err:;
+               libuxre_regdeltree(tp, 1);
+               tp = 0;
+       }
+       return tp;
+}
+
+static Tree *
+post(Lex *lxp)
+{
+       Tree *lp;
+
+       if ((lp = leaf(lxp)) == 0)
+               return 0;
+       switch (lxp->tok)
+       {
+       case ROP_EMPTY: /* this was {0,0} ROP_BRACE */
+               libuxre_regdeltree(lp, 1);
+               lp = 0;
+               /*FALLTHROUGH*/
+       case ROP_BRACE:
+       case ROP_STAR:
+       case ROP_PLUS:
+       case ROP_QUEST:
+               if ((lp = libuxre_reg1tree(lxp->tok, lp)) == 0)
+               {
+                       lxp->err = REG_ESPACE;
+                       return 0;
+               }
+               if (lxp->tok == ROP_BRACE)
+                       lp->right.info = lxp->info;
+               /*FALLTHROUGH*/
+       case ROP_NOP:   /* this was {1,1} ROP_BRACE */
+               if (lex(lxp) != 0)
+               {
+                       libuxre_regdeltree(lp, 1);
+                       return 0;
+               }
+               break;
+       }
+       return lp;
+}
+
+static Tree *
+cat(Lex *lxp)
+{
+       Tree *lp, *rp;
+
+       if ((lp = post(lxp)) == 0)
+               return 0;
+       for (;;)
+       {
+               if (lxp->tok == ROP_OR || lxp->tok == ROP_RP
+                       || lxp->tok == ROP_END)
+               {
+                       return lp;
+               }
+               if ((rp = post(lxp)) == 0)
+                       break;
+               if ((lp = libuxre_reg2tree(ROP_CAT, lp, rp)) == 0)
+               {
+                       lxp->err = REG_ESPACE;
+                       return 0;
+               }
+       }
+       libuxre_regdeltree(lp, 1);
+       return 0;
+}
+
+static Tree *
+alt(Lex *lxp)
+{
+       Tree *lp, *rp;
+
+       if ((lp = cat(lxp)) == 0)
+               return 0;
+       for (;;)
+       {
+               if (lxp->tok != ROP_OR)
+                       return lp;
+               if (lex(lxp) != 0)
+                       break;
+               if (lxp->tok == ROP_END)
+                       return lp;      /* ignore trailing '|' */
+               if ((rp = cat(lxp)) == 0)
+                       break;
+               if ((lp = libuxre_reg2tree(ROP_OR, lp, rp)) == 0)
+               {
+                       lxp->err = REG_ESPACE;
+                       return 0;
+               }
+       }
+       libuxre_regdeltree(lp, 1);
+       return 0;
+}
+
+LIBUXRE_STATIC Tree *
+libuxre_regparse(Lex *lxp, const unsigned char *pat, int flags)
+{
+       Tree *lp, *rp;
+
+       lp = 0;                 /* in case of error */
+       lxp->clist = 0;
+       lxp->col = 0;
+       lxp->err = 0;
+       lxp->maxref = 0;
+       lxp->nleft = 0;
+       lxp->nright = 0;
+       lxp->nclist = 0;
+       lxp->mb_cur_max = MB_CUR_MAX;
+       if (flags & REG_OR && *pat == '|')
+               pat++;  /* skip initial OR like egrep did */
+       lxp->pat = pat;
+       lxp->flags = flags;
+       lxp->tok = ROP_OR;      /* enables ^ as anchor */
+       /*
+       * Get initial token.
+       */
+       if (lex(lxp) != 0)
+       {
+       err:;
+               if (lp != 0)
+               {
+                       libuxre_regdeltree(lp, 1);
+                       lp = 0;
+               }
+               if (lxp->err == 0)
+                       lxp->err = REG_ESPACE;
+               goto ret;
+       }
+       if (lxp->tok == ROP_END)
+       {
+               lxp->err = REG_NOPAT;
+               goto err;
+       }
+       if ((lp = alt(lxp)) == 0)       /* parse entire RE */
+               goto err;
+       if (lxp->maxref != 0 || (flags & REG_NOSUB) == 0)
+       {
+               if ((lp = libuxre_reg1tree(ROP_LP, lp)) == 0)
+                       goto err;
+               lp->right.info.sub = 0;
+       }
+       if ((rp = libuxre_reg1tree(ROP_END, 0)) == 0)
+               goto err;
+       if ((lp = libuxre_reg2tree(ROP_CAT, lp, rp)) == 0)
+               goto err;
+       lp->parent = 0;
+ret:;
+       if (lxp->clist != 0)
+               free(lxp->clist);
+       return lp;
+}
+
+#ifdef REGDEBUG
+
+LIBUXRE_STATIC void
+libuxre_regtree(Tree *tp, int n)
+{
+       const char *opstr;
+       char buf[32];
+       int kind, next;
+
+       if (n < 0)
+               next = -n + 2;
+       else
+               next = n + 2;
+       switch (tp->op)
+       {
+       case ROP_OR:
+               opstr = "|";
+               kind = BINARY_ROP;
+               break;
+       case ROP_CAT:
+               opstr = "&";
+               kind = BINARY_ROP;
+               break;
+       case ROP_STAR:
+               opstr = "*";
+               kind = UNARY_ROP;
+               break;
+       case ROP_PLUS:
+               opstr = "+";
+               kind = UNARY_ROP;
+               break;
+       case ROP_QUEST:
+               opstr = "?";
+               kind = UNARY_ROP;
+               break;
+       case ROP_BRACE:
+               opstr = buf;
+               if (tp->right.info.num[1] == BRACE_INF)
+               {
+                       sprintf(buf, "{%u,inf}",
+                               (unsigned)tp->right.info.num[0]);
+               }
+               else
+               {
+                       sprintf(buf, "{%u,%u}",
+                               (unsigned)tp->right.info.num[0],
+                               (unsigned)tp->right.info.num[1]);
+               }
+               kind = UNARY_ROP;
+               break;
+       case ROP_LP:
+               opstr = buf;
+               sprintf(buf, "%lu(", (unsigned long)tp->right.info.sub);
+               kind = UNARY_ROP;
+               break;
+       case ROP_RP:
+               opstr = buf;
+               sprintf(buf, ")%lu", (unsigned long)tp->right.info.sub);
+               kind = UNARY_ROP;
+               break;
+       case ROP_NOP:
+               opstr = "<NOP>";
+               kind = LEAF_ROP;
+               break;
+       case ROP_BOL:
+               opstr = "<BOL>";
+               kind = LEAF_ROP;
+               break;
+       case ROP_EOL:
+               opstr = "<EOL>";
+               kind = LEAF_ROP;
+               break;
+       case ROP_ALL:
+               opstr = "<ALL>";
+               kind = LEAF_ROP;
+               break;
+       case ROP_ANYCH:
+               opstr = "<ANYCH>";
+               kind = LEAF_ROP;
+               break;
+       case ROP_NOTNL:
+               opstr = "<NOTNL>";
+               kind = LEAF_ROP;
+               break;
+       case ROP_EMPTY:
+               opstr = "<MT>";
+               kind = LEAF_ROP;
+               break;
+       case ROP_NONE:
+               opstr = "<NONE>";
+               kind = LEAF_ROP;
+               break;
+       case ROP_BKT:
+               opstr = buf;
+               sprintf(buf, "[%#lx]", (unsigned long)tp->right.info.bkt);
+               kind = LEAF_ROP;
+               break;
+       case ROP_BKTCOPY:
+               opstr = buf;
+               sprintf(buf, "[%#lx]CPY", (unsigned long)tp->right.info.bkt);
+               kind = LEAF_ROP;
+               break;
+       case ROP_LT:
+               opstr = "\\<";
+               kind = LEAF_ROP;
+               break;
+       case ROP_GT:
+               opstr = "\\>";
+               kind = LEAF_ROP;
+               break;
+       case ROP_REF:
+               opstr = buf;
+               sprintf(buf, "\\%lu", (unsigned long)tp->right.info.sub);
+               kind = LEAF_ROP;
+               break;
+       case ROP_END:
+               opstr = "<END>";
+               kind = LEAF_ROP;
+               break;
+       default:
+               opstr = buf;
+               if (tp->op > UCHAR_MAX)
+                       sprintf(buf, "W%#x", tp->op);
+               else if (tp->op <= 0)
+                       sprintf(buf, "UNK=%u", tp->op);
+               else
+                       sprintf(buf, "%c", tp->op);
+               kind = LEAF_ROP;
+               break;
+       }
+       if (kind == BINARY_ROP)
+               libuxre_regtree(tp->right.ptr, -next);
+       printf("%*c:%s\n", next - 1, n < 0 ? 'R' : n > 0 ? 'L' : 'T', opstr);
+       if (kind != LEAF_ROP)
+               libuxre_regtree(tp->left.ptr, next);
+}
+
+#endif /*REGDEBUG*/
diff --git a/libuxre/stubs.c b/libuxre/stubs.c
new file mode 100644 (file)
index 0000000..bd670db
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Changes by Gunnar Ritter, Freiburg i. Br., Germany, November 2002.
+ *
+ * Sccsid @(#)stubs.c  1.24 (gritter) 10/12/04
+ */
+/*  UNIX(R) Regular Expresssion Library
+ *
+ *  Note: Code is released under the GNU LGPL
+ *
+ *  Copyright (C) 2001 Caldera International, Inc.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to:
+ *        Free Software Foundation, Inc.
+ *        59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* stubbed-out routines needed to complete the RE libc code */
+
+#include "colldata.h"
+
+struct lc_collate *
+libuxre_lc_collate(struct lc_collate *cp)
+{
+       static struct lc_collate curinfo = {0}; /* means CHF_ENCODED */
+
+       return &curinfo;
+}
+
+#include "wcharm.h"
+
+LIBUXRE_STATIC int
+libuxre_mb2wc(w_type *wt, const unsigned char *s)
+{
+       wchar_t wc;
+       int len;
+
+       if ((len = mbtowc(&wc, (const char *)&s[-1], MB_LEN_MAX)) > 0)
+               *wt = wc;
+       else if (len == 0)
+               *wt = '\0';
+       else /*if (len < 0)*/
+               *wt = (w_type)WEOF;
+       return len > 0 ? len - 1 : len;
+}
+
+#if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4
+#define        USED    __attribute__ ((used))
+#elif defined __GNUC__
+#define        USED    __attribute__ ((unused))
+#else
+#define        USED
+#endif
+static const char sccsid[] USED = "@(#)libuxre.sl      1.24 (gritter) 10/12/04";
+/*
+_collelem.c:
+       _collelem.c     1.4 (gritter) 10/18/03
+_collmult.c:
+       _collmult.c     1.4 (gritter) 9/22/03
+bracket.c:
+       bracket.c       1.14 (gritter) 10/18/03
+colldata.h:
+       colldata.h      1.4 (gritter) 10/18/03
+onefile.c:
+       onefile.c       1.1 (gritter) 9/22/03
+re.h:
+       re.h    1.14 (gritter) 10/18/03
+regcomp.c:
+       regcomp.c       1.6 (gritter) 9/22/03
+regdfa.c:
+       regdfa.c        1.9 (gritter) 9/22/03
+regdfa.h:
+       regdfa.h        1.3 (gritter) 9/22/03
+regerror.c:
+       regerror.c      1.4 (gritter) 3/29/03
+regex.h:
+       regex.h 1.12 (gritter) 9/22/03
+regexec.c:
+       regexec.c       1.6 (gritter) 9/22/03
+regfree.c:
+       regfree.c       1.3 (gritter) 9/22/03
+regnfa.c:
+       regnfa.c        1.7 (gritter) 9/22/03
+regparse.c:
+       regparse.c      1.12 (gritter) 9/22/03
+wcharm.h:
+       wcharm.h        1.12 (gritter) 10/18/03
+*/
diff --git a/libuxre/wcharm.h b/libuxre/wcharm.h
new file mode 100644 (file)
index 0000000..8985d6b
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Changes by Gunnar Ritter, Freiburg i. Br., Germany, November 2002.
+ *
+ * Sccsid @(#)wcharm.h 1.12 (gritter) 10/18/03
+ */
+/*  UNIX(R) Regular Expresssion Library
+ *
+ *  Note: Code is released under the GNU LGPL
+ *
+ *  Copyright (C) 2001 Caldera International, Inc.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to:
+ *        Free Software Foundation, Inc.
+ *        59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* Stubbed-out wide character locale information */
+
+#ifndef        LIBUXRE_WCHARM_H
+#define        LIBUXRE_WCHARM_H
+
+#ifndef        LIBUXRE_STATIC
+#define        LIBUXRE_STATIC
+#endif
+
+#ifndef LIBUXRE_WUCHAR_T
+#define LIBUXRE_WUCHAR_T
+typedef unsigned int wuchar_type;
+#endif
+
+#ifndef        LIBUXRE_W_TYPE
+#define        LIBUXRE_W_TYPE
+typedef        int w_type;
+#endif
+
+#include <wchar.h>
+#include <wctype.h>
+#include <stdlib.h>
+
+#ifdef notdef
+#define ISONEBYTE(ch)  ((ch), 1)
+
+#define libuxre_mb2wc(wp, cp)  ((wp), (cp), 0)
+#endif /* notdef */
+
+#define        ISONEBYTE(ch)   (((ch) & 0200) == 0 || mb_cur_max == 1)
+
+#define        to_lower(ch)    (mb_cur_max > 1 ? towlower(ch) : tolower(ch))
+#define        to_upper(ch)    (mb_cur_max > 1 ? towupper(ch) : toupper(ch))
+
+LIBUXRE_STATIC int     libuxre_mb2wc(w_type *, const unsigned char *);
+
+#endif /* !LIBUXRE_WCHARM_H */
diff --git a/makeoptions b/makeoptions
new file mode 100644 (file)
index 0000000..d29746a
--- /dev/null
@@ -0,0 +1,123 @@
+#!/bin/sh
+#
+# This code contains changes by
+#      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+#
+# Conditions 1, 2, and 4 and the no-warranty notice below apply
+# to these changes.
+#
+#
+# Copyright (c) 1980, 1993
+#      The Regents of the University of California.  All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+#    must display the following acknowledgement:
+#      This product includes software developed by the University of
+#      California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+#    may be used to endorse or promote products derived from this software
+#    without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+# Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#   Redistributions of source code and documentation must retain the
+#    above copyright notice, this list of conditions and the following
+#    disclaimer.
+#   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.
+#   All advertising materials mentioning features or use of this software
+#    must display the following acknowledgement:
+#      This product includes software developed or owned by Caldera
+#      International, Inc.
+#   Neither the name of Caldera International, Inc. nor the names of
+#    other contributors may be used to endorse or promote products
+#    derived from this software without specific prior written permission.
+#
+# USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+# INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+#
+#      from makeoptions        6.4 (Berkeley) 5/31/85
+#
+#      @(#)makeoptions 1.8 (gritter) 7/1/02
+#
+
+#
+# remake options -- this isn't necessary unless you add/delete options
+#
+trap "rm -f /tmp/ex_foo$$.c /tmp/ex_bar$$.c" 0
+trap '' 2
+cat < ex_data.c > /tmp/ex_bar$$.c
+ex -s /tmp/ex_bar$$.c <<\!
+       g/^#include/d
+       w
+       q
+!
+cc -E ${@+"$@"} /tmp/ex_bar$$.c >/tmp/ex_foo$$.c
+ex -s /tmp/ex_foo$$.c <<\!
+       " delete all preprocessor output (# line, etc)
+       g/^# /d
+       set sh=/bin/sh
+       " delete junk (all but data lines)
+       g/^[    ]*$/d
+       1,/option options/d
+       /};/-1,$d
+       " get rid of all of line but option name
+       1,$s/[  ]*{[    ]*"//
+       1,$s/".*//
+       " begin kludge since options start at 0 but nl starts at 1
+       "              move first to end and later move it back and renumber
+       1m$
+       %!nl
+       $t0
+       1s/[0-9][0-9]*/0/
+       " end kludge
+       " make #define lines
+       1,$s/[  ]*\([0-9][0-9]*\)[      ]*\(.*\)/#define        \U\2\L  \1/
+       " filter through expand to make it line up nice
+       %!expand -8\,24
+       " blank line and number of options.
+       $i
+
+.
+       $s/e[   ].*[    ]/e     NOPTS   /
+       0a
+       /* sccs id   @(#)ex_vars.h makeoptions  1.8 (gritter) 7/1/02 */
+.
+       w! ex_vars.h
+       q
+!
diff --git a/malloc.c b/malloc.c
new file mode 100644 (file)
index 0000000..738144d
--- /dev/null
+++ b/malloc.c
@@ -0,0 +1,364 @@
+/*
+ * AT&T Unix 7th Edition memory allocation routines.
+ *
+ * Modified for ex by Gunnar Ritter, Freiburg i. Br., Germany,
+ * July 2000.
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+ *
+ *     @(#)malloc.c    1.19 (gritter) 2/20/05
+ */
+
+#ifdef VMUNIX
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "config.h"
+
+#ifdef LANGMSG
+#include <nl_types.h>
+extern nl_catd catd;
+#else
+#define        catgets(a, b, c, d)     (d)
+#endif
+
+/*
+ * Since ex makes use of sbrk(), the C library's version of malloc()
+ * must be avoided.
+ *
+ * In ex, malloc() calls sbrk() only one time with an argument of
+ * POOL. Ex itselves never uses malloc() internally, so POOL
+ * must be sufficient for library calls like setlocale() only.
+ *
+ * Known problem: If linking against ncurses, changing the terminal
+ * type repeatedly outruns the pool. Even that is not really a
+ * problem since the work continues with the old terminal type, so
+ * there is no need for a large pool here.
+ */
+#define        POOL    32768
+
+#ifdef debug
+#define ASSERT(p) if(!(p))botch("p");else
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+int 
+botch(char *s)
+{
+       const char msg[] = "assertion botched\n";
+       write(2, msg, sizeof msg - 1);
+       /*printf("assertion botched: %s\n",s);*/
+       abort();
+}
+static int allock(void);
+#else
+#define ASSERT(p)
+#endif
+
+/*     avoid break bug */
+#ifdef pdp11
+#define GRANULE 64
+#else
+#define GRANULE 0
+#endif
+/*     C storage allocator
+ *     circular first-fit strategy
+ *     works with noncontiguous, but monotonically linked, arena
+ *     each block is preceded by a ptr to the (pointer of) 
+ *     the next following block
+ *     blocks are exact number of words long 
+ *     aligned to the data type requirements of ALIGN
+ *     pointers to blocks must have BUSY bit 0
+ *     bit in ptr is 1 for busy, 0 for idle
+ *     gaps in arena are merely noted as busy blocks
+ *     last block of arena (pointed to by alloct) is empty and
+ *     has a pointer to first
+ *     idle blocks are coalesced during space search
+ *
+ *     a different implementation may need to redefine
+ *     ALIGN, NALIGN, BLOCK, BUSY, INT
+ *     where INT is integer type to which a pointer can be cast
+*/
+#define        INT             intptr_t
+#define        ALIGN           intptr_t
+#define        NALIGN          1
+#define        WORD            sizeof (union store)
+#define        BLOCK           1024    /* a multiple of WORD*/
+#define        BUSY            ((intptr_t)1)
+#ifdef NULL
+#undef NULL
+#endif
+#define NULL            0
+#define        testbusy(p)     ((INT)(p)&BUSY)
+#define        setbusy(p)      (union store *)((INT)(p)|BUSY)
+#define        clearbusy(p)    (union store *)((INT)(p)&~BUSY)
+
+union store { union store *ptr;
+             ALIGN dummy[NALIGN];
+             INT callocsp;     /*calloc clears an array of integers*/
+};
+
+static union store allocs[2];  /*initial arena*/
+static union store *allocp;    /*search ptr*/
+static union store *alloct;    /*arena top*/
+static union store *allocx;    /*for benefit of realloc*/
+extern int     error(char *, ...);
+
+char *
+poolsbrk(intptr_t inc)
+{
+       static char *pool;
+       static intptr_t ps;
+       intptr_t os, ns;
+
+       if (pool == NULL)
+               if ((pool = sbrk(POOL)) == (char *)-1)
+                       error(catgets(catd, 1, 241,
+                               "No memory pool"));
+       if (inc == 0)
+               return pool + ps;
+       os = ps;
+       ns = ps + inc;
+       if (ns >= POOL)
+               error(catgets(catd, 1, 242,
+                               "Memory pool exhausted"));
+       ps = ns;
+       return pool + os;
+}
+
+void *
+malloc(size_t nbytes)
+{
+       register union store *p, *q;
+       register int nw;
+       static int temp;        /*coroutines assume no auto*/
+
+       if(allocs[0].ptr==0) {  /*first time*/
+               allocs[0].ptr = setbusy(&allocs[1]);
+               allocs[1].ptr = setbusy(&allocs[0]);
+               alloct = &allocs[1];
+               allocp = &allocs[0];
+       }
+       nw = (nbytes+WORD+WORD-1)/WORD;
+       ASSERT(allocp>=allocs && allocp<=alloct);
+       ASSERT(allock());
+       for(p=allocp; ; ) {
+               for(temp=0; ; ) {
+                       if(!testbusy(p->ptr)) {
+                               while(!testbusy((q=p->ptr)->ptr)) {
+                                       int ua = p->ptr==allocp;
+                                       ASSERT(q>p&&q<alloct);
+                                       p->ptr = q->ptr;
+                                       if (ua)
+                                               allocp = p->ptr;
+                               }
+                               if(q>=p+nw && p+nw>=p)
+                                       goto found;
+                       }
+                       q = p;
+                       p = clearbusy(p->ptr);
+                       if(p>q)
+                               ASSERT(p<=alloct);
+                       else if(q!=alloct || p!=allocs) {
+                               ASSERT(q==alloct&&p==allocs);
+                               errno = ENOMEM;
+                               return(NULL);
+                       } else if(++temp>1)
+                               break;
+               }
+               temp = ((nw+BLOCK/WORD)/(BLOCK/WORD))*(BLOCK/WORD);
+               q = (union store *)poolsbrk(0);
+               if(q+temp+GRANULE < q) {
+                       errno = ENOMEM;
+                       return(NULL);
+               }
+               q = (union store *)poolsbrk(temp*WORD);
+               if((INT)q == -1) {
+                       errno = ENOMEM;
+                       return(NULL);
+               }
+               ASSERT(q>alloct);
+               alloct->ptr = q;
+               if(q!=alloct+1)
+                       alloct->ptr = setbusy(alloct->ptr);
+               alloct = q->ptr = q+temp-1;
+               alloct->ptr = setbusy(allocs);
+       }
+found:
+       allocp = p + nw;
+       ASSERT(allocp<=alloct);
+       if(q>allocp) {
+               allocx = allocp->ptr;
+               allocp->ptr = p->ptr;
+       }
+       p->ptr = setbusy(allocp);
+       return((char *)(p+1));
+}
+
+/*     freeing strategy tuned for LIFO allocation
+*/
+void
+free(register void *ap)
+{
+       register union store *p = ap;
+
+       if (ap == NULL)
+               return;
+       ASSERT(p>clearbusy(allocs[1].ptr)&&p<=alloct);
+       ASSERT(allock());
+       allocp = --p;
+       ASSERT(testbusy(p->ptr));
+       p->ptr = clearbusy(p->ptr);
+       ASSERT(p->ptr > allocp && p->ptr <= alloct);
+}
+
+/*     realloc(p, nbytes) reallocates a block obtained from malloc()
+ *     and freed since last call of malloc()
+ *     to have new size nbytes, and old content
+ *     returns new location, or 0 on failure
+*/
+
+void *
+realloc(void *ap, size_t nbytes)
+{
+       register union store *p = ap;
+       register union store *q;
+       union store *s, *t;
+       register size_t nw;
+       size_t onw;
+
+       if (p == NULL)
+               return malloc(nbytes);
+       if (nbytes == 0) {
+               free(p);
+               return NULL;
+       }
+       if(testbusy(p[-1].ptr))
+               free(p);
+       onw = p[-1].ptr - p;
+       q = malloc(nbytes);
+       if(q==NULL || q==p)
+               return(q);
+       s = p;
+       t = q;
+       nw = (nbytes+WORD-1)/WORD;
+       if(nw<onw)
+               onw = nw;
+       while(onw--!=0)
+               *t++ = *s++;
+       if(q<p && q+nw>=p)
+               (q+(q+nw-p))->ptr = allocx;
+       return(q);
+}
+
+#ifdef debug
+int 
+allock(void)
+{
+#ifdef longdebug
+       register union store *p;
+       int x;
+       x = 0;
+       for(p= &allocs[0]; clearbusy(p->ptr) > p; p=clearbusy(p->ptr)) {
+               if(p==allocp)
+                       x++;
+       }
+       ASSERT(p==alloct);
+       return(x==1|p==allocp);
+#else
+       return(1);
+#endif
+}
+#endif
+
+/*     calloc - allocate and clear memory block
+*/
+#define CHARPERINT (sizeof(INT)/sizeof(char))
+
+void *
+calloc(size_t num, size_t size)
+{
+       register char *mp;
+       register INT *q;
+       register int m;
+
+       num *= size;
+       mp = malloc(num);
+       if(mp == NULL)
+               return(NULL);
+       q = (INT *) mp;
+       m = (num+CHARPERINT-1)/CHARPERINT;
+       while(--m >= 0)
+               *q++ = 0;
+       return(mp);
+}
+
+#ifdef notdef
+/*ARGSUSED*/
+void 
+cfree(char *p, size_t num, size_t size)
+{
+       free(p);
+}
+
+/*
+ * Just in case ...
+ */
+char *
+memalign(size_t alignment, size_t size)
+{
+       return NULL;
+}
+
+char *
+valloc(size_t size)
+{
+       return NULL;
+}
+
+char *
+mallinfo(void)
+{
+       return NULL;
+}
+
+int 
+mallopt(void)
+{
+       return -1;
+}
+#endif /* notdef */
+
+#endif /* VMUNIX */
diff --git a/mapmalloc.c b/mapmalloc.c
new file mode 100644 (file)
index 0000000..017b992
--- /dev/null
@@ -0,0 +1,439 @@
+/*
+ * AT&T Unix 7th Edition memory allocation routines.
+ *
+ * Modified for ex by Gunnar Ritter, Freiburg i. Br., Germany,
+ * February 2005.
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+ *
+ *     Sccsid @(#)mapmalloc.c  1.4 (gritter) 2/20/05
+ */
+
+#ifdef VMUNIX
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/mman.h>
+
+#ifndef        MAP_FAILED
+#define        MAP_FAILED      ((void *)-1)
+#endif /* !MAP_FAILED */
+
+#ifndef        MAP_ANON
+#ifdef MAP_ANONYMOUS
+#define        MAP_ANON        MAP_ANONYMOUS
+#else  /* !MAP_ANONYMOUS */
+#include <sys/stat.h>
+#include <fcntl.h>
+#endif /* !MAP_ANONYMOUS */
+#endif /* !MAP_ANON */
+
+#include "config.h"
+
+/*
+ * Since ex makes use of sbrk(), the C library's version of malloc()
+ * must be avoided.
+ */
+
+/*
+#define debug
+#define longdebug
+*/
+
+#ifdef debug
+#define ASSERT(p) if(!(p))botch("p");else
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <inttypes.h>
+int 
+botch(char *s)
+{
+       const char msg[] = "assertion botched\n";
+       write(2, msg, sizeof msg - 1);
+       /*printf("assertion botched: %s\n",s);*/
+       abort();
+}
+static int allock(void *);
+#ifdef debugprint
+void dump(const char *msg, uintptr_t t)
+{
+       const char hex[] = "0123456789ABCDEF";
+       int     i;
+       write(2, msg, strlen(msg));
+       write(2, ": ", 2);
+       for (i = sizeof t - 1; i >= 0; i--) {
+               write(2, &hex[(t & (0x0f << 8*i+4)) >> 8*i+4], 1);
+               write(2, &hex[(t & (0x0f << 8*i)) >> 8*i], 1);
+       }
+       write(2, "\n", 1);
+}
+#else
+#define        dump(a, b)
+#endif
+#else
+#define ASSERT(p)
+#define        dump(a, b)
+#endif
+
+/*     avoid break bug */
+#ifdef pdp11
+#define GRANULE 64
+#else
+#define GRANULE 0
+#endif
+/*     C storage allocator
+ *     circular first-fit strategy
+ *     works with noncontiguous, but monotonically linked, arena
+ *     each block is preceded by a ptr to the (pointer of) 
+ *     the next following block
+ *     blocks are exact number of words long 
+ *     aligned to the data type requirements of ALIGN
+ *     pointers to blocks must have BUSY bit 0
+ *     bit in ptr is 1 for busy, 0 for idle
+ *     gaps in arena are merely noted as busy blocks
+ *     last block of arena (pointed to by alloct) is empty and
+ *     has a pointer to first
+ *     idle blocks are coalesced during space search
+ *
+ *     this variant uses mmap() instead of sbrk()
+ *     mmap() is used to allocate pools of increasing size
+ *     memory is then allocated from the first possible pool
+ *
+ *     a different implementation may need to redefine
+ *     ALIGN, NALIGN, BLOCK, BUSY, INT
+ *     where INT is integer type to which a pointer can be cast
+*/
+#define        INT             intptr_t
+#define        ALIGN           intptr_t
+#define        NALIGN          1
+#define        WORD            sizeof (union store)
+#define        BLOCK           1024    /* a multiple of WORD*/
+#define        BUSY            ((intptr_t)1)
+#ifdef NULL
+#undef NULL
+#endif
+#define NULL            0
+#define        testbusy(p)     ((INT)(p)&BUSY)
+#define        setbusy(p)      (union store *)((INT)(p)|BUSY)
+#define        clearbusy(p)    (union store *)((INT)(p)&~BUSY)
+
+static struct pool *pool0;
+
+union store { union store *ptr;
+             struct pool *pool;
+             ALIGN dummy[NALIGN];
+             INT callocsp;     /*calloc clears an array of integers*/
+};
+
+struct pool {
+       struct pool *Next;
+       union store Allocs[2];  /*initial arena*/
+       union store *Allocp;    /*search ptr*/
+       union store *Alloct;    /*arena top*/
+       union store *Allocx;    /*for benefit of realloc*/
+       char *Brk;
+       char *End;
+       ALIGN Dummy[NALIGN];
+};
+
+#define        allocs  (o->Allocs)
+#define        allocp  (o->Allocp)
+#define        alloct  (o->Alloct)
+#define        allocx  (o->Allocx)
+
+static void *
+map(void *addr, size_t len)
+{
+#ifndef        MAP_ANON
+       int flags = 0;
+       static int fd = -1;
+
+       if (fd==-1 && ((fd=open("/dev/zero",O_RDWR))<0 ||
+                       fcntl(fd,F_SETFD,FD_CLOEXEC)<0))
+               return(MAP_FAILED);
+#else  /* MAP_ANON */
+       int flags = MAP_ANON;
+       int fd = -1;
+#endif /* MAP_ANON */
+       flags |= MAP_PRIVATE;
+       if (addr)
+               flags |= MAP_FIXED;
+       return(mmap(addr,len,PROT_READ|PROT_WRITE,flags,fd,0));
+}
+
+void *
+malloc(size_t nbytes)
+{
+       register union store *p, *q;
+       struct pool *o;
+       register int nw;
+       static int temp;        /*coroutines assume no auto*/
+       static size_t poolblock = 0100000;
+
+       if (nbytes == 0)
+               nbytes = 1;
+       if(pool0==0 || pool0==MAP_FAILED) {     /*first time*/
+               if((pool0=map(NULL, poolblock))==MAP_FAILED) {
+                       errno = ENOMEM;
+                       return(NULL);
+               }
+               pool0->Brk = (char *)pool0->Dummy;
+               pool0->End = (char *)pool0+poolblock;
+       }
+       o = pool0;
+first: if(allocs[0].ptr==0) {  /*first time for this pool*/
+               allocs[0].ptr = setbusy(&allocs[1]);
+               allocs[1].ptr = setbusy(&allocs[0]);
+               alloct = &allocs[1];
+               allocp = &allocs[0];
+       }
+       nw = (nbytes+2*WORD+WORD-1)/WORD;
+       ASSERT(allocp>=allocs && allocp<=alloct);
+       ASSERT(allock(o));
+       for(p=allocp; ; ) {
+               for(temp=0; ; ) {
+                       if(!testbusy(p->ptr)) {
+                               while(!testbusy((q=p->ptr)->ptr)) {
+                                       int ua = p->ptr==allocp;
+                                       ASSERT(q>p&&q<alloct);
+                                       p->ptr = q->ptr;
+                                       if (ua)
+                                               allocp = p->ptr;
+                               }
+                               if(q>=p+nw && p+nw>=p)
+                                       goto found;
+                       }
+                       q = p;
+                       p = clearbusy(p->ptr);
+                       if(p>q)
+                               ASSERT(p<=alloct);
+                       else if(q!=alloct || p!=allocs) {
+                               ASSERT(q==alloct&&p==allocs);
+                               errno = ENOMEM;
+                               return(NULL);
+                       } else if(++temp>1)
+                               break;
+               }
+               temp = ((nw+BLOCK/WORD)/(BLOCK/WORD))*(BLOCK/WORD);
+               q = (void *)o->Brk;
+               if(q+temp+GRANULE < q) {
+                       errno = ENOMEM;
+                       return(NULL);
+               }
+               if(o->Brk+temp*WORD>=o->End) {
+                       size_t new;
+                       if(o->Next!=0&&o->Next!=MAP_FAILED) {
+                               o = o->Next;
+                               goto first;
+                       }
+                       poolblock += poolblock/(poolblock&(poolblock-1) ? 3:2);
+                       new = (((nw*WORD)+poolblock)/poolblock)*poolblock;
+                       if ((o->Next=map(0,new))==MAP_FAILED) {
+                               poolblock /= 4;
+                               new=(((nw*WORD)+poolblock)/poolblock)*poolblock;
+                               if ((o->Next=map(0,new))==MAP_FAILED) {
+                                       errno = ENOMEM;
+                                       return(NULL);
+                               }
+                       }
+                       o = o->Next;
+                       o->Brk = (char *)o->Dummy;
+                       o->End = (char *)o + new;
+                       goto first;
+               }
+               o->Brk += temp*WORD;
+               ASSERT(q>alloct);
+               alloct->ptr = q;
+               if(q!=alloct+1)
+                       alloct->ptr = setbusy(alloct->ptr);
+               alloct = q->ptr = q+temp-1;
+               alloct->ptr = setbusy(allocs);
+       }
+found:
+       allocp = p + nw;
+       ASSERT(allocp<=alloct);
+       if(q>allocp) {
+               allocx = allocp->ptr;
+               allocp->ptr = p->ptr;
+       }
+       p->ptr = setbusy(allocp);
+       p[1].pool = o;
+       dump("malloc", (uintptr_t)(p + 2));
+       return(p+2);
+}
+
+/*     freeing strategy tuned for LIFO allocation
+*/
+void 
+free(register void *ap)
+{
+       register union store *p = ap;
+       struct pool *o;
+
+       dump("  free", (uintptr_t)ap);
+       if (ap == NULL)
+               return;
+       o = p[-1].pool;
+       ASSERT(p>clearbusy(allocs[1].ptr)&&p<=alloct);
+       ASSERT(allock(o));
+       allocp = p -= 2;
+       ASSERT(testbusy(p->ptr));
+       p->ptr = clearbusy(p->ptr);
+       ASSERT(p->ptr > allocp && p->ptr <= alloct);
+}
+
+/*     realloc(p, nbytes) reallocates a block obtained from malloc()
+ *     and freed since last call of malloc()
+ *     to have new size nbytes, and old content
+ *     returns new location, or 0 on failure
+*/
+
+void *
+realloc(void *ap, size_t nbytes)
+{
+       register union store *p = ap;
+       register union store *q;
+       struct pool *o;
+       union store *s, *t;
+       register size_t nw;
+       size_t onw;
+
+       if (p==NULL)
+               return(malloc(nbytes));
+       if (nbytes==0) {
+               free(p);
+               return NULL;
+       }
+       if(testbusy(p[-2].ptr))
+               free(p);
+       onw = p[-2].ptr - p;
+       o = p[-1].pool;
+       q = malloc(nbytes);
+       if(q==NULL || q==p)
+               return(q);
+       s = p;
+       t = q;
+       nw = (nbytes+WORD-1)/WORD;
+       if(nw<onw)
+               onw = nw;
+       while(onw--!=0)
+               *t++ = *s++;
+       if(q<p && q+nw>=p && p[-1].pool==q[-1].pool)
+               (q+(q+nw-p))->ptr = allocx;
+       return(q);
+}
+
+#ifdef debug
+int 
+allock(void *ao)
+{
+#ifdef longdebug
+       struct pool *o = ao;
+       register union store *p;
+       int x;
+       x = 0;
+       for(p= &allocs[0]; clearbusy(p->ptr) > p; p=clearbusy(p->ptr)) {
+               if(p==allocp)
+                       x++;
+       }
+       ASSERT(p==alloct);
+       ASSERT(x==1|p==allocp);
+#endif
+       return(1);
+}
+#endif
+
+/*     calloc - allocate and clear memory block
+*/
+#define CHARPERINT (sizeof(INT)/sizeof(char))
+
+void *
+calloc(size_t num, size_t size)
+{
+       register char *mp;
+       register INT *q;
+       register int m;
+
+       num *= size;
+       mp = malloc(num);
+       if(mp==NULL)
+               return(NULL);
+       q = (INT *) mp;
+       m = (num+CHARPERINT-1)/CHARPERINT;
+       while(--m >= 0)
+               *q++ = 0;
+       return(mp);
+}
+
+#ifdef notdef
+/*ARGSUSED*/
+void 
+cfree(char *p, size_t num, size_t size)
+{
+       free(p);
+}
+
+/*
+ * Just in case ...
+ */
+char *
+memalign(size_t alignment, size_t size)
+{
+       return NULL;
+}
+
+char *
+valloc(size_t size)
+{
+       return NULL;
+}
+
+char *
+mallinfo(void)
+{
+       return NULL;
+}
+
+int 
+mallopt(void)
+{
+       return -1;
+}
+#endif /* notdef */
+
+char *poolsbrk(intptr_t val) { return NULL; }
+
+#endif /* VMUNIX */
diff --git a/printf.c b/printf.c
new file mode 100644 (file)
index 0000000..1c95186
--- /dev/null
+++ b/printf.c
@@ -0,0 +1,440 @@
+/*
+ * This code contains changes by
+ *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef        lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)printf.c   1.15 (gritter) 12/1/04";
+#endif
+#endif
+
+/* from printf.c       7.3 (Berkeley) 6/7/85 */
+
+/* The pwb version this is based on */
+/* from printf.c:2.2 6/5/79 */
+
+#include "ex.h"
+
+/*
+ * This version of printf is compatible with the Version 7 C
+ * printf. The differences are only minor except that this
+ * printf assumes it is to print through putchar. Version 7
+ * printf is more general (and is much larger) and includes
+ * provisions for floating point.
+ */
+static int width, sign, fill;
+
+int vprintf(const char *, va_list);
+char *p_dconv(long, char *);
+static int p_emit(char *, char *);
+
+int
+printf(const char *fmt, ...)
+{
+       va_list ap;
+       int ret;
+
+       va_start(ap, fmt);
+       ret = vprintf(fmt, ap);
+       va_end(ap);
+       return ret;
+}
+
+int
+vprintf(const char *fmt, va_list ap)
+{
+       int     cnt = 0;
+       int fcode;
+       int prec;
+       int length,mask1,nbits,n,m;
+       long int mask2, num;
+       register char *bptr;
+       char *ptr;
+       char buf[134];
+
+       for (;;) {
+               /* process format string first */
+               while (nextc(fcode,fmt,m), fmt+=m, fcode!='%') {
+                       /* ordinary (non-%) character */
+                       if (fcode=='\0')
+                               return cnt;
+                       putchar(fcode);
+                       cnt += m;
+               }
+               /* length modifier: -1 for h, 1 for l, 0 for none */
+               length = 0;
+               /* check for a leading - sign */
+               sign = 0;
+               if (*fmt == '-') {
+                       sign++;
+                       fmt++;
+               }
+               /* a '0' may follow the - sign */
+               /* this is the requested fill character */
+               fill = 1;
+               if (*fmt == '0') {
+                       fill--;
+                       fmt++;
+               }
+               
+               /* Now comes a digit string which may be a '*' */
+               if (*fmt == '*') {
+                       width = va_arg(ap, int);
+                       if (width < 0) {
+                               width = -width;
+                               sign = !sign;
+                       }
+                       fmt++;
+               }
+               else {
+                       width = 0;
+                       while (*fmt>='0' && *fmt<='9')
+                               width = width * 10 + (*fmt++ - '0');
+               }
+               
+               /* maybe a decimal point followed by more digits (or '*') */
+               if (*fmt=='.') {
+                       if (*++fmt == '*') {
+                               prec = va_arg(ap, int);
+                               fmt++;
+                       }
+                       else {
+                               prec = 0;
+                               while (*fmt>='0' && *fmt<='9')
+                                       prec = prec * 10 + (*fmt++ - '0');
+                       }
+               }
+               else
+                       prec = -1;
+               
+               /*
+                * At this point, "sign" is nonzero if there was
+                * a sign, "fill" is 0 if there was a leading
+                * zero and 1 otherwise, "width" and "prec"
+                * contain numbers corresponding to the digit
+                * strings before and after the decimal point,
+                * respectively, and "fmt" addresses the next
+                * character after the whole mess. If there was
+                * no decimal point, "prec" will be -1.
+                */
+               switch (*fmt) {
+                       case 'L':
+                       case 'l':
+                               length = 2;
+                               /* no break!! */
+                       case 'h':
+                       case 'H':
+                               length--;
+                               fmt++;
+                               break;
+               }
+               
+               /*
+                * At exit from the following switch, we will
+                * emit the characters starting at "bptr" and
+                * ending at "ptr"-1, unless fcode is '\0'.
+                */
+               switch (nextc(fcode, fmt, m), fmt += m, fcode) {
+                       /* process characters and strings first */
+                       case 'c':
+                               buf[0] = va_arg(ap, int);
+                               ptr = bptr = &buf[0];
+                               if (buf[0] != '\0')
+                                       ptr++;
+                               break;
+                       case 's':
+                               bptr = va_arg(ap,char *);
+                               if (bptr==0)
+                                       bptr = catgets(catd, 1, 248,
+                                                       "(null pointer)");
+                               if (prec < 0)
+                                       prec = LRGINT;
+                               for (n=0; *bptr++ && n < prec; n++) ;
+                               ptr = --bptr;
+                               bptr -= n;
+                               break;
+                       case 'O':
+                               length = 1;
+                               fcode = 'o';
+                               /* no break */
+                       case 'o':
+                       case 'X':
+                       case 'x':
+                               if (length > 0)
+                                       num = va_arg(ap,long);
+                               else
+                                       num = (unsigned)va_arg(ap,int);
+                               if (fcode=='o') {
+                                       mask1 = 0x7;
+                                       mask2 = 0x1fffffffL;
+                                       nbits = 3;
+                               }
+                               else {
+                                       mask1 = 0xf;
+                                       mask2 = 0x0fffffffL;
+                                       nbits = 4;
+                               }
+                               n = (num!=0);
+                               bptr = buf + MAXOCT + 3;
+                               /* shift and mask for speed */
+                               do
+                                   if (((int) num & mask1) < 10)
+                                       *--bptr = ((int) num & mask1) + 060;
+                                   else
+                                       *--bptr = ((int) num & mask1) + 0127;
+                               while (num = (num >> nbits) & mask2);
+                               
+                               if (fcode=='o') {
+                                       if (n)
+                                               *--bptr = '0';
+                               }
+                               else
+                                       if (!sign && fill <= 0) {
+                                               putchar('0');
+                                               putchar(fcode);
+                                               width -= 2;
+                                       }
+                                       else {
+                                               *--bptr = fcode;
+                                               *--bptr = '0';
+                                       }
+                               ptr = buf + MAXOCT + 3;
+                               break;
+                       case 'D':
+                       case 'U':
+                       case 'I':
+                               length = 1;
+                               fcode = fcode + 'a' - 'A';
+                               /* no break */
+                       case 'd':
+                       case 'i':
+                       case 'u':
+                               if (length > 0)
+                                       num = va_arg(ap,long);
+                               else {
+                                       n = va_arg(ap,int);
+                                       if (fcode=='u')
+                                               num = (unsigned) n;
+                                       else
+                                               num = (long) n;
+                               }
+                               if (n = (fcode != 'u' && num < 0))
+                                       num = -num;
+                               /* now convert to digits */
+                               bptr = p_dconv(num, buf);
+                               if (n)
+                                       *--bptr = '-';
+                               if (fill == 0)
+                                       fill = -1;
+                               ptr = buf + MAXDIGS + 1;
+                               break;
+                       default:
+                               /* not a control character, 
+                                * print it.
+                                */
+                               ptr = bptr = (char *)&fmt[-m];
+                               ptr++;
+                               break;
+                       }
+                       if (fcode != '\0')
+                               cnt += p_emit(bptr,ptr);
+       }
+}
+
+/* p_dconv converts the unsigned long integer "value" to
+ * printable decimal and places it in "buffer", right-justified.
+ * The value returned is the address of the first non-zero character,
+ * or the address of the last character if all are zero.
+ * The result is NOT null terminated, and is MAXDIGS characters long,
+ * starting at buffer[1] (to allow for insertion of a sign).
+ *
+ * This program assumes it is running on 2's complement machine
+ * with reasonable overflow treatment.
+ */
+char *
+p_dconv(long value, char *buffer)
+{
+       register char *bp;
+       register int svalue;
+       int n;
+       long lval;
+       
+       bp = buffer;
+       
+       /* zero is a special case */
+       if (value == 0) {
+               bp += MAXDIGS;
+               *bp = '0';
+               return(bp);
+       }
+       
+       /* develop the leading digit of the value in "n" */
+       n = 0;
+       while (value < 0) {
+               value -= BIG;   /* will eventually underflow */
+               n++;
+       }
+       while (value >= BIG && (lval = value - BIG) >= 0) {
+               value = lval;
+               n++;
+       }
+       
+       /* stash it in buffer[1] to allow for a sign */
+       bp[1] = n + '0';
+       /*
+        * Now develop the rest of the digits. Since speed counts here,
+        * we do it in two loops. The first gets "value" down until it
+        * is no larger than LRGINT. The second one uses integer divides
+        * rather than long divides to speed it up.
+        */
+       bp += MAXDIGS + 1;
+       while (value > LRGINT) {
+               *--bp = (int)(value % 10) + '0';
+               value /= 10;
+       }
+       
+       /* cannot lose precision */
+       svalue = value;
+       while (svalue > 0) {
+               *--bp = (svalue % 10) + '0';
+               svalue /= 10;
+       }
+       
+       /* fill in intermediate zeroes if needed */
+       if (buffer[1] != '0') {
+               while (bp > buffer + 2)
+                       *--bp = '0';
+               --bp;
+       }
+       return(bp);
+}
+
+/*
+ * This program sends string "s" to putchar. The character after
+ * the end of "s" is given by "send". This allows the size of the
+ * field to be computed; it is stored in "alen". "width" contains the
+ * user specified length. If width<alen, the width will be taken to
+ * be alen. "sign" is zero if the string is to be right-justified
+ * in the field, nonzero if it is to be left-justified. "fill" is
+ * 0 if the string is to be padded with '0', positive if it is to be
+ * padded with ' ', and negative if an initial '-' should appear before
+ * any padding in right-justification (to avoid printing "-3" as
+ * "000-3" where "-0003" was intended).
+ */
+static int 
+p_emit(register char *s, char *send)
+{
+       char cfill;
+       register int alen;
+       int npad;
+       int     cnt = 0;
+       int     c, m;
+       
+       alen = send - s;
+       if (alen > width)
+               width = alen;
+       cfill = fill>0? ' ': '0';
+       
+       /* we may want to print a leading '-' before anything */
+       if (*s == '-' && fill < 0) {
+               putchar(*s++);
+               cnt++;
+               alen--;
+               width--;
+       }
+       npad = width - alen;
+       
+       /* emit any leading pad characters */
+       if (!sign)
+               while (--npad >= 0) {
+                       putchar(cfill);
+                       cnt++;
+               }
+                       
+       /* emit the string itself */
+       while (--alen >= 0) {
+               nextc(c, s, m);
+               s += m;
+               putchar(c);
+               cnt += m;
+               alen -= m-1;
+       }
+               
+       /* emit trailing pad characters */
+       if (sign)
+               while (--npad >= 0) {
+                       putchar(cfill);
+                       cnt++;
+               }
+       return cnt;
+}
diff --git a/regexp.h b/regexp.h
new file mode 100644 (file)
index 0000000..fad1c74
--- /dev/null
+++ b/regexp.h
@@ -0,0 +1,1210 @@
+/*
+ * Simple Regular Expression functions. Derived from Unix 7th Edition,
+ * /usr/src/cmd/expr.y
+ *
+ * Modified by Gunnar Ritter, Freiburg i. Br., Germany, February 2002.
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   Redistributions of source code and documentation must retain the
+ *    above copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *   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.
+ *   All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed or owned by Caldera
+ *      International, Inc.
+ *   Neither the name of Caldera International, Inc. nor the names of
+ *    other contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+ */
+
+#if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4
+#define        REGEXP_H_USED   __attribute__ ((used))
+#elif defined __GNUC__
+#define        REGEXP_H_USED   __attribute__ ((unused))
+#else
+#define        REGEXP_H_USED
+#endif
+static const char regexp_h_sccsid[] REGEXP_H_USED =
+       "@(#)regexp.sl  1.54 (gritter) 2/19/05";
+
+#if !defined (REGEXP_H_USED_FROM_VI) && !defined (__dietlibc__)
+#define        REGEXP_H_WCHARS
+#endif
+
+#define        CBRA    2
+#define        CCHR    4
+#define        CDOT    8
+#define        CCL     12
+/*     CLNUM   14      used in sed */
+/*     CEND    16      used in sed */
+#define        CDOL    20
+#define        CCEOF   22
+#define        CKET    24
+#define        CBACK   36
+#define        CNCL    40
+#define        CBRC    44
+#define        CLET    48
+#define        CCH1    52
+#define        CCH2    56
+#define        CCH3    60
+
+#define        STAR    01
+#define RNGE   03
+#define        REGEXP_H_LEAST  0100
+
+#ifdef REGEXP_H_WCHARS
+#define        CMB     0200
+#else  /* !REGEXP_H_WCHARS */
+#define        CMB     0
+#endif /* !REGEXP_H_WCHARS */
+
+#define        NBRA    9
+
+#define PLACE(c)       ep[c >> 3] |= bittab[c & 07]
+#define ISTHERE(c)     (ep[c >> 3] & bittab[c & 07])
+
+#ifdef REGEXP_H_WCHARS
+#define        REGEXP_H_IS_THERE(ep, c)        ((ep)[c >> 3] & bittab[c & 07])
+#endif
+
+#include       <ctype.h>
+#include       <string.h>
+#include       <limits.h>
+#ifdef REGEXP_H_WCHARS
+#include       <stdlib.h>
+#include       <wchar.h>
+#include       <wctype.h>
+#endif /* REGEXP_H_WCHARS */
+
+#define        regexp_h_uletter(c)     (isalpha(c) || (c) == '_')
+#ifdef REGEXP_H_WCHARS
+#define        regexp_h_wuletter(c)    (iswalpha(c) || (c) == L'_')
+
+/*
+ * Used to allocate memory for the multibyte star algorithm.
+ */
+#ifndef        regexp_h_malloc
+#define        regexp_h_malloc(n)      malloc(n)
+#endif
+#ifndef        regexp_h_free
+#define        regexp_h_free(p)        free(p)
+#endif
+
+/*
+ * Can be predefined to 'inline' to inline some multibyte functions;
+ * may improve performance for files that contain many multibyte
+ * sequences.
+ */
+#ifndef        regexp_h_inline
+#define        regexp_h_inline
+#endif
+
+/*
+ * Mask to determine whether the first byte of a sequence possibly
+ * starts a multibyte character. Set to 0377 to force mbtowc() for
+ * any byte sequence (except 0).
+ */
+#ifndef        REGEXP_H_MASK
+#define        REGEXP_H_MASK   0200
+#endif
+#endif /* REGEXP_H_WCHARS */
+
+/*
+ * For regexpr.h.
+ */
+#ifndef        regexp_h_static
+#define        regexp_h_static
+#endif
+#ifndef        REGEXP_H_STEP_INIT
+#define        REGEXP_H_STEP_INIT
+#endif
+#ifndef        REGEXP_H_ADVANCE_INIT
+#define        REGEXP_H_ADVANCE_INIT
+#endif
+
+char   *braslist[NBRA];
+char   *braelist[NBRA];
+int    nbra;
+char   *loc1, *loc2, *locs;
+int    sed;
+int    nodelim;
+
+regexp_h_static int    circf;
+regexp_h_static int    low;
+regexp_h_static int    size;
+
+regexp_h_static unsigned char  bittab[] = {
+       1,
+       2,
+       4,
+       8,
+       16,
+       32,
+       64,
+       128
+};
+static int     regexp_h_advance(register const char *lp,
+                       register const char *ep);
+static void    regexp_h_getrnge(register const char *str, int least);
+
+static const char      *regexp_h_bol;  /* beginning of input line (for \<) */
+
+#ifdef REGEXP_H_WCHARS
+static int     regexp_h_wchars;
+static int     regexp_h_mbcurmax;
+
+static const char      *regexp_h_firstwc;      /* location of first
+                                                  multibyte character
+                                                  on input line */
+
+#define        regexp_h_getwc(c)       { \
+       if (regexp_h_wchars) { \
+               char mbbuf[MB_LEN_MAX + 1], *mbptr; \
+               wchar_t wcbuf; \
+               int mb, len; \
+               mbptr = mbbuf; \
+               do { \
+                       mb = GETC(); \
+                       *mbptr++ = mb; \
+                       *mbptr = '\0'; \
+               } while ((len = mbtowc(&wcbuf, mbbuf, regexp_h_mbcurmax)) < 0 \
+                       && mb != eof && mbptr < mbbuf + MB_LEN_MAX); \
+               if (len == -1) \
+                       ERROR(67); \
+               c = wcbuf; \
+       } else { \
+               c = GETC(); \
+       } \
+}
+
+#define        regexp_h_store(wc, mb, me)      { \
+       int len; \
+       if (wc == WEOF) \
+               ERROR(67); \
+       if ((len = me - mb) <= regexp_h_mbcurmax) { \
+               char mt[MB_LEN_MAX]; \
+               if (wctomb(mt, wc) >= len) \
+                       ERROR(50); \
+       } \
+       switch (len = wctomb(mb, wc)) { \
+       case -1: \
+                ERROR(67); \
+       case 0: \
+               mb++; \
+               break; \
+       default: \
+               mb += len; \
+       } \
+}
+
+static regexp_h_inline wint_t
+regexp_h_fetchwc(const char **mb, int islp)
+{
+       wchar_t wc;
+       int len;
+
+       if ((len = mbtowc(&wc, *mb, regexp_h_mbcurmax)) < 0) {
+               (*mb)++;
+               return WEOF;
+       }
+       if (islp && regexp_h_firstwc == NULL)
+               regexp_h_firstwc = *mb;
+       /*if (len == 0) {
+               (*mb)++;
+               return L'\0';
+       } handled in singlebyte code */
+       *mb += len;
+       return wc;
+}
+
+#define        regexp_h_fetch(mb, islp)        ((*(mb) & REGEXP_H_MASK) == 0 ? \
+                                               (*(mb)++&0377): \
+                                               regexp_h_fetchwc(&(mb), islp))
+
+static regexp_h_inline wint_t
+regexp_h_showwc(const char *mb)
+{
+       wchar_t wc;
+
+       if (mbtowc(&wc, mb, regexp_h_mbcurmax) < 0)
+               return WEOF;
+       return wc;
+}
+
+#define        regexp_h_show(mb)       ((*(mb) & REGEXP_H_MASK) == 0 ? (*(mb)&0377): \
+                                       regexp_h_showwc(mb))
+
+/*
+ * Return the character immediately preceding mb. Since no byte is
+ * required to be the first byte of a character, the longest multibyte
+ * character ending at &[mb-1] is searched.
+ */
+static regexp_h_inline wint_t
+regexp_h_previous(const char *mb)
+{
+       const char *p = mb;
+       wchar_t wc, lastwc = WEOF;
+       int len, max = 0;
+
+       if (regexp_h_firstwc == NULL || mb <= regexp_h_firstwc)
+               return (mb > regexp_h_bol ? (mb[-1] & 0377) : WEOF);
+       while (p-- > regexp_h_bol) {
+               mbtowc(NULL, NULL, 0);
+               if ((len = mbtowc(&wc, p, mb - p)) >= 0) {
+                       if (len < max || len < mb - p)
+                               break;
+                       max = len;
+                       lastwc = wc;
+               } else if (len < 0 && max > 0)
+                       break;
+       }
+       return lastwc;
+}
+
+#define        regexp_h_cclass(set, c, af)     \
+       ((c) == 0 || (c) == WEOF ? 0 : ( \
+               ((c) > 0177) ? \
+                       regexp_h_cclass_wc(set, c, af) : ( \
+                               REGEXP_H_IS_THERE((set)+1, (c)) ? (af) : !(af) \
+                       ) \
+               ) \
+       )
+
+static regexp_h_inline int
+regexp_h_cclass_wc(const char *set, register wint_t c, int af)
+{
+       register wint_t wc, wl = WEOF;
+       const char *end;
+
+       end = &set[18] + set[0] - 1;
+       set += 17;
+       while (set < end) {
+               wc = regexp_h_fetch(set, 0);
+#ifdef REGEXP_H_VI_BACKSLASH
+               if (wc == '\\' && set < end &&
+                               (*set == ']' || *set == '-' ||
+                                *set == '^' || *set == '\\')) {
+                       wc = regexp_h_fetch(set, 0);
+               } else
+#endif /* REGEXP_H_VI_BACKSLASH */
+               if (wc == '-' && wl != WEOF && set < end) {
+                       wc = regexp_h_fetch(set, 0);
+#ifdef REGEXP_H_VI_BACKSLASH
+                       if (wc == '\\' && set < end &&
+                                       (*set == ']' || *set == '-' ||
+                                        *set == '^' || *set == '\\')) {
+                               wc = regexp_h_fetch(set, 0);
+                       }
+#endif /* REGEXP_H_VI_BACKSLASH */
+                       if (c > wl && c < wc)
+                               return af;
+               }
+               if (c == wc)
+                       return af;
+               wl = wc;
+       }
+       return !af;
+}
+#else  /* !REGEXP_H_WCHARS */
+#define        regexp_h_wchars         0
+#define        regexp_h_getwc(c)       { c = GETC(); }
+#endif /* !REGEXP_H_WCHARS */
+
+regexp_h_static char *
+compile(char *instring, char *ep, const char *endbuf, int seof)
+{
+       INIT    /* Dependent declarations and initializations */
+       register int c;
+       register int eof = seof;
+       char *lastep = instring;
+       int cclcnt;
+       char bracket[NBRA], *bracketp;
+       int closed;
+       char neg;
+       int lc;
+       int i, cflg;
+
+#ifdef REGEXP_H_WCHARS
+       char *eq;
+       regexp_h_mbcurmax = MB_CUR_MAX;
+       regexp_h_wchars = regexp_h_mbcurmax > 1 ? CMB : 0;
+#endif
+       lastep = 0;
+       bracketp = bracket;
+       if((c = GETC()) == eof || c == '\n') {
+               if (c == '\n') {
+                       UNGETC(c);
+                       nodelim = 1;
+               }
+               if(*ep == 0 && !sed)
+                       ERROR(41);
+               if (bracketp > bracket)
+                       ERROR(42);
+               RETURN(ep);
+       }
+       circf = closed = nbra = 0;
+       if (c == '^')
+               circf++;
+       else
+               UNGETC(c);
+       for (;;) {
+               if (ep >= endbuf)
+                       ERROR(50);
+               regexp_h_getwc(c);
+               if(c != '*' && ((c != '\\') || (PEEKC() != '{')))
+                       lastep = ep;
+               if (c == eof) {
+                       *ep++ = CCEOF;
+                       if (bracketp > bracket)
+                               ERROR(42);
+                       RETURN(ep);
+               }
+               switch (c) {
+
+               case '.':
+                       *ep++ = CDOT|regexp_h_wchars;
+                       continue;
+
+               case '\n':
+                       if (sed == 0) {
+                               UNGETC(c);
+                               *ep++ = CCEOF;
+                               nodelim = 1;
+                               RETURN(ep);
+                       }
+                       ERROR(36);
+               case '*':
+                       if (lastep==0 || *lastep==CBRA || *lastep==CKET ||
+                                       *lastep==(CBRC|regexp_h_wchars) ||
+                                       *lastep==(CLET|regexp_h_wchars))
+                               goto defchar;
+                       *lastep |= STAR;
+                       continue;
+
+               case '$':
+                       if(PEEKC() != eof)
+                               goto defchar;
+                       *ep++ = CDOL;
+                       continue;
+
+               case '[':
+#ifdef REGEXP_H_WCHARS
+                       if (regexp_h_wchars == 0) {
+#endif
+                               if(&ep[33] >= endbuf)
+                                       ERROR(50);
+
+                               *ep++ = CCL;
+                               lc = 0;
+                               for(i = 0; i < 32; i++)
+                                       ep[i] = 0;
+
+                               neg = 0;
+                               if((c = GETC()) == '^') {
+                                       neg = 1;
+                                       c = GETC();
+                               }
+
+                               do {
+                                       c &= 0377;
+                                       if(c == '\0' || c == '\n')
+                                               ERROR(49);
+#ifdef REGEXP_H_VI_BACKSLASH
+                                       if(c == '\\' && ((c = PEEKC()) == ']' ||
+                                                       c == '-' || c == '^' ||
+                                                       c == '\\')) {
+                                               c = GETC();
+                                               c &= 0377;
+                                       } else
+#endif /* REGEXP_H_VI_BACKSLASH */
+                                       if(c == '-' && lc != 0) {
+                                               if ((c = GETC()) == ']') {
+                                                       PLACE('-');
+                                                       break;
+                                               }
+#ifdef REGEXP_H_VI_BACKSLASH
+                                               if(c == '\\' &&
+                                                       ((c = PEEKC()) == ']' ||
+                                                               c == '-' ||
+                                                               c == '^' ||
+                                                               c == '\\'))
+                                                       c = GETC();
+#endif /* REGEXP_H_VI_BACKSLASH */
+                                               c &= 0377;
+                                               while(lc < c) {
+                                                       PLACE(lc);
+                                                       lc++;
+                                               }
+                                       }
+                                       lc = c;
+                                       PLACE(c);
+                               } while((c = GETC()) != ']');
+                               if(neg) {
+                                       for(cclcnt = 0; cclcnt < 32; cclcnt++)
+                                               ep[cclcnt] ^= 0377;
+                                       ep[0] &= 0376;
+                               }
+
+                               ep += 32;
+#ifdef REGEXP_H_WCHARS
+                       } else {
+                               if (&ep[18] >= endbuf)
+                                       ERROR(50);
+                               *ep++ = CCL|CMB;
+                               *ep++ = 0;
+                               lc = 0;
+                               for (i = 0; i < 16; i++)
+                                       ep[i] = 0;
+                               eq = &ep[16];
+                               regexp_h_getwc(c);
+                               if (c == L'^') {
+                                       regexp_h_getwc(c);
+                                       ep[-2] = CNCL|CMB;
+                               }
+                               do {
+                                       if (c == '\0' || c == '\n')
+                                               ERROR(49);
+#ifdef REGEXP_H_VI_BACKSLASH
+                                       if(c == '\\' && ((c = PEEKC()) == ']' ||
+                                                       c == '-' || c == '^' ||
+                                                       c == '\\')) {
+                                               regexp_h_store(c, eq, endbuf);
+                                               regexp_h_getwc(c);
+                                       } else
+#endif /* REGEXP_H_VI_BACKSLASH */
+                                       if (c == '-' && lc != 0 && lc <= 0177) {
+                                               regexp_h_store(c, eq, endbuf);
+                                               regexp_h_getwc(c);
+                                               if (c == ']') {
+                                                       PLACE('-');
+                                                       break;
+                                               }
+#ifdef REGEXP_H_VI_BACKSLASH
+                                               if(c == '\\' &&
+                                                       ((c = PEEKC()) == ']' ||
+                                                               c == '-' ||
+                                                               c == '^' ||
+                                                               c == '\\')) {
+                                                       regexp_h_store(c, eq,
+                                                               endbuf);
+                                                       regexp_h_getwc(c);
+                                               }
+#endif /* REGEXP_H_VI_BACKSLASH */
+                                               while (lc < (c & 0177)) {
+                                                       PLACE(lc);
+                                                       lc++;
+                                               }
+                                       }
+                                       lc = c;
+                                       if (c <= 0177)
+                                               PLACE(c);
+                                       regexp_h_store(c, eq, endbuf);
+                                       regexp_h_getwc(c);
+                               } while (c != L']');
+                               if ((i = eq - &ep[16]) > 255)
+                                       ERROR(50);
+                               lastep[1] = i;
+                               ep = eq;
+                       }
+#endif /* REGEXP_H_WCHARS */
+
+                       continue;
+
+               case '\\':
+                       regexp_h_getwc(c);
+                       switch(c) {
+
+                       case '(':
+                               if(nbra >= NBRA)
+                                       ERROR(43);
+                               *bracketp++ = nbra;
+                               *ep++ = CBRA;
+                               *ep++ = nbra++;
+                               continue;
+
+                       case ')':
+                               if(bracketp <= bracket)
+                                       ERROR(42);
+                               *ep++ = CKET;
+                               *ep++ = *--bracketp;
+                               closed++;
+                               continue;
+
+                       case '<':
+                               *ep++ = CBRC|regexp_h_wchars;
+                               continue;
+
+                       case '>':
+                               *ep++ = CLET|regexp_h_wchars;
+                               continue;
+
+                       case '{':
+                               if(lastep == (char *) (0))
+                                       goto defchar;
+                               *lastep |= RNGE;
+                               cflg = 0;
+                       nlim:
+                               c = GETC();
+                               i = 0;
+                               do {
+                                       if ('0' <= c && c <= '9')
+                                               i = 10 * i + c - '0';
+                                       else
+                                               ERROR(16);
+                               } while(((c = GETC()) != '\\') && (c != ','));
+                               if (i > 255)
+                                       ERROR(11);
+                               *ep++ = i;
+                               if (c == ',') {
+                                       if(cflg++)
+                                               ERROR(44);
+                                       if((c = GETC()) == '\\') {
+                                               *ep++ = (char)255;
+                                               *lastep |= REGEXP_H_LEAST;
+                                       } else {
+                                               UNGETC(c);
+                                               goto nlim; /* get 2'nd number */
+                                       }
+                               }
+                               if(GETC() != '}')
+                                       ERROR(45);
+                               if(!cflg)       /* one number */
+                                       *ep++ = i;
+                               else if((ep[-1] & 0377) < (ep[-2] & 0377))
+                                       ERROR(46);
+                               continue;
+
+                       case '\n':
+                               ERROR(36);
+
+                       case 'n':
+                               c = '\n';
+                               goto defchar;
+
+                       default:
+                               if(c >= '1' && c <= '9') {
+                                       if((c -= '1') >= closed)
+                                               ERROR(25);
+                                       *ep++ = CBACK;
+                                       *ep++ = c;
+                                       continue;
+                               }
+                       }
+                       /* Drop through to default to use \ to turn off special chars */
+
+               defchar:
+               default:
+                       lastep = ep;
+#ifdef REGEXP_H_WCHARS
+                       if (regexp_h_wchars == 0) {
+#endif
+                               *ep++ = CCHR;
+                               *ep++ = c;
+#ifdef REGEXP_H_WCHARS
+                       } else {
+                               char    mbbuf[MB_LEN_MAX];
+
+                               switch (wctomb(mbbuf, c)) {
+                               case 1: *ep++ = CCH1;
+                                       break;
+                               case 2: *ep++ = CCH2;
+                                       break;
+                               case 3: *ep++ = CCH3;
+                                       break;
+                               default:
+                                       *ep++ = CCHR|CMB;
+                               }
+                               regexp_h_store(c, ep, endbuf);
+                       }
+#endif /* REGEXP_H_WCHARS */
+               }
+       }
+}
+
+int
+step(const char *p1, const char *p2)
+{
+       register int c;
+#ifdef REGEXP_H_WCHARS
+       register int d;
+#endif /* REGEXP_H_WCHARS */
+
+       REGEXP_H_STEP_INIT      /* get circf */
+       regexp_h_bol = p1;
+#ifdef REGEXP_H_WCHARS
+       regexp_h_firstwc = NULL;
+#endif /* REGEXP_H_WCHARS */
+       if (circf) {
+               loc1 = (char *)p1;
+               return(regexp_h_advance(p1, p2));
+       }
+       /* fast check for first character */
+       if (*p2==CCHR) {
+               c = p2[1] & 0377;
+               do {
+                       if ((*p1 & 0377) != c)
+                               continue;
+                       if (regexp_h_advance(p1, p2)) {
+                               loc1 = (char *)p1;
+                               return(1);
+                       }
+               } while (*p1++);
+               return(0);
+       }
+#ifdef REGEXP_H_WCHARS
+       else if (*p2==CCH1) {
+               do {
+                       if (p1[0] == p2[1] && regexp_h_advance(p1, p2)) {
+                               loc1 = (char *)p1;
+                               return(1);
+                       }
+                       c = regexp_h_fetch(p1, 1);
+               } while (c);
+               return(0);
+       } else if (*p2==CCH2) {
+               do {
+                       if (p1[0] == p2[1] && p1[1] == p2[2] &&
+                                       regexp_h_advance(p1, p2)) {
+                               loc1 = (char *)p1;
+                               return(1);
+                       }
+                       c = regexp_h_fetch(p1, 1);
+               } while (c);
+               return(0);
+       } else if (*p2==CCH3) {
+               do {
+                       if (p1[0] == p2[1] && p1[1] == p2[2] && p1[2] == p2[3]&&
+                                       regexp_h_advance(p1, p2)) {
+                               loc1 = (char *)p1;
+                               return(1);
+                       }
+                       c = regexp_h_fetch(p1, 1);
+               } while (c);
+               return(0);
+       } else if ((*p2&0377)==(CCHR|CMB)) {
+               d = regexp_h_fetch(p2, 0);
+               do {
+                       c = regexp_h_fetch(p1, 1);
+                       if (c == d && regexp_h_advance(p1, p2)) {
+                               loc1 = (char *)p1;
+                               return(1);
+                       }
+               } while(c);
+               return(0);
+       }
+               /* regular algorithm */
+       if (regexp_h_wchars)
+               do {
+                       if (regexp_h_advance(p1, p2)) {
+                               loc1 = (char *)p1;
+                               return(1);
+                       }
+                       c = regexp_h_fetch(p1, 1);
+               } while (c);
+       else
+#endif /* REGEXP_H_WCHARS */
+               do {
+                       if (regexp_h_advance(p1, p2)) {
+                               loc1 = (char *)p1;
+                               return(1);
+                       }
+               } while (*p1++);
+       return(0);
+}
+
+#ifdef REGEXP_H_WCHARS
+/*
+ * It is painfully slow to read character-wise backwards in a
+ * multibyte string (see regexp_h_previous() above). For the star
+ * algorithm, we therefore keep track of every character as it is
+ * read in forward direction.
+ *
+ * Don't use alloca() for stack blocks since there is no measurable
+ * speedup and huge amounts of memory are used up for long input
+ * lines.
+ */
+#ifndef        REGEXP_H_STAKBLOK
+#define        REGEXP_H_STAKBLOK       1000
+#endif
+
+struct regexp_h_stack {
+       struct regexp_h_stack   *s_nxt;
+       struct regexp_h_stack   *s_prv;
+       const char      *s_ptr[REGEXP_H_STAKBLOK];
+};
+
+#define        regexp_h_push(sb, sp, sc, lp)   (regexp_h_wchars ? \
+                       regexp_h_pushwc(sb, sp, sc, lp) : (void)0)
+
+static regexp_h_inline void
+regexp_h_pushwc(struct regexp_h_stack **sb,
+               struct regexp_h_stack **sp,
+               const char ***sc, const char *lp)
+{
+       if (regexp_h_firstwc == NULL || lp < regexp_h_firstwc)
+               return;
+       if (*sb == NULL) {
+               if ((*sb = regexp_h_malloc(sizeof **sb)) == NULL)
+                       return;
+               (*sb)->s_nxt = (*sb)->s_prv = NULL;
+               *sp = *sb;
+               *sc = &(*sb)->s_ptr[0];
+       } else if (*sc >= &(*sp)->s_ptr[REGEXP_H_STAKBLOK]) {
+               if ((*sp)->s_nxt == NULL) {
+                       struct regexp_h_stack   *bq;
+
+                       if ((bq = regexp_h_malloc(sizeof *bq)) == NULL)
+                               return;
+                       bq->s_nxt = NULL;
+                       bq->s_prv = *sp;
+                       (*sp)->s_nxt = bq;
+                       *sp = bq;
+               } else
+                       *sp = (*sp)->s_nxt;
+               *sc = &(*sp)->s_ptr[0];
+       }
+       *(*sc)++ = lp;
+}
+
+static regexp_h_inline const char *
+regexp_h_pop(struct regexp_h_stack **sp, const char ***sc,
+               const char *lp)
+{
+       if (regexp_h_firstwc == NULL || lp <= regexp_h_firstwc)
+               return &lp[-1];
+       if (*sp == NULL)
+               return regexp_h_firstwc;
+       if (*sc == &(*sp)->s_ptr[0]) {
+               if ((*sp)->s_prv == NULL) {
+                       regexp_h_free(*sp);
+                       *sp = NULL;
+                       return regexp_h_firstwc;
+               }
+               *sp = (*sp)->s_prv;
+               regexp_h_free((*sp)->s_nxt);
+               (*sp)->s_nxt = NULL ;
+               *sc = &(*sp)->s_ptr[REGEXP_H_STAKBLOK];
+       }
+       return *(--(*sc));
+}
+
+static void
+regexp_h_zerostak(struct regexp_h_stack **sb, struct regexp_h_stack **sp)
+{
+       for (*sp = *sb; *sp && (*sp)->s_nxt; *sp = (*sp)->s_nxt)
+               if ((*sp)->s_prv)
+                       regexp_h_free((*sp)->s_prv);
+       if (*sp) {
+               if ((*sp)->s_prv)
+                       regexp_h_free((*sp)->s_prv);
+               regexp_h_free(*sp);
+       }
+       *sp = *sb = NULL;
+}
+#else  /* !REGEXP_H_WCHARS */
+#define        regexp_h_push(sb, sp, sc, lp)
+#endif /* !REGEXP_H_WCHARS */
+
+static int
+regexp_h_advance(const char *lp, const char *ep)
+{
+       register const char *curlp;
+       int c, least;
+#ifdef REGEXP_H_WCHARS
+       int d;
+       struct regexp_h_stack   *sb = NULL, *sp = NULL;
+       const char      **sc;
+#endif /* REGEXP_H_WCHARS */
+       char *bbeg;
+       int ct;
+
+       for (;;) switch (least = *ep++ & 0377, least & ~REGEXP_H_LEAST) {
+
+       case CCHR:
+#ifdef REGEXP_H_WCHARS
+       case CCH1:
+#endif
+               if (*ep++ == *lp++)
+                       continue;
+               return(0);
+
+#ifdef REGEXP_H_WCHARS
+       case CCHR|CMB:
+               if (regexp_h_fetch(ep, 0) == regexp_h_fetch(lp, 1))
+                       continue;
+               return(0);
+
+       case CCH2:
+               if (ep[0] == lp[0] && ep[1] == lp[1]) {
+                       ep += 2, lp += 2;
+                       continue;
+               }
+               return(0);
+
+       case CCH3:
+               if (ep[0] == lp[0] && ep[1] == lp[1] && ep[2] == lp[2]) {
+                       ep += 3, lp += 3;
+                       continue;
+               }
+               return(0);
+#endif /* REGEXP_H_WCHARS */
+
+       case CDOT:
+               if (*lp++)
+                       continue;
+               return(0);
+#ifdef REGEXP_H_WCHARS
+       case CDOT|CMB:
+               if ((c = regexp_h_fetch(lp, 1)) != L'\0' && c != WEOF)
+                       continue;
+               return(0);
+#endif /* REGEXP_H_WCHARS */
+
+       case CDOL:
+               if (*lp==0)
+                       continue;
+               return(0);
+
+       case CCEOF:
+               loc2 = (char *)lp;
+               return(1);
+
+       case CCL:
+               c = *lp++ & 0377;
+               if(ISTHERE(c)) {
+                       ep += 32;
+                       continue;
+               }
+               return(0);
+
+#ifdef REGEXP_H_WCHARS
+       case CCL|CMB:
+       case CNCL|CMB:
+               c = regexp_h_fetch(lp, 1);
+               if (regexp_h_cclass(ep, c, (ep[-1] & 0377) == (CCL|CMB))) {
+                       ep += (*ep & 0377) + 17;
+                       continue;
+               }
+               return 0;
+#endif /* REGEXP_H_WCHARS */
+
+       case CBRA:
+               braslist[*ep++ & 0377] = (char *)lp;
+               continue;
+
+       case CKET:
+               braelist[*ep++ & 0377] = (char *)lp;
+               continue;
+
+       case CBRC:
+               if (lp == regexp_h_bol && locs == NULL)
+                       continue;
+               if ((isdigit(lp[0] & 0377) || regexp_h_uletter(lp[0] & 0377))
+                               && !regexp_h_uletter(lp[-1] & 0377)
+                               && !isdigit(lp[-1] & 0377))
+                       continue;
+               return(0);
+
+#ifdef REGEXP_H_WCHARS
+       case CBRC|CMB:
+               c = regexp_h_show(lp);
+               d = regexp_h_previous(lp);
+               if ((iswdigit(c) || regexp_h_wuletter(c))
+                               && !regexp_h_wuletter(d)
+                               && !iswdigit(d))
+                       continue;
+               return(0);
+#endif /* REGEXP_H_WCHARS */
+
+       case CLET:
+               if (!regexp_h_uletter(lp[0] & 0377) && !isdigit(lp[0] & 0377))
+                       continue;
+               return(0);
+
+#ifdef REGEXP_H_WCHARS
+       case CLET|CMB:
+               c = regexp_h_show(lp);
+               if (!regexp_h_wuletter(c) && !iswdigit(c))
+                       continue;
+               return(0);
+#endif /* REGEXP_H_WCHARS */
+
+       case CCHR|RNGE:
+               c = *ep++;
+               regexp_h_getrnge(ep, least);
+               while(low--)
+                       if(*lp++ != c)
+                               return(0);
+               curlp = lp;
+               while(size--) {
+                       regexp_h_push(&sb, &sp, &sc, lp);
+                       if(*lp++ != c)
+                               break;
+               }
+               if(size < 0) {
+                       regexp_h_push(&sb, &sp, &sc, lp);
+                       lp++;
+               }
+               ep += 2;
+               goto star;
+
+#ifdef REGEXP_H_WCHARS
+       case CCHR|RNGE|CMB:
+       case CCH1|RNGE:
+       case CCH2|RNGE:
+       case CCH3|RNGE:
+               c = regexp_h_fetch(ep, 0);
+               regexp_h_getrnge(ep, least);
+               while (low--)
+                       if (regexp_h_fetch(lp, 1) != c)
+                               return 0;
+               curlp = lp;
+               while (size--) {
+                       regexp_h_push(&sb, &sp, &sc, lp);
+                       if (regexp_h_fetch(lp, 1) != c)
+                               break;
+               }
+               if(size < 0) {
+                       regexp_h_push(&sb, &sp, &sc, lp);
+                       regexp_h_fetch(lp, 1);
+               }
+               ep += 2;
+               goto star;
+#endif /* REGEXP_H_WCHARS */
+
+       case CDOT|RNGE:
+               regexp_h_getrnge(ep, least);
+               while(low--)
+                       if(*lp++ == '\0')
+                               return(0);
+               curlp = lp;
+               while(size--) {
+                       regexp_h_push(&sb, &sp, &sc, lp);
+                       if(*lp++ == '\0')
+                               break;
+               }
+               if(size < 0) {
+                       regexp_h_push(&sb, &sp, &sc, lp);
+                       lp++;
+               }
+               ep += 2;
+               goto star;
+
+#ifdef REGEXP_H_WCHARS
+       case CDOT|RNGE|CMB:
+               regexp_h_getrnge(ep, least);
+               while (low--)
+                       if ((c = regexp_h_fetch(lp, 1)) == L'\0' || c == WEOF)
+                               return 0;
+               curlp = lp;
+               while (size--) {
+                       regexp_h_push(&sb, &sp, &sc, lp);
+                       if ((c = regexp_h_fetch(lp, 1)) == L'\0' || c == WEOF)
+                               break;
+               }
+               if (size < 0) {
+                       regexp_h_push(&sb, &sp, &sc, lp);
+                       regexp_h_fetch(lp, 1);
+               }
+               ep += 2;
+               goto star;
+#endif /* REGEXP_H_WCHARS */
+
+       case CCL|RNGE:
+               regexp_h_getrnge(ep + 32, least);
+               while(low--) {
+                       c = *lp++ & 0377;
+                       if(!ISTHERE(c))
+                               return(0);
+               }
+               curlp = lp;
+               while(size--) {
+                       regexp_h_push(&sb, &sp, &sc, lp);
+                       c = *lp++ & 0377;
+                       if(!ISTHERE(c))
+                               break;
+               }
+               if(size < 0) {
+                       regexp_h_push(&sb, &sp, &sc, lp);
+                       lp++;
+               }
+               ep += 34;               /* 32 + 2 */
+               goto star;
+
+#ifdef REGEXP_H_WCHARS
+       case CCL|RNGE|CMB:
+       case CNCL|RNGE|CMB:
+               regexp_h_getrnge(ep + (*ep & 0377) + 17, least);
+               while (low--) {
+                       c = regexp_h_fetch(lp, 1);
+                       if (!regexp_h_cclass(ep, c,
+                                       (ep[-1] & 0377 & ~REGEXP_H_LEAST)
+                                       == (CCL|RNGE|CMB)))
+                               return 0;
+               }
+               curlp = lp;
+               while (size--) {
+                       regexp_h_push(&sb, &sp, &sc, lp);
+                       c = regexp_h_fetch(lp, 1);
+                       if (!regexp_h_cclass(ep, c,
+                                       (ep[-1] & 0377 & ~REGEXP_H_LEAST)
+                                       == (CCL|RNGE|CMB)))
+                               break;
+               }
+               if (size < 0) {
+                       regexp_h_push(&sb, &sp, &sc, lp);
+                       regexp_h_fetch(lp, 1);
+               }
+               ep += (*ep & 0377) + 19;
+               goto star;
+#endif /* REGEXP_H_WCHARS */
+
+       case CBACK:
+               bbeg = braslist[*ep & 0377];
+               ct = braelist[*ep++ & 0377] - bbeg;
+
+               if(strncmp(bbeg, lp, ct) == 0) {
+                       lp += ct;
+                       continue;
+               }
+               return(0);
+
+       case CBACK|STAR:
+               bbeg = braslist[*ep & 0377];
+               ct = braelist[*ep++ & 0377] - bbeg;
+               curlp = lp;
+               while(strncmp(bbeg, lp, ct) == 0)
+                       lp += ct;
+
+               while(lp >= curlp) {
+                       if(regexp_h_advance(lp, ep))    return(1);
+                       lp -= ct;
+               }
+               return(0);
+
+
+       case CDOT|STAR:
+               curlp = lp;
+               do
+                       regexp_h_push(&sb, &sp, &sc, lp);
+               while (*lp++);
+               goto star;
+
+#ifdef REGEXP_H_WCHARS
+       case CDOT|STAR|CMB:
+               curlp = lp;
+               do
+                       regexp_h_push(&sb, &sp, &sc, lp);
+               while ((c = regexp_h_fetch(lp, 1)) != L'\0' && c != WEOF);
+               goto star;
+#endif /* REGEXP_H_WCHARS */
+
+       case CCHR|STAR:
+               curlp = lp;
+               do
+                       regexp_h_push(&sb, &sp, &sc, lp);
+               while (*lp++ == *ep);
+               ep++;
+               goto star;
+
+#ifdef REGEXP_H_WCHARS
+       case CCHR|STAR|CMB:
+       case CCH1|STAR:
+       case CCH2|STAR:
+       case CCH3|STAR:
+               curlp = lp;
+               d = regexp_h_fetch(ep, 0);
+               do
+                       regexp_h_push(&sb, &sp, &sc, lp);
+               while (regexp_h_fetch(lp, 1) == d);
+               goto star;
+#endif /* REGEXP_H_WCHARS */
+
+       case CCL|STAR:
+               curlp = lp;
+               do {
+                       regexp_h_push(&sb, &sp, &sc, lp);
+                       c = *lp++ & 0377;
+               } while(ISTHERE(c));
+               ep += 32;
+               goto star;
+
+#ifdef REGEXP_H_WCHARS
+       case CCL|STAR|CMB:
+       case CNCL|STAR|CMB:
+               curlp = lp;
+               do {
+                       regexp_h_push(&sb, &sp, &sc, lp);
+                       c = regexp_h_fetch(lp, 1);
+               } while (regexp_h_cclass(ep, c, (ep[-1] & 0377)
+                                       == (CCL|STAR|CMB)));
+               ep += (*ep & 0377) + 17;
+               goto star;
+#endif /* REGEXP_H_WCHARS */
+
+       star:
+#ifdef REGEXP_H_WCHARS
+               if (regexp_h_wchars == 0) {
+#endif
+                       do {
+                               if(--lp == locs)
+                                       break;
+                               if (regexp_h_advance(lp, ep))
+                                       return(1);
+                       } while (lp > curlp);
+#ifdef REGEXP_H_WCHARS
+               } else {
+                       do {
+                               lp = regexp_h_pop(&sp, &sc, lp);
+                               if (lp <= locs)
+                                       break;
+                               if (regexp_h_advance(lp, ep)) {
+                                       regexp_h_zerostak(&sb, &sp);
+                                       return(1);
+                               }
+                       } while (lp > curlp);
+                       regexp_h_zerostak(&sb, &sp);
+               }
+#endif /* REGEXP_H_WCHARS */
+               return(0);
+
+       }
+}
+
+static void
+regexp_h_getrnge(register const char *str, int least)
+{
+       low = *str++ & 0377;
+       size = least & REGEXP_H_LEAST ? /*20000*/INT_MAX : (*str & 0377) - low;
+}
+
+int
+advance(const char *lp, const char *ep)
+{
+       REGEXP_H_ADVANCE_INIT   /* skip past circf */
+       regexp_h_bol = lp;
+#ifdef REGEXP_H_WCHARS
+       regexp_h_firstwc = NULL;
+#endif /* REGEXP_H_WCHARS */
+       return regexp_h_advance(lp, ep);
+}
diff --git a/vi.1 b/vi.1
new file mode 100644 (file)
index 0000000..d242add
--- /dev/null
+++ b/vi.1
@@ -0,0 +1,1025 @@
+.\"
+.\" This code contains changes by
+.\"      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+.\"
+.\" Conditions 1, 2, and 4 and the no-warranty notice below apply
+.\" to these changes.
+.\"
+.\"
+.\" Copyright (c) 1980, 1993
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"
+.\" Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"   Redistributions of source code and documentation must retain the
+.\"    above copyright notice, this list of conditions and the following
+.\"    disclaimer.
+.\"   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.
+.\"   All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"      This product includes software developed or owned by Caldera
+.\"      International, Inc.
+.\"   Neither the name of Caldera International, Inc. nor the names of
+.\"    other contributors may be used to endorse or promote products
+.\"    derived from this software without specific prior written permission.
+.\"
+.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+.\" INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+.\"
+.\"    from vi.1       6.1 (Berkeley) 4/29/85
+.\"
+.\"    Sccsid @(#)vi.1 1.26 (gritter) 3/12/03
+.\"
+.ie \n(.g==1 \{\
+.ds lq \(lq
+.ds rq \(rq
+.\}
+.el \{\
+.ds lq ``
+.ds rq ''
+.\}
+.TH VI 1 "3/12/03" "Ancient Unix Ports" "User Commands"
+.SH NAME
+vi, view, vedit \- screen oriented (visual) display editor based on ex
+.SH SYNOPSIS
+.HP
+.ad l
+\fBvi\fR [\fB\-c\fI\ command\fR|\fB+\fIcommand\fR]
+[\fB\-r\fR\ [\fIfilename\fR]] [\fB\-s\fR|\fB\-\fR]
+[\fB\-t\fI\ tagstring\fR] [\fB\-w\fI\ size\fR]
+[\fB\-lLRV\fR] [\fIfile\fR ...]
+.HP
+.ad l
+\fBview\fR [\fB\-c\fI\ command\fR|\fB+\fIcommand\fR]
+[\fB\-r\fR\ [\fIfilename\fR]] [\fB\-s\fR|\fB\-\fR]
+[\fB\-t\fI\ tagstring\fR] [\fB\-w\fI\ size\fR]
+[\fB\-lLRV\fR] [\fIfile\fR ...]
+.HP
+.ad l
+\fBvedit\fR [\fB\-c\fI\ command\fR|\fB+\fIcommand\fR]
+[\fB\-r\fR\ [\fIfilename\fR]] [\fB\-s\fR|\fB\-\fR]
+[\fB\-t\fI\ tagstring\fR] [\fB\-w\fI\ size\fR]
+[\fB\-lLRV\fR] [\fIfile\fR ...]
+.br
+.ad b
+.SH DESCRIPTION
+.I Vi
+(visual) is a display oriented text editor based on
+.IR ex (1).
+.I Ex
+and
+.I vi
+run the same code; it is possible to get to
+the command mode of
+.I ex
+from within
+.I vi
+and vice-versa.
+.PP
+The
+.I view
+command is identical to
+.I vi
+except that files are opened read-only.
+The
+.I vedit
+command is also identical,
+but sets some options to values more useful for novices.
+.PP
+The following options are accepted:
+.TP
+\fB\-c\fP\fI\ command\fP or \fB+\fP\fIcommand\fP
+Execute
+.I command
+when editing begins.
+.TP
+.B \-l
+Start in a special mode useful for the
+.I Lisp
+programming language.
+.TP
+\fB\-r\fI\ [filename]\fR or \fB\-L\fR
+When no argument is supplied with this option,
+all files to be recovered are listed
+and the editor exits immediately.
+If a
+.I filename
+is specified,
+the corresponding temporary file is opened in recovery mode.
+.TP
+.B \-R
+Files are opened read-only when this option is given.
+.TP
+.BR \-s \ or\  \-
+Script mode;
+all feedback for interactive editing is disabled.
+.SM EXINIT
+and
+.I .exrc
+files are not processed.
+.TP
+.BI \-t \ tagstring
+Read the
+.I tags
+file,
+then choose the file and position specified by
+.I tagstring
+for editing.
+.TP
+.B \-V
+Echo command input to standard error,
+unless it originates from a terminal.
+.TP
+.BI \-w \ size
+Specify the size of the editing window for visual mode.
+.PP
+The
+.I "Introduction to Display Editing with Vi"
+provides full details on using
+.I vi.
+.PP
+Most of the
+.I ex
+commands are available in
+.I visual
+mode when prefixed by a
+.B :
+character. See
+.BR ex (1)
+for a description of them.
+.\"    from vi.chars   8.1 (Berkeley) 6/8/93
+.PP
+The following gives the uses the editor makes of each character.  The
+characters are presented in their order in the \s-1ASCII\s0 character
+set:  Control characters come first, then most special characters, then
+the digits, upper and then lower case characters.
+.PP
+For each character we tell a meaning it has as a command and any meaning it
+has during an insert.
+If it has only meaning as a command, then only this is discussed.
+Section numbers in parentheses indicate where the character is discussed
+in \*(lqAn Introduction to Display Editing with Vi\*(rq;
+a `f' after the section number means that the character is mentioned
+in a footnote.
+.TP
+\fB^@\fR
+Not a command character.
+If typed as the first character of an insertion it is replaced with the
+last text inserted, and the insert terminates.  Only 128 characters are
+saved from the last insert; if more characters were inserted the mechanism
+is not available.
+A \fB^@\fR cannot be part of the file due to the editor implementation
+(7.5f).
+.TP
+\fB^A\fR
+Unused.
+.TP
+\fB^B\fR
+Backward window.
+A count specifies repetition.
+Two lines of continuity are kept if possible (2.1, 6.1, 7.2).
+.TP
+\fB^C\fR
+Unused.
+.TP
+\fB^D\fR
+As a command, scrolls down a half-window of text.  
+A count gives the number of (logical) lines to scroll, and is remembered
+for future \fB^D\fR and \fB^U\fR commands (2.1, 7.2).
+During an insert, backtabs over \fIautoindent\fR white space at the beginning
+of a line (6.6, 7.5); this white space cannot be backspaced over.
+.TP
+\fB^E\fR
+Exposes one more line below the current screen in the file, leaving
+the cursor where it is if possible.
+.TP
+\fB^F\fR
+Forward window.  A count specifies repetition.
+Two lines of continuity are kept if possible (2.1, 6.1, 7.2).
+.TP
+\fB^G\fR
+Equivalent to \fB:f\fR\s-1CR\s0, printing the current file, whether
+it has been modified, the current line number and the number of lines
+in the file, and the percentage of the way through the file.
+.TP
+\fB^H (\fR\s-1BS\s0\fB)\fR
+Same as
+.BR "left arrow" .
+(See
+.BR h ).
+During an insert, eliminates the last input character, backing over it
+but not erasing it; it remains so the user can see what he typed if he
+wishes to type something only slightly different (3.1, 7.5).
+.TP
+\fB^I\ (\fR\s-1TAB\s0\fB)\fR
+Not a command character.
+When inserted it prints as some
+number of spaces.
+When the cursor is at a tab character it rests at the last of the spaces
+which represent the tab.
+The spacing of tabstops is controlled by the \fItabstop\fR option (4.1, 6.6).
+.TP
+\fB^J\ (\fR\s-1LF\s0\fB)\fR
+Same as
+.B "down arrow"
+(see
+.BR j ).
+.TP
+\fB^K\fR
+Unused.
+.TP
+\fB^L\fR
+The \s-1ASCII\s0 formfeed character, this causes the screen to be cleared
+and redrawn.  This is useful after a transmission error, if characters
+typed by a program other than the editor scramble the screen,
+or after output is stopped by an interrupt (5.4, 7.2f).
+.TP
+\fB^M\ (\fR\s-1CR\s0\fB)\fR
+A carriage return advances to the next line, at the first non-white position
+in the line.  Given a count, it advances that many lines (2.3).
+During an insert, a \s-1CR\s0 causes the insert to continue onto
+another line (3.1).
+.TP
+\fB^N\fR
+Same as
+.B "down arrow"
+(see
+.BR j ).
+.TP
+\fB^O\fR
+Unused.
+.TP
+\fB^P\fR
+Same as
+.B "up arrow"
+(see
+.BR k ).
+.TP
+\fB^Q\fR
+Not a command character.
+In input mode,
+.B ^Q
+quotes the next character, the same as
+.B ^V ,
+except that some teletype drivers will eat the
+.B ^Q
+so that the editor never sees it.
+.TP
+\fB^R\fR
+Redraws the current screen, eliminating logical lines not corresponding
+to physical lines (lines with only a single @ character on them).
+On hardcopy terminals in \fIopen\fR mode, retypes the current line
+(5.4, 7.2, 7.8).
+.TP
+\fB^S\fR
+Unused.  Some teletype drivers use
+.B ^S
+to suspend output until
+.B ^Q is pressed.
+.TP
+\fB^T\fR
+Not a command character.
+During an insert, with \fIautoindent\fR set and at the beginning of the
+line, inserts \fIshiftwidth\fR white space.
+.TP
+\fB^U\fR
+Scrolls the screen up, inverting \fB^D\fR which scrolls down.  Counts work as
+they do for \fB^D\fR, and the previous scroll amount is common to both.
+On a dumb terminal, \fB^U\fR will often necessitate clearing and redrawing
+the screen further back in the file (2.1, 7.2).
+.TP
+\fB^V\fR
+Not a command character.
+In input mode, quotes the next character so that it is possible
+to insert non-printing and special characters into the file (4.2, 7.5).
+.TP
+\fB^W\fR
+Not a command character.
+During an insert, backs up as \fBb\fR would in command mode; the deleted
+characters remain on the display (see \fB^H\fR) (7.5).
+.TP
+\fB^X\fR
+Unused.
+.TP
+\fB^Y\fR
+Exposes one more line above the current screen, leaving the cursor where
+it is if possible.  (No mnemonic value for this key; however, it is next
+to \fB^U\fR which scrolls up a bunch.)
+.TP
+\fB^Z\fR
+If supported by the Unix system,
+stops the editor, exiting to the top level shell.
+Same as \fB:stop\fP\s-1CR\s0.
+Otherwise, unused.
+.TP
+\fB^[\ (\fR\s-1ESC\s0\fB)\fR
+Cancels a partially formed command, such as a \fBz\fR when no following
+character has yet been given; terminates inputs on the last line (read
+by commands such as \fB: /\fR and \fB?\fR); ends insertions of new text
+into the buffer.
+If an \s-1ESC\s0 is given when quiescent in command state, the editor
+rings the bell or flashes the screen.  The user can thus hit \s-1ESC\s0 if
+he doesn't know what is happening till the editor rings the bell.
+If the user doesn't know whether he is in insert mode
+he can type \s-1ESC\s0\fBa\fR,
+and then material to be input; the material will be inserted correctly
+whether or not he was in insert mode when he started (1.6, 3.1, 7.5).
+.TP
+\fB^\e\fR
+Unused.
+.TP
+\fB^]\fR
+Searches for the word which is after the cursor as a tag.  Equivalent
+to typing \fB:ta\fR, this word, and then a \s-1CR\s0.
+Mnemonically, this command is \*(lq right to\*(rq (7.3).
+.TP
+\fB^^\fR
+Equivalent to \fB:e #\fR\s-1CR\s0, returning to the previous position
+in the last edited file, or editing a file which the user specified if he
+got a `No write since last change diagnostic' and does not want to have
+to type the file name again (7.3).
+(The user has to do a \fB:w\fR before \fB^^\fR
+will work in this case.  If he does not wish to write the file he should
+do \fB:e!\ #\fR\s-1CR\s0 instead.)
+.TP
+\fB^_\fR
+Unused.
+Reserved as the command character for the
+Tektronix 4025 and 4027 terminal.
+.TP
+\fB\fR\s-1SPACE\s0\fB\fR
+Same as
+.B "right arrow"
+(see
+.BR l ).
+.TP
+\fB!\fR
+An operator, which processes lines from the buffer with reformatting commands.
+Follow \fB!\fR with the object to be processed, and then the command name
+terminated by \s-1CR\s0.  Doubling \fB!\fR and preceding it by a count
+causes count lines to be filtered; otherwise the count
+is passed on to the object after the \fB!\fR.  Thus \fB2!}\fR\fIfmt\fR\s-1CR\s0
+reformats the next two paragraphs by running them through the program
+\fIfmt\fR.  If working on \s-1LISP\s0,
+the command \fB!%\fR\fIgrind\fR\s-1CR\s0,
+.\"*
+.\".FS
+.\"*Both
+.\".I fmt
+.\"and
+.\".I grind
+.\"are Berkeley programs and may not be present at all installations.
+.\".FE
+given at the beginning of a
+function, will run the text of the function through the \s-1LISP\s0 grinder
+(6.7, 7.3).
+To read a file or the output of a command into the buffer \fB:r\fR (7.3)
+can be used.
+To simply execute a command, \fB:!\fR (7.3).
+.tr \a"
+.iP \a 15
+Precedes a named buffer specification.  There are named buffers \fB1\-9\fR
+used for saving deleted text and named buffers \fBa\-z\fR into which the
+user can place text (4.3, 6.3)
+.tr \a\a
+.TP
+\fB#\fR
+The macro character which, when followed by a number, will substitute
+for a function key on terminals without function keys (6.9).
+In input mode, 
+if this is the erase character, it will delete the last character
+typed in input mode, and must be preceded with a \fB\e\fR to insert
+it, since it normally backs over the last input character.
+.TP
+\fB$\fR
+Moves to the end of the current line.  If the \fBlist\fR option is set,
+then the end of each line will be shown by printing a \fB$\fR after the
+end of the displayed text in the line.  Given a count, advances to the
+count'th following end of line; thus \fB2$\fR advances to the end of the
+following line.
+.TP
+\fB%\fR
+Moves to the parenthesis or brace \fB{ }\fR which balances the parenthesis
+or brace at the current cursor position.
+.TP
+\fB&\fR
+A synonym for \fB:&\fR\s-1CR\s0, by analogy with the
+.I ex
+.B &
+command.
+.TP
+\fB\(aa\fR
+When followed by a \fB\(aa\fR returns to the previous context at the
+beginning of a line.  The previous context is set whenever the current
+line is moved in a non-relative way.
+When followed by a letter \fBa\fR\-\fBz\fR, returns to the line which
+was marked with this letter with a \fBm\fR command, at the first non-white
+character in the line. (2.2, 5.3).
+When used with an operator such as \fBd\fR, the operation takes place
+over complete lines; if \fB\(ga\fR is used, the operation takes place
+from the exact marked place to the current cursor position within the
+line.
+.TP
+\fB(\fR
+Retreats to the beginning of a
+sentence, or to the beginning of a \s-1LISP\s0 s-expression
+if the \fIlisp\fR option is set.
+A sentence ends at a \fB. !\fR or \fB?\fR which is followed by either
+the end of a line or by two spaces.  Any number of closing \fB) ] "\fR
+and \fB\(aa\fR characters may appear after the \fB. !\fR or \fB?\fR,
+and before the spaces or end of line.  Sentences also begin
+at paragraph and section boundaries
+(see \fB{\fR and \fB[[\fR below).
+A count advances that many sentences (4.2, 6.8).
+.TP
+\fB)\fR
+Advances to the beginning of a sentence.
+A count repeats the effect.
+See \fB(\fR above for the definition of a sentence (4.2, 6.8).
+.TP
+\fB*\fR
+Unused.
+.TP
+\fB+\fR
+Same as \s-1CR\s0 when used as a command.
+.TP
+\fB,\fR
+Reverse of the last \fBf F t\fR or \fBT\fR command, looking the other way
+in the current line.  Especially useful after hitting too many \fB;\fR
+characters.  A count repeats the search.
+.TP
+\fB\-\fR
+Retreats to the previous line at the first non-white character.
+This is the inverse of \fB+\fR and \s-1RETURN\s0.
+If the line moved to is not on the screen, the screen is scrolled, or
+cleared and redrawn if this is not possible.
+If a large amount of scrolling would be required the screen is also cleared
+and redrawn, with the current line at the center (2.3).
+.TP
+\fB\&.\fR
+Repeats the last command which changed the buffer.  Especially useful
+when deleting words or lines; the user can delete some words/lines and then
+hit \fB.\fR to delete more and more words/lines.
+Given a count, it passes it on to the command being repeated.  Thus after
+a \fB2dw\fR, \fB3.\fR deletes three words (3.3, 6.3, 7.2, 7.4).
+.TP
+\fB/\fR
+Reads a string from the last line on the screen, and scans forward for
+the next occurrence of this string.  The normal input editing sequences may
+be used during the input on the bottom line; an returns to command state
+without ever searching.
+The search begins when the user hits \s-1CR\s0 to terminate the pattern;
+the cursor moves to the beginning of the last line to indicate that the search
+is in progress; the search may then
+be terminated with a \s-1DEL\s0 or \s-1RUB\s0, or by backspacing when
+at the beginning of the bottom line, returning the cursor to
+its initial position.
+Searches normally wrap end-around to find a string
+anywhere in the buffer.
+.IP
+When used with an operator the enclosed region is normally affected.
+By mentioning an
+offset from the line matched by the pattern the user can force whole lines
+to be affected.  To do this a pattern with a closing
+a closing \fB/\fR and then an offset \fB+\fR\fIn\fR or \fB\-\fR\fIn\fR
+must be given.
+.IP
+To include the character \fB/\fR in the search string, it must be escaped
+with a preceding \fB\e\fR.
+A \fB^\fR at the beginning of the pattern forces the match to occur
+at the beginning of a line only; this speeds the search.  A \fB$\fR at
+the end of the pattern forces the match to occur at the end of a line
+only.
+More extended pattern matching is available, see section 7.4;
+unless \fBnomagic\fR ist set in the \fI\&.exrc\fR file the user will have
+to preceed the characters \fB. [ *\fR and \fB~\fR in the search pattern
+with a \fB\e\fR to get them to work as one would naively expect (1.6, 2.2,
+6.1, 7.2, 7.4).
+.TP
+\fB0\fR
+Moves to the first character on the current line.
+Also used, in forming numbers, after an initial \fB1\fR\-\fB9\fR.
+.TP
+\fB1\-9\fR
+Used to form numeric arguments to commands (2.3, 7.2).
+.TP
+\fB:\fR
+A prefix to a set of commands for file and option manipulation and escapes
+to the system.  Input is given on the bottom line and terminated with
+an \s-1CR\s0, and the command then executed.  The user can return to where
+he was by hitting \s-1DEL\s0 or \s-1RUB\s0 if he hit \fB:\fR accidentally
+(see 
+.BR ex (1)
+and primarily 6.2 and 7.3).
+.TP
+\fB;\fR
+Repeats the last single character find which used \fBf F t\fR or \fBT\fR.
+A count iterates the basic scan (4.1).
+.TP
+\fB<\fR
+An operator which shifts lines left one \fIshiftwidth\fR, normally 8
+spaces.  Like all operators, affects lines when repeated, as in
+\fB<<\fR.  Counts are passed through to the basic object, thus \fB3<<\fR
+shifts three lines (6.6, 7.2).
+.TP
+\fB=\fR
+Reindents line for \s-1LISP\s0, as though they were typed in with \fIlisp\fR
+and \fIautoindent\fR set (6.8).
+.TP
+\fB>\fR
+An operator which shifts lines right one \fIshiftwidth\fR, normally 8
+spaces.  Affects lines when repeated as in \fB>>\fR.  Counts repeat the
+basic object (6.6, 7.2).
+.TP
+\fB?\fR
+Scans backwards, the opposite of \fB/\fR.  See the \fB/\fR description
+above for details on scanning (2.2, 6.1, 7.4).
+.TP
+\fB@\fR
+A macro character (6.9).  If this is the kill character, it must be escaped
+with a \e
+to type it in during input mode, as it normally backs over the input
+given on the current line (3.1, 3.4, 7.5).
+.TP
+\fBA\fR
+Appends at the end of line, a synonym for \fB$a\fR (7.2).
+.TP
+\fBB\fR
+Backs up a word, where words are composed of non-blank sequences, placing
+the cursor at the beginning of the word.  A count repeats the effect
+(2.4).
+.TP
+\fBC\fR
+Changes the rest of the text on the current line; a synonym for \fBc$\fR.
+.TP
+\fBD\fR
+Deletes the rest of the text on the current line; a synonym for \fBd$\fR.
+.TP
+\fBE\fR
+Moves forward to the end of a word, defined as blanks and non-blanks,
+like \fBB\fR and \fBW\fR.  A count repeats the effect.
+.TP
+\fBF\fR
+Finds a single following character, backwards in the current line.
+A count repeats this search that many times (4.1).
+.TP
+\fBG\fR
+Goes to the line number given as preceding argument, or the end of the
+file if no preceding count is given.  The screen is redrawn with the
+new current line in the center if necessary (7.2).
+.TP
+\fBH\fR
+.BR "Home arrow" .
+Homes the cursor to the top line on the screen.  If a count is given,
+then the cursor is moved to the count'th line on the screen.
+In any case the cursor is moved to the first non-white character on the
+line.  If used as the target of an operator, full lines are affected
+(2.3, 3.2).
+.TP
+\fBI\fR
+Inserts at the beginning of a line; a synonym for \fB^i\fR.
+.TP
+\fBJ\fR
+Joins together lines, supplying appropriate white space: one space between
+words, two spaces after a \fB.\fR, and no spaces at all if the first
+character of the joined on line is \fB)\fR.  A count causes that many
+lines to be joined rather than the default two (6.5, 7.1f).
+.TP
+\fBK\fR
+Unused.
+.TP
+\fBL\fR
+Moves the cursor to the first non-white character of the last line on
+the screen.  With a count, to the first non-white of the count'th line
+from the bottom.  Operators affect whole lines when used with \fBL\fR
+(2.3).
+.TP
+\fBM\fR
+Moves the cursor to the middle line on the screen, at the first non-white
+position on the line (2.3).
+.TP
+\fBN\fR
+Scans for the next match of the last pattern given to
+\fB/\fR or \fB?\fR, but in the reverse direction; this is the reverse
+of \fBn\fR.
+.TP
+\fBO\fR
+Opens a new line above the current line and inputs text there up to an
+\s-1ESC\s0.  A count can be used on dumb terminals to specify a number
+of lines to be opened; this is generally obsolete, as the \fIslowopen\fR
+option works better (3.1).
+.TP
+\fBP\fR
+Puts the last deleted text back before/above the cursor.  The text goes
+back as whole lines above the cursor if it was deleted as whole lines.
+Otherwise the text is inserted between the characters before and at the
+cursor.  May be preceded by a named buffer specification \fB"\fR\fIx\fR
+to retrieve the contents of the buffer; buffers \fB1\fR\-\fB9\fR contain
+deleted material, buffers \fBa\fR\-\fBz\fR are available for general
+use (6.3).
+.TP
+\fBQ\fR
+Quits from \fIvi\fR to \fIex\fR command mode.  In this mode, whole lines
+form commands, ending with a \s-1RETURN\s0.  One can give all the \fB:\fR
+commands; the editor supplies the \fB:\fR as a prompt (7.7).
+.TP
+\fBR\fR
+Replaces characters on the screen with characters typed (overlay fashion).
+Terminates with an \s-1ESC\s0.
+.TP
+\fBS\fR
+Changes whole lines, a synonym for \fBcc\fR.  A count substitutes for
+that many lines.  The lines are saved in the numeric buffers, and erased
+on the screen before the substitution begins.
+.TP
+\fBT\fR
+Takes a single following character, locates the character before the
+cursor in the current line, and places the cursor just after that character.
+A count repeats the effect.  Most useful with operators such as \fBd\fR
+(4.1).
+.TP
+\fBU\fR
+Restores the current line to its state before the user started changing it
+(3.5).
+.TP
+\fBV\fR
+Unused.
+.TP
+\fBW\fR
+Moves forward to the beginning of a word in the current line,
+where words are defined as sequences of blank/non-blank characters.
+A count repeats the effect (2.4).
+.TP
+\fBX\fR
+Deletes the character before the cursor.  A count repeats the effect,
+but only characters on the current line are deleted.
+.TP
+\fBY\fR
+Yanks a copy of the current line into the unnamed buffer, to be put back
+by a later \fBp\fR or \fBP\fR; a very useful synonym for \fByy\fR. 
+A count yanks that many lines.  May be preceded by a buffer name to put
+lines in that buffer (7.4).
+.TP
+\fBZZ\fR
+Exits the editor.
+(Same as \fB:x\fP\s-1CR\s0.)
+If any changes have been made, the buffer is written out to the current file.
+Then the editor quits.
+.TP
+\fB[[\fR
+Backs up to the previous section boundary.  A section begins at each
+macro in the \fIsections\fR option,
+normally a `.NH' or `.SH' and also at lines which which start
+with a formfeed \fB^L\fR.  Lines beginning with \fB{\fR also stop \fB[[\fR;
+this makes it useful for looking backwards, a function at a time, in C
+programs.  If the option \fIlisp\fR is set, stops at each \fB(\fR at the
+beginning of a line, and is thus useful for moving backwards at the top
+level \s-1LISP\s0 objects. (4.2, 6.1, 6.6, 7.2).
+.TP
+\fB\e\fR
+Unused.
+.TP
+\fB]]\fR
+Forward to a section boundary, see \fB[[\fR for a definition (4.2, 6.1,
+6.6, 7.2).
+.TP
+\fB^\fR
+Moves to the first non-white position on the current line (4.4).
+.TP
+\fB_\fR
+Unused.
+.TP
+\fB\(ga\fR
+When followed by a \fB\(ga\fR returns to the previous context.
+The previous context is set whenever the current
+line is moved in a non-relative way.
+When followed by a letter \fBa\fR\-\fBz\fR, returns to the position which
+was marked with this letter with a \fBm\fR command.
+When used with an operator such as \fBd\fR, the operation takes place
+from the exact marked place to the current position within the line;
+if using \fB\(aa\fR, the operation takes place over complete lines
+(2.2, 5.3).
+.TP
+\fBa\fR
+Appends arbitrary text after the current cursor position; the insert
+can continue onto multiple lines by using \s-1RETURN\s0 within the insert.
+A count causes the inserted text to be replicated, but only if the inserted
+text is all on one line.
+The insertion terminates with an \s-1ESC\s0 (3.1, 7.2).
+.TP
+\fBb\fR
+Backs up to the beginning of a word in the current line.  A word is a
+sequence of alphanumerics, or a sequence of special characters.
+A count repeats the effect (2.4).
+.TP
+\fBc\fR
+An operator which changes the following object, replacing it with the
+following input text up to an \s-1ESC\s0.  If more than part of a single
+line is affected, the text which is changed away is saved in the numeric named
+buffers.  If only part of the current line is affected, then the last
+character to be changed away is marked with a \fB$\fR.
+A count causes that many objects to be affected, thus both
+\fB3c)\fR and \fBc3)\fR change the following three sentences (7.4).
+.TP
+\fBd\fR
+An operator which deletes the following object.  If more than part of
+a line is affected, the text is saved in the numeric buffers.
+A count causes that many objects to be affected; thus \fB3dw\fR is the
+same as \fBd3w\fR (3.3, 3.4, 4.1, 7.4).
+.TP
+\fBe\fR
+Advances to the end of the next word, defined as for \fBb\fR and \fBw\fR.
+A count repeats the effect (2.4, 3.1).
+.TP
+\fBf\fR
+Finds the first instance of the next character following the cursor on
+the current line.  A count repeats the find (4.1).
+.TP
+\fBg\fR
+Unused.
+.sp
+Arrow keys
+.BR h ,
+.BR j ,
+.BR k ,
+.BR l ,
+and
+.BR H .
+.TP
+\fBh\fR
+.B "Left arrow" .
+Moves the cursor one character to the left.
+Like the other arrow keys, either
+.BR h ,
+the
+.B "left arrow"
+key, or one of the synonyms (\fB^H\fP) has the same effect.
+A count repeats the effect (3.1, 7.5).
+.TP
+\fBi\fR
+Inserts text before the cursor, otherwise like \fBa\fR (7.2).
+.TP
+\fBj\fR
+.B "Down arrow" .
+Moves the cursor one line down in the same column.
+If the position does not exist,
+.I vi
+comes as close as possible to the same column.
+Synonyms include
+.B ^J
+(linefeed) and
+.B ^N .
+.TP
+\fBk\fR
+.B "Up arrow" .
+Moves the cursor one line up.
+.B ^P
+is a synonym.
+.TP
+\fBl\fR
+.B "Right arrow" .
+Moves the cursor one character to the right.
+\s-1SPACE\s0 is a synonym.
+.TP
+\fBm\fR
+Marks the current position of the cursor in the mark register which is
+specified by the next character \fBa\fR\-\fBz\fR.  The user can return
+to this position or use it with an operator
+using \fB\(ga\fR or \fB\(aa\fR (5.3).
+.TP
+\fBn\fR
+Repeats the last \fB/\fR or \fB?\fR scanning commands (2.2).
+.TP
+\fBo\fR
+Opens new lines below the current line; otherwise like \fBO\fR (3.1).
+.TP
+\fBp\fR
+Puts text after/below the cursor; otherwise like \fBP\fR (6.3).
+.TP
+\fBq\fR
+Unused.
+.TP
+\fBr\fR
+Replaces the single character at the cursor with a single character typed.
+The new character may be a \s-1RETURN\s0; this is the easiest
+way to split lines.  A count replaces each of the following count characters
+with the single character given; see \fBR\fR above which is the more
+usually useful iteration of \fBr\fR (3.2).
+.TP
+\fBs\fR
+Changes the single character under the cursor to the text which follows
+up to an \s-1ESC\s0; given a count, that many characters from the current
+line are changed.  The last character to be changed is marked with \fB$\fR
+as in \fBc\fR (3.2).
+.TP
+\fBt\fR
+Advances the cursor upto the character before the next character typed.
+Most useful with operators such as \fBd\fR and \fBc\fR to delete the
+characters up to a following character.  One can use \fB.\fR to delete
+more if this doesn't delete enough the first time (4.1).
+.TP
+\fBu\fR
+Undoes the last change made to the current buffer.  If repeated, will
+alternate between these two states, thus is its own inverse. When used
+after an insert which inserted text on more than one line, the lines are
+saved in the numeric named buffers (3.5).
+.TP
+\fBv\fR
+Unused.
+.TP
+\fBw\fR
+Advances to the beginning of the next word, as defined by \fBb\fR (2.4).
+.TP
+\fBx\fR
+Deletes the single character under the cursor.  With a count deletes
+deletes that many characters forward from the cursor position, but only
+on the current line (6.5).
+.TP
+\fBy\fR
+An operator, yanks the following object into the unnamed temporary buffer.
+If preceded by a named buffer specification, \fB"\fR\fIx\fR, the text
+is placed in that buffer also.  Text can be recovered by a later \fBp\fR
+or \fBP\fR (7.4).
+.TP
+\fBz\fR
+Redraws the screen with the current line placed as specified by the following
+character: \s-1RETURN\s0 specifies the top of the screen, \fB.\fR the
+center of the screen, and \fB\-\fR at the bottom of the screen.
+A count may be given after the \fBz\fR and before the following character
+to specify the new screen size for the redraw.
+A count before the \fBz\fR gives the number of the line to place in the
+center of the screen instead of the default current line. (5.4)
+.TP
+\fB{\fR
+Retreats to the beginning of the beginning of the preceding paragraph.
+A paragraph begins at each macro in the \fIparagraphs\fR option, normally
+`.IP', `.LP', `.PP', `.QP' and `.bp'.
+A paragraph also begins after a completely
+empty line, and at each section boundary (see \fB[[\fR above) (4.2, 6.8,
+7.6).
+.TP
+\fB|\fR
+Places the cursor on the character in the column specified
+by the count (7.1, 7.2).
+.TP
+\fB}\fR
+Advances to the beginning of the next paragraph.  See \fB{\fR for the
+definition of paragraph (4.2, 6.8, 7.6).
+.TP
+\fB~\fR
+Switches the case of the given count of characters
+starting from the current cursor position to the end of the current line.
+Non-alphabetic characters remain unchanged.
+.TP
+\fB^?\ (\s-1\fRDEL\fB\s0)\fR
+Interrupts the editor, returning it to command accepting state (1.6,
+7.5).
+.SH "ENVIRONMENT VARIABLES"
+.PP
+The following environment variables affect the behaviour of vi:
+.TP
+.B COLUMNS
+Overrides the system-supplied number of terminal columns.
+.TP
+.B EXINIT
+Contains commands to execute at editor startup.
+If this variable is present, the
+.I .exrc
+file in the user's home directory is ignored.
+.TP
+.B HOME
+Used to locate the editor startup file.
+.TP
+.BR LANG ", " LC_ALL
+See
+.IR locale (7).
+.TP
+.B LC_CTYPE
+Determines the mapping of bytes to characters,
+types of characters,
+case conversion
+and composition of character classes in regular expressions.
+.TP
+.B LC_MESSAGES
+Sets the language used for diagnostic and informal messages.
+.TP
+.B LINES
+Overrides the system-supplied number of terminal lines.
+.TP
+.B NLSPATH
+See
+.IR catopen (3).
+.TP
+.B SHELL
+The program file used to execute external commands.
+.TP
+.B TERM
+Determines the terminal type.
+.SH FILES
+.TP
+.B /usr/libexec/expreserve
+preserve command
+.TP
+.B /usr/libexec/exrecover
+recover command
+.TP
+.B /etc/termcap
+describes capabilities of terminals
+.TP
+.B $HOME/.exrc
+editor startup file
+.TP
+.B /var/tmp/Ex\fInnnnnnnnnn\fP
+editor temporary
+.TP
+.B /var/tmp/Rx\fInnnnnnnnnn\fP
+named buffer temporary
+.TP
+.B /var/preserve
+preservation directory
+.SH SEE ALSO
+ex(1),
+edit(1),
+\*(lqVi Quick Reference\*(rq card,
+\*(lqAn Introduction to Display Editing with Vi\*(rq.
+.SH AUTHOR
+William Joy.
+.PP
+Mark Horton added macros to
+.I visual
+mode and was maintaining version 3.
+.PP
+This version incorporates changes by Gunnar Ritter.
+.SH NOTES
+Software tabs using \fB^T\fP work only immediately after the
+.I autoindent.
+.PP
+Left and right shifts on intelligent terminals don't make use of
+insert and delete character operations in the terminal.
+.PP
+The
+.I wrapmargin
+option can be fooled since it looks at output columns when blanks are typed.
+If a long word passes through the margin and onto the next line without a 
+break, then the line won't be broken.
+.PP
+Insert/delete within a line can be slow if tabs are present on intelligent
+terminals, since the terminals need help in doing this correctly.
+.\".PP
+.\"Saving text on deletes in the named buffers is somewhat inefficient.
+.PP
+The
+.I source
+command does not work when executed as \fB:source\fP;
+there is no way to use the \fB:append\fP, \fB:change\fP,
+and \fB:insert\fP commands, since it is not possible to give
+more than one line of input to a \fB:\fP escape.  To use these
+on a \fB:global\fP one must \fBQ\fP to \fIex\fP command mode,
+execute them, and then reenter the screen editor with
+.I vi
+or
+.I open.