summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaciej Wereski <m.wereski@partner.samsung.com>2013-08-08 14:32:30 +0200
committerMaciej Wereski <m.wereski@partner.samsung.com>2013-08-08 14:32:30 +0200
commit8559e3346c0a43ee8671a8ea43767b1809c80021 (patch)
tree3b429ba2c5953876f9ace98b2056f993f57d8aae
downloadvi-8559e3346c0a43ee8671a8ea43767b1809c80021.tar.gz
vi-8559e3346c0a43ee8671a8ea43767b1809c80021.tar.bz2
vi-8559e3346c0a43ee8671a8ea43767b1809c80021.zip
Imported Upstream version 050325upstream/050325upstream
-rw-r--r--Changes241
-rw-r--r--LICENSE69
-rw-r--r--Makefile392
-rw-r--r--README145
-rw-r--r--TODO12
-rw-r--r--catd/en_US309
-rw-r--r--config.h146
-rw-r--r--ex.12045
-rw-r--r--ex.c678
-rw-r--r--ex.h583
-rw-r--r--ex.spec58
-rw-r--r--ex_addr.c406
-rw-r--r--ex_argv.h105
-rw-r--r--ex_cmds.c988
-rw-r--r--ex_cmds2.c674
-rw-r--r--ex_cmdsub.c1443
-rw-r--r--ex_data.c170
-rw-r--r--ex_extern.c98
-rw-r--r--ex_get.c355
-rw-r--r--ex_io.c1125
-rw-r--r--ex_proto.h567
-rw-r--r--ex_put.c1310
-rw-r--r--ex_re.c1313
-rw-r--r--ex_re.h125
-rw-r--r--ex_set.c313
-rw-r--r--ex_subr.c1153
-rw-r--r--ex_tagio.c177
-rw-r--r--ex_temp.c771
-rw-r--r--ex_temp.h191
-rw-r--r--ex_tty.c407
-rw-r--r--ex_tty.h249
-rw-r--r--ex_tune.h225
-rw-r--r--ex_unix.c450
-rw-r--r--ex_v.c527
-rw-r--r--ex_vadj.c1162
-rw-r--r--ex_vars.h48
-rw-r--r--ex_version.c90
-rw-r--r--ex_vget.c879
-rw-r--r--ex_vis.h321
-rw-r--r--ex_vmain.c1442
-rw-r--r--ex_voper.c976
-rw-r--r--ex_vops.c1068
-rw-r--r--ex_vops2.c1097
-rw-r--r--ex_vops3.c730
-rw-r--r--ex_vput.c1625
-rw-r--r--ex_vwind.c500
-rw-r--r--expreserve.c555
-rw-r--r--exrecover.c902
-rw-r--r--libterm/Makefile64
-rw-r--r--libterm/libterm.h56
-rw-r--r--libterm/termcap.c410
-rw-r--r--libterm/tgoto.c222
-rw-r--r--libterm/tputs.c134
-rw-r--r--libuxre/COPYING.LGPL504
-rw-r--r--libuxre/Makefile12
-rw-r--r--libuxre/NOTES14
-rw-r--r--libuxre/_collelem.c119
-rw-r--r--libuxre/_collmult.c55
-rw-r--r--libuxre/bracket.c829
-rw-r--r--libuxre/colldata.h226
-rw-r--r--libuxre/onefile.c38
-rw-r--r--libuxre/re.h228
-rw-r--r--libuxre/regcomp.c77
-rw-r--r--libuxre/regdfa.c877
-rw-r--r--libuxre/regdfa.h75
-rw-r--r--libuxre/regerror.c95
-rw-r--r--libuxre/regex.h153
-rw-r--r--libuxre/regexec.c68
-rw-r--r--libuxre/regfree.c42
-rw-r--r--libuxre/regnfa.c1070
-rw-r--r--libuxre/regparse.c1091
-rw-r--r--libuxre/stubs.c97
-rw-r--r--libuxre/wcharm.h63
-rw-r--r--makeoptions123
-rw-r--r--malloc.c364
-rw-r--r--mapmalloc.c439
-rw-r--r--printf.c440
-rw-r--r--regexp.h1210
-rw-r--r--vi.11025
79 files changed, 39435 insertions, 0 deletions
diff --git a/Changes b/Changes
new file mode 100644
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
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
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
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
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
index 0000000..7888f2c
--- /dev/null
+++ b/catd/en_US
@@ -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
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
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
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
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
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
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
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
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
index 0000000..8d822bd
--- /dev/null
+++ b/ex_cmds2.c
@@ -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
index 0000000..1f798d7
--- /dev/null
+++ b/ex_cmdsub.c
@@ -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
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
index 0000000..c6a0974
--- /dev/null
+++ b/ex_extern.c
@@ -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
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
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
index 0000000..6af8c18
--- /dev/null
+++ b/ex_proto.h
@@ -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
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
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
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
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
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
index 0000000..f7570f5
--- /dev/null
+++ b/ex_tagio.c
@@ -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
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
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
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
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
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
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
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
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
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
index 0000000..317fdd3
--- /dev/null
+++ b/ex_version.c
@@ -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
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
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
index 0000000..ac07f92
--- /dev/null
+++ b/ex_vmain.c
@@ -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
index 0000000..d86b5d9
--- /dev/null
+++ b/ex_voper.c
@@ -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
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
index 0000000..d7cd3fb
--- /dev/null
+++ b/ex_vops2.c
@@ -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
index 0000000..57cdebf
--- /dev/null
+++ b/ex_vops3.c
@@ -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
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
index 0000000..751c991
--- /dev/null
+++ b/ex_vwind.c
@@ -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
index 0000000..e0e49a8
--- /dev/null
+++ b/expreserve.c
@@ -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
index 0000000..cca2250
--- /dev/null
+++ b/exrecover.c
@@ -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
index 0000000..b3432b1
--- /dev/null
+++ b/libterm/Makefile
@@ -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
index 0000000..8629224
--- /dev/null
+++ b/libterm/libterm.h
@@ -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
index 0000000..6cee780
--- /dev/null
+++ b/libterm/termcap.c
@@ -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
index 0000000..07316db
--- /dev/null
+++ b/libterm/tgoto.c
@@ -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
index 0000000..58e0665
--- /dev/null
+++ b/libterm/tputs.c
@@ -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
index 0000000..b1e3f5a
--- /dev/null
+++ b/libuxre/COPYING.LGPL
@@ -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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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
+
+ 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
index 0000000..46d7320
--- /dev/null
+++ b/libuxre/Makefile
@@ -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
index 0000000..19aedf1
--- /dev/null
+++ b/libuxre/NOTES
@@ -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
index 0000000..c5dbb05
--- /dev/null
+++ b/libuxre/_collelem.c
@@ -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
index 0000000..7a199b3
--- /dev/null
+++ b/libuxre/_collmult.c
@@ -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
index 0000000..bc31b23
--- /dev/null
+++ b/libuxre/bracket.c
@@ -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
index 0000000..e3a3784
--- /dev/null
+++ b/libuxre/colldata.h
@@ -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
index 0000000..78f22a0
--- /dev/null
+++ b/libuxre/onefile.c
@@ -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
index 0000000..2738a05
--- /dev/null
+++ b/libuxre/re.h
@@ -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
index 0000000..20a197d
--- /dev/null
+++ b/libuxre/regcomp.c
@@ -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
index 0000000..8142e8d
--- /dev/null
+++ b/libuxre/regdfa.c
@@ -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
index 0000000..8cb0d48
--- /dev/null
+++ b/libuxre/regdfa.h
@@ -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
index 0000000..397e3e5
--- /dev/null
+++ b/libuxre/regerror.c
@@ -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
index 0000000..8dbd028
--- /dev/null
+++ b/libuxre/regex.h
@@ -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
index 0000000..667868f
--- /dev/null
+++ b/libuxre/regexec.c
@@ -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
index 0000000..31180d7
--- /dev/null
+++ b/libuxre/regfree.c
@@ -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
index 0000000..6953f1f
--- /dev/null
+++ b/libuxre/regnfa.c
@@ -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
index 0000000..0a5c6b2
--- /dev/null
+++ b/libuxre/regparse.c
@@ -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
index 0000000..bd670db
--- /dev/null
+++ b/libuxre/stubs.c
@@ -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
index 0000000..8985d6b
--- /dev/null
+++ b/libuxre/wcharm.h
@@ -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
index 0000000..d29746a
--- /dev/null
+++ b/makeoptions
@@ -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
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
index 0000000..017b992
--- /dev/null
+++ b/mapmalloc.c
@@ -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
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
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
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 "
+.iP  15
+Precedes a named buffer specification. There are named buffers \fB1\-9\fR
+used for saving deleted text and named buffers \fBa\-z\fR into which the
+user can place text (4.3, 6.3)
+.tr 
+.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.