From 8559e3346c0a43ee8671a8ea43767b1809c80021 Mon Sep 17 00:00:00 2001 From: Maciej Wereski Date: Thu, 8 Aug 2013 14:32:30 +0200 Subject: [PATCH 1/1] Imported Upstream version 050325 --- Changes | 241 ++++++ LICENSE | 69 ++ Makefile | 392 ++++++++++ README | 145 ++++ TODO | 12 + catd/en_US | 309 ++++++++ config.h | 146 ++++ ex.1 | 2045 ++++++++++++++++++++++++++++++++++++++++++++++++++ ex.c | 678 +++++++++++++++++ ex.h | 583 ++++++++++++++ ex.spec | 58 ++ ex_addr.c | 406 ++++++++++ ex_argv.h | 105 +++ ex_cmds.c | 988 ++++++++++++++++++++++++ ex_cmds2.c | 674 +++++++++++++++++ ex_cmdsub.c | 1443 +++++++++++++++++++++++++++++++++++ ex_data.c | 170 +++++ ex_extern.c | 98 +++ ex_get.c | 355 +++++++++ ex_io.c | 1125 +++++++++++++++++++++++++++ ex_proto.h | 567 ++++++++++++++ ex_put.c | 1310 ++++++++++++++++++++++++++++++++ ex_re.c | 1313 ++++++++++++++++++++++++++++++++ ex_re.h | 125 +++ ex_set.c | 313 ++++++++ ex_subr.c | 1153 ++++++++++++++++++++++++++++ ex_tagio.c | 177 +++++ ex_temp.c | 771 +++++++++++++++++++ ex_temp.h | 191 +++++ ex_tty.c | 407 ++++++++++ ex_tty.h | 249 ++++++ ex_tune.h | 225 ++++++ ex_unix.c | 450 +++++++++++ ex_v.c | 527 +++++++++++++ ex_vadj.c | 1162 ++++++++++++++++++++++++++++ ex_vars.h | 48 ++ ex_version.c | 90 +++ ex_vget.c | 879 ++++++++++++++++++++++ ex_vis.h | 321 ++++++++ ex_vmain.c | 1442 +++++++++++++++++++++++++++++++++++ ex_voper.c | 976 ++++++++++++++++++++++++ ex_vops.c | 1068 ++++++++++++++++++++++++++ ex_vops2.c | 1097 +++++++++++++++++++++++++++ ex_vops3.c | 730 ++++++++++++++++++ ex_vput.c | 1625 +++++++++++++++++++++++++++++++++++++++ ex_vwind.c | 500 ++++++++++++ expreserve.c | 555 ++++++++++++++ exrecover.c | 902 ++++++++++++++++++++++ libterm/Makefile | 64 ++ libterm/libterm.h | 56 ++ libterm/termcap.c | 410 ++++++++++ libterm/tgoto.c | 222 ++++++ libterm/tputs.c | 134 ++++ libuxre/COPYING.LGPL | 504 +++++++++++++ libuxre/Makefile | 12 + libuxre/NOTES | 14 + libuxre/_collelem.c | 119 +++ libuxre/_collmult.c | 55 ++ libuxre/bracket.c | 829 ++++++++++++++++++++ libuxre/colldata.h | 226 ++++++ libuxre/onefile.c | 38 + libuxre/re.h | 228 ++++++ libuxre/regcomp.c | 77 ++ libuxre/regdfa.c | 877 ++++++++++++++++++++++ libuxre/regdfa.h | 75 ++ libuxre/regerror.c | 95 +++ libuxre/regex.h | 153 ++++ libuxre/regexec.c | 68 ++ libuxre/regfree.c | 42 ++ libuxre/regnfa.c | 1070 ++++++++++++++++++++++++++ libuxre/regparse.c | 1091 +++++++++++++++++++++++++++ libuxre/stubs.c | 97 +++ libuxre/wcharm.h | 63 ++ makeoptions | 123 +++ malloc.c | 364 +++++++++ mapmalloc.c | 439 +++++++++++ printf.c | 440 +++++++++++ regexp.h | 1210 +++++++++++++++++++++++++++++ vi.1 | 1025 +++++++++++++++++++++++++ 79 files changed, 39435 insertions(+) create mode 100644 Changes create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README create mode 100644 TODO create mode 100644 catd/en_US create mode 100644 config.h create mode 100644 ex.1 create mode 100644 ex.c create mode 100644 ex.h create mode 100644 ex.spec create mode 100644 ex_addr.c create mode 100644 ex_argv.h create mode 100644 ex_cmds.c create mode 100644 ex_cmds2.c create mode 100644 ex_cmdsub.c create mode 100644 ex_data.c create mode 100644 ex_extern.c create mode 100644 ex_get.c create mode 100644 ex_io.c create mode 100644 ex_proto.h create mode 100644 ex_put.c create mode 100644 ex_re.c create mode 100644 ex_re.h create mode 100644 ex_set.c create mode 100644 ex_subr.c create mode 100644 ex_tagio.c create mode 100644 ex_temp.c create mode 100644 ex_temp.h create mode 100644 ex_tty.c create mode 100644 ex_tty.h create mode 100644 ex_tune.h create mode 100644 ex_unix.c create mode 100644 ex_v.c create mode 100644 ex_vadj.c create mode 100644 ex_vars.h create mode 100644 ex_version.c create mode 100644 ex_vget.c create mode 100644 ex_vis.h create mode 100644 ex_vmain.c create mode 100644 ex_voper.c create mode 100644 ex_vops.c create mode 100644 ex_vops2.c create mode 100644 ex_vops3.c create mode 100644 ex_vput.c create mode 100644 ex_vwind.c create mode 100644 expreserve.c create mode 100644 exrecover.c create mode 100644 libterm/Makefile create mode 100644 libterm/libterm.h create mode 100644 libterm/termcap.c create mode 100644 libterm/tgoto.c create mode 100644 libterm/tputs.c create mode 100644 libuxre/COPYING.LGPL create mode 100644 libuxre/Makefile create mode 100644 libuxre/NOTES create mode 100644 libuxre/_collelem.c create mode 100644 libuxre/_collmult.c create mode 100644 libuxre/bracket.c create mode 100644 libuxre/colldata.h create mode 100644 libuxre/onefile.c create mode 100644 libuxre/re.h create mode 100644 libuxre/regcomp.c create mode 100644 libuxre/regdfa.c create mode 100644 libuxre/regdfa.h create mode 100644 libuxre/regerror.c create mode 100644 libuxre/regex.h create mode 100644 libuxre/regexec.c create mode 100644 libuxre/regfree.c create mode 100644 libuxre/regnfa.c create mode 100644 libuxre/regparse.c create mode 100644 libuxre/stubs.c create mode 100644 libuxre/wcharm.h create mode 100644 makeoptions create mode 100644 malloc.c create mode 100644 mapmalloc.c create mode 100644 printf.c create mode 100644 regexp.h create mode 100644 vi.1 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 ~ 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 ~ 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. +* ~ 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 :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 +. + +The project homepage is currently at . + + +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-.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 . + +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 + 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef BIT8 +#ifndef ISO8859_1 +#include +#endif +#endif + +#ifdef MB +#include +#include +#endif + +#include +#include +#include +#include +#ifndef TIOCGWINSZ +#include +#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 + 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 +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 +URL: +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= &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 + +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/\ 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 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 +#include + +/* + * 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 = ®rbuf; + 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 + +/* + * 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; ivliny < 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= 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 current line to top + * z like z + * 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"E) && (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"E)) { + *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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#ifdef LANGMSG +#include +#include +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 +#ifdef notdef /* GR */ +#include /* 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 +#include + +#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 +#include +#include +#include +#include +#include +#include + +/* + * 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 +#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 + +#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. + + + Copyright (C) + + 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. + + , 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 . 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 + +#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 + +#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 +#include +#include +#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 +#include +/* #include */ + +/* +* 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 /* */ +/* #define __fnm_collate lc_collate */ /* */ + +#include +#include +/* #include */ +#include + +#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 . + */ +#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 . + */ +#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 +#include +#include +#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 + +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 +#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 /* 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 +#include +#include "re.h" +#include +#include + +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 +#include +#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 = ""; + kind = LEAF_ROP; + break; + case ROP_BOL: + opstr = ""; + kind = LEAF_ROP; + break; + case ROP_EOL: + opstr = ""; + kind = LEAF_ROP; + break; + case ROP_ALL: + opstr = ""; + kind = LEAF_ROP; + break; + case ROP_ANYCH: + opstr = ""; + kind = LEAF_ROP; + break; + case ROP_NOTNL: + opstr = ""; + kind = LEAF_ROP; + break; + case ROP_EMPTY: + opstr = ""; + kind = LEAF_ROP; + break; + case ROP_NONE: + opstr = ""; + 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 = ""; + 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 +#include +#include + +#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 +#include +#include + +#include "config.h" + +#ifdef LANGMSG +#include +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 +#include +#include +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&&qptr = 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=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 +#include +#include +#include + +#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 +#include +#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 +#include +#include +#include +#include +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&&qptr = 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=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 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 +#include +#include +#ifdef REGEXP_H_WCHARS +#include +#include +#include +#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. -- 2.7.4