summaryrefslogtreecommitdiff
path: root/usr.bin/vi
diff options
context:
space:
mode:
authorsvn2git <svn2git@FreeBSD.org>1994-05-01 08:00:00 +0000
committersvn2git <svn2git@FreeBSD.org>1994-05-01 08:00:00 +0000
commita16f65c7d117419bd266c28a1901ef129a337569 (patch)
tree2626602f66dc3551e7a7c7bc9ad763c3bc7ab40a /usr.bin/vi
parent8503f4f13f77abf7adc8f7e329c6f9c1d52b6a20 (diff)
Diffstat (limited to 'usr.bin/vi')
-rw-r--r--usr.bin/vi/Makefile99
-rw-r--r--usr.bin/vi/README193
-rw-r--r--usr.bin/vi/args.h53
-rw-r--r--usr.bin/vi/ascii.c115
-rw-r--r--usr.bin/vi/cut.c526
-rw-r--r--usr.bin/vi/cut.h90
-rw-r--r--usr.bin/vi/delete.c189
-rw-r--r--usr.bin/vi/exf.c690
-rw-r--r--usr.bin/vi/exf.h129
-rw-r--r--usr.bin/vi/gs.h91
-rw-r--r--usr.bin/vi/include/bitstring.h143
-rw-r--r--usr.bin/vi/include/cdefs.h98
-rw-r--r--usr.bin/vi/include/compat.h241
-rw-r--r--usr.bin/vi/include/err.h16
-rw-r--r--usr.bin/vi/include/file.h15
-rw-r--r--usr.bin/vi/include/glob.h92
-rw-r--r--usr.bin/vi/include/mpool.h135
-rw-r--r--usr.bin/vi/include/ndbm.h77
-rw-r--r--usr.bin/vi/include/queue.h245
-rw-r--r--usr.bin/vi/interrupt.h96
-rw-r--r--usr.bin/vi/line.c464
-rw-r--r--usr.bin/vi/log.c663
-rw-r--r--usr.bin/vi/log.h53
-rw-r--r--usr.bin/vi/main.c743
-rw-r--r--usr.bin/vi/mark.c257
-rw-r--r--usr.bin/vi/mark.h64
-rw-r--r--usr.bin/vi/mem.h166
-rw-r--r--usr.bin/vi/msg.h68
-rw-r--r--usr.bin/vi/nex/ex.c1523
-rw-r--r--usr.bin/vi/nex/ex_abbrev.c105
-rw-r--r--usr.bin/vi/nex/ex_append.c180
-rw-r--r--usr.bin/vi/nex/ex_args.c274
-rw-r--r--usr.bin/vi/nex/ex_argv.c575
-rw-r--r--usr.bin/vi/nex/ex_at.c99
-rw-r--r--usr.bin/vi/nex/ex_bang.c192
-rw-r--r--usr.bin/vi/nex/ex_cd.c82
-rw-r--r--usr.bin/vi/nex/ex_delete.c74
-rw-r--r--usr.bin/vi/nex/ex_digraph.c313
-rw-r--r--usr.bin/vi/nex/ex_display.c145
-rw-r--r--usr.bin/vi/nex/ex_edit.c114
-rw-r--r--usr.bin/vi/nex/ex_equal.c63
-rw-r--r--usr.bin/vi/nex/ex_exit.c82
-rw-r--r--usr.bin/vi/nex/ex_file.c92
-rw-r--r--usr.bin/vi/nex/ex_global.c403
-rw-r--r--usr.bin/vi/nex/ex_init.c189
-rw-r--r--usr.bin/vi/nex/ex_join.c189
-rw-r--r--usr.bin/vi/nex/ex_map.c158
-rw-r--r--usr.bin/vi/nex/ex_mark.c54
-rw-r--r--usr.bin/vi/nex/ex_mkexrc.c120
-rw-r--r--usr.bin/vi/nex/ex_move.c133
-rw-r--r--usr.bin/vi/nex/ex_open.c63
-rw-r--r--usr.bin/vi/nex/ex_preserve.c80
-rw-r--r--usr.bin/vi/nex/ex_print.c196
-rw-r--r--usr.bin/vi/nex/ex_put.c65
-rw-r--r--usr.bin/vi/nex/ex_read.c223
-rw-r--r--usr.bin/vi/nex/ex_screen.c134
-rw-r--r--usr.bin/vi/nex/ex_script.c570
-rw-r--r--usr.bin/vi/nex/ex_set.c58
-rw-r--r--usr.bin/vi/nex/ex_shell.c136
-rw-r--r--usr.bin/vi/nex/ex_shift.c193
-rw-r--r--usr.bin/vi/nex/ex_source.c54
-rw-r--r--usr.bin/vi/nex/ex_stop.c68
-rw-r--r--usr.bin/vi/nex/ex_subst.c984
-rw-r--r--usr.bin/vi/nex/ex_tag.c859
-rw-r--r--usr.bin/vi/nex/ex_undo.c99
-rw-r--r--usr.bin/vi/nex/ex_usage.c163
-rw-r--r--usr.bin/vi/nex/ex_util.c78
-rw-r--r--usr.bin/vi/nex/ex_version.c59
-rw-r--r--usr.bin/vi/nex/ex_visual.c122
-rw-r--r--usr.bin/vi/nex/ex_write.c277
-rw-r--r--usr.bin/vi/nex/ex_yank.c57
-rw-r--r--usr.bin/vi/nex/ex_z.c160
-rw-r--r--usr.bin/vi/nex/excmd.c431
-rw-r--r--usr.bin/vi/nex/excmd.h.stub340
-rw-r--r--usr.bin/vi/nex/filter.c393
-rw-r--r--usr.bin/vi/nex/script.h45
-rw-r--r--usr.bin/vi/nex/tag.h58
-rw-r--r--usr.bin/vi/nvi/getc.c252
-rw-r--r--usr.bin/vi/nvi/v_again.c61
-rw-r--r--usr.bin/vi/nvi/v_at.c61
-rw-r--r--usr.bin/vi/nvi/v_ch.c273
-rw-r--r--usr.bin/vi/nvi/v_delete.c147
-rw-r--r--usr.bin/vi/nvi/v_ex.c55
-rw-r--r--usr.bin/vi/nvi/v_exit.c79
-rw-r--r--usr.bin/vi/nvi/v_exmode.c58
-rw-r--r--usr.bin/vi/nvi/v_filter.c92
-rw-r--r--usr.bin/vi/nvi/v_increment.c153
-rw-r--r--usr.bin/vi/nvi/v_init.c228
-rw-r--r--usr.bin/vi/nvi/v_join.c75
-rw-r--r--usr.bin/vi/nvi/v_left.c166
-rw-r--r--usr.bin/vi/nvi/v_mark.c87
-rw-r--r--usr.bin/vi/nvi/v_match.c152
-rw-r--r--usr.bin/vi/nvi/v_ntext.c1728
-rw-r--r--usr.bin/vi/nvi/v_paragraph.c273
-rw-r--r--usr.bin/vi/nvi/v_put.c130
-rw-r--r--usr.bin/vi/nvi/v_redraw.c56
-rw-r--r--usr.bin/vi/nvi/v_replace.c176
-rw-r--r--usr.bin/vi/nvi/v_right.c144
-rw-r--r--usr.bin/vi/nvi/v_screen.c79
-rw-r--r--usr.bin/vi/nvi/v_scroll.c354
-rw-r--r--usr.bin/vi/nvi/v_search.c364
-rw-r--r--usr.bin/vi/nvi/v_section.c146
-rw-r--r--usr.bin/vi/nvi/v_sentence.c328
-rw-r--r--usr.bin/vi/nvi/v_shift.c78
-rw-r--r--usr.bin/vi/nvi/v_status.c129
-rw-r--r--usr.bin/vi/nvi/v_stop.c61
-rw-r--r--usr.bin/vi/nvi/v_switch.c86
-rw-r--r--usr.bin/vi/nvi/v_tag.c79
-rw-r--r--usr.bin/vi/nvi/v_text.c827
-rw-r--r--usr.bin/vi/nvi/v_ulcase.c159
-rw-r--r--usr.bin/vi/nvi/v_undo.c136
-rw-r--r--usr.bin/vi/nvi/v_util.c127
-rw-r--r--usr.bin/vi/nvi/v_word.c560
-rw-r--r--usr.bin/vi/nvi/v_xchar.c135
-rw-r--r--usr.bin/vi/nvi/v_yank.c114
-rw-r--r--usr.bin/vi/nvi/v_z.c133
-rw-r--r--usr.bin/vi/nvi/vcmd.c522
-rw-r--r--usr.bin/vi/nvi/vcmd.h274
-rw-r--r--usr.bin/vi/nvi/vi.c780
-rw-r--r--usr.bin/vi/options.c822
-rw-r--r--usr.bin/vi/options.h.stub110
-rw-r--r--usr.bin/vi/options_f.c547
-rw-r--r--usr.bin/vi/pathnames.h45
-rw-r--r--usr.bin/vi/recover.c622
-rw-r--r--usr.bin/vi/screen.c296
-rw-r--r--usr.bin/vi/screen.h314
-rw-r--r--usr.bin/vi/search.c860
-rw-r--r--usr.bin/vi/search.h55
-rw-r--r--usr.bin/vi/seq.c299
-rw-r--r--usr.bin/vi/seq.h75
-rw-r--r--usr.bin/vi/sex/sex_confirm.c74
-rw-r--r--usr.bin/vi/sex/sex_get.c323
-rw-r--r--usr.bin/vi/sex/sex_refresh.c72
-rw-r--r--usr.bin/vi/sex/sex_screen.c214
-rw-r--r--usr.bin/vi/sex/sex_screen.h66
-rw-r--r--usr.bin/vi/sex/sex_term.c227
-rw-r--r--usr.bin/vi/sex/sex_util.c97
-rw-r--r--usr.bin/vi/svi/svi_confirm.c84
-rw-r--r--usr.bin/vi/svi/svi_ex.c476
-rw-r--r--usr.bin/vi/svi/svi_get.c150
-rw-r--r--usr.bin/vi/svi/svi_line.c421
-rw-r--r--usr.bin/vi/svi/svi_refresh.c839
-rw-r--r--usr.bin/vi/svi/svi_relative.c205
-rw-r--r--usr.bin/vi/svi/svi_screen.c534
-rw-r--r--usr.bin/vi/svi/svi_screen.h238
-rw-r--r--usr.bin/vi/svi/svi_smap.c1041
-rw-r--r--usr.bin/vi/svi/svi_split.c581
-rw-r--r--usr.bin/vi/svi/svi_util.c314
-rw-r--r--usr.bin/vi/term.c687
-rw-r--r--usr.bin/vi/term.h184
-rw-r--r--usr.bin/vi/timer.c166
-rw-r--r--usr.bin/vi/trace.c72
-rw-r--r--usr.bin/vi/util.c578
-rw-r--r--usr.bin/vi/vi.1451
-rw-r--r--usr.bin/vi/vi.h165
-rw-r--r--usr.bin/vi/xaw/xaw_screen.c85
156 files changed, 39241 insertions, 0 deletions
diff --git a/usr.bin/vi/Makefile b/usr.bin/vi/Makefile
new file mode 100644
index 000000000000..fbb1588cebd8
--- /dev/null
+++ b/usr.bin/vi/Makefile
@@ -0,0 +1,99 @@
+# @(#)Makefile 8.26 (Berkeley) 1/12/94
+
+PROG= vi
+MAN1= vi.1
+BINDIR?= /usr/bin
+
+#CFLAGS=-g -DDEBUG
+#CFLAGS+=-pg
+CFLAGS+=-I. -I${.CURDIR} -I${.CURDIR}/obj -I${.CURDIR}/include -I${.CURDIR}/nex -I${.CURDIR}/nvi
+#STRIP=
+.PATH: ${.CURDIR}/nex ${.CURDIR}/sex ${.CURDIR}/nvi ${.CURDIR}/svi \
+ ${.CURDIR}/xaw
+CLEANFILES+=ex
+
+# General sources.
+SRCS= ascii.c cut.c delete.c exf.c line.c log.c main.c mark.c \
+ options.c options_f.c screen.c search.c seq.c recover.c \
+ term.c timer.c trace.c util.c
+
+# Ex source.
+SRCS+= ex.c ex_abbrev.c ex_append.c ex_args.c ex_argv.c ex_at.c \
+ ex_bang.c ex_cd.c ex_delete.c ex_digraph.c ex_display.c \
+ ex_edit.c ex_equal.c ex_exit.c ex_file.c ex_global.c ex_init.c \
+ ex_join.c ex_map.c ex_mark.c ex_mkexrc.c ex_move.c ex_open.c \
+ ex_preserve.c ex_print.c ex_put.c ex_read.c ex_screen.c \
+ ex_script.c ex_set.c ex_shell.c ex_shift.c ex_source.c ex_stop.c \
+ ex_subst.c ex_tag.c ex_undo.c ex_usage.c ex_util.c ex_version.c \
+ ex_visual.c ex_write.c ex_yank.c ex_z.c excmd.c filter.c
+
+# Ex screen source.
+SRCS+= sex_confirm.c sex_get.c sex_refresh.c sex_screen.c sex_term.c \
+ sex_util.c
+
+# Vi source.
+SRCS+= getc.c v_again.c v_at.c v_ch.c v_delete.c v_ex.c v_exit.c \
+ v_exmode.c v_filter.c v_increment.c v_init.c v_join.c v_left.c \
+ v_mark.c v_match.c v_ntext.c v_paragraph.c v_put.c v_redraw.c \
+ v_replace.c v_right.c v_screen.c v_scroll.c v_search.c v_section.c \
+ v_sentence.c v_shift.c v_status.c v_stop.c v_switch.c v_tag.c \
+ v_text.c v_ulcase.c v_undo.c v_util.c v_word.c v_xchar.c v_yank.c \
+ v_z.c vcmd.c vi.c
+
+# Vi curses screen source.
+SRCS+= svi_confirm.c svi_ex.c svi_get.c svi_line.c svi_refresh.c \
+ svi_relative.c svi_screen.c svi_smap.c svi_split.c svi_util.c
+
+# Athena widget set screen source.
+SRCS+= xaw_screen.c
+
+#LDADD+=-pg
+DPADD+= ${LIBCURSES} ${LIBTERM} ${LIBUTIL}
+LDADD+= -lcurses -ltermlib -lutil
+SPECHDR=excmd.h options.h
+CLEANFILES+=${SPECHDR}
+DPSRCS+=${SPECHDR}
+LINKS= ${BINDIR}/vi ${BINDIR}/ex ${BINDIR}/vi ${BINDIR}/view
+
+all: vi vi.1
+
+warn:: ${SRCS}
+ -(cd ${.CURDIR} && \
+ gcc -Wall -O -DDEBUG -Iobj -Invi -Inex -I. ${.ALLSRC} \
+ -lcurses -ltermlib 2>&1 | \
+ sed -e "/warning: .*sccsid.*defined but not used/d" \
+ -e "/warning: suggest parentheses around/d" \
+ -e "/In function /d" \
+ -e "/At top level:/d" \
+ -e "/warning: .*inline call to/d" \
+ -e "/warning: comparison is always 1 due /d") > \
+ ${.CURDIR}/WARN.OUT
+
+options.h: options.h.stub options.c # Makefile
+ rm -f options.h
+ cp ${.CURDIR}/options.h.stub options.h
+ chmod 664 options.h
+ (echo '/^\/\* O_[0-9A-Z_]*/ {'; \
+ echo 'printf("#define %s %d\n", $$2, cnt++)'; \
+ echo 'next'; \
+ echo '}'; \
+ echo 'END {'; \
+ echo 'printf("#define O_OPTIONCOUNT %d\n", cnt)'; \
+ echo '}') > /tmp/__vi.options.h
+ awk -f /tmp/__vi.options.h ${.CURDIR}/options.c >> options.h
+ rm -f /tmp/__vi.options.h
+
+excmd.h: excmd.h.stub excmd.c # Makefile
+ rm -f excmd.h
+ cp ${.CURDIR}/nex/excmd.h.stub excmd.h
+ chmod 664 excmd.h
+ (echo '/^\/\* C_[0-9A-Z_]* \*\/$$/ {'; \
+ echo 'printf("#define %s %d\n", $$2, cnt++)'; \
+ echo 'next'; \
+ echo '}') > /tmp/__vi.excmd.h
+ awk -f /tmp/__vi.excmd.h ${.CURDIR}/nex/excmd.c >> excmd.h
+ rm -f /tmp/__vi.excmd.h
+
+.include <bsd.prog.mk>
+
+.depend: ${SPECHDR}
diff --git a/usr.bin/vi/README b/usr.bin/vi/README
new file mode 100644
index 000000000000..a33bfc6f04c3
--- /dev/null
+++ b/usr.bin/vi/README
@@ -0,0 +1,193 @@
+# @(#)README 8.40 (Berkeley) 1/12/94
+
+This is the README for version 1.01 of nvi, a freely redistributable
+replacement for the vi and ex text editors. It can be retrieved via
+anonymous ftp from ftp.uu.net, or from ftp.cs.berkeley.edu. In the
+latter, it is in the directory ucb/4bsd, and is named nvi.tar.Z.
+
+If you have any questions or problems with nvi, please send them to
+me by electronic mail at one of the following addresses:
+
+ uunet!bostic
+ bostic@cs.berkeley.edu
+
+Keith Bostic
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+o Redistribution:
+
+Nvi is copyrighted by the The Regents of the University of California,
+but may be freely redistributed (or used to line your birdcage) under
+the following conditions:
+
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+o Credit where it's due:
+
+ This software was originally derived from software contributed
+ to the University of California, Berkeley by Steve Kirkendall,
+ the author of the vi clone elvis. Without his work, this work
+ would have been far more difficult.
+
+ POSIX 1003.2 style regular expression support is courtesy of
+ Henry Spencer, for which I am *very* grateful.
+
+ The curses library was originally done by Ken Arnold. Scrolling
+ and general reworking for 4.4BSD was done by Elan Amir.
+
+o From the original vi acknowledgements, by William Joy and Mark Horton:
+
+ Bruce Englar encouraged the early development of this display
+ editor. Peter Kessler helped bring sanity to version 2's
+ command layout. Bill Joy wrote versions 1 and 2.0 through 2.7,
+ and created the framework that users see in the present editor.
+ Mark Horton added macros and other features and made the editor
+ work on a large number of terminals and Unix systems.
+
+o And...
+
+ The financial support of UUNET Communications Services is gratefully
+ acknowledged.
+
+=-=-=-=-=-=-=-=-=-=-=
+o Comments:
+
+This software is beta software, although it's pretty stable. Almost
+of the necessary functionality for ex/vi is in it, the only missing
+pieces are fairly obscure.
+
+Code fixes are very much appreciated, of course, but if you can't
+provide them, please send me as much information as you can as to how
+to reproduce the bug, and I'll try to fix it here. In particular, the
+screen routines are nasty stuff, and you probably don't want to mess
+with them. Stack traces of core dumps are sometimes helpful, but an
+example file with a set of keystrokes that causes the problem is far
+better.
+
+Nvi is mostly 8-bit clean. This isn't difficult to fix, and was left in
+during initial development to make things easier. Wide character support
+will be integrated at the same time it is made fully 8-bit clean.
+
+=-=-=-=-=-=-=-=-=-=-=
+o New features:
+
+There aren't a lot of new features in nex/nvi, but there are a few things
+you might like:
+
+ o 8-bit clean data, practically infinite lines/files.
+ ^Vx[0-9A-Fa-f]* in input mode will insert any
+ legal character value.
+ o Split screens:
+ :sp[lit] [file ...] splits the screen.
+ ^W switches between screens.
+ :resize count grows/shrinks the current screen.
+ o Background/foreground screens
+ :bg backgrounds the current screen.
+ :di[splay] s[creens] lists the hidden screens.
+ :fg [file] foregrounds the specified (or next) screen.
+ o Shell screens:
+ :sc[ript] [file ...] runs a shell in the screen.
+ Carriage return sends a line to the shell.
+ o Buffer, screens, tags display:
+ :di[splay] b[uffers] displays the current cut buffers.
+ :di[splay] s[creens] displays the hidden screen names.
+ :di[splay] t[ags] displays the current tags stack.
+ o Tag stacks:
+ ^T returns to previous tag location.
+ :tagpop [number | file] returns to previous tag location,
+ or, optionally tag #N, or the tag in a specific file.
+ :tagtop returns to first tag location.
+ o Infinite undo:
+ A '.' command immediately after a 'u' command continues
+ either forward or backward depending on whether the 'u'
+ command was an undo or a redo.
+ o Usage information:
+ :exu[sage] [cmd] for ex commands.
+ :viu[sage] [key] for vi commands.
+ :help
+ o Extended RE expressions:
+ :set extended turns on extended RE's, so you can
+ do "/in|or" and search for the next occurrence of
+ more than one expression.
+ o Word search:
+ ^A searches for the word referenced by the cursor.
+ o Number increment:
+ # increments the number referenced by the cursor.
+ o Previous file:
+ :prev[ious][!] edits the previous file from the
+ argument list.
+
+=-=-=-=-=-=-=-=-=-=-=
+o Porting information:
+
+The directory PORT has directories per machine/OS combination, with
+V7-style Makefiles which build nvi. See the file PORT/README for
+more detailed information.
+
+=-=-=-=-=-=-=-=-=-=-=
+o Directories:
+
+The main directory, nvi, contains source files for pieces of code that
+are shared by all the editors, like searching and logging code or code
+translating line numbers into requests to the dbopen(3) database code.
+It also has the code for adding, deleting, and changing "records" in
+the underlying database.
+
+nvi/docs:
+ The nvi/docs directory has technical information about data
+ structures and some of the trickier parts of vi like quoting,
+ key mapping, input queues, and executing buffers, and a
+ description of how nvi does edit session recovery.
+
+nvi/ex:
+ The nvi/ex directory is the ex source code. Because vi has the
+ colon command, lots of this code is used by vi. Generally, if
+ functionality is shared by both ex and vi, it's in nvi/ex, if
+ it's vi only, it's in nvi/vi. Files are generally named by the
+ command(s) they support, but occasionally with a name that
+ describes their functionality.
+
+nvi/sex:
+ The nvi/sex directory is the screen support for the ex editor.
+
+nvi/svi:
+ The nvi/svi directory is the screen support for a curses based
+ vi editor.
+
+nvi/vi:
+ The nvi/vi directory is the vi source code.
+
+nvi/xaw:
+ Place reserved for an X11 (Athena Widget) screen.
diff --git a/usr.bin/vi/args.h b/usr.bin/vi/args.h
new file mode 100644
index 000000000000..4d437442d866
--- /dev/null
+++ b/usr.bin/vi/args.h
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 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.
+ *
+ * @(#)args.h 8.3 (Berkeley) 12/19/93
+ */
+
+/*
+ * Structure for building "argc/argv" vector of arguments.
+ *
+ * !!!
+ * All arguments are nul terminated as well as having an associated length.
+ * The argument vector is NOT necessarily NULL terminated. The proper way
+ * to check the number of arguments is to use the argc value in the EXCMDARG
+ * structure or to walk the array until an ARGS structure with a length of 0
+ * is found.
+ */
+typedef struct _args {
+ CHAR_T *bp; /* Argument. */
+ size_t blen; /* Buffer length. */
+ size_t len; /* Argument length. */
+
+#define A_ALLOCATED 0x01 /* If allocated space. */
+ u_char flags;
+} ARGS;
diff --git a/usr.bin/vi/ascii.c b/usr.bin/vi/ascii.c
new file mode 100644
index 000000000000..7a7102574165
--- /dev/null
+++ b/usr.bin/vi/ascii.c
@@ -0,0 +1,115 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ascii.c 8.5 (Berkeley) 11/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+
+CHNAME const asciiname[UCHAR_MAX + 1] = {
+ {"^@", 2}, {"^A", 2}, {"^B", 2}, {"^C", 2},
+ {"^D", 2}, {"^E", 2}, {"^F", 2}, {"^G", 2},
+ {"^H", 2}, {"^I", 2}, {"^J", 2}, {"^K", 2},
+ {"^L", 2}, {"^M", 2}, {"^N", 2}, {"^O", 2},
+ {"^P", 2}, {"^Q", 2}, {"^R", 2}, {"^S", 2},
+ {"^T", 2}, {"^U", 2}, {"^V", 2}, {"^W", 2},
+ {"^X", 2}, {"^Y", 2}, {"^Z", 2}, {"^[", 2},
+ {"^\\", 2}, {"^]", 2}, {"^^", 2}, {"^_", 2},
+ {" ", 1}, {"!", 1}, {"\"", 1}, {"#", 1},
+ {"$", 1}, {"%", 1}, {"&", 1}, {"'", 1},
+ {"(", 1}, {")", 1}, {"*", 1}, {"+", 1},
+ {",", 1}, {"-", 1}, {".", 1}, {"/", 1},
+ {"0", 1}, {"1", 1}, {"2", 1}, {"3", 1},
+ {"4", 1}, {"5", 1}, {"6", 1}, {"7", 1},
+ {"8", 1}, {"9", 1}, {":", 1}, {";", 1},
+ {"<", 1}, {"=", 1}, {">", 1}, {"?", 1},
+ {"@", 1}, {"A", 1}, {"B", 1}, {"C", 1},
+ {"D", 1}, {"E", 1}, {"F", 1}, {"G", 1},
+ {"H", 1}, {"I", 1}, {"J", 1}, {"K", 1},
+ {"L", 1}, {"M", 1}, {"N", 1}, {"O", 1},
+ {"P", 1}, {"Q", 1}, {"R", 1}, {"S", 1},
+ {"T", 1}, {"U", 1}, {"V", 1}, {"W", 1},
+ {"X", 1}, {"Y", 1}, {"Z", 1}, {"[", 1},
+ {"\\", 1}, {"]", 1}, {"^", 1}, {"_", 1},
+ {"`", 1}, {"a", 1}, {"b", 1}, {"c", 1},
+ {"d", 1}, {"e", 1}, {"f", 1}, {"g", 1},
+ {"h", 1}, {"i", 1}, {"j", 1}, {"k", 1},
+ {"l", 1}, {"m", 1}, {"n", 1}, {"o", 1},
+ {"p", 1}, {"q", 1}, {"r", 1}, {"s", 1},
+ {"t", 1}, {"u", 1}, {"v", 1}, {"w", 1},
+ {"x", 1}, {"y", 1}, {"z", 1}, {"{", 1},
+ {"|", 1}, {"}", 1}, {"~", 1}, {"^?", 2},
+ {"0x80", 4}, {"0x81", 4}, {"0x82", 4}, {"0x83", 4},
+ {"0x84", 4}, {"0x85", 4}, {"0x86", 4}, {"0x87", 4},
+ {"0x88", 4}, {"0x89", 4}, {"0x8a", 4}, {"0x8b", 4},
+ {"0x8c", 4}, {"0x8d", 4}, {"0x8e", 4}, {"0x8f", 4},
+ {"0x90", 4}, {"0x91", 4}, {"0x92", 4}, {"0x93", 4},
+ {"0x94", 4}, {"0x95", 4}, {"0x96", 4}, {"0x97", 4},
+ {"0x98", 4}, {"0x99", 4}, {"0x9a", 4}, {"0x9b", 4},
+ {"0x9c", 4}, {"0x9d", 4}, {"0x9e", 4}, {"0x9f", 4},
+ {"\xa0", 1}, {"\xa1", 1}, {"\xa2", 1}, {"\xa3", 1},
+ {"\xa4", 1}, {"\xa5", 1}, {"\xa6", 1}, {"\xa7", 1},
+ {"\xa8", 1}, {"\xa9", 1}, {"\xaa", 1}, {"\xab", 1},
+ {"\xac", 1}, {"\xad", 1}, {"\xae", 1}, {"\xaf", 1},
+ {"\xb0", 1}, {"\xb1", 1}, {"\xb2", 1}, {"\xb3", 1},
+ {"\xb4", 1}, {"\xb5", 1}, {"\xb6", 1}, {"\xb7", 1},
+ {"\xb8", 1}, {"\xb9", 1}, {"\xba", 1}, {"\xbb", 1},
+ {"\xbc", 1}, {"\xbd", 1}, {"\xbe", 1}, {"\xbf", 1},
+ {"\xc0", 1}, {"\xc1", 1}, {"\xc2", 1}, {"\xc3", 1},
+ {"\xc4", 1}, {"\xc5", 1}, {"\xc6", 1}, {"\xc7", 1},
+ {"\xc8", 1}, {"\xc9", 1}, {"\xca", 1}, {"\xcb", 1},
+ {"\xcc", 1}, {"\xcd", 1}, {"\xce", 1}, {"\xcf", 1},
+ {"\xd0", 1}, {"\xd1", 1}, {"\xd2", 1}, {"\xd3", 1},
+ {"\xd4", 1}, {"\xd5", 1}, {"\xd6", 1}, {"\xd7", 1},
+ {"\xd8", 1}, {"\xd9", 1}, {"\xda", 1}, {"\xdb", 1},
+ {"\xdc", 1}, {"\xdd", 1}, {"\xde", 1}, {"\xdf", 1},
+ {"\xe0", 1}, {"\xe1", 1}, {"\xe2", 1}, {"\xe3", 1},
+ {"\xe4", 1}, {"\xe5", 1}, {"\xe6", 1}, {"\xe7", 1},
+ {"\xe8", 1}, {"\xe9", 1}, {"\xea", 1}, {"\xeb", 1},
+ {"\xec", 1}, {"\xed", 1}, {"\xee", 1}, {"\xef", 1},
+ {"\xf0", 1}, {"\xf1", 1}, {"\xf2", 1}, {"\xf3", 1},
+ {"\xf4", 1}, {"\xf5", 1}, {"\xf6", 1}, {"\xf7", 1},
+ {"\xf8", 1}, {"\xf9", 1}, {"\xfa", 1}, {"\xfb", 1},
+ {"\xfc", 1}, {"\xfd", 1}, {"\xfe", 1}, {"\xff", 1},
+};
+
+char *
+charname(sp, ch)
+ SCR *sp;
+ ARG_CHAR_T ch;
+{
+ return (sp->gp->cname[ch & UCHAR_MAX].name);
+}
diff --git a/usr.bin/vi/cut.c b/usr.bin/vi/cut.c
new file mode 100644
index 000000000000..5a9b292ce175
--- /dev/null
+++ b/usr.bin/vi/cut.c
@@ -0,0 +1,526 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)cut.c 8.20 (Berkeley) 1/23/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+
+static int cb_line __P((SCR *, EXF *, recno_t, size_t, size_t, TEXT **));
+static int cb_rotate __P((SCR *));
+
+/*
+ * cut --
+ * Put a range of lines/columns into a buffer.
+ *
+ * There are two buffer areas, both found in the global structure. The first
+ * is the linked list of all the buffers the user has named, the second is the
+ * default buffer storage. There is a pointer, too, which is the current
+ * default buffer, i.e. it may point to the default buffer or a named buffer
+ * depending on into what buffer the last text was cut. In both delete and
+ * yank operations, text is cut into either the buffer named by the user, or
+ * the default buffer. If it's a delete of information on more than a single
+ * line, the contents of the numbered buffers are rotated up one, the contents
+ * of the buffer named '9' are discarded, and the text is also cut into the
+ * buffer named '1'.
+ *
+ * In all cases, upper-case buffer names are the same as lower-case names,
+ * with the exception that they cause the buffer to be appended to instead
+ * of replaced.
+ *
+ * !!!
+ * The contents of the default buffer would disappear after most operations in
+ * historic vi. It's unclear that this is useful, so we don't bother.
+ *
+ * When users explicitly cut text into the numeric buffers, historic vi became
+ * genuinely strange. I've never been able to figure out what was supposed to
+ * happen. It behaved differently if you deleted text than if you yanked text,
+ * and, in the latter case, the text was appended to the buffer instead of
+ * replacing the contents. Hopefully it's not worth getting right.
+ */
+int
+cut(sp, ep, cbp, namep, fm, tm, flags)
+ SCR *sp;
+ EXF *ep;
+ CB *cbp;
+ CHAR_T *namep;
+ int flags;
+ MARK *fm, *tm;
+{
+ CHAR_T name;
+ TEXT *tp;
+ recno_t lno;
+ size_t len;
+ int append, namedbuffer, setdefcb;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "cut: from {%lu, %d}, to {%lu, %d}%s\n",
+ fm->lno, fm->cno, tm->lno, tm->cno,
+ LF_ISSET(CUT_LINEMODE) ? " LINE MODE" : "");
+#endif
+ if (cbp == NULL) {
+ if (LF_ISSET(CUT_DELETE) &&
+ (LF_ISSET(CUT_LINEMODE) || fm->lno != tm->lno)) {
+ (void)cb_rotate(sp);
+ name = '1';
+ goto defcb;
+ }
+ if (namep == NULL) {
+ cbp = sp->gp->dcb_store;
+ append = namedbuffer = 0;
+ setdefcb = 1;
+ } else {
+ name = *namep;
+defcb: CBNAME(sp, cbp, name);
+ append = isupper(name);
+ namedbuffer = setdefcb = 1;
+ }
+ } else
+ append = namedbuffer = setdefcb = 0;
+
+ /*
+ * If this is a new buffer, create it and add it into the list.
+ * Otherwise, if it's not an append, free its current contents.
+ */
+ if (cbp == NULL) {
+ CALLOC(sp, cbp, CB *, 1, sizeof(CB));
+ cbp->name = name;
+ CIRCLEQ_INIT(&cbp->textq);
+ if (namedbuffer) {
+ LIST_INSERT_HEAD(&sp->gp->cutq, cbp, q);
+ } else
+ sp->gp->dcb_store = cbp;
+ } else if (!append) {
+ text_lfree(&cbp->textq);
+ cbp->len = 0;
+ cbp->flags = 0;
+ }
+
+ /* In line mode, it's pretty easy, just cut the lines. */
+ if (LF_ISSET(CUT_LINEMODE)) {
+ for (lno = fm->lno; lno <= tm->lno; ++lno) {
+ if (cb_line(sp, ep, lno, 0, 0, &tp))
+ goto mem;
+ CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q);
+ cbp->len += tp->len;
+ }
+ cbp->flags |= CB_LMODE;
+ } else {
+ /* Get the first line. */
+ len = fm->lno < tm->lno ? 0 : tm->cno - fm->cno;
+ if (cb_line(sp, ep, fm->lno, fm->cno, len, &tp))
+ goto mem;
+ CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q);
+ cbp->len += tp->len;
+
+ /* Get the intermediate lines. */
+ for (lno = fm->lno; ++lno < tm->lno;) {
+ if (cb_line(sp, ep, lno, 0, 0, &tp))
+ goto mem;
+ CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q);
+ cbp->len += tp->len;
+ }
+
+ /* Get the last line. */
+ if (tm->lno > fm->lno && tm->cno > 0) {
+ if (cb_line(sp, ep, lno, 0, tm->cno, &tp)) {
+mem: if (append)
+ msgq(sp, M_ERR,
+ "Contents of %s buffer lost.",
+ charname(sp, name));
+ text_lfree(&cbp->textq);
+ cbp->len = 0;
+ cbp->flags = 0;
+ return (1);
+ }
+ CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q);
+ cbp->len += tp->len;
+ }
+ }
+ if (setdefcb)
+ sp->gp->dcbp = cbp; /* Repoint default buffer. */
+ return (0);
+}
+
+/*
+ * cb_rotate --
+ * Rotate the numbered buffers up one.
+ */
+static int
+cb_rotate(sp)
+ SCR *sp;
+{
+ CB *cbp, *del_cbp;
+
+ del_cbp = NULL;
+ for (cbp = sp->gp->cutq.lh_first; cbp != NULL; cbp = cbp->q.le_next)
+ switch(cbp->name) {
+ case '1':
+ cbp->name = '2';
+ break;
+ case '2':
+ cbp->name = '3';
+ break;
+ case '3':
+ cbp->name = '4';
+ break;
+ case '4':
+ cbp->name = '5';
+ break;
+ case '5':
+ cbp->name = '6';
+ break;
+ case '6':
+ cbp->name = '7';
+ break;
+ case '7':
+ cbp->name = '8';
+ break;
+ case '8':
+ cbp->name = '9';
+ break;
+ case '9':
+ del_cbp = cbp;
+ break;
+ }
+ if (del_cbp != NULL) {
+ LIST_REMOVE(del_cbp, q);
+ text_lfree(&del_cbp->textq);
+ FREE(del_cbp, sizeof(CB));
+ }
+ return (0);
+}
+
+/*
+ * cb_line --
+ * Cut a portion of a single line.
+ */
+static int
+cb_line(sp, ep, lno, fcno, clen, newp)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ size_t fcno, clen;
+ TEXT **newp;
+{
+ TEXT *tp;
+ size_t len;
+ char *p;
+
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+
+ if ((*newp = tp = text_init(sp, NULL, 0, len)) == NULL)
+ return (1);
+
+ /*
+ * A length of zero means to cut from the MARK to the end
+ * of the line.
+ */
+ if (len != 0) {
+ if (clen == 0)
+ clen = len - fcno;
+ memmove(tp->lb, p + fcno, clen);
+ tp->len = clen;
+ }
+ return (0);
+}
+
+/*
+ * text_init --
+ * Allocate a new TEXT structure.
+ */
+TEXT *
+text_init(sp, p, len, total_len)
+ SCR *sp;
+ const char *p;
+ size_t len, total_len;
+{
+ TEXT *tp;
+
+ MALLOC(sp, tp, TEXT *, sizeof(TEXT));
+ if (tp == NULL)
+ return (NULL);
+ /* ANSI C doesn't define a call to malloc(2) for 0 bytes. */
+ if (tp->lb_len = total_len) {
+ MALLOC(sp, tp->lb, CHAR_T *, tp->lb_len);
+ if (tp->lb == NULL) {
+ free(tp);
+ return (NULL);
+ }
+ if (p != NULL && len != 0)
+ memmove(tp->lb, p, len);
+ } else
+ tp->lb = NULL;
+ tp->len = len;
+ tp->ai = tp->insert = tp->offset = tp->owrite = 0;
+ tp->wd = NULL;
+ tp->wd_len = 0;
+ return (tp);
+}
+
+/*
+ * text_lfree --
+ * Free a chain of text structures.
+ */
+void
+text_lfree(headp)
+ TEXTH *headp;
+{
+ TEXT *tp;
+
+ while ((tp = headp->cqh_first) != (void *)headp) {
+ CIRCLEQ_REMOVE(headp, tp, q);
+ text_free(tp);
+ }
+}
+
+/*
+ * text_free --
+ * Free a text structure.
+ */
+void
+text_free(tp)
+ TEXT *tp;
+{
+ if (tp->lb != NULL)
+ FREE(tp->lb, tp->lb_len);
+ if (tp->wd != NULL)
+ FREE(tp->wd, tp->wd_len);
+ FREE(tp, sizeof(TEXT));
+}
+
+/*
+ * put --
+ * Put text buffer contents into the file.
+ *
+ * !!!
+ * Historically, pasting into a file with no lines in vi would preserve
+ * the single blank line. This is almost certainly a result of the fact
+ * that historic vi couldn't deal with a file that had no lines in it.
+ * This implementation treats that as a bug, and does not retain the
+ * blank line.
+ */
+int
+put(sp, ep, cbp, namep, cp, rp, append)
+ SCR *sp;
+ EXF *ep;
+ CB *cbp;
+ CHAR_T *namep;
+ MARK *cp, *rp;
+ int append;
+{
+ CHAR_T name;
+ TEXT *ltp, *tp;
+ recno_t lno;
+ size_t blen, clen, len;
+ char *bp, *p, *t;
+
+ if (cbp == NULL)
+ if (namep == NULL) {
+ cbp = sp->gp->dcbp;
+ if (cbp == NULL) {
+ msgq(sp, M_ERR, "The default buffer is empty.");
+ return (1);
+ }
+ } else {
+ name = *namep;
+ CBNAME(sp, cbp, name);
+ if (cbp == NULL) {
+ msgq(sp, M_ERR,
+ "Buffer %s is empty.", charname(sp, name));
+ return (1);
+ }
+ }
+ tp = cbp->textq.cqh_first;
+
+ /*
+ * It's possible to do a put into an empty file, meaning that the
+ * cut buffer simply becomes the file. It's a special case so
+ * that we can ignore it in general.
+ *
+ * Historical practice is that the cursor ends up on the first
+ * non-blank character of the first line inserted.
+ */
+ if (cp->lno == 1) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0) {
+ for (; tp != (void *)&cbp->textq;
+ ++lno, tp = tp->q.cqe_next)
+ if (file_aline(sp, ep, 1, lno, tp->lb, tp->len))
+ return (1);
+ rp->lno = 1;
+ rp->cno = 0;
+ (void)nonblank(sp, ep, rp->lno, &rp->cno);
+ goto ret;
+ }
+ }
+
+ /* If a line mode buffer, append each new line into the file. */
+ if (F_ISSET(cbp, CB_LMODE)) {
+ lno = append ? cp->lno : cp->lno - 1;
+ rp->lno = lno + 1;
+ for (; tp != (void *)&cbp->textq; ++lno, tp = tp->q.cqe_next)
+ if (file_aline(sp, ep, 1, lno, tp->lb, tp->len))
+ return (1);
+ rp->cno = 0;
+ (void)nonblank(sp, ep, rp->lno, &rp->cno);
+ goto ret;
+ }
+
+ /*
+ * If buffer was cut in character mode, replace the current line with
+ * one built from the portion of the first line to the left of the
+ * split plus the first line in the CB. Append each intermediate line
+ * in the CB. Append a line built from the portion of the first line
+ * to the right of the split plus the last line in the CB.
+ *
+ * Get the first line.
+ */
+ lno = cp->lno;
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+
+ GET_SPACE_RET(sp, bp, blen, tp->len + len + 1);
+ t = bp;
+
+ /* Original line, left of the split. */
+ if (len > 0 && (clen = cp->cno + (append ? 1 : 0)) > 0) {
+ memmove(bp, p, clen);
+ p += clen;
+ t += clen;
+ }
+
+ /* First line from the CB. */
+ memmove(t, tp->lb, tp->len);
+ t += tp->len;
+
+ /* Calculate length left in original line. */
+ clen = len ? len - cp->cno - (append ? 1 : 0) : 0;
+
+ /*
+ * If no more lines in the CB, append the rest of the original
+ * line and quit. Otherwise, build the last line before doing
+ * the intermediate lines, because the line changes will lose
+ * the cached line.
+ */
+ if (tp->q.cqe_next == (void *)&cbp->textq) {
+ /*
+ * Historical practice is that if a non-line mode put
+ * is inside a single line, the cursor ends up on the
+ * last character inserted.
+ */
+ rp->lno = lno;
+ rp->cno = (t - bp) - 1;
+
+ if (clen > 0) {
+ memmove(t, p, clen);
+ t += clen;
+ }
+ if (file_sline(sp, ep, lno, bp, t - bp))
+ goto mem;
+ } else {
+ /*
+ * Have to build both the first and last lines of the
+ * put before doing any sets or we'll lose the cached
+ * line. Build both the first and last lines in the
+ * same buffer, so we don't have to have another buffer
+ * floating around.
+ *
+ * Last part of original line; check for space, reset
+ * the pointer into the buffer.
+ */
+ ltp = cbp->textq.cqh_last;
+ len = t - bp;
+ ADD_SPACE_RET(sp, bp, blen, ltp->len + clen);
+ t = bp + len;
+
+ /* Add in last part of the CB. */
+ memmove(t, ltp->lb, ltp->len);
+ if (clen)
+ memmove(t + ltp->len, p, clen);
+ clen += ltp->len;
+
+ /*
+ * Now: bp points to the first character of the first
+ * line, t points to the last character of the last
+ * line, t - bp is the length of the first line, and
+ * clen is the length of the last. Just figured you'd
+ * want to know.
+ *
+ * Output the line replacing the original line.
+ */
+ if (file_sline(sp, ep, lno, bp, t - bp))
+ goto mem;
+
+ /*
+ * Historical practice is that if a non-line mode put
+ * covers multiple lines, the cursor ends up on the
+ * first character inserted. (Of course.)
+ */
+ rp->lno = lno;
+ rp->cno = (t - bp) - 1;
+
+ /* Output any intermediate lines in the CB. */
+ for (tp = tp->q.cqe_next;
+ tp->q.cqe_next != (void *)&cbp->textq;
+ ++lno, tp = tp->q.cqe_next)
+ if (file_aline(sp, ep, 1, lno, tp->lb, tp->len))
+ goto mem;
+
+ if (file_aline(sp, ep, 1, lno, t, clen)) {
+mem: FREE_SPACE(sp, bp, blen);
+ return (1);
+ }
+ }
+ FREE_SPACE(sp, bp, blen);
+
+ /* Reporting... */
+ret: sp->rptlines[L_PUT] += lno - cp->lno;
+
+ return (0);
+}
diff --git a/usr.bin/vi/cut.h b/usr.bin/vi/cut.h
new file mode 100644
index 000000000000..8ade88f05633
--- /dev/null
+++ b/usr.bin/vi/cut.h
@@ -0,0 +1,90 @@
+/*-
+ * Copyright (c) 1991, 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.
+ *
+ * @(#)cut.h 8.11 (Berkeley) 1/11/94
+ */
+
+typedef struct _texth TEXTH; /* TEXT list head structure. */
+CIRCLEQ_HEAD(_texth, _text);
+
+/* Cut buffers. */
+struct _cb {
+ LIST_ENTRY(_cb) q; /* Linked list of cut buffers. */
+ TEXTH textq; /* Linked list of TEXT structures. */
+ CHAR_T name; /* Cut buffer name. */
+ size_t len; /* Total length of cut text. */
+
+#define CB_LMODE 0x01 /* Cut was in line mode. */
+ u_char flags;
+};
+
+/* Lines/blocks of text. */
+struct _text { /* Text: a linked list of lines. */
+ CIRCLEQ_ENTRY(_text) q; /* Linked list of text structures. */
+ char *lb; /* Line buffer. */
+ size_t lb_len; /* Line buffer length. */
+ size_t len; /* Line length. */
+
+ /* These fields are used by the vi text input routine. */
+ recno_t lno; /* 1-N: line number. */
+ size_t ai; /* 0-N: autoindent bytes. */
+ size_t insert; /* 0-N: bytes to insert (push). */
+ size_t offset; /* 0-N: initial, unerasable bytes. */
+ size_t owrite; /* 0-N: bytes to overwrite. */
+
+ /* These fields are used by the ex text input routine. */
+ u_char *wd; /* Width buffer. */
+ size_t wd_len; /* Width buffer length. */
+};
+
+/*
+ * Get named buffer 'name'.
+ * Translate upper-case buffer names to lower-case buffer names.
+ */
+#define CBNAME(sp, cbp, name) { \
+ CHAR_T __name; \
+ __name = isupper(name) ? tolower(name) : (name); \
+ for (cbp = sp->gp->cutq.lh_first; \
+ cbp != NULL; cbp = cbp->q.le_next) \
+ if (cbp->name == __name) \
+ break; \
+}
+
+#define CUT_DELETE 0x01 /* Delete (rotate numeric buffers). */
+#define CUT_LINEMODE 0x02 /* Cut in line mode. */
+int cut __P((SCR *, EXF *, CB *, CHAR_T *, MARK *, MARK *, int));
+int delete __P((SCR *, EXF *, MARK *, MARK *, int));
+int put __P((SCR *, EXF *, CB *, CHAR_T *, MARK *, MARK *, int));
+
+void text_free __P((TEXT *));
+TEXT *text_init __P((SCR *, const char *, size_t, size_t));
+void text_lfree __P((TEXTH *));
diff --git a/usr.bin/vi/delete.c b/usr.bin/vi/delete.c
new file mode 100644
index 000000000000..e8c0a5d80e1f
--- /dev/null
+++ b/usr.bin/vi/delete.c
@@ -0,0 +1,189 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)delete.c 8.7 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+
+/*
+ * delete --
+ * Delete a range of text.
+ */
+int
+delete(sp, ep, fm, tm, lmode)
+ SCR *sp;
+ EXF *ep;
+ MARK *fm, *tm;
+ int lmode;
+{
+ recno_t lno;
+ size_t blen, len, tlen;
+ char *bp, *p;
+ int eof;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "delete: from %lu/%d to %lu/%d%s\n",
+ fm->lno, fm->cno, tm->lno, tm->cno, lmode ? " (LINE MODE)" : "");
+#endif
+ bp = NULL;
+
+ /* Case 1 -- delete in line mode. */
+ if (lmode) {
+ for (lno = tm->lno; lno >= fm->lno; --lno)
+ if (file_dline(sp, ep, lno))
+ return (1);
+ goto vdone;
+ }
+
+ /*
+ * Case 2 -- delete to EOF. This is a special case because it's
+ * easier to pick it off than try and find it in the other cases.
+ */
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (tm->lno >= lno) {
+ if (tm->lno == lno) {
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+ eof = tm->cno >= len ? 1 : 0;
+ } else
+ eof = 1;
+ if (eof) {
+ for (lno = tm->lno; lno > fm->lno; --lno) {
+ if (file_dline(sp, ep, lno))
+ return (1);
+ ++sp->rptlines[L_DELETED];
+ }
+ if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, fm->lno);
+ return (1);
+ }
+ GET_SPACE_RET(sp, bp, blen, fm->cno);
+ memmove(bp, p, fm->cno);
+ if (file_sline(sp, ep, fm->lno, bp, fm->cno))
+ return (1);
+ goto done;
+ }
+ }
+
+ /* Case 3 -- delete within a single line. */
+ if (tm->lno == fm->lno) {
+ if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, fm->lno);
+ return (1);
+ }
+ GET_SPACE_RET(sp, bp, blen, len);
+ memmove(bp, p, fm->cno);
+ memmove(bp + fm->cno, p + tm->cno, len - tm->cno);
+ if (file_sline(sp, ep, fm->lno, bp, len - (tm->cno - fm->cno)))
+ goto err;
+ goto done;
+ }
+
+ /*
+ * Case 4 -- delete over multiple lines.
+ *
+ * Figure out how big a buffer we need.
+ */
+ if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, fm->lno);
+ return (1);
+ }
+ tlen = len;
+ if ((p = file_gline(sp, ep, tm->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, tm->lno);
+ return (1);
+ }
+
+ /*
+ * XXX
+ * We can overflow memory here, if (len + tlen) > SIZE_T_MAX. The
+ * only portable way I've found to test is to depend on the overflow
+ * being less than the value.
+ */
+ tlen += len;
+ if (len > tlen) {
+ msgq(sp, M_ERR, "Error: line length overflow");
+ return (1);
+ }
+
+ GET_SPACE_RET(sp, bp, blen, tlen);
+
+ /* Copy the start partial line into place. */
+ if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, fm->lno);
+ goto err;
+ }
+ memmove(bp, p, fm->cno);
+ tlen = fm->cno;
+
+ /* Copy the end partial line into place. */
+ if ((p = file_gline(sp, ep, tm->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, tm->lno);
+ goto err;
+ }
+ memmove(bp + tlen, p + tm->cno, len - tm->cno);
+ tlen += len - tm->cno;
+
+ /* Set the current line. */
+ if (file_sline(sp, ep, fm->lno, bp, tlen))
+ goto err;
+
+ /* Delete the last and intermediate lines. */
+ for (lno = tm->lno; lno > fm->lno; --lno)
+ if (file_dline(sp, ep, lno))
+ return (1);
+
+ /* Reporting. */
+vdone: sp->rptlines[L_DELETED] += tm->lno - fm->lno + 1;
+
+done: if (bp != NULL)
+ FREE_SPACE(sp, bp, blen);
+
+ return (0);
+
+ /* Free memory. */
+err: FREE_SPACE(sp, bp, blen);
+
+ return (1);
+}
diff --git a/usr.bin/vi/exf.c b/usr.bin/vi/exf.c
new file mode 100644
index 000000000000..0f9ac4ba0e4b
--- /dev/null
+++ b/usr.bin/vi/exf.c
@@ -0,0 +1,690 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)exf.c 8.65 (Berkeley) 1/11/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+/*
+ * We include <sys/file.h>, because the flock(2) #defines were
+ * found there on historical systems. We also include <fcntl.h>
+ * because the open(2) #defines are found there on newer systems.
+ */
+#include <sys/file.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "pathnames.h"
+
+/*
+ * file_add --
+ * Insert a file name into the FREF list, if it doesn't already
+ * appear in it.
+ *
+ * !!!
+ * The "if it doesn't already appear" changes vi's semantics slightly. If
+ * you do a "vi foo bar", and then execute "next bar baz", the edit of bar
+ * will reflect the line/column of the previous edit session. Historic nvi
+ * did not do this. The change is a logical extension of the change where
+ * vi now remembers the last location in any file that it has ever edited,
+ * not just the previously edited file.
+ */
+FREF *
+file_add(sp, frp_append, name, ignore)
+ SCR *sp;
+ FREF *frp_append;
+ CHAR_T *name;
+ int ignore;
+{
+ FREF *frp;
+ char *p;
+
+ /*
+ * Return it if it already exists. Note that we test against the
+ * user's current name, whatever that happens to be, including if
+ * it's a temporary file. If the user is trying to set an argument
+ * list, the ignore argument will be on -- if we're ignoring the
+ * file turn off the ignore bit, so it's back in the argument list.
+ */
+ if (name != NULL)
+ for (frp = sp->frefq.cqh_first;
+ frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next)
+ if ((p = FILENAME(frp)) != NULL && !strcmp(p, name)) {
+ if (!ignore)
+ F_CLR(frp, FR_IGNORE);
+ return (frp);
+ }
+
+ /* Allocate and initialize the FREF structure. */
+ CALLOC(sp, frp, FREF *, 1, sizeof(FREF));
+ if (frp == NULL)
+ return (NULL);
+
+ /*
+ * If no file name specified, or if the file name is a request
+ * for something temporary, file_init() will allocate the file
+ * name. Temporary files are always ignored.
+ */
+#define TEMPORARY_FILE_STRING "/tmp"
+ if (name != NULL && strcmp(name, TEMPORARY_FILE_STRING) &&
+ (frp->name = strdup(name)) == NULL) {
+ FREE(frp, sizeof(FREF));
+ msgq(sp, M_SYSERR, NULL);
+ return (NULL);
+ }
+
+ /* Only the initial argument list is "remembered". */
+ if (ignore)
+ F_SET(frp, FR_IGNORE);
+
+ /* Append into the chain of file names. */
+ if (frp_append != NULL) {
+ CIRCLEQ_INSERT_AFTER(&sp->frefq, frp_append, frp, q);
+ } else
+ CIRCLEQ_INSERT_TAIL(&sp->frefq, frp, q);
+
+ return (frp);
+}
+
+/*
+ * file_first --
+ * Return the first file name for editing, if any.
+ */
+FREF *
+file_first(sp)
+ SCR *sp;
+{
+ FREF *frp;
+
+ /* Return the first file name. */
+ for (frp = sp->frefq.cqh_first;
+ frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next)
+ if (!F_ISSET(frp, FR_IGNORE))
+ return (frp);
+ return (NULL);
+}
+
+/*
+ * file_next --
+ * Return the next file name, if any.
+ */
+FREF *
+file_next(sp, frp)
+ SCR *sp;
+ FREF *frp;
+{
+ while ((frp = frp->q.cqe_next) != (FREF *)&sp->frefq)
+ if (!F_ISSET(frp, FR_IGNORE))
+ return (frp);
+ return (NULL);
+}
+
+/*
+ * file_prev --
+ * Return the previous file name, if any.
+ */
+FREF *
+file_prev(sp, frp)
+ SCR *sp;
+ FREF *frp;
+{
+ while ((frp = frp->q.cqe_prev) != (FREF *)&sp->frefq)
+ if (!F_ISSET(frp, FR_IGNORE))
+ return (frp);
+ return (NULL);
+}
+
+/*
+ * file_unedited --
+ * Return if there are files that aren't ignored and are unedited.
+ */
+FREF *
+file_unedited(sp)
+ SCR *sp;
+{
+ FREF *frp;
+
+ /* Return the next file name. */
+ for (frp = sp->frefq.cqh_first;
+ frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next)
+ if (!F_ISSET(frp, FR_EDITED | FR_IGNORE))
+ return (frp);
+ return (NULL);
+}
+
+/*
+ * file_init --
+ * Start editing a file, based on the FREF structure. If successsful,
+ * let go of any previous file. Don't release the previous file until
+ * absolutely sure we have the new one.
+ */
+int
+file_init(sp, frp, rcv_name, force)
+ SCR *sp;
+ FREF *frp;
+ char *rcv_name;
+ int force;
+{
+ EXF *ep;
+ RECNOINFO oinfo;
+ struct stat sb;
+ size_t psize;
+ int fd;
+ char *p, *oname, tname[MAXPATHLEN];
+
+ /*
+ * Required ep initialization:
+ * Flush the line caches.
+ * Default recover mail file fd to -1.
+ * Set initial EXF flag bits.
+ */
+ CALLOC_RET(sp, ep, EXF *, 1, sizeof(EXF));
+ ep->c_lno = ep->c_nlines = OOBLNO;
+ ep->rcv_fd = -1;
+ LIST_INIT(&ep->marks);
+ F_SET(ep, F_FIRSTMODIFY);
+
+ /*
+ * If no name or backing file, create a backing temporary file, saving
+ * the temp file name so can later unlink it. Repoint the name to the
+ * temporary name (we display it to the user until they rename it).
+ * There are some games we play with the FR_FREE_TNAME and FR_NONAME
+ * flags (see ex/ex_file.c) to make sure that the temporary memory gets
+ * free'd up.
+ */
+ if ((oname = FILENAME(frp)) == NULL || stat(oname, &sb)) {
+ (void)snprintf(tname, sizeof(tname),
+ "%s/vi.XXXXXX", O_STR(sp, O_DIRECTORY));
+ if ((fd = mkstemp(tname)) == -1) {
+ msgq(sp, M_SYSERR, "Temporary file");
+ goto err;
+ }
+ (void)close(fd);
+ if ((frp->tname = strdup(tname)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ (void)unlink(tname);
+ goto err;
+ }
+ oname = frp->tname;
+ psize = 4 * 1024;
+ F_SET(frp, FR_NEWFILE);
+ } else {
+ /* Try to keep it at 10 pages or less per file. */
+ if (sb.st_size < 40 * 1024)
+ psize = 4 * 1024;
+ else if (sb.st_size < 320 * 1024)
+ psize = 32 * 1024;
+ else
+ psize = 64 * 1024;
+
+ frp->mtime = sb.st_mtime;
+
+ if (!S_ISREG(sb.st_mode))
+ msgq(sp, M_ERR,
+ "Warning: %s is not a regular file.", oname);
+ }
+
+ /* Set up recovery. */
+ memset(&oinfo, 0, sizeof(RECNOINFO));
+ oinfo.bval = '\n'; /* Always set. */
+ oinfo.psize = psize;
+ oinfo.flags = F_ISSET(sp->gp, G_SNAPSHOT) ? R_SNAPSHOT : 0;
+ if (rcv_name == NULL) {
+ if (rcv_tmp(sp, ep, FILENAME(frp)))
+ msgq(sp, M_ERR,
+ "Modifications not recoverable if the system crashes.");
+ else
+ oinfo.bfname = ep->rcv_path;
+ } else if ((ep->rcv_path = strdup(rcv_name)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ goto err;
+ } else {
+ oinfo.bfname = ep->rcv_path;
+ F_SET(ep, F_MODIFIED | F_RCV_ON);
+ }
+
+ /* Open a db structure. */
+ if ((ep->db = dbopen(rcv_name == NULL ? oname : NULL,
+ O_NONBLOCK | O_RDONLY, DEFFILEMODE, DB_RECNO, &oinfo)) == NULL) {
+ msgq(sp, M_SYSERR, rcv_name == NULL ? oname : rcv_name);
+ goto err;
+ }
+
+ /* Init file marks. */
+ if (mark_init(sp, ep))
+ goto err;
+
+ /* Start logging. */
+ if (log_init(sp, ep))
+ goto err;
+
+ /*
+ * The -R flag, or doing a "set readonly" during a session causes
+ * all files edited during the session (using an edit command, or
+ * even using tags) to be marked read-only. Changing the file name
+ * (see ex/ex_file.c), clears this flag.
+ *
+ * Otherwise, try and figure out if a file is readonly. This is a
+ * dangerous thing to do. The kernel is the only arbiter of whether
+ * or not a file is writeable, and the best that a user program can
+ * do is guess. Obvious loopholes are files that are on a file system
+ * mounted readonly (access catches this one on a few systems), or
+ * alternate protection mechanisms, ACL's for example, that we can't
+ * portably check. Lots of fun, and only here because users whined.
+ *
+ * !!!
+ * Historic vi displayed the readonly message if none of the file
+ * write bits were set, or if an an access(2) call on the path
+ * failed. This seems reasonable. If the file is mode 444, root
+ * users may want to know that the owner of the file did not expect
+ * it to be written.
+ *
+ * Historic vi set the readonly bit if no write bits were set for
+ * a file, even if the access call would have succeeded. This makes
+ * the superuser force the write even when vi expects that it will
+ * succeed. I'm less supportive of this semantic, but it's historic
+ * practice and the conservative approach to vi'ing files as root.
+ *
+ * It would be nice if there was some way to update this when the user
+ * does a "^Z; chmod ...". The problem is that we'd first have to
+ * distinguish between readonly bits set because of file permissions
+ * and those set for other reasons. That's not too hard, but deciding
+ * when to reevaluate the permissions is trickier. An alternative
+ * might be to turn off the readonly bit if the user forces a write
+ * and it succeeds.
+ *
+ * XXX
+ * Access(2) doesn't consider the effective uid/gid values. This
+ * probably isn't a problem for vi when it's running standalone.
+ */
+ if (O_ISSET(sp, O_READONLY) || !F_ISSET(frp, FR_NEWFILE) &&
+ (!(sb.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) ||
+ access(FILENAME(frp), W_OK)))
+ F_SET(frp, FR_RDONLY);
+ else
+ F_CLR(frp, FR_RDONLY);
+
+ /*
+ * Close the previous file; if that fails, close the new one
+ * and run for the border.
+ */
+ if (sp->ep != NULL && file_end(sp, sp->ep, force)) {
+ (void)file_end(sp, ep, 1);
+ goto err;
+ }
+
+ /*
+ * 4.4BSD supports locking in the open call, other systems don't.
+ * Since the user can't interrupt us between the open and here,
+ * it's a don't care.
+ *
+ * !!!
+ * We need to distinguish a lock not being available for the file
+ * from the file system not supporting locking. Assume that EAGAIN
+ * or EWOULDBLOCK is the former. There isn't a portable way to do
+ * this.
+ *
+ * XXX
+ * The locking is flock(2) style, not fcntl(2). The latter is known
+ * to fail badly on some systems, and its only advantage is that it
+ * occasionally works over NFS.
+ */
+ if (flock(ep->db->fd(ep->db), LOCK_EX | LOCK_NB))
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ msgq(sp, M_INFO,
+ "%s already locked, session is read-only", oname);
+ F_SET(frp, FR_RDONLY);
+ } else
+ msgq(sp, M_VINFO, "%s cannot be locked", oname);
+
+ /*
+ * Set the previous file pointer and the alternate file name to be
+ * the file we're about to discard.
+ *
+ * !!!
+ * If the current file was a temporary file, the call to file_end()
+ * unlinked it and free'd the name. So, there is no previous file,
+ * and there is no alternate file name. This matches historical
+ * practice, although in historical vi it could only happen as the
+ * result of the initial command, i.e. if vi was execute without a
+ * file name.
+ */
+ if (sp->frp != NULL) {
+ p = FILENAME(sp->frp);
+ if (p == NULL)
+ sp->p_frp = NULL;
+ else
+ sp->p_frp = sp->frp;
+ set_alt_name(sp, p);
+ }
+
+ /* The new file has now been officially edited. */
+ F_SET(frp, FR_EDITED);
+
+ /* Switch... */
+ ++ep->refcnt;
+ sp->ep = ep;
+ sp->frp = frp;
+ return (0);
+
+err: if (frp->tname != NULL) {
+ (void)unlink(frp->tname);
+ free(frp->tname);
+ frp->tname = NULL;
+ }
+ if (ep->rcv_path != NULL) {
+ free(ep->rcv_path);
+ ep->rcv_path = NULL;
+ }
+ FREE(ep, sizeof(EXF));
+ return (1);
+}
+
+/*
+ * file_end --
+ * Stop editing a file.
+ */
+int
+file_end(sp, ep, force)
+ SCR *sp;
+ EXF *ep;
+ int force;
+{
+ FREF *frp;
+
+ /*
+ *
+ * sp->ep MAY NOT BE THE SAME AS THE ARGUMENT ep, SO DON'T USE IT!
+ *
+ * Save the cursor location.
+ *
+ * XXX
+ * It would be cleaner to do this somewhere else, but by the time
+ * ex or vi knows that we're changing files it's already happened.
+ */
+ frp = sp->frp;
+ frp->lno = sp->lno;
+ frp->cno = sp->cno;
+ F_SET(frp, FR_CURSORSET);
+
+ /* If multiply referenced, just decrement the count and return. */
+ if (--ep->refcnt != 0)
+ return (0);
+
+ /* Close the db structure. */
+ if (ep->db->close != NULL && ep->db->close(ep->db) && !force) {
+ msgq(sp, M_ERR,
+ "%s: close: %s", FILENAME(frp), strerror(errno));
+ ++ep->refcnt;
+ return (1);
+ }
+
+ /* COMMITTED TO THE CLOSE. THERE'S NO GOING BACK... */
+
+ /* Stop logging. */
+ (void)log_end(sp, ep);
+
+ /* Free up any marks. */
+ mark_end(sp, ep);
+
+ /*
+ * Delete the recovery files, close the open descriptor,
+ * free recovery memory.
+ */
+ if (!F_ISSET(ep, F_RCV_NORM)) {
+ if (ep->rcv_path != NULL && unlink(ep->rcv_path))
+ msgq(sp, M_ERR,
+ "%s: remove: %s", ep->rcv_path, strerror(errno));
+ if (ep->rcv_mpath != NULL && unlink(ep->rcv_mpath))
+ msgq(sp, M_ERR,
+ "%s: remove: %s", ep->rcv_mpath, strerror(errno));
+ }
+ if (ep->rcv_fd != -1)
+ (void)close(ep->rcv_fd);
+ if (ep->rcv_path != NULL)
+ free(ep->rcv_path);
+ if (ep->rcv_mpath != NULL)
+ free(ep->rcv_mpath);
+
+ /*
+ * Unlink any temporary file, file name. We also turn on the
+ * ignore bit at this point, because it was a "created" file,
+ * not an argument file.
+ */
+ if (frp->tname != NULL) {
+ if (unlink(frp->tname))
+ msgq(sp, M_ERR,
+ "%s: remove: %s", frp->tname, strerror(errno));
+ free(frp->tname);
+ frp->tname = NULL;
+
+ if (frp->name == NULL && frp->cname == NULL)
+ F_SET(frp, FR_IGNORE);
+ }
+ /* Free the EXF structure. */
+ FREE(ep, sizeof(EXF));
+ return (0);
+}
+
+/*
+ * file_write --
+ * Write the file to disk. Historic vi had fairly convoluted
+ * semantics for whether or not writes would happen. That's
+ * why all the flags.
+ */
+int
+file_write(sp, ep, fm, tm, name, flags)
+ SCR *sp;
+ EXF *ep;
+ MARK *fm, *tm;
+ char *name;
+ int flags;
+{
+ struct stat sb;
+ FILE *fp;
+ FREF *frp;
+ MARK from, to;
+ u_long nlno, nch;
+ int fd, oflags, rval;
+ char *msg;
+
+ /*
+ * Don't permit writing to temporary files. The problem is that
+ * if it's a temp file, and the user does ":wq", we write and quit,
+ * unlinking the temporary file. Not what the user had in mind
+ * at all. This test cannot be forced.
+ */
+ frp = sp->frp;
+ if (name == NULL && frp->cname == NULL && frp->name == NULL) {
+ msgq(sp, M_ERR, "No filename to which to write.");
+ return (1);
+ }
+
+ /* Can't write files marked read-only, unless forced. */
+ if (!LF_ISSET(FS_FORCE) &&
+ name == NULL && F_ISSET(frp, FR_RDONLY)) {
+ if (LF_ISSET(FS_POSSIBLE))
+ msgq(sp, M_ERR,
+ "Read-only file, not written; use ! to override.");
+ else
+ msgq(sp, M_ERR,
+ "Read-only file, not written.");
+ return (1);
+ }
+
+ /* If not forced, not appending, and "writeany" not set ... */
+ if (!LF_ISSET(FS_FORCE | FS_APPEND) && !O_ISSET(sp, O_WRITEANY)) {
+ /* Don't overwrite anything but the original file. */
+ if (name != NULL) {
+ if (!stat(name, &sb))
+ goto exists;
+ } else if (frp->cname != NULL &&
+ !F_ISSET(frp, FR_CHANGEWRITE) && !stat(frp->cname, &sb)) {
+ name = frp->cname;
+exists: if (LF_ISSET(FS_POSSIBLE))
+ msgq(sp, M_ERR,
+ "%s exists, not written; use ! to override.", name);
+ else
+ msgq(sp, M_ERR,
+ "%s exists, not written.", name);
+ return (1);
+ }
+
+ /*
+ * Don't write part of any existing file. Only test for the
+ * original file, the previous test catches anything else.
+ */
+ if (!LF_ISSET(FS_ALL) && name == NULL &&
+ frp->cname == NULL && !stat(frp->name, &sb)) {
+ if (LF_ISSET(FS_POSSIBLE))
+ msgq(sp, M_ERR,
+ "Use ! to write a partial file.");
+ else
+ msgq(sp, M_ERR, "Partial file, not written.");
+ return (1);
+ }
+ }
+
+ /*
+ * Figure out if the file already exists -- if it doesn't, we display
+ * the "new file" message. The stat might not be necessary, but we
+ * just repeat it because it's easier than hacking the previous tests.
+ * The information is only used for the user message and modification
+ * time test, so we can ignore the obvious race condition.
+ *
+ * If the user is overwriting a file other than the original file, and
+ * O_WRITEANY was what got us here (neither force nor append was set),
+ * display the "existing file" messsage. Since the FR_CHANGEWRITE flag
+ * is set on a successful write, the message only appears once when the
+ * user changes a file name. This is historic practice.
+ *
+ * One final test. If we're not forcing or appending, and we have a
+ * saved modification time, stop the user if it's been written since
+ * we last edited or wrote it, and make them force it.
+ */
+ if (stat(name == NULL ? FILENAME(frp) : name, &sb))
+ msg = ": new file";
+ else {
+ msg = "";
+ if (!LF_ISSET(FS_FORCE | FS_APPEND)) {
+ if (frp->mtime && sb.st_mtime > frp->mtime) {
+ msgq(sp, M_ERR,
+ "%s: file modified more recently than this copy%s.",
+ name == NULL ? frp->name : name,
+ LF_ISSET(FS_POSSIBLE) ?
+ "; use ! to override" : "");
+ return (1);
+ }
+ if (name != NULL ||
+ !F_ISSET(frp, FR_CHANGEWRITE) && frp->cname != NULL)
+ msg = ": existing file";
+ }
+ }
+
+ /* We no longer care where the name came from. */
+ if (name == NULL)
+ name = FILENAME(frp);
+
+ /* Set flags to either append or truncate. */
+ oflags = O_CREAT | O_WRONLY;
+ if (LF_ISSET(FS_APPEND))
+ oflags |= O_APPEND;
+ else
+ oflags |= O_TRUNC;
+
+ /* Open the file. */
+ if ((fd = open(name, oflags, DEFFILEMODE)) < 0) {
+ msgq(sp, M_SYSERR, name);
+ return (1);
+ }
+
+ /* Use stdio for buffering. */
+ if ((fp = fdopen(fd, "w")) == NULL) {
+ (void)close(fd);
+ msgq(sp, M_SYSERR, name);
+ return (1);
+ }
+
+ /* Build fake addresses, if necessary. */
+ if (fm == NULL) {
+ from.lno = 1;
+ from.cno = 0;
+ fm = &from;
+ if (file_lline(sp, ep, &to.lno))
+ return (1);
+ to.cno = 0;
+ tm = &to;
+ }
+
+ /* Write the file. */
+ rval = ex_writefp(sp, ep, name, fp, fm, tm, &nlno, &nch);
+
+ /*
+ * Save the new last modification time -- even if the write fails
+ * we re-init the time if we wrote anything. That way the user can
+ * clean up the disk and rewrite without having to force it.
+ */
+ if (nlno || nch)
+ frp->mtime = stat(name, &sb) ? 0 : sb.st_mtime;
+
+ /* If the write failed, complain loudly. */
+ if (rval) {
+ if (!LF_ISSET(FS_APPEND))
+ msgq(sp, M_ERR, "%s: WARNING: file truncated!", name);
+ return (1);
+ }
+
+ /*
+ * Once we've actually written the file, it doesn't matter that the
+ * file name was changed -- if it was, we've already whacked it.
+ */
+ F_SET(frp, FR_CHANGEWRITE);
+
+ /* If wrote the entire file, clear the modified bit. */
+ if (LF_ISSET(FS_ALL))
+ F_CLR(ep, F_MODIFIED);
+
+ msgq(sp, M_INFO, "%s%s: %lu line%s, %lu characters.",
+ name, msg, nlno, nlno == 1 ? "" : "s", nch);
+
+ return (0);
+}
diff --git a/usr.bin/vi/exf.h b/usr.bin/vi/exf.h
new file mode 100644
index 000000000000..5049f3ea2418
--- /dev/null
+++ b/usr.bin/vi/exf.h
@@ -0,0 +1,129 @@
+/*-
+ * Copyright (c) 1992, 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.
+ *
+ * @(#)exf.h 8.20 (Berkeley) 12/28/93
+ */
+ /* Undo direction. */
+/*
+ * exf --
+ * The file structure.
+ */
+struct _exf {
+ int refcnt; /* Reference count. */
+
+ /* Underlying database state. */
+ DB *db; /* File db structure. */
+ char *c_lp; /* Cached line. */
+ size_t c_len; /* Cached line length. */
+ recno_t c_lno; /* Cached line number. */
+ recno_t c_nlines; /* Cached lines in the file. */
+
+ DB *log; /* Log db structure. */
+ char *l_lp; /* Log buffer. */
+ size_t l_len; /* Log buffer length. */
+ recno_t l_high; /* Log last + 1 record number. */
+ recno_t l_cur; /* Log current record number. */
+ MARK l_cursor; /* Log cursor position. */
+ enum direction lundo; /* Last undo direction. */
+
+ LIST_HEAD(_markh, _mark) marks; /* Linked list of file MARK's. */
+
+ /*
+ * Paths for the recovery mail file and the vi recovery file and
+ * a file descriptor for the former. We keep a file descriptor
+ * to the recovery file open and locked, while the file is in use.
+ * This allows the recovery option to distinguish between files
+ * that are live, and those that should be recovered.
+ *
+ * F_RCV_ON is set as long as we believe that the file is recoverable.
+ * This doesn't mean that any initialization has been done, however.
+ * If F_RCV_NORM is not set and rcv_path and rcv_mpath are not NULL,
+ * they are unlinked on file exit. If not NULL they are free'd on file
+ * exit. On file exit, if rcv_fd is not -1, it is closed.
+ */
+#define RCV_PERIOD 120 /* Sync every two minutes. */
+ char *rcv_path; /* Recover file name. */
+ char *rcv_mpath; /* Recover mail file name. */
+ int rcv_fd; /* Locked mail file descriptor. */
+
+#define F_FIRSTMODIFY 0x001 /* File not yet modified. */
+#define F_MODIFIED 0x002 /* File is currently dirty. */
+#define F_MULTILOCK 0x004 /* Multiple processes running, lock. */
+#define F_NOLOG 0x008 /* Logging turned off. */
+#define F_RCV_ALRM 0x010 /* File needs to be synced. */
+#define F_RCV_NORM 0x020 /* Don't delete recovery files. */
+#define F_RCV_ON 0x040 /* Recovery is possible. */
+#define F_UNDO 0x080 /* No change since last undo. */
+ u_int flags;
+};
+
+/* Flags to file_write(). */
+#define FS_ALL 0x01 /* Write the entire file. */
+#define FS_APPEND 0x02 /* Append to the file. */
+#define FS_FORCE 0x04 /* Force is set. */
+#define FS_POSSIBLE 0x08 /* Force could be set. */
+
+#define GETLINE_ERR(sp, lno) { \
+ msgq((sp), M_ERR, \
+ "Error: %s/%d: unable to retrieve line %u.", \
+ tail(__FILE__), __LINE__, (lno)); \
+}
+
+/* FREF routines. */
+FREF *file_add __P((SCR *, FREF *, CHAR_T *, int));
+FREF *file_first __P((SCR *));
+FREF *file_next __P((SCR *, FREF *));
+FREF *file_prev __P((SCR *, FREF *));
+FREF *file_unedited __P((SCR *));
+
+/* EXF routines. */
+int file_end __P((SCR *, EXF *, int));
+int file_init __P((SCR *, FREF *, char *, int));
+int file_write __P((SCR *, EXF *, MARK *, MARK *, char *, int));
+
+/* Recovery routines. */
+void rcv_hup __P((void));
+int rcv_init __P((SCR *, EXF *));
+int rcv_list __P((SCR *));
+int rcv_read __P((SCR *, char *));
+int rcv_sync __P((SCR *, EXF *));
+void rcv_term __P((void));
+int rcv_tmp __P((SCR *, EXF *, char *));
+
+/* DB interface routines */
+int file_aline __P((SCR *, EXF *, int, recno_t, char *, size_t));
+int file_dline __P((SCR *, EXF *, recno_t));
+char *file_gline __P((SCR *, EXF *, recno_t, size_t *));
+int file_iline __P((SCR *, EXF *, recno_t, char *, size_t));
+int file_lline __P((SCR *, EXF *, recno_t *));
+char *file_rline __P((SCR *, EXF *, recno_t, size_t *));
+int file_sline __P((SCR *, EXF *, recno_t, char *, size_t));
diff --git a/usr.bin/vi/gs.h b/usr.bin/vi/gs.h
new file mode 100644
index 000000000000..86af7299b3c7
--- /dev/null
+++ b/usr.bin/vi/gs.h
@@ -0,0 +1,91 @@
+/*-
+ * Copyright (c) 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.
+ *
+ * @(#)gs.h 8.26 (Berkeley) 1/9/94
+ */
+
+struct _gs {
+ CIRCLEQ_HEAD(_dqh, _scr) dq; /* Displayed screens. */
+ CIRCLEQ_HEAD(_hqh, _scr) hq; /* Hidden screens. */
+
+ mode_t origmode; /* Original terminal mode. */
+ struct termios
+ original_termios; /* Original terminal values. */
+ struct termios
+ s5_curses_botch; /* System V curses workaround. */
+
+ MSGH msgq; /* User message list. */
+
+ char *tmp_bp; /* Temporary buffer. */
+ size_t tmp_blen; /* Size of temporary buffer. */
+
+#ifdef DEBUG
+ FILE *tracefp; /* Trace file pointer. */
+#endif
+
+/* INFORMATION SHARED BY ALL SCREENS. */
+ IBUF *tty; /* Key input buffer. */
+
+ CB *dcbp; /* Default cut buffer pointer. */
+ CB *dcb_store; /* Default cut buffer storage. */
+ LIST_HEAD(_cuth, _cb) cutq; /* Linked list of cut buffers. */
+
+#define MAX_BIT_SEQ 128 /* Max + 1 fast check character. */
+ LIST_HEAD(_seqh, _seq) seqq; /* Linked list of maps, abbrevs. */
+ bitstr_t bit_decl(seqb, MAX_BIT_SEQ);
+
+#define term_key_val(sp, ch) \
+ ((ch) <= MAX_FAST_KEY ? sp->gp->special_key[ch] : \
+ (ch) > sp->gp->max_special ? 0 : __term_key_val(sp, ch))
+#define MAX_FAST_KEY 255 /* Max + 1 fast check character.*/
+ CHAR_T max_special; /* Max special character. */
+ u_char *special_key; /* Fast lookup table. */
+ CHNAME const *cname; /* Display names of ASCII characters. */
+
+#define G_ABBREV 0x00001 /* If have abbreviations. */
+#define G_BELLSCHED 0x00002 /* Bell scheduled. */
+#define G_CURSES_INIT 0x00004 /* Curses: initialized. */
+#define G_CURSES_S5CB 0x00008 /* Curses: s5_curses_botch set. */
+#define G_ISFROMTTY 0x00010 /* Reading from a tty. */
+#define G_RECOVER_SET 0x00020 /* Recover system initialized. */
+#define G_SETMODE 0x00040 /* Tty mode changed. */
+#define G_SIGALRM 0x00080 /* SIGALRM arrived. */
+#define G_SIGHUP 0x00100 /* SIGHUP arrived. */
+#define G_SIGTERM 0x00200 /* SIGTERM arrived. */
+#define G_SIGWINCH 0x00400 /* SIGWINCH arrived. */
+#define G_SLEEPING 0x00800 /* Asleep (die on signal). */
+#define G_SNAPSHOT 0x01000 /* Always snapshot files. */
+#define G_TMP_INUSE 0x02000 /* Temporary buffer in use. */
+ u_int flags;
+};
+
+extern GS *__global_list; /* List of screens. */
diff --git a/usr.bin/vi/include/bitstring.h b/usr.bin/vi/include/bitstring.h
new file mode 100644
index 000000000000..88437e7fb9f7
--- /dev/null
+++ b/usr.bin/vi/include/bitstring.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Paul Vixie.
+ *
+ * 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.
+ *
+ * @(#)bitstring.h 8.1 (Berkeley) 7/19/93
+ */
+
+#ifndef _BITSTRING_H_
+#define _BITSTRING_H_
+
+typedef unsigned char bitstr_t;
+
+/* internal macros */
+ /* byte of the bitstring bit is in */
+#define _bit_byte(bit) \
+ ((bit) >> 3)
+
+ /* mask for the bit within its byte */
+#define _bit_mask(bit) \
+ (1 << ((bit)&0x7))
+
+/* external macros */
+ /* bytes in a bitstring of nbits bits */
+#define bitstr_size(nbits) \
+ ((((nbits) - 1) >> 3) + 1)
+
+ /* allocate a bitstring */
+#define bit_alloc(nbits) \
+ (bitstr_t *)calloc(1, \
+ (unsigned int)bitstr_size(nbits) * sizeof(bitstr_t))
+
+ /* allocate a bitstring on the stack */
+#define bit_decl(name, nbits) \
+ (name)[bitstr_size(nbits)]
+
+ /* is bit N of bitstring name set? */
+#define bit_test(name, bit) \
+ ((name)[_bit_byte(bit)] & _bit_mask(bit))
+
+ /* set bit N of bitstring name */
+#define bit_set(name, bit) \
+ (name)[_bit_byte(bit)] |= _bit_mask(bit)
+
+ /* clear bit N of bitstring name */
+#define bit_clear(name, bit) \
+ (name)[_bit_byte(bit)] &= ~_bit_mask(bit)
+
+ /* clear bits start ... stop in bitstring */
+#define bit_nclear(name, start, stop) { \
+ register bitstr_t *_name = name; \
+ register int _start = start, _stop = stop; \
+ register int _startbyte = _bit_byte(_start); \
+ register int _stopbyte = _bit_byte(_stop); \
+ if (_startbyte == _stopbyte) { \
+ _name[_startbyte] &= ((0xff >> (8 - (_start&0x7))) | \
+ (0xff << ((_stop&0x7) + 1))); \
+ } else { \
+ _name[_startbyte] &= 0xff >> (8 - (_start&0x7)); \
+ while (++_startbyte < _stopbyte) \
+ _name[_startbyte] = 0; \
+ _name[_stopbyte] &= 0xff << ((_stop&0x7) + 1); \
+ } \
+}
+
+ /* set bits start ... stop in bitstring */
+#define bit_nset(name, start, stop) { \
+ register bitstr_t *_name = name; \
+ register int _start = start, _stop = stop; \
+ register int _startbyte = _bit_byte(_start); \
+ register int _stopbyte = _bit_byte(_stop); \
+ if (_startbyte == _stopbyte) { \
+ _name[_startbyte] |= ((0xff << (_start&0x7)) & \
+ (0xff >> (7 - (_stop&0x7)))); \
+ } else { \
+ _name[_startbyte] |= 0xff << ((_start)&0x7); \
+ while (++_startbyte < _stopbyte) \
+ _name[_startbyte] = 0xff; \
+ _name[_stopbyte] |= 0xff >> (7 - (_stop&0x7)); \
+ } \
+}
+
+ /* find first bit clear in name */
+#define bit_ffc(name, nbits, value) { \
+ register bitstr_t *_name = name; \
+ register int _byte, _nbits = nbits; \
+ register int _stopbyte = _bit_byte(_nbits), _value = -1; \
+ for (_byte = 0; _byte <= _stopbyte; ++_byte) \
+ if (_name[_byte] != 0xff) { \
+ _value = _byte << 3; \
+ for (_stopbyte = _name[_byte]; (_stopbyte&0x1); \
+ ++_value, _stopbyte >>= 1); \
+ break; \
+ } \
+ *(value) = _value; \
+}
+
+ /* find first bit set in name */
+#define bit_ffs(name, nbits, value) { \
+ register bitstr_t *_name = name; \
+ register int _byte, _nbits = nbits; \
+ register int _stopbyte = _bit_byte(_nbits), _value = -1; \
+ for (_byte = 0; _byte <= _stopbyte; ++_byte) \
+ if (_name[_byte]) { \
+ _value = _byte << 3; \
+ for (_stopbyte = _name[_byte]; !(_stopbyte&0x1); \
+ ++_value, _stopbyte >>= 1); \
+ break; \
+ } \
+ *(value) = _value; \
+}
+
+#endif /* !_BITSTRING_H_ */
diff --git a/usr.bin/vi/include/cdefs.h b/usr.bin/vi/include/cdefs.h
new file mode 100644
index 000000000000..c4157bcd1a8d
--- /dev/null
+++ b/usr.bin/vi/include/cdefs.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 1991, 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.
+ *
+ * @(#)cdefs.h 8.2 (Berkeley) 10/4/93
+ */
+
+#ifndef _CDEFS_H_
+#define _CDEFS_H_
+
+#if defined(__cplusplus)
+#define __BEGIN_DECLS extern "C" {
+#define __END_DECLS };
+#else
+#define __BEGIN_DECLS
+#define __END_DECLS
+#endif
+
+/*
+ * The __CONCAT macro is used to concatenate parts of symbol names, e.g.
+ * with "#define OLD(foo) __CONCAT(old,foo)", OLD(foo) produces oldfoo.
+ * The __CONCAT macro is a bit tricky -- make sure you don't put spaces
+ * in between its arguments. __CONCAT can also concatenate double-quoted
+ * strings produced by the __STRING macro, but this only works with ANSI C.
+ */
+#if defined(__STDC__) || defined(__cplusplus)
+#define __P(protos) protos /* full-blown ANSI C */
+#define __CONCAT(x,y) x ## y
+#define __STRING(x) #x
+
+#if !defined(__GNUC__) && !defined(__cplusplus)
+#define inline
+#endif
+
+#else /* !(__STDC__ || __cplusplus) */
+#define __P(protos) () /* traditional C preprocessor */
+#define __CONCAT(x,y) x/**/y
+#define __STRING(x) "x"
+
+#ifdef __GNUC__
+#define const __const /* GCC: ANSI C with -traditional */
+#define inline __inline
+#define signed __signed
+#define volatile __volatile
+
+#else /* !__GNUC__ */
+#define const /* delete ANSI C keywords */
+#define inline
+#define signed
+#define volatile
+#endif /* !__GNUC__ */
+#endif /* !(__STDC__ || __cplusplus) */
+
+/*
+ * GCC has extensions for declaring functions as `pure' (always returns
+ * the same value given the same inputs, i.e., has no external state and
+ * no side effects) and `dead' (nonreturning). These mainly affect
+ * optimization and warnings. Unfortunately, GCC complains if these are
+ * used under strict ANSI mode (`gcc -ansi -pedantic'), hence we need to
+ * define them only if compiling without this.
+ */
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+#define __dead __volatile
+#define __pure __const
+#else
+#define __dead
+#define __pure
+#endif
+
+#endif /* !_CDEFS_H_ */
diff --git a/usr.bin/vi/include/compat.h b/usr.bin/vi/include/compat.h
new file mode 100644
index 000000000000..10406993014b
--- /dev/null
+++ b/usr.bin/vi/include/compat.h
@@ -0,0 +1,241 @@
+/*-
+ * Copyright (c) 1991, 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.
+ *
+ * @(#)compat.h 8.10 (Berkeley) 1/11/94
+ */
+
+#ifndef _COMPAT_H_
+#define _COMPAT_H_
+
+#include <sys/types.h>
+#include <machine/limits.h>
+#include <termios.h>
+#include <errno.h>
+
+/*
+ * If your system doesn't typedef u_long, u_short, or u_char, change
+ * the 0 to a 1.
+ */
+#if 0
+typedef unsigned char u_char; /* 4.[34]BSD names. */
+typedef unsigned int u_int;
+typedef unsigned long u_long;
+typedef unsigned short u_short;
+#endif
+
+/* If your system doesn't typedef size_t, change the 0 to a 1. */
+#if 0
+typedef unsigned int size_t; /* 4.[34]BSD names. */
+#endif
+
+/*
+ * If your system doesn't have the POSIX type for a signal mask,
+ * change the 0 to a 1.
+ */
+#if 0 /* POSIX 1003.1 signal mask type. */
+typedef unsigned int sigset_t;
+#endif
+
+/*
+ * If your system's vsprintf returns a char *, not an int,
+ * change the 0 to a 1.
+ */
+#if 0
+#define VSPRINTF_CHARSTAR
+#endif
+
+/*
+ * If you don't have POSIX 1003.1 signals, the signal code surrounding the
+ * temporary file creation is intended to block all of the possible signals
+ * long enough to create the file and unlink it. All of this stuff is
+ * intended to use old-style BSD calls to fake POSIX 1003.1 calls.
+ */
+#ifdef NO_POSIX_SIGNALS
+#define sigemptyset(set) (*(set) = 0)
+#define sigfillset(set) (*(set) = ~(sigset_t)0, 0)
+#define sigaddset(set,signo) (*(set) |= sigmask(signo), 0)
+#define sigdelset(set,signo) (*(set) &= ~sigmask(signo), 0)
+#define sigismember(set,signo) ((*(set) & sigmask(signo)) != 0)
+
+#define SIG_BLOCK 1
+#define SIG_UNBLOCK 2
+#define SIG_SETMASK 3
+
+static int __sigtemp; /* For the use of sigprocmask */
+
+/* Repeated test of oset != NULL is to avoid "*0". */
+#define sigprocmask(how, set, oset) \
+ ((__sigtemp = \
+ (((how) == SIG_BLOCK) ? \
+ sigblock(0) | *(set) : \
+ (((how) == SIG_UNBLOCK) ? \
+ sigblock(0) & ~(*(set)) : \
+ ((how) == SIG_SETMASK ? \
+ *(set) : sigblock(0))))), \
+ ((oset) ? (*(oset ? oset : set) = sigsetmask(__sigtemp)) : \
+ sigsetmask(__sigtemp)), 0)
+#endif
+
+/*
+ * If realloc(3) of a NULL pointer on your system isn't the same as
+ * a malloc(3) call, change the 0 to a 1, and add realloc.o to the
+ * MISC line in your Makefile.
+ */
+#if 0
+#define realloc __fix_realloc
+#endif
+
+/*
+ * If your system doesn't have an include file with the appropriate
+ * byte order set, make sure you specify the correct one.
+ */
+#ifndef BYTE_ORDER
+#define LITTLE_ENDIAN 1234 /* LSB first: i386, vax */
+#define BIG_ENDIAN 4321 /* MSB first: 68000, ibm, net */
+#define BYTE_ORDER LITTLE_ENDIAN /* Set for your system. */
+#endif
+
+#if defined(SYSV) || defined(SYSTEM5)
+#define index(a, b) strchr(a, b)
+#define rindex(a, b) strrchr(a, b)
+#define bzero(a, b) memset(a, 0, b)
+#define bcmp(a, b, n) memcmp(a, b, n)
+#define bcopy(a, b, n) memmove(b, a, n)
+#endif
+
+#if defined(BSD) || defined(BSD4_3)
+#define strchr(a, b) index(a, b)
+#define strrchr(a, b) rindex(a, b)
+#define memcmp(a, b, n) bcmp(a, b, n)
+#define memmove(a, b, n) bcopy(b, a, n)
+#endif
+
+/*
+ * 32-bit machine. The db routines are theoretically independent of
+ * the size of u_shorts and u_longs, but I don't know that anyone has
+ * ever actually tried it. At a minimum, change the following #define's
+ * if you are trying to compile on a different type of system.
+ */
+#ifndef USHRT_MAX
+#define USHRT_MAX 0xFFFF
+#define ULONG_MAX 0xFFFFFFFF
+#endif
+
+#ifndef O_ACCMODE /* POSIX 1003.1 access mode mask. */
+#define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
+#endif
+
+#ifndef _POSIX2_RE_DUP_MAX /* POSIX 1003.2 RE limit. */
+#define _POSIX2_RE_DUP_MAX 255
+#endif
+
+/*
+ * If you can't provide lock values in the open(2) call. Note, this
+ * allows races to happen.
+ */
+#ifndef O_EXLOCK /* 4.4BSD extension. */
+#define O_EXLOCK 0
+#endif
+
+#ifndef O_SHLOCK /* 4.4BSD extension. */
+#define O_SHLOCK 0
+#endif
+
+#ifndef EFTYPE
+#define EFTYPE EINVAL /* POSIX 1003.1 format errno. */
+#endif
+
+#ifndef WCOREDUMP /* 4.4BSD extension */
+#define WCOREDUMP(a) 0
+#endif
+
+#ifndef STDERR_FILENO
+#define STDIN_FILENO 0 /* ANSI C #defines */
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+#endif
+
+#ifndef SEEK_END
+#define SEEK_SET 0 /* POSIX 1003.1 seek values */
+#define SEEK_CUR 1
+#define SEEK_END 2
+#endif
+
+#ifndef _POSIX_VDISABLE /* POSIX 1003.1 disabling char. */
+#define _POSIX_VDISABLE 0 /* Some systems used 0. */
+#endif
+
+#ifndef S_ISLNK /* BSD POSIX 1003.1 extensions */
+#define S_ISLNK(m) ((m & 0170000) == 0120000)
+#define S_ISSOCK(m) ((m & 0170000) == 0140000)
+#endif
+
+#ifndef TCSASOFT /* 4.4BSD extension. */
+#define TCSASOFT 0
+#endif
+
+#ifndef _POSIX2_RE_DUP_MAX /* POSIX 1003.2 values. */
+#define _POSIX2_RE_DUP_MAX 255
+#endif
+
+#ifndef NULL /* ANSI C #defines NULL everywhere. */
+#define NULL 0
+#endif
+
+#ifndef MAX /* Usually found in <sys/param.h>. */
+#define MAX(_a,_b) ((_a)<(_b)?(_b):(_a))
+#endif
+#ifndef MIN /* Usually found in <sys/param.h>. */
+#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b))
+#endif
+
+/* Default file permissions. */
+#ifndef DEFFILEMODE /* 4.4BSD extension. */
+#define DEFFILEMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
+#endif
+
+#ifndef S_ISDIR /* POSIX 1003.1 file type tests. */
+#define S_ISDIR(m) ((m & 0170000) == 0040000) /* directory */
+#define S_ISCHR(m) ((m & 0170000) == 0020000) /* char special */
+#define S_ISBLK(m) ((m & 0170000) == 0060000) /* block special */
+#define S_ISREG(m) ((m & 0170000) == 0100000) /* regular file */
+#define S_ISFIFO(m) ((m & 0170000) == 0010000) /* fifo */
+#define S_ISLNK(m) ((m & 0170000) == 0120000) /* symbolic link */
+#define S_ISSOCK(m) ((m & 0170000) == 0140000) /* socket */
+#endif
+
+/* The type of a va_list. */
+#ifndef _BSD_VA_LIST_ /* 4.4BSD #define. */
+#define _BSD_VA_LIST_ char *
+#endif
+
+#endif /* !_COMPAT_H_ */
diff --git a/usr.bin/vi/include/err.h b/usr.bin/vi/include/err.h
new file mode 100644
index 000000000000..b6d7e5f94b87
--- /dev/null
+++ b/usr.bin/vi/include/err.h
@@ -0,0 +1,16 @@
+#include <sys/cdefs.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void err __P((int, const char *, ...));
+void verr __P((int, const char *, va_list));
+void errx __P((int, const char *, ...));
+void verrx __P((int, const char *, va_list));
+void warn __P((const char *, ...));
+void vwarn __P((const char *, va_list));
+void warnx __P((const char *, ...));
+void vwarnx __P((const char *, va_list));
diff --git a/usr.bin/vi/include/file.h b/usr.bin/vi/include/file.h
new file mode 100644
index 000000000000..680797fa6a52
--- /dev/null
+++ b/usr.bin/vi/include/file.h
@@ -0,0 +1,15 @@
+/*
+ * If we're doing flock(2) emulation, we need to get the LOCK_* #defines.
+ * This stub <sys/file.h> includes the real one, and, if they're not in
+ * it, we #define them here.
+ */
+
+#include </usr/include/sys/file.h>
+
+#ifndef LOCK_SH
+/* lock operations for flock(2) */
+#define LOCK_SH 0x01 /* shared file lock */
+#define LOCK_EX 0x02 /* exclusive file lock */
+#define LOCK_NB 0x04 /* don't block when locking */
+#define LOCK_UN 0x08 /* unlock file */
+#endif
diff --git a/usr.bin/vi/include/glob.h b/usr.bin/vi/include/glob.h
new file mode 100644
index 000000000000..b679f6aef62a
--- /dev/null
+++ b/usr.bin/vi/include/glob.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * 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.
+ *
+ * @(#)glob.h 8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _GLOB_H_
+#define _GLOB_H_
+
+#include <sys/cdefs.h>
+
+#include "compat.h"
+
+struct stat;
+typedef struct {
+ int gl_pathc; /* Count of total paths so far. */
+ int gl_matchc; /* Count of paths matching pattern. */
+ int gl_offs; /* Reserved at beginning of gl_pathv. */
+ int gl_flags; /* Copy of flags parameter to glob. */
+ char **gl_pathv; /* List of paths matching pattern. */
+ /* Copy of errfunc parameter to glob. */
+ int (*gl_errfunc) __P((const char *, int));
+
+ /*
+ * Alternate filesystem access methods for glob; replacement
+ * versions of closedir(3), readdir(3), opendir(3), stat(2)
+ * and lstat(2).
+ */
+ void (*gl_closedir) __P((void *));
+ struct dirent *(*gl_readdir) __P((void *));
+ void *(*gl_opendir) __P((const char *));
+ int (*gl_lstat) __P((const char *, struct stat *));
+ int (*gl_stat) __P((const char *, struct stat *));
+} glob_t;
+
+#define GLOB_APPEND 0x0001 /* Append to output from previous call. */
+#define GLOB_DOOFFS 0x0002 /* Use gl_offs. */
+#define GLOB_ERR 0x0004 /* Return on error. */
+#define GLOB_MARK 0x0008 /* Append / to matching directories. */
+#define GLOB_NOCHECK 0x0010 /* Return pattern itself if nothing matches. */
+#define GLOB_NOSORT 0x0020 /* Don't sort. */
+
+#ifndef _POSIX_SOURCE
+#define GLOB_ALTDIRFUNC 0x0040 /* Use alternately specified directory funcs. */
+#define GLOB_BRACE 0x0080 /* Expand braces ala csh. */
+#define GLOB_MAGCHAR 0x0100 /* Pattern had globbing characters. */
+#define GLOB_NOMAGIC 0x0200 /* GLOB_NOCHECK without magic chars (csh). */
+#define GLOB_QUOTE 0x0400 /* Quote special chars with \. */
+#define GLOB_TILDE 0x0800 /* Expand tilde names from the passwd file. */
+#endif
+
+#define GLOB_NOSPACE (-1) /* Malloc call failed. */
+#define GLOB_ABEND (-2) /* Unignored error. */
+
+__BEGIN_DECLS
+int glob __P((const char *, int, int (*)(const char *, int), glob_t *));
+void globfree __P((glob_t *));
+__END_DECLS
+
+#endif /* !_GLOB_H_ */
diff --git a/usr.bin/vi/include/mpool.h b/usr.bin/vi/include/mpool.h
new file mode 100644
index 000000000000..910e0782aa9d
--- /dev/null
+++ b/usr.bin/vi/include/mpool.h
@@ -0,0 +1,135 @@
+/*-
+ * Copyright (c) 1991, 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.
+ *
+ * @(#)mpool.h 8.1 (Berkeley) 6/2/93
+ */
+
+/*
+ * The memory pool scheme is a simple one. Each in memory page is referenced
+ * by a bucket which is threaded in three ways. All active pages are threaded
+ * on a hash chain (hashed by the page number) and an lru chain. Inactive
+ * pages are threaded on a free chain. Each reference to a memory pool is
+ * handed an MPOOL which is the opaque cookie passed to all of the memory
+ * routines.
+ */
+#define HASHSIZE 128
+#define HASHKEY(pgno) ((pgno - 1) % HASHSIZE)
+
+/* The BKT structures are the elements of the lists. */
+typedef struct BKT {
+ struct BKT *hnext; /* next hash bucket */
+ struct BKT *hprev; /* previous hash bucket */
+ struct BKT *cnext; /* next free/lru bucket */
+ struct BKT *cprev; /* previous free/lru bucket */
+ void *page; /* page */
+ pgno_t pgno; /* page number */
+
+#define MPOOL_DIRTY 0x01 /* page needs to be written */
+#define MPOOL_PINNED 0x02 /* page is pinned into memory */
+ unsigned long flags; /* flags */
+} BKT;
+
+/* The BKTHDR structures are the heads of the lists. */
+typedef struct BKTHDR {
+ struct BKT *hnext; /* next hash bucket */
+ struct BKT *hprev; /* previous hash bucket */
+ struct BKT *cnext; /* next free/lru bucket */
+ struct BKT *cprev; /* previous free/lru bucket */
+} BKTHDR;
+
+typedef struct MPOOL {
+ BKTHDR free; /* The free list. */
+ BKTHDR lru; /* The LRU list. */
+ BKTHDR hashtable[HASHSIZE]; /* Hashed list by page number. */
+ pgno_t curcache; /* Current number of cached pages. */
+ pgno_t maxcache; /* Max number of cached pages. */
+ pgno_t npages; /* Number of pages in the file. */
+ u_long pagesize; /* File page size. */
+ int fd; /* File descriptor. */
+ /* Page in conversion routine. */
+ void (*pgin) __P((void *, pgno_t, void *));
+ /* Page out conversion routine. */
+ void (*pgout) __P((void *, pgno_t, void *));
+ void *pgcookie; /* Cookie for page in/out routines. */
+#ifdef STATISTICS
+ unsigned long cachehit;
+ unsigned long cachemiss;
+ unsigned long pagealloc;
+ unsigned long pageflush;
+ unsigned long pageget;
+ unsigned long pagenew;
+ unsigned long pageput;
+ unsigned long pageread;
+ unsigned long pagewrite;
+#endif
+} MPOOL;
+
+#ifdef __MPOOLINTERFACE_PRIVATE
+/* Macros to insert/delete into/from hash chain. */
+#define rmhash(bp) { \
+ (bp)->hprev->hnext = (bp)->hnext; \
+ (bp)->hnext->hprev = (bp)->hprev; \
+}
+#define inshash(bp, pg) { \
+ hp = &mp->hashtable[HASHKEY(pg)]; \
+ (bp)->hnext = hp->hnext; \
+ (bp)->hprev = (struct BKT *)hp; \
+ hp->hnext->hprev = (bp); \
+ hp->hnext = (bp); \
+}
+
+/* Macros to insert/delete into/from lru and free chains. */
+#define rmchain(bp) { \
+ (bp)->cprev->cnext = (bp)->cnext; \
+ (bp)->cnext->cprev = (bp)->cprev; \
+}
+#define inschain(bp, dp) { \
+ (bp)->cnext = (dp)->cnext; \
+ (bp)->cprev = (struct BKT *)(dp); \
+ (dp)->cnext->cprev = (bp); \
+ (dp)->cnext = (bp); \
+}
+#endif
+
+__BEGIN_DECLS
+MPOOL *mpool_open __P((DBT *, int, pgno_t, pgno_t));
+void mpool_filter __P((MPOOL *, void (*)(void *, pgno_t, void *),
+ void (*)(void *, pgno_t, void *), void *));
+void *mpool_new __P((MPOOL *, pgno_t *));
+void *mpool_get __P((MPOOL *, pgno_t, u_int));
+int mpool_put __P((MPOOL *, void *, u_int));
+int mpool_sync __P((MPOOL *));
+int mpool_close __P((MPOOL *));
+#ifdef STATISTICS
+void mpool_stat __P((MPOOL *));
+#endif
+__END_DECLS
diff --git a/usr.bin/vi/include/ndbm.h b/usr.bin/vi/include/ndbm.h
new file mode 100644
index 000000000000..a545bca1326e
--- /dev/null
+++ b/usr.bin/vi/include/ndbm.h
@@ -0,0 +1,77 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * 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.
+ *
+ * @(#)ndbm.h 8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _NDBM_H_
+#define _NDBM_H_
+
+#include <db.h>
+
+/* Map dbm interface onto db(3). */
+#define DBM_RDONLY O_RDONLY
+
+/* Flags to dbm_store(). */
+#define DBM_INSERT 0
+#define DBM_REPLACE 1
+
+/*
+ * The db(3) support for ndbm(3) always appends this suffix to the
+ * file name to avoid overwriting the user's original database.
+ */
+#define DBM_SUFFIX ".db"
+
+typedef struct {
+ char *dptr;
+ int dsize;
+} datum;
+
+typedef DB DBM;
+#define dbm_pagfno(a) DBM_PAGFNO_NOT_AVAILABLE
+
+__BEGIN_DECLS
+void dbm_close __P((DBM *));
+int dbm_delete __P((DBM *, datum));
+datum dbm_fetch __P((DBM *, datum));
+datum dbm_firstkey __P((DBM *));
+long dbm_forder __P((DBM *, datum));
+datum dbm_nextkey __P((DBM *));
+DBM *dbm_open __P((const char *, int, int));
+int dbm_store __P((DBM *, datum, datum, int));
+int dbm_dirfno __P((DBM *));
+__END_DECLS
+
+#endif /* !_NDBM_H_ */
diff --git a/usr.bin/vi/include/queue.h b/usr.bin/vi/include/queue.h
new file mode 100644
index 000000000000..40d32ccb6e29
--- /dev/null
+++ b/usr.bin/vi/include/queue.h
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 1991, 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.
+ *
+ * @(#)queue.h 8.3 (Berkeley) 12/13/93
+ */
+
+#ifndef _QUEUE_H_
+#define _QUEUE_H_
+
+/*
+ * This file defines three types of data structures: lists, tail queues,
+ * and circular queues.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list after
+ * an existing element or at the head of the list. A list may only be
+ * traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A tail queue may only be traversed in the forward direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List functions.
+ */
+#define LIST_INIT(head) { \
+ (head)->lh_first = NULL; \
+}
+
+#define LIST_INSERT_AFTER(listelm, elm, field) { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+}
+
+#define LIST_INSERT_HEAD(head, elm, field) { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+}
+
+#define LIST_REMOVE(elm, field) { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+}
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_INIT(head) { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+}
+
+#define TAILQ_INSERT_HEAD(head, elm, field) { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+}
+
+#define TAILQ_INSERT_TAIL(head, elm, field) { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+}
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+}
+
+#define TAILQ_REMOVE(head, elm, field) { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+}
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_INIT(head) { \
+ (head)->cqh_first = (void *)(head); \
+ (head)->cqh_last = (void *)(head); \
+}
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == (void *)(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+}
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == (void *)(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+}
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = (void *)(head); \
+ if ((head)->cqh_last == (void *)(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+}
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) { \
+ (elm)->field.cqe_next = (void *)(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == (void *)(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+}
+
+#define CIRCLEQ_REMOVE(head, elm, field) { \
+ if ((elm)->field.cqe_next == (void *)(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == (void *)(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+}
+#endif /* !_QUEUE_H_ */
diff --git a/usr.bin/vi/interrupt.h b/usr.bin/vi/interrupt.h
new file mode 100644
index 000000000000..baa75fef2ca9
--- /dev/null
+++ b/usr.bin/vi/interrupt.h
@@ -0,0 +1,96 @@
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)interrupt.h 8.1 (Berkeley) 1/9/94
+ */
+
+/*
+ * Macros to declare the variables and then turn on and off interrupts.
+ */
+#define DECLARE_INTERRUPTS \
+ struct sigaction __act, __oact; \
+ struct termios __nterm, __term; \
+ u_int __istate; \
+ int __isig, __termreset
+
+/*
+ * Search, global, and substitute interruptions.
+ *
+ * ISIG turns on VINTR, VQUIT and VSUSP. We want VINTR to interrupt the
+ * search, so we install a handler. VQUIT is ignored by main() because
+ * nvi never wants to catch it. A handler for VSUSP should have been
+ * installed by the screen code.
+ */
+#define SET_UP_INTERRUPTS(handler) { \
+ if (F_ISSET(sp->gp, G_ISFROMTTY)) { \
+ __act.sa_handler = handler; \
+ sigemptyset(&__act.sa_mask); \
+ __act.sa_flags = 0; \
+ if (__isig = !sigaction(SIGINT, &__act, &__oact)) { \
+ __termreset = 0; \
+ __istate = F_ISSET(sp, S_INTERRUPTIBLE); \
+ F_CLR(sp, S_INTERRUPTED); \
+ F_SET(sp, S_INTERRUPTIBLE); \
+ if (tcgetattr(STDIN_FILENO, &__term)) { \
+ rval = 1; \
+ msgq(sp, M_SYSERR, "tcgetattr"); \
+ goto interrupt_err; \
+ } \
+ __nterm = __term; \
+ __nterm.c_lflag |= ISIG; \
+ if (tcsetattr(STDIN_FILENO, \
+ TCSANOW | TCSASOFT, &__nterm)) { \
+ rval = 1; \
+ msgq(sp, M_SYSERR, "tcsetattr"); \
+ goto interrupt_err; \
+ } \
+ __termreset = 1; \
+ } \
+ } \
+}
+
+#define TEAR_DOWN_INTERRUPTS { \
+ if (F_ISSET(sp->gp, G_ISFROMTTY) && __isig) { \
+ if (sigaction(SIGINT, &__oact, NULL)) { \
+ rval = 1; \
+ msgq(sp, M_SYSERR, "signal"); \
+ } \
+ if (__termreset && tcsetattr(STDIN_FILENO, \
+ TCSANOW | TCSASOFT, &__term)) { \
+ rval = 1; \
+ msgq(sp, M_SYSERR, "tcsetattr"); \
+ } \
+ F_CLR(sp, S_INTERRUPTED); \
+ if (!__istate) \
+ F_CLR(sp, S_INTERRUPTIBLE); \
+ } \
+}
diff --git a/usr.bin/vi/line.c b/usr.bin/vi/line.c
new file mode 100644
index 000000000000..a7473491cf17
--- /dev/null
+++ b/usr.bin/vi/line.c
@@ -0,0 +1,464 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)line.c 8.20 (Berkeley) 12/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+static inline int scr_update __P((SCR *, EXF *, recno_t, enum operation, int));
+
+/*
+ * file_gline --
+ * Look in the text buffers for a line; if it's not there
+ * call file_rline to retrieve it from the database.
+ */
+char *
+file_gline(sp, ep, lno, lenp)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno; /* Line number. */
+ size_t *lenp; /* Length store. */
+{
+ TEXT *tp;
+ recno_t l1, l2;
+
+ /*
+ * The underlying recno stuff handles zero by returning NULL, but
+ * have to have an oob condition for the look-aside into the input
+ * buffer anyway.
+ */
+ if (lno == 0)
+ return (NULL);
+
+ /*
+ * Look-aside into the TEXT buffers and see if the line we want
+ * is there.
+ */
+ if (F_ISSET(sp, S_INPUT)) {
+ l1 = ((TEXT *)sp->tiq.cqh_first)->lno;
+ l2 = ((TEXT *)sp->tiq.cqh_last)->lno;
+ if (l1 <= lno && l2 >= lno) {
+ for (tp = sp->tiq.cqh_first;
+ tp->lno != lno; tp = tp->q.cqe_next);
+ if (lenp)
+ *lenp = tp->len;
+ return (tp->lb);
+ }
+ /*
+ * Adjust the line number for the number of lines used
+ * by the text input buffers.
+ */
+ if (lno > l2)
+ lno -= l2 - l1;
+ }
+ return (file_rline(sp, ep, lno, lenp));
+}
+
+/*
+ * file_rline --
+ * Look in the cache for a line; if it's not there retrieve
+ * it from the file.
+ */
+char *
+file_rline(sp, ep, lno, lenp)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno; /* Line number. */
+ size_t *lenp; /* Length store. */
+{
+ DBT data, key;
+
+ /* Check the cache. */
+ if (lno == ep->c_lno) {
+ if (lenp)
+ *lenp = ep->c_len;
+ return (ep->c_lp);
+ }
+ ep->c_lno = OOBLNO;
+
+ /* Get the line from the underlying database. */
+ key.data = &lno;
+ key.size = sizeof(lno);
+ switch (ep->db->get(ep->db, &key, &data, 0)) {
+ case -1:
+ msgq(sp, M_ERR,
+ "Error: %s/%d: unable to get line %u: %s.",
+ tail(__FILE__), __LINE__, lno, strerror(errno));
+ /* FALLTHROUGH */
+ case 1:
+ return (NULL);
+ /* NOTREACHED */
+ }
+ if (lenp)
+ *lenp = data.size;
+
+ /* Fill the cache. */
+ ep->c_lno = lno;
+ ep->c_len = data.size;
+ ep->c_lp = data.data;
+
+ return (data.data);
+}
+
+/*
+ * file_dline --
+ * Delete a line from the file.
+ */
+int
+file_dline(sp, ep, lno)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+{
+ DBT key;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "delete line %lu\n", lno);
+#endif
+ /*
+ * XXX
+ *
+ * Marks and global commands have to know when lines are
+ * inserted or deleted.
+ */
+ mark_insdel(sp, ep, LINE_DELETE, lno);
+ global_insdel(sp, ep, LINE_DELETE, lno);
+
+ /* Log change. */
+ log_line(sp, ep, lno, LOG_LINE_DELETE);
+
+ /* Update file. */
+ key.data = &lno;
+ key.size = sizeof(lno);
+ if (ep->db->del(ep->db, &key, 0) == 1) {
+ msgq(sp, M_ERR,
+ "Error: %s/%d: unable to delete line %u: %s.",
+ tail(__FILE__), __LINE__, lno, strerror(errno));
+ return (1);
+ }
+
+ /* Flush the cache, update line count, before screen update. */
+ if (lno <= ep->c_lno)
+ ep->c_lno = OOBLNO;
+ if (ep->c_nlines != OOBLNO)
+ --ep->c_nlines;
+
+ /* File now dirty. */
+ if (F_ISSET(ep, F_FIRSTMODIFY))
+ (void)rcv_init(sp, ep);
+ F_SET(ep, F_MODIFIED);
+
+ /* Update screen. */
+ return (scr_update(sp, ep, lno, LINE_DELETE, 1));
+}
+
+/*
+ * file_aline --
+ * Append a line into the file.
+ */
+int
+file_aline(sp, ep, update, lno, p, len)
+ SCR *sp;
+ EXF *ep;
+ int update;
+ recno_t lno;
+ char *p;
+ size_t len;
+{
+ DBT data, key;
+ recno_t lline;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "append to %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
+#endif
+ /*
+ * Very nasty special case. The historic vi code displays a single
+ * space (or a '$' if the list option is set) for the first line in
+ * an "empty" file. If we "insert" a line, that line gets scrolled
+ * down, not repainted, so it's incorrect when we refresh the the
+ * screen. This is really hard to find and fix in the vi code -- the
+ * text input functions detect it explicitly and don't insert a new
+ * line. The hack here is to repaint the screen if we're appending
+ * to an empty file.
+ */
+ if (lno == 0) {
+ if (file_lline(sp, ep, &lline))
+ return (1);
+ if (lline == 0)
+ F_SET(sp, S_REDRAW);
+ }
+
+ /* Update file. */
+ key.data = &lno;
+ key.size = sizeof(lno);
+ data.data = p;
+ data.size = len;
+ if (ep->db->put(ep->db, &key, &data, R_IAFTER) == -1) {
+ msgq(sp, M_ERR,
+ "Error: %s/%d: unable to append to line %u: %s.",
+ tail(__FILE__), __LINE__, lno, strerror(errno));
+ return (1);
+ }
+
+ /* Flush the cache, update line count, before screen update. */
+ if (lno < ep->c_lno)
+ ep->c_lno = OOBLNO;
+ if (ep->c_nlines != OOBLNO)
+ ++ep->c_nlines;
+
+ /* File now dirty. */
+ if (F_ISSET(ep, F_FIRSTMODIFY))
+ (void)rcv_init(sp, ep);
+ F_SET(ep, F_MODIFIED);
+
+ /* Log change. */
+ log_line(sp, ep, lno + 1, LOG_LINE_APPEND);
+
+ /*
+ * XXX
+ *
+ * Marks and global commands have to know when lines are
+ * inserted or deleted.
+ */
+ mark_insdel(sp, ep, LINE_INSERT, lno + 1);
+ global_insdel(sp, ep, LINE_INSERT, lno + 1);
+
+ /*
+ * Update screen.
+ *
+ * XXX
+ * Nasty hack. If multiple lines are input by the user, they aren't
+ * committed until an <ESC> is entered. The problem is the screen was
+ * updated/scrolled as each line was entered. So, when this routine
+ * is called to copy the new lines from the cut buffer into the file,
+ * it has to know not to update the screen again.
+ */
+ return (scr_update(sp, ep, lno, LINE_APPEND, update));
+}
+
+/*
+ * file_iline --
+ * Insert a line into the file.
+ */
+int
+file_iline(sp, ep, lno, p, len)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ char *p;
+ size_t len;
+{
+ DBT data, key;
+ recno_t lline;
+
+#if defined(DEBUG) && 0
+ TRACE(sp,
+ "insert before %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
+#endif
+
+ /* Very nasty special case. See comment in file_aline(). */
+ if (lno == 1) {
+ if (file_lline(sp, ep, &lline))
+ return (1);
+ if (lline == 0)
+ F_SET(sp, S_REDRAW);
+ }
+
+ /* Update file. */
+ key.data = &lno;
+ key.size = sizeof(lno);
+ data.data = p;
+ data.size = len;
+ if (ep->db->put(ep->db, &key, &data, R_IBEFORE) == -1) {
+ msgq(sp, M_ERR,
+ "Error: %s/%d: unable to insert at line %u: %s.",
+ tail(__FILE__), __LINE__, lno, strerror(errno));
+ return (1);
+ }
+
+ /* Flush the cache, update line count, before screen update. */
+ if (lno >= ep->c_lno)
+ ep->c_lno = OOBLNO;
+ if (ep->c_nlines != OOBLNO)
+ ++ep->c_nlines;
+
+ /* File now dirty. */
+ if (F_ISSET(ep, F_FIRSTMODIFY))
+ (void)rcv_init(sp, ep);
+ F_SET(ep, F_MODIFIED);
+
+ /* Log change. */
+ log_line(sp, ep, lno, LOG_LINE_INSERT);
+
+ /*
+ * XXX
+ *
+ * Marks and global commands have to know when lines are
+ * inserted or deleted.
+ */
+ mark_insdel(sp, ep, LINE_INSERT, lno);
+ global_insdel(sp, ep, LINE_INSERT, lno);
+
+ /* Update screen. */
+ return (scr_update(sp, ep, lno, LINE_INSERT, 1));
+}
+
+/*
+ * file_sline --
+ * Store a line in the file.
+ */
+int
+file_sline(sp, ep, lno, p, len)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ char *p;
+ size_t len;
+{
+ DBT data, key;
+
+#if defined(DEBUG) && 0
+ TRACE(sp,
+ "replace line %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
+#endif
+ /* Log before change. */
+ log_line(sp, ep, lno, LOG_LINE_RESET_B);
+
+ /* Update file. */
+ key.data = &lno;
+ key.size = sizeof(lno);
+ data.data = p;
+ data.size = len;
+ if (ep->db->put(ep->db, &key, &data, 0) == -1) {
+ msgq(sp, M_ERR,
+ "Error: %s/%d: unable to store line %u: %s.",
+ tail(__FILE__), __LINE__, lno, strerror(errno));
+ return (1);
+ }
+
+ /* Flush the cache, before logging or screen update. */
+ if (lno == ep->c_lno)
+ ep->c_lno = OOBLNO;
+
+ /* File now dirty. */
+ if (F_ISSET(ep, F_FIRSTMODIFY))
+ (void)rcv_init(sp, ep);
+ F_SET(ep, F_MODIFIED);
+
+ /* Log after change. */
+ log_line(sp, ep, lno, LOG_LINE_RESET_F);
+
+ /* Update screen. */
+ return (scr_update(sp, ep, lno, LINE_RESET, 1));
+}
+
+/*
+ * file_lline --
+ * Return the number of lines in the file.
+ */
+int
+file_lline(sp, ep, lnop)
+ SCR *sp;
+ EXF *ep;
+ recno_t *lnop;
+{
+ DBT data, key;
+ recno_t lno;
+
+ /* Check the cache. */
+ if (ep->c_nlines != OOBLNO) {
+ *lnop = (F_ISSET(sp, S_INPUT) &&
+ ((TEXT *)sp->tiq.cqh_last)->lno > ep->c_nlines ?
+ ((TEXT *)sp->tiq.cqh_last)->lno : ep->c_nlines);
+ return (0);
+ }
+
+ key.data = &lno;
+ key.size = sizeof(lno);
+
+ switch (ep->db->seq(ep->db, &key, &data, R_LAST)) {
+ case -1:
+ msgq(sp, M_ERR,
+ "Error: %s/%d: unable to get last line: %s.",
+ tail(__FILE__), __LINE__, strerror(errno));
+ *lnop = 0;
+ return (1);
+ case 1:
+ lno = 0;
+ break;
+ default:
+ memmove(&lno, key.data, sizeof(lno));
+ break;
+ }
+
+ /* Fill the cache. */
+ ep->c_nlines = ep->c_lno = lno;
+ ep->c_len = data.size;
+ ep->c_lp = data.data;
+
+ *lnop = (F_ISSET(sp, S_INPUT) &&
+ ((TEXT *)sp->tiq.cqh_last)->lno > lno ?
+ ((TEXT *)sp->tiq.cqh_last)->lno : lno);
+ return (0);
+}
+
+/*
+ * scr_update --
+ * Update all of the screens that are backed by the file that
+ * just changed.
+ */
+static inline int
+scr_update(sp, ep, lno, op, current)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ enum operation op;
+ int current;
+{
+ SCR *tsp;
+
+ if (ep->refcnt != 1)
+ for (tsp = sp->gp->dq.cqh_first;
+ tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next)
+ if (sp != tsp && tsp->ep == ep)
+ (void)sp->s_change(tsp, ep, lno, op);
+ return (current && sp->s_change(sp, ep, lno, op));
+}
diff --git a/usr.bin/vi/log.c b/usr.bin/vi/log.c
new file mode 100644
index 000000000000..fdf959f4d8a9
--- /dev/null
+++ b/usr.bin/vi/log.c
@@ -0,0 +1,663 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)log.c 8.9 (Berkeley) 12/28/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+
+/*
+ * The log consists of records, each containing a type byte and a variable
+ * length byte string, as follows:
+ *
+ * LOG_CURSOR_INIT MARK
+ * LOG_CURSOR_END MARK
+ * LOG_LINE_APPEND recno_t char *
+ * LOG_LINE_DELETE recno_t char *
+ * LOG_LINE_INSERT recno_t char *
+ * LOG_LINE_RESET_F recno_t char *
+ * LOG_LINE_RESET_B recno_t char *
+ * LOG_MARK MARK
+ *
+ * We do before image physical logging. This means that the editor layer
+ * MAY NOT modify records in place, even if simply deleting or overwriting
+ * characters. Since the smallest unit of logging is a line, we're using
+ * up lots of space. This may eventually have to be reduced, probably by
+ * doing logical logging, which is a much cooler database phrase.
+ *
+ * The implementation of the historic vi 'u' command, using roll-forward and
+ * roll-back, is simple. Each set of changes has a LOG_CURSOR_INIT record,
+ * followed by a number of other records, followed by a LOG_CURSOR_END record.
+ * LOG_LINE_RESET records come in pairs. The first is a LOG_LINE_RESET_B
+ * record, and is the line before the change. The second is LOG_LINE_RESET_F,
+ * and is the line after the change. Roll-back is done by backing up to the
+ * first LOG_CURSOR_INIT record before a change. Roll-forward is done in a
+ * similar fashion.
+ *
+ * The 'U' command is implemented by rolling backward to a LOG_CURSOR_END
+ * record for a line different from the current one. It should be noted that
+ * this means that a subsequent 'u' command will make a change based on the
+ * new position of the log's cursor. This is okay, and, in fact, historic vi
+ * behaved that way.
+ */
+
+static int log_cursor1 __P((SCR *, EXF *, int));
+#if defined(DEBUG) && 0
+static void log_trace __P((SCR *, char *, recno_t, u_char *));
+#endif
+
+/* Try and restart the log on failure, i.e. if we run out of memory. */
+#define LOG_ERR { \
+ msgq(sp, M_ERR, "Error: %s/%d: put log error: %s.", \
+ tail(__FILE__), __LINE__, strerror(errno)); \
+ (void)ep->log->close(ep->log); \
+ if (!log_init(sp, ep)) \
+ msgq(sp, M_ERR, "Log restarted."); \
+ return (1); \
+}
+
+/*
+ * log_init --
+ * Initialize the logging subsystem.
+ */
+int
+log_init(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ /*
+ * Initialize the buffer. The logging subsystem has its own
+ * buffers because the global ones are almost by definition
+ * going to be in use when the log runs.
+ */
+ ep->l_lp = NULL;
+ ep->l_len = 0;
+ ep->l_cursor.lno = 1; /* XXX Any valid recno. */
+ ep->l_cursor.cno = 0;
+ ep->l_high = ep->l_cur = 1;
+
+ ep->log = dbopen(NULL, O_CREAT | O_NONBLOCK | O_RDWR,
+ S_IRUSR | S_IWUSR, DB_RECNO, NULL);
+ if (ep->log == NULL) {
+ msgq(sp, M_ERR, "log db: %s", strerror(errno));
+ F_SET(ep, F_NOLOG);
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * log_end --
+ * Close the logging subsystem.
+ */
+int
+log_end(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ if (ep->log != NULL) {
+ (void)(ep->log->close)(ep->log);
+ ep->log = NULL;
+ }
+ if (ep->l_lp != NULL) {
+ free(ep->l_lp);
+ ep->l_lp = NULL;
+ }
+ ep->l_len = 0;
+ ep->l_cursor.lno = 1; /* XXX Any valid recno. */
+ ep->l_cursor.cno = 0;
+ ep->l_high = ep->l_cur = 1;
+ return (0);
+}
+
+/*
+ * log_cursor --
+ * Log the current cursor position, starting an event.
+ */
+int
+log_cursor(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ /*
+ * If any changes were made since the last cursor init,
+ * put out the ending cursor record.
+ */
+ if (ep->l_cursor.lno == OOBLNO) {
+ ep->l_cursor.lno = sp->lno;
+ ep->l_cursor.cno = sp->cno;
+ return (log_cursor1(sp, ep, LOG_CURSOR_END));
+ }
+ ep->l_cursor.lno = sp->lno;
+ ep->l_cursor.cno = sp->cno;
+ return (0);
+}
+
+/*
+ * log_cursor1 --
+ * Actually push a cursor record out.
+ */
+static int
+log_cursor1(sp, ep, type)
+ SCR *sp;
+ EXF *ep;
+ int type;
+{
+ DBT data, key;
+
+ BINC_RET(sp, ep->l_lp, ep->l_len, sizeof(u_char) + sizeof(MARK));
+ ep->l_lp[0] = type;
+ memmove(ep->l_lp + sizeof(u_char), &ep->l_cursor, sizeof(MARK));
+
+ key.data = &ep->l_cur;
+ key.size = sizeof(recno_t);
+ data.data = ep->l_lp;
+ data.size = sizeof(u_char) + sizeof(MARK);
+ if (ep->log->put(ep->log, &key, &data, 0) == -1)
+ LOG_ERR;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "%lu: %s: %u/%u\n", ep->l_cur,
+ type == LOG_CURSOR_INIT ? "log_cursor_init" : "log_cursor_end",
+ sp->lno, sp->cno);
+#endif
+ /* Reset high water mark. */
+ ep->l_high = ++ep->l_cur;
+
+ return (0);
+}
+
+/*
+ * log_line --
+ * Log a line change.
+ */
+int
+log_line(sp, ep, lno, action)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ u_int action;
+{
+ DBT data, key;
+ size_t len;
+ char *lp;
+
+ if (F_ISSET(ep, F_NOLOG))
+ return (0);
+
+ /*
+ * XXX
+ *
+ * Kluge for vi. Clear the EXF undo flag so that the
+ * next 'u' command does a roll-back, regardless.
+ */
+ F_CLR(ep, F_UNDO);
+
+ /* Put out one initial cursor record per set of changes. */
+ if (ep->l_cursor.lno != OOBLNO) {
+ if (log_cursor1(sp, ep, LOG_CURSOR_INIT))
+ return (1);
+ ep->l_cursor.lno = OOBLNO;
+ }
+
+ /*
+ * Put out the changes. If it's a LOG_LINE_RESET_B call, it's a
+ * special case, avoid the caches. Also, if it fails and it's
+ * line 1, it just means that the user started with an empty file,
+ * so fake an empty length line.
+ */
+ if (action == LOG_LINE_RESET_B) {
+ if ((lp = file_rline(sp, ep, lno, &len)) == NULL) {
+ if (lno != 1) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+ len = 0;
+ lp = "";
+ }
+ } else
+ if ((lp = file_gline(sp, ep, lno, &len)) == NULL) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+ BINC_RET(sp,
+ ep->l_lp, ep->l_len, len + sizeof(u_char) + sizeof(recno_t));
+ ep->l_lp[0] = action;
+ memmove(ep->l_lp + sizeof(u_char), &lno, sizeof(recno_t));
+ memmove(ep->l_lp + sizeof(u_char) + sizeof(recno_t), lp, len);
+
+ key.data = &ep->l_cur;
+ key.size = sizeof(recno_t);
+ data.data = ep->l_lp;
+ data.size = len + sizeof(u_char) + sizeof(recno_t);
+ if (ep->log->put(ep->log, &key, &data, 0) == -1)
+ LOG_ERR;
+
+#if defined(DEBUG) && 0
+ switch (action) {
+ case LOG_LINE_APPEND:
+ TRACE(sp, "%u: log_line: append: %lu {%u}\n",
+ ep->l_cur, lno, len);
+ break;
+ case LOG_LINE_DELETE:
+ TRACE(sp, "%lu: log_line: delete: %lu {%u}\n",
+ ep->l_cur, lno, len);
+ break;
+ case LOG_LINE_INSERT:
+ TRACE(sp, "%lu: log_line: insert: %lu {%u}\n",
+ ep->l_cur, lno, len);
+ break;
+ case LOG_LINE_RESET_F:
+ TRACE(sp, "%lu: log_line: reset_f: %lu {%u}\n",
+ ep->l_cur, lno, len);
+ break;
+ case LOG_LINE_RESET_B:
+ TRACE(sp, "%lu: log_line: reset_b: %lu {%u}\n",
+ ep->l_cur, lno, len);
+ break;
+ }
+#endif
+ /* Reset high water mark. */
+ ep->l_high = ++ep->l_cur;
+
+ return (0);
+}
+
+/*
+ * log_mark --
+ * Log a mark position. For the log to work, we assume that there
+ * aren't any operations that just put out a log record -- this
+ * would mean that undo operations would only reset marks, and not
+ * cause any other change.
+ */
+int
+log_mark(sp, ep, mp)
+ SCR *sp;
+ EXF *ep;
+ MARK *mp;
+{
+ DBT data, key;
+
+ if (F_ISSET(ep, F_NOLOG))
+ return (0);
+
+ /* Put out one initial cursor record per set of changes. */
+ if (ep->l_cursor.lno != OOBLNO) {
+ if (log_cursor1(sp, ep, LOG_CURSOR_INIT))
+ return (1);
+ ep->l_cursor.lno = OOBLNO;
+ }
+
+ BINC_RET(sp, ep->l_lp,
+ ep->l_len, sizeof(u_char) + sizeof(MARK));
+ ep->l_lp[0] = LOG_MARK;
+ memmove(ep->l_lp + sizeof(u_char), mp, sizeof(MARK));
+
+ key.data = &ep->l_cur;
+ key.size = sizeof(recno_t);
+ data.data = ep->l_lp;
+ data.size = sizeof(u_char) + sizeof(MARK);
+ if (ep->log->put(ep->log, &key, &data, 0) == -1)
+ LOG_ERR;
+
+ /* Reset high water mark. */
+ ep->l_high = ++ep->l_cur;
+ return (0);
+}
+
+/*
+ * Log_backward --
+ * Roll the log backward one operation.
+ */
+int
+log_backward(sp, ep, rp)
+ SCR *sp;
+ EXF *ep;
+ MARK *rp;
+{
+ DBT key, data;
+ MARK m;
+ recno_t lno;
+ int didop;
+ u_char *p;
+
+ if (F_ISSET(ep, F_NOLOG)) {
+ msgq(sp, M_ERR,
+ "Logging not being performed, undo not possible.");
+ return (1);
+ }
+
+ if (ep->l_cur == 1) {
+ msgq(sp, M_BERR, "No changes to undo.");
+ return (1);
+ }
+
+ F_SET(ep, F_NOLOG); /* Turn off logging. */
+
+ key.data = &ep->l_cur; /* Initialize db request. */
+ key.size = sizeof(recno_t);
+ for (didop = 0;;) {
+ --ep->l_cur;
+ if (ep->log->get(ep->log, &key, &data, 0))
+ LOG_ERR;
+#if defined(DEBUG) && 0
+ log_trace(sp, "log_backward", ep->l_cur, data.data);
+#endif
+ switch (*(p = (u_char *)data.data)) {
+ case LOG_CURSOR_INIT:
+ if (didop) {
+ memmove(rp, p + sizeof(u_char), sizeof(MARK));
+ F_CLR(ep, F_NOLOG);
+ return (0);
+ }
+ break;
+ case LOG_CURSOR_END:
+ break;
+ case LOG_LINE_APPEND:
+ case LOG_LINE_INSERT:
+ didop = 1;
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ if (file_dline(sp, ep, lno))
+ goto err;
+ ++sp->rptlines[L_DELETED];
+ break;
+ case LOG_LINE_DELETE:
+ didop = 1;
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ if (file_iline(sp, ep, lno, p + sizeof(u_char) +
+ sizeof(recno_t), data.size - sizeof(u_char) -
+ sizeof(recno_t)))
+ goto err;
+ ++sp->rptlines[L_ADDED];
+ break;
+ case LOG_LINE_RESET_F:
+ break;
+ case LOG_LINE_RESET_B:
+ didop = 1;
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ if (file_sline(sp, ep, lno, p + sizeof(u_char) +
+ sizeof(recno_t), data.size - sizeof(u_char) -
+ sizeof(recno_t)))
+ goto err;
+ ++sp->rptlines[L_CHANGED];
+ break;
+ case LOG_MARK:
+ didop = 1;
+ memmove(&m, p + sizeof(u_char), sizeof(MARK));
+ if (mark_set(sp, ep, m.name, &m, 0))
+ goto err;
+ break;
+ default:
+ abort();
+ }
+ }
+
+err: F_CLR(ep, F_NOLOG);
+ return (1);
+}
+
+/*
+ * Log_setline --
+ * Reset the line to its original appearance.
+ *
+ * XXX
+ * There's a bug in this code due to our not logging cursor movements
+ * unless a change was made. If you do a change, move off the line,
+ * then move back on and do a 'U', the line will be restored to the way
+ * it was before the original change.
+ */
+int
+log_setline(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ DBT key, data;
+ MARK m;
+ recno_t lno;
+ u_char *p;
+
+ if (F_ISSET(ep, F_NOLOG)) {
+ msgq(sp, M_ERR,
+ "Logging not being performed, undo not possible.");
+ return (1);
+ }
+
+ if (ep->l_cur == 1)
+ return (1);
+
+ F_SET(ep, F_NOLOG); /* Turn off logging. */
+
+ key.data = &ep->l_cur; /* Initialize db request. */
+ key.size = sizeof(recno_t);
+
+ for (;;) {
+ --ep->l_cur;
+ if (ep->log->get(ep->log, &key, &data, 0))
+ LOG_ERR;
+#if defined(DEBUG) && 0
+ log_trace(sp, "log_setline", ep->l_cur, data.data);
+#endif
+ switch (*(p = (u_char *)data.data)) {
+ case LOG_CURSOR_INIT:
+ memmove(&m, p + sizeof(u_char), sizeof(MARK));
+ if (m.lno != sp->lno || ep->l_cur == 1) {
+ F_CLR(ep, F_NOLOG);
+ return (0);
+ }
+ break;
+ case LOG_CURSOR_END:
+ memmove(&m, p + sizeof(u_char), sizeof(MARK));
+ if (m.lno != sp->lno) {
+ ++ep->l_cur;
+ F_CLR(ep, F_NOLOG);
+ return (0);
+ }
+ break;
+ case LOG_LINE_APPEND:
+ case LOG_LINE_INSERT:
+ case LOG_LINE_DELETE:
+ case LOG_LINE_RESET_F:
+ break;
+ case LOG_LINE_RESET_B:
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ if (lno == sp->lno &&
+ file_sline(sp, ep, lno, p + sizeof(u_char) +
+ sizeof(recno_t), data.size - sizeof(u_char) -
+ sizeof(recno_t)))
+ goto err;
+ ++sp->rptlines[L_CHANGED];
+ case LOG_MARK:
+ memmove(&m, p + sizeof(u_char), sizeof(MARK));
+ if (mark_set(sp, ep, m.name, &m, 0))
+ goto err;
+ break;
+ default:
+ abort();
+ }
+ }
+
+err: F_CLR(ep, F_NOLOG);
+ return (1);
+}
+
+/*
+ * Log_forward --
+ * Roll the log forward one operation.
+ */
+int
+log_forward(sp, ep, rp)
+ SCR *sp;
+ EXF *ep;
+ MARK *rp;
+{
+ DBT key, data;
+ MARK m;
+ recno_t lno;
+ int didop;
+ u_char *p;
+
+ if (F_ISSET(ep, F_NOLOG)) {
+ msgq(sp, M_ERR,
+ "Logging not being performed, roll-forward not possible.");
+ return (1);
+ }
+
+ if (ep->l_cur == ep->l_high) {
+ msgq(sp, M_BERR, "No changes to re-do.");
+ return (1);
+ }
+
+ F_SET(ep, F_NOLOG); /* Turn off logging. */
+
+ key.data = &ep->l_cur; /* Initialize db request. */
+ key.size = sizeof(recno_t);
+ for (didop = 0;;) {
+ ++ep->l_cur;
+ if (ep->log->get(ep->log, &key, &data, 0))
+ LOG_ERR;
+#if defined(DEBUG) && 0
+ log_trace(sp, "log_forward", ep->l_cur, data.data);
+#endif
+ switch (*(p = (u_char *)data.data)) {
+ case LOG_CURSOR_END:
+ if (didop) {
+ ++ep->l_cur;
+ memmove(rp, p + sizeof(u_char), sizeof(MARK));
+ F_CLR(ep, F_NOLOG);
+ return (0);
+ }
+ break;
+ case LOG_CURSOR_INIT:
+ break;
+ case LOG_LINE_APPEND:
+ case LOG_LINE_INSERT:
+ didop = 1;
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ if (file_iline(sp, ep, lno, p + sizeof(u_char) +
+ sizeof(recno_t), data.size - sizeof(u_char) -
+ sizeof(recno_t)))
+ goto err;
+ ++sp->rptlines[L_ADDED];
+ break;
+ case LOG_LINE_DELETE:
+ didop = 1;
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ if (file_dline(sp, ep, lno))
+ goto err;
+ ++sp->rptlines[L_DELETED];
+ break;
+ case LOG_LINE_RESET_B:
+ break;
+ case LOG_LINE_RESET_F:
+ didop = 1;
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ if (file_sline(sp, ep, lno, p + sizeof(u_char) +
+ sizeof(recno_t), data.size - sizeof(u_char) -
+ sizeof(recno_t)))
+ goto err;
+ ++sp->rptlines[L_CHANGED];
+ break;
+ case LOG_MARK:
+ didop = 1;
+ memmove(&m, p + sizeof(u_char), sizeof(MARK));
+ if (mark_set(sp, ep, m.name, &m, 0))
+ goto err;
+ break;
+ default:
+ abort();
+ }
+ }
+
+err: F_CLR(ep, F_NOLOG);
+ return (1);
+}
+
+#if defined(DEBUG) && 0
+static void
+log_trace(sp, msg, rno, p)
+ SCR *sp;
+ char *msg;
+ recno_t rno;
+ u_char *p;
+{
+ MARK m;
+ recno_t lno;
+
+ switch (*p) {
+ case LOG_CURSOR_INIT:
+ memmove(&m, p + sizeof(u_char), sizeof(MARK));
+ TRACE(sp, "%lu: %s: C_INIT: %u/%u\n", rno, msg, m.lno, m.cno);
+ break;
+ case LOG_CURSOR_END:
+ memmove(&m, p + sizeof(u_char), sizeof(MARK));
+ TRACE(sp, "%lu: %s: C_END: %u/%u\n", rno, msg, m.lno, m.cno);
+ break;
+ case LOG_LINE_APPEND:
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ TRACE(sp, "%lu: %s: APPEND: %lu\n", rno, msg, lno);
+ break;
+ case LOG_LINE_INSERT:
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ TRACE(sp, "%lu: %s: INSERT: %lu\n", rno, msg, lno);
+ break;
+ case LOG_LINE_DELETE:
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ TRACE(sp, "%lu: %s: DELETE: %lu\n", rno, msg, lno);
+ break;
+ case LOG_LINE_RESET_F:
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ TRACE(sp, "%lu: %s: RESET_F: %lu\n", rno, msg, lno);
+ break;
+ case LOG_LINE_RESET_B:
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ TRACE(sp, "%lu: %s: RESET_B: %lu\n", rno, msg, lno);
+ break;
+ case LOG_MARK:
+ memmove(&m, p + sizeof(u_char), sizeof(MARK));
+ TRACE(sp, "%lu: %s: MARK: %u/%u\n", rno, msg, m.lno, m.cno);
+ break;
+ default:
+ abort();
+ }
+}
+#endif
diff --git a/usr.bin/vi/log.h b/usr.bin/vi/log.h
new file mode 100644
index 000000000000..840cf8532c0c
--- /dev/null
+++ b/usr.bin/vi/log.h
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 1992, 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.
+ *
+ * @(#)log.h 8.3 (Berkeley) 12/28/93
+ */
+
+#define LOG_NOTYPE 0
+#define LOG_CURSOR_INIT 1
+#define LOG_CURSOR_END 2
+#define LOG_LINE_APPEND 3
+#define LOG_LINE_DELETE 4
+#define LOG_LINE_INSERT 5
+#define LOG_LINE_RESET_F 6
+#define LOG_LINE_RESET_B 7
+#define LOG_MARK 8
+
+int log_backward __P((SCR *, EXF *, MARK *));
+int log_cursor __P((SCR *, EXF *));
+int log_end __P((SCR *, EXF *));
+int log_forward __P((SCR *, EXF *, MARK *));
+int log_init __P((SCR *, EXF *));
+int log_line __P((SCR *, EXF *, recno_t, u_int));
+int log_mark __P((SCR *, EXF *, MARK *));
+int log_setline __P((SCR *, EXF *));
diff --git a/usr.bin/vi/main.c b/usr.bin/vi/main.c
new file mode 100644
index 000000000000..3413a475ddf8
--- /dev/null
+++ b/usr.bin/vi/main.c
@@ -0,0 +1,743 @@
+/*-
+ * Copyright (c) 1992, 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
+static char copyright[] =
+"@(#) Copyright (c) 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.65 (Berkeley) 1/23/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "vi.h"
+#include "excmd.h"
+#include "pathnames.h"
+#include "tag.h"
+
+static int exrc_isok __P((SCR *, char *, int));
+static void gs_end __P((GS *));
+static GS *gs_init __P((void));
+static void h_hup __P((int));
+static void h_term __P((int));
+static void h_winch __P((int));
+static void obsolete __P((char *[]));
+static void usage __P((int));
+
+GS *__global_list; /* GLOBAL: List of screens. */
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern int optind;
+ extern char *optarg;
+ static int reenter; /* STATIC: Re-entrancy check. */
+ struct sigaction act;
+ GS *gp;
+ FREF *frp;
+ SCR *sp;
+ u_int flags, saved_vi_mode;
+ int ch, eval, flagchk, readonly, silent, snapshot;
+ char *excmdarg, *myname, *p, *rec_f, *tag_f, *trace_f, *wsizearg;
+ char path[MAXPATHLEN];
+
+ /* Stop if indirecting through a NULL pointer. */
+ if (reenter++)
+ abort();
+
+ /* Set screen type and mode based on the program name. */
+ readonly = 0;
+ if ((myname = strrchr(*argv, '/')) == NULL)
+ myname = *argv;
+ else
+ ++myname;
+ if (!strcmp(myname, "ex") || !strcmp(myname, "nex"))
+ LF_INIT(S_EX);
+ else {
+ /* View is readonly. */
+ if (!strcmp(myname, "view"))
+ readonly = 1;
+ LF_INIT(S_VI_CURSES);
+ }
+ saved_vi_mode = S_VI_CURSES;
+
+ /* Convert old-style arguments into new-style ones. */
+ obsolete(argv);
+
+ /* Parse the arguments. */
+ flagchk = '\0';
+ excmdarg = rec_f = tag_f = trace_f = wsizearg = NULL;
+ silent = 0;
+ snapshot = 1;
+ while ((ch = getopt(argc, argv, "c:eFlRr:sT:t:vw:x:")) != EOF)
+ switch (ch) {
+ case 'c': /* Run the command. */
+ excmdarg = optarg;
+ break;
+ case 'e': /* Ex mode. */
+ LF_CLR(S_SCREENS);
+ LF_SET(S_EX);
+ break;
+ case 'F': /* No snapshot. */
+ snapshot = 0;
+ break;
+ case 'l':
+ if (flagchk != '\0' && flagchk != 'l')
+ errx(1,
+ "only one of -%c and -l may be specified.",
+ flagchk);
+ flagchk = 'l';
+ break;
+ case 'R': /* Readonly. */
+ readonly = 1;
+ break;
+ case 'r': /* Recover. */
+ if (flagchk == 'r')
+ errx(1,
+ "only one recovery file may be specified.");
+ if (flagchk != '\0')
+ errx(1,
+ "only one of -%c and -r may be specified.",
+ flagchk);
+ flagchk = 'r';
+ rec_f = optarg;
+ break;
+ case 's':
+ if (!LF_ISSET(S_EX))
+ errx(1, "-s only applicable to ex.");
+ silent = 1;
+ break;
+ case 'T': /* Trace. */
+ trace_f = optarg;
+ break;
+ case 't': /* Tag. */
+ if (flagchk == 't')
+ errx(1,
+ "only one tag file may be specified.");
+ if (flagchk != '\0')
+ errx(1,
+ "only one of -%c and -t may be specified.",
+ flagchk);
+ flagchk = 't';
+ tag_f = optarg;
+ break;
+ case 'v': /* Vi mode. */
+ LF_CLR(S_SCREENS);
+ LF_SET(S_VI_CURSES);
+ break;
+ case 'w':
+ wsizearg = optarg;
+ break;
+ case 'x':
+ if (!strcmp(optarg, "aw")) {
+ LF_CLR(S_SCREENS);
+ LF_SET(S_VI_XAW);
+ saved_vi_mode = S_VI_XAW;
+ break;
+ }
+ /* FALLTHROUGH */
+ case '?':
+ default:
+ usage(LF_ISSET(S_EX));
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* Build and initialize the GS structure. */
+ __global_list = gp = gs_init();
+
+ if (snapshot)
+ F_SET(gp, G_SNAPSHOT);
+
+ /*
+ * Build and initialize the first/current screen. This is a bit
+ * tricky. If an error is returned, we may or may not have a
+ * screen structure. If we have a screen structure, put it on a
+ * display queue so that the error messages get displayed.
+ */
+ if (screen_init(NULL, &sp, flags)) {
+ if (sp != NULL)
+ CIRCLEQ_INSERT_HEAD(&__global_list->dq, sp, q);
+ goto err;
+ }
+ sp->saved_vi_mode = saved_vi_mode;
+ CIRCLEQ_INSERT_HEAD(&__global_list->dq, sp, q);
+
+ if (trace_f != NULL) {
+#ifdef DEBUG
+ if ((gp->tracefp = fopen(optarg, "w")) == NULL)
+ err(1, "%s", optarg);
+ (void)fprintf(gp->tracefp, "\n===\ntrace: open %s\n", optarg);
+#else
+ msgq(sp, M_ERR, "-T support not compiled into this version.");
+#endif
+ }
+
+ if (set_window_size(sp, 0, 0)) /* Set the window size. */
+ goto err;
+ if (opts_init(sp)) /* Options initialization. */
+ goto err;
+ if (readonly) /* Global read-only bit. */
+ O_SET(sp, O_READONLY);
+ if (silent) { /* Ex batch mode. */
+ O_CLR(sp, O_AUTOPRINT);
+ O_CLR(sp, O_PROMPT);
+ O_CLR(sp, O_VERBOSE);
+ O_CLR(sp, O_WARN);
+ F_SET(sp, S_EXSILENT);
+ }
+ if (wsizearg != NULL) {
+ ARGS *av[2], a, b;
+ if (strtol(optarg, &p, 10) < 0 || *p)
+ errx(1, "illegal window size -- %s", optarg);
+ (void)snprintf(path, sizeof(path), "window=%s", optarg);
+ a.bp = (CHAR_T *)path;
+ a.len = strlen(path);
+ b.bp = NULL;
+ b.len = 0;
+ av[0] = &a;
+ av[1] = &b;
+ if (opts_set(sp, av))
+ msgq(sp, M_ERR,
+ "Unable to set command line window option");
+ }
+
+ /* Keymaps, special keys, must follow option initializations. */
+ if (term_init(sp))
+ goto err;
+
+#ifdef DIGRAPHS
+ if (digraph_init(sp)) /* Digraph initialization. */
+ goto err;
+#endif
+
+ /*
+ * Source the system, environment, ~user and local .exrc values.
+ * Vi historically didn't check ~user/.exrc if the environment
+ * variable EXINIT was set. This is all done before the file is
+ * read in because things in the .exrc information can set, for
+ * example, the recovery directory.
+ *
+ * !!!
+ * While nvi can handle any of the options settings of historic vi,
+ * the converse is not true. Since users are going to have to have
+ * files and environmental variables that work with both, we use nvi
+ * versions if they exist, otherwise the historic ones.
+ */
+ if (!silent) {
+ if (exrc_isok(sp, _PATH_SYSEXRC, 1))
+ (void)ex_cfile(sp, NULL, _PATH_SYSEXRC);
+
+ /* Source the {N,}EXINIT environment variable. */
+ if ((p = getenv("NEXINIT")) != NULL ||
+ (p = getenv("EXINIT")) != NULL)
+ if ((p = strdup(p)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ goto err;
+ } else {
+ (void)ex_icmd(sp, NULL, p, strlen(p));
+ free(p);
+ }
+ else if ((p = getenv("HOME")) != NULL && *p) {
+ (void)snprintf(path,
+ sizeof(path), "%s/%s", p, _PATH_NEXRC);
+ if (exrc_isok(sp, path, 0))
+ (void)ex_cfile(sp, NULL, path);
+ else {
+ (void)snprintf(path,
+ sizeof(path), "%s/%s", p, _PATH_EXRC);
+ if (exrc_isok(sp, path, 0))
+ (void)ex_cfile(sp, NULL, path);
+ }
+ }
+ /*
+ * !!!
+ * According to O'Reilly ("Learning the VI Editor", Fifth Ed.,
+ * May 1992, page 106), System V release 3.2 and later, has an
+ * option "[no]exrc", causing vi to not "read .exrc files in
+ * the current directory unless you first set the exrc option
+ * in your home directory's .exrc file". Yeah, right. Did
+ * someone actually believe that users would change their home
+ * .exrc file based on whether or not they wanted to source the
+ * current local .exrc? Or that users would want ALL the local
+ * .exrc files on some systems, and none of them on others?
+ * I think not.
+ *
+ * Apply the same tests to local .exrc files that are applied
+ * to any other .exrc file.
+ */
+ if (exrc_isok(sp, _PATH_EXRC, 0))
+ (void)ex_cfile(sp, NULL, _PATH_EXRC);
+ }
+
+ /* List recovery files if -l specified. */
+ if (flagchk == 'l')
+ exit(rcv_list(sp));
+
+ /* Use a tag file or recovery file if specified. */
+ if (tag_f != NULL && ex_tagfirst(sp, tag_f))
+ goto err;
+ else if (rec_f != NULL && rcv_read(sp, rec_f))
+ goto err;
+
+ /* Append any remaining arguments as file names. */
+ if (*argv != NULL)
+ for (; *argv != NULL; ++argv)
+ if (file_add(sp, NULL, *argv, 0) == NULL)
+ goto err;
+
+ /*
+ * If no recovery or tag file, get an EXF structure.
+ * If no argv file, use a temporary file.
+ */
+ if (tag_f == NULL && rec_f == NULL) {
+ if ((frp = file_first(sp)) == NULL &&
+ (frp = file_add(sp, NULL, NULL, 1)) == NULL)
+ goto err;
+ if (file_init(sp, frp, NULL, 0))
+ goto err;
+ }
+
+ /* Set up the argument pointer. */
+ sp->a_frp = sp->frp;
+
+ /*
+ * Initialize the signals. Use sigaction(2), not signal(3), because
+ * we don't want to always restart system calls on 4BSD systems. It
+ * would be nice in some cases to restart system calls, but SA_RESTART
+ * is a 4BSD extension so we can't use it.
+ *
+ * SIGWINCH, SIGHUP, SIGTERM:
+ * Catch and set a global bit.
+ */
+ act.sa_handler = h_hup;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ (void)sigaction(SIGHUP, &act, NULL);
+ act.sa_handler = h_term;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ (void)sigaction(SIGTERM, &act, NULL);
+ act.sa_handler = h_winch;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ (void)sigaction(SIGWINCH, &act, NULL);
+
+ /*
+ * SIGQUIT:
+ * Always ignore.
+ */
+ act.sa_handler = SIG_IGN;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ (void)sigaction(SIGQUIT, &act, NULL);
+
+ /*
+ * If there's an initial command, push it on the command stack.
+ * Historically, it was always an ex command, not vi in vi mode
+ * or ex in ex mode. So, make it look like an ex command to vi.
+ */
+ if (excmdarg != NULL)
+ if (IN_EX_MODE(sp)) {
+ if (term_push(sp, excmdarg, strlen(excmdarg), 0, 0))
+ goto err;
+ } else if (IN_VI_MODE(sp)) {
+ if (term_push(sp, "\n", 1, 0, 0))
+ goto err;
+ if (term_push(sp, excmdarg, strlen(excmdarg), 0, 0))
+ goto err;
+ if (term_push(sp, ":", 1, 0, 0))
+ goto err;
+ }
+
+ /* Vi reads from the terminal. */
+ if (!F_ISSET(gp, G_ISFROMTTY) && !F_ISSET(sp, S_EX)) {
+ msgq(sp, M_ERR, "Vi's standard input must be a terminal.");
+ goto err;
+ }
+
+ for (;;) {
+ if (sp->s_edit(sp, sp->ep))
+ goto err;
+
+ /*
+ * Edit the next screen on the display queue, or, move
+ * a screen from the hidden queue to the display queue.
+ */
+ if ((sp = __global_list->dq.cqh_first) ==
+ (void *)&__global_list->dq)
+ if ((sp = __global_list->hq.cqh_first) !=
+ (void *)&__global_list->hq) {
+ CIRCLEQ_REMOVE(&sp->gp->hq, sp, q);
+ CIRCLEQ_INSERT_TAIL(&sp->gp->dq, sp, q);
+ } else
+ break;
+
+ /*
+ * The screen type may have changed -- reinitialize the
+ * functions in case it has.
+ */
+ switch (F_ISSET(sp, S_SCREENS)) {
+ case S_EX:
+ if (sex_screen_init(sp))
+ goto err;
+ break;
+ case S_VI_CURSES:
+ if (svi_screen_init(sp))
+ goto err;
+ break;
+ case S_VI_XAW:
+ if (xaw_screen_init(sp))
+ goto err;
+ break;
+ default:
+ abort();
+ }
+ }
+
+ eval = 0;
+ if (0)
+err: eval = 1;
+
+ /*
+ * NOTE: sp may be GONE when the screen returns, so only
+ * the gp can be trusted.
+ */
+ gs_end(gp);
+
+ /*
+ * XXX
+ * Make absolutely sure that the modes are restored correctly.
+ *
+ * This should no longer be needed, and it's here to handle what I
+ * believe are SunOS/Solaris curses problems. The problem is that
+ * for some unknown reason, when endwin() is called in the svi
+ * routines, it isn't resetting the terminal correctly. I have not
+ * been able to figure it out, so this resets the terminal to the
+ * right modes regardless. The problem is that, in most tty driver
+ * implementations, you can only reset the terminal modes once
+ * (changing from !ICANON to ICANON) without losing the re-parsing
+ * effect on the pending input. This means that this "fix" will make
+ * other systems mess up characters typed after the quit command to
+ * vi but before vi actually exits.
+ */
+ if (F_ISSET(gp, G_ISFROMTTY))
+ (void)tcsetattr(STDIN_FILENO, TCSADRAIN, &gp->original_termios);
+ exit(eval);
+}
+
+/*
+ * gs_init --
+ * Build and initialize the GS structure.
+ */
+static GS *
+gs_init()
+{
+ GS *gp;
+ int fd;
+
+ CALLOC_NOMSG(NULL, gp, GS *, 1, sizeof(GS));
+ if (gp == NULL)
+ err(1, NULL);
+
+ CIRCLEQ_INIT(&gp->dq);
+ CIRCLEQ_INIT(&gp->hq);
+ LIST_INIT(&gp->msgq);
+
+ /* Structures shared by screens so stored in the GS structure. */
+ CALLOC_NOMSG(NULL, gp->tty, IBUF *, 1, sizeof(IBUF));
+ if (gp->tty == NULL)
+ err(1, NULL);
+
+ LIST_INIT(&gp->cutq);
+ LIST_INIT(&gp->seqq);
+
+ /* Set a flag if we're reading from the tty. */
+ if (isatty(STDIN_FILENO))
+ F_SET(gp, G_ISFROMTTY);
+
+ /*
+ * XXX
+ * Set a flag and don't do terminal sets/resets if the input isn't
+ * from a tty. Under all circumstances put reasonable things into
+ * the original_termios field, as some routines (seq.c:seq_save()
+ * and term.c:term_init()) want values for special characters.
+ */
+ if (F_ISSET(gp, G_ISFROMTTY)) {
+ if (tcgetattr(STDIN_FILENO, &gp->original_termios))
+ err(1, "tcgetattr");
+ } else {
+ if ((fd = open(_PATH_TTY, O_RDONLY, 0)) == -1)
+ err(1, "%s", _PATH_TTY);
+ if (tcgetattr(fd, &gp->original_termios))
+ err(1, "tcgetattr");
+ (void)close(fd);
+ }
+
+ return (gp);
+}
+
+
+/*
+ * gs_end --
+ * End the GS structure.
+ */
+static void
+gs_end(gp)
+ GS *gp;
+{
+ MSG *mp;
+ SCR *sp;
+ char *tty;
+
+ /* Reset anything that needs resetting. */
+ if (gp->flags & G_SETMODE) /* O_MESG */
+ if ((tty = ttyname(STDERR_FILENO)) == NULL)
+ warn("ttyname");
+ else if (chmod(tty, gp->origmode) < 0)
+ warn("%s", tty);
+
+ /* Ring the bell if scheduled. */
+ if (F_ISSET(gp, G_BELLSCHED))
+ (void)fprintf(stderr, "\07"); /* \a */
+
+ /* If there are any remaining screens, flush their messages. */
+ for (sp = __global_list->dq.cqh_first;
+ sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
+ for (mp = sp->msgq.lh_first;
+ mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next)
+ (void)fprintf(stderr, "%.*s\n", (int)mp->len, mp->mbuf);
+ for (sp = __global_list->hq.cqh_first;
+ sp != (void *)&__global_list->hq; sp = sp->q.cqe_next)
+ for (mp = sp->msgq.lh_first;
+ mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next)
+ (void)fprintf(stderr, "%.*s\n", (int)mp->len, mp->mbuf);
+ /* Flush messages on the global queue. */
+ for (mp = gp->msgq.lh_first;
+ mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next)
+ (void)fprintf(stderr, "%.*s\n", (int)mp->len, mp->mbuf);
+
+ if (gp->special_key != NULL)
+ FREE(gp->special_key, MAX_FAST_KEY);
+
+ /*
+ * DON'T FREE THE GLOBAL STRUCTURE -- WE DIDN'T TURN
+ * OFF SIGNALS/TIMERS, SO IT MAY STILL BE REFERENCED.
+ */
+}
+
+/*
+ * h_hup --
+ * Handle SIGHUP.
+ */
+static void
+h_hup(signo)
+ int signo;
+{
+ F_SET(__global_list, G_SIGHUP);
+
+ /*
+ * If we're asleep, just die.
+ *
+ * XXX
+ * This isn't right if the windows are independent.
+ */
+ if (F_ISSET(__global_list, G_SLEEPING))
+ rcv_hup();
+}
+
+/*
+ * h_term --
+ * Handle SIGTERM.
+ */
+static void
+h_term(signo)
+ int signo;
+{
+ F_SET(__global_list, G_SIGTERM);
+
+ /*
+ * If we're asleep, just die.
+ *
+ * XXX
+ * This isn't right if the windows are independent.
+ */
+ if (F_ISSET(__global_list, G_SLEEPING))
+ rcv_term();
+}
+
+/*
+ * h_winch --
+ * Handle SIGWINCH.
+ */
+static void
+h_winch(signo)
+ int signo;
+{
+ F_SET(__global_list, G_SIGWINCH);
+}
+
+/*
+ * exrc_isok --
+ * Check a .exrc for source-ability.
+ */
+static int
+exrc_isok(sp, path, rootok)
+ SCR *sp;
+ char *path;
+ int rootok;
+{
+ struct stat sb;
+ uid_t uid;
+ char *emsg, buf[MAXPATHLEN];
+
+ /* Check for the file's existence. */
+ if (stat(path, &sb))
+ return (0);
+
+ /*
+ * !!!
+ * Historically, vi did not read the .exrc files if they were owned
+ * by someone other than the user, unless the undocumented option
+ * sourceany was set. We don't support the sourceany option. We
+ * check that the user (or root, for system files) owns the file and
+ * require that it not be writeable by anyone other than the owner.
+ */
+
+ /* Owned by the user or root. */
+ uid = getuid();
+ if (rootok) {
+ if (sb.st_uid != uid && sb.st_uid != 0) {
+ emsg = "not owned by you or root";
+ goto err;
+ }
+ } else
+ if (sb.st_uid != uid) {
+ emsg = "not owned by you";
+ goto err;
+ }
+
+ /* Not writeable by anyone but the owner. */
+ if (sb.st_mode & (S_IWGRP | S_IWOTH)) {
+ emsg = "writeable by a user other than the owner";
+err: if (strchr(path, '/') == NULL &&
+ getcwd(buf, sizeof(buf)) != NULL)
+ msgq(sp, M_ERR,
+ "%s/%s: not sourced: %s.", buf, path, emsg);
+ else
+ msgq(sp, M_ERR,
+ "%s: not sourced: %s.", path, emsg);
+ return (0);
+ }
+ return (1);
+}
+
+static void
+obsolete(argv)
+ char *argv[];
+{
+ size_t len;
+ char *p, *myname;
+
+ /*
+ * Translate old style arguments into something getopt will like.
+ * Make sure it's not text space memory, because ex changes the
+ * strings.
+ * Change "+" into "-c$".
+ * Change "+<anything else>" into "-c<anything else>".
+ * Change "-" into "-s"
+ * Change "-r" into "-l"
+ */
+ for (myname = argv[0]; *++argv;)
+ if (argv[0][0] == '+') {
+ if (argv[0][1] == '\0') {
+ MALLOC_NOMSG(NULL, argv[0], char *, 4);
+ if (argv[0] == NULL)
+ err(1, NULL);
+ (void)strcpy(argv[0], "-c$");
+ } else {
+ p = argv[0];
+ len = strlen(argv[0]);
+ MALLOC_NOMSG(NULL, argv[0], char *, len + 2);
+ if (argv[0] == NULL)
+ err(1, NULL);
+ argv[0][0] = '-';
+ argv[0][1] = 'c';
+ (void)strcpy(argv[0] + 2, p + 1);
+ }
+ } else if (argv[0][0] == '-') {
+ if (argv[0][1] == 'r') {
+ if (argv[0][2] == '\0' && argv[1] == NULL)
+ argv[0][1] = 'l';
+ } else if (argv[0][1] == '\0') {
+ MALLOC_NOMSG(NULL, argv[0], char *, 3);
+ if (argv[0] == NULL)
+ err(1, NULL);
+ (void)strcpy(argv[0], "-s");
+ }
+ }
+}
+
+static void
+usage(is_ex)
+ int is_ex;
+{
+#define EX_USAGE \
+ "usage: ex [-eFlRsv] [-c command] [-r file] [-t tag] [-w size] [-x aw]"
+#define VI_USAGE \
+ "usage: vi [-eFlRv] [-c command] [-r file] [-t tag] [-w size] [-x aw]"
+
+ (void)fprintf(stderr, "%s\n", is_ex ? EX_USAGE : VI_USAGE);
+ exit(1);
+}
diff --git a/usr.bin/vi/mark.c b/usr.bin/vi/mark.c
new file mode 100644
index 000000000000..c510a22002fc
--- /dev/null
+++ b/usr.bin/vi/mark.c
@@ -0,0 +1,257 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)mark.c 8.12 (Berkeley) 12/27/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+
+static MARK *mark_find __P((SCR *, EXF *, ARG_CHAR_T));
+
+/*
+ * Marks are maintained in a key sorted doubly linked list. We can't
+ * use arrays because we have no idea how big an index key could be.
+ * The underlying assumption is that users don't have more than, say,
+ * 10 marks at any one time, so this will be is fast enough.
+ *
+ * Marks are fixed, and modifications to the line don't update the mark's
+ * position in the line. This can be hard. If you add text to the line,
+ * place a mark in that text, undo the addition and use ` to move to the
+ * mark, the location will have disappeared. It's tempting to try to adjust
+ * the mark with the changes in the line, but this is hard to do, especially
+ * if we've given the line to v_ntext.c:v_ntext() for editing. Historic vi
+ * would move to the first non-blank on the line when the mark location was
+ * past the end of the line. This can be complicated by deleting to a mark
+ * that has disappeared using the ` command. Historic vi vi treated this as
+ * a line-mode motion and deleted the line. This implementation complains to
+ * the user.
+ *
+ * In historic vi, marks returned if the operation was undone, unless the
+ * mark had been subsequently reset. Tricky. This is hard to start with,
+ * but in the presence of repeated undo it gets nasty. When a line is
+ * deleted, we delete (and log) any marks on that line. An undo will create
+ * the mark. Any mark creations are noted as to whether the user created
+ * it or if it was created by an undo. The former cannot be reset by another
+ * undo, but the latter may.
+ *
+ * All of these routines translate ABSMARK2 to ABSMARK1. Setting either of
+ * the absolute mark locations sets both, so that "m'" and "m`" work like
+ * they, ah, for lack of a better word, "should".
+ */
+
+/*
+ * mark_init --
+ * Set up the marks.
+ */
+int
+mark_init(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ MARK *mp;
+
+ /*
+ * Make sure the marks have been set up. If they
+ * haven't, do so, and create the absolute mark.
+ */
+ MALLOC_RET(sp, mp, MARK *, sizeof(MARK));
+ mp->lno = 1;
+ mp->cno = 0;
+ mp->name = ABSMARK1;
+ mp->flags = 0;
+ LIST_INSERT_HEAD(&ep->marks, mp, q);
+ return (0);
+}
+
+/*
+ * mark_end --
+ * Free up the marks.
+ */
+int
+mark_end(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ MARK *mp;
+
+ while ((mp = ep->marks.lh_first) != NULL) {
+ LIST_REMOVE(mp, q);
+ FREE(mp, sizeof(MARK));
+ }
+ return (0);
+}
+
+/*
+ * mark_get --
+ * Get the location referenced by a mark.
+ */
+MARK *
+mark_get(sp, ep, key)
+ SCR *sp;
+ EXF *ep;
+ ARG_CHAR_T key;
+{
+ MARK *mp;
+ size_t len;
+ char *p;
+
+ if (key == ABSMARK2)
+ key = ABSMARK1;
+
+ mp = mark_find(sp, ep, key);
+ if (mp == NULL || mp->name != key) {
+ msgq(sp, M_BERR, "Mark %s: not set.", charname(sp, key));
+ return (NULL);
+ }
+ if (F_ISSET(mp, MARK_DELETED)) {
+ msgq(sp, M_BERR,
+ "Mark %s: the line was deleted.", charname(sp, key));
+ return (NULL);
+ }
+ if ((p = file_gline(sp, ep, mp->lno, &len)) == NULL ||
+ mp->cno > len || mp->cno == len && len != 0) {
+ msgq(sp, M_BERR, "Mark %s: cursor position no longer exists.",
+ charname(sp, key));
+ return (NULL);
+ }
+ return (mp);
+}
+
+/*
+ * mark_set --
+ * Set the location referenced by a mark.
+ */
+int
+mark_set(sp, ep, key, value, userset)
+ SCR *sp;
+ EXF *ep;
+ ARG_CHAR_T key;
+ MARK *value;
+ int userset;
+{
+ MARK *mp, *mt;
+
+ if (key == ABSMARK2)
+ key = ABSMARK1;
+
+ /*
+ * The rules are simple. If the user is setting a mark (if it's a
+ * new mark this is always true), it always happens. If not, it's
+ * an undo, and we set it if it's not already set or if it was set
+ * by a previous undo.
+ */
+ mp = mark_find(sp, ep, key);
+ if (mp == NULL || mp->name != key) {
+ MALLOC_RET(sp, mt, MARK *, sizeof(MARK));
+ if (mp == NULL) {
+ LIST_INSERT_HEAD(&ep->marks, mt, q);
+ } else
+ LIST_INSERT_AFTER(mp, mt, q);
+ mp = mt;
+ } else if (!userset &&
+ !F_ISSET(mp, MARK_DELETED) && F_ISSET(mp, MARK_USERSET))
+ return (0);
+
+ mp->lno = value->lno;
+ mp->cno = value->cno;
+ mp->name = key;
+ mp->flags = userset ? MARK_USERSET : 0;
+ return (0);
+}
+
+/*
+ * mark_find --
+ * Find the requested mark, or, the slot immediately before
+ * where it would go.
+ */
+static MARK *
+mark_find(sp, ep, key)
+ SCR *sp;
+ EXF *ep;
+ ARG_CHAR_T key;
+{
+ MARK *mp, *lastmp;
+
+ /*
+ * Return the requested mark or the slot immediately before
+ * where it should go.
+ */
+ for (lastmp = NULL, mp = ep->marks.lh_first;
+ mp != NULL; lastmp = mp, mp = mp->q.le_next)
+ if (mp->name >= key)
+ return (mp->name == key ? mp : lastmp);
+ return (lastmp);
+}
+
+/*
+ * mark_insdel --
+ * Update the marks based on an insertion or deletion.
+ */
+void
+mark_insdel(sp, ep, op, lno)
+ SCR *sp;
+ EXF *ep;
+ enum operation op;
+ recno_t lno;
+{
+ MARK *mp;
+
+ switch (op) {
+ case LINE_APPEND:
+ return;
+ case LINE_DELETE:
+ for (mp = ep->marks.lh_first; mp != NULL; mp = mp->q.le_next)
+ if (mp->lno >= lno)
+ if (mp->lno == lno) {
+ F_SET(mp, MARK_DELETED);
+ (void)log_mark(sp, ep, mp);
+ } else
+ --mp->lno;
+ return;
+ case LINE_INSERT:
+ for (mp = ep->marks.lh_first; mp != NULL; mp = mp->q.le_next)
+ if (mp->lno >= lno)
+ ++mp->lno;
+ return;
+ case LINE_RESET:
+ return;
+ }
+ /* NOTREACHED */
+}
diff --git a/usr.bin/vi/mark.h b/usr.bin/vi/mark.h
new file mode 100644
index 000000000000..9c28151314b9
--- /dev/null
+++ b/usr.bin/vi/mark.h
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 1992, 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.
+ *
+ * @(#)mark.h 8.5 (Berkeley) 12/27/93
+ */
+
+/*
+ * The MARK structure defines a position in the file. Because of the different
+ * interfaces used by the db(3) package, curses, and users, the line number is
+ * 1 based, while the column number is 0 based. Additionally, it is known that
+ * the out-of-band line number is less than any legal line number. The line
+ * number is of type recno_t, as that's the underlying type of the database.
+ * The column number is of type size_t, guaranteeing that we can malloc a line.
+ */
+struct _mark {
+ LIST_ENTRY(_mark) q; /* Linked list of marks. */
+#define OOBLNO 0 /* Out-of-band line number. */
+ recno_t lno; /* Line number. */
+ size_t cno; /* Column number. */
+ CHAR_T name; /* Mark name. */
+
+#define MARK_DELETED 0x01 /* Mark was deleted. */
+#define MARK_USERSET 0x02 /* User set this mark. */
+ u_char flags;
+};
+
+#define ABSMARK1 '\'' /* Absolute mark name. */
+#define ABSMARK2 '`' /* Absolute mark name. */
+
+/* Mark routines. */
+int mark_end __P((SCR *, EXF *));
+MARK *mark_get __P((SCR *, EXF *, ARG_CHAR_T));
+int mark_init __P((SCR *, EXF *));
+void mark_insdel __P((SCR *, EXF *, enum operation, recno_t));
+int mark_set __P((SCR *, EXF *, ARG_CHAR_T, MARK *, int));
diff --git a/usr.bin/vi/mem.h b/usr.bin/vi/mem.h
new file mode 100644
index 000000000000..0f8fac4b8f39
--- /dev/null
+++ b/usr.bin/vi/mem.h
@@ -0,0 +1,166 @@
+/*-
+ * Copyright (c) 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.
+ *
+ * @(#)mem.h 8.2 (Berkeley) 12/19/93
+ */
+
+/* Increase the size of a malloc'd buffer. Two versions, one that
+ * returns, one that jumps to an error label.
+ */
+#define BINC_GOTO(sp, lp, llen, nlen) { \
+ if ((nlen) > llen && binc(sp, &(lp), &(llen), nlen)) \
+ goto binc_err; \
+}
+#define BINC_RET(sp, lp, llen, nlen) { \
+ if ((nlen) > llen && binc(sp, &(lp), &(llen), nlen)) \
+ return (1); \
+}
+
+/*
+ * Get some temporary space, preferably from the global temporary buffer,
+ * from a malloc'd buffer otherwise. Two versions, one that returns, one
+ * that jumps to an error label.
+ */
+#define GET_SPACE_GOTO(sp, bp, blen, nlen) { \
+ GS *__gp = (sp)->gp; \
+ if (F_ISSET(__gp, G_TMP_INUSE)) { \
+ bp = NULL; \
+ blen = 0; \
+ BINC_GOTO(sp, bp, blen, nlen); \
+ } else { \
+ BINC_GOTO(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \
+ bp = __gp->tmp_bp; \
+ blen = __gp->tmp_blen; \
+ F_SET(__gp, G_TMP_INUSE); \
+ } \
+}
+#define GET_SPACE_RET(sp, bp, blen, nlen) { \
+ GS *__gp = (sp)->gp; \
+ if (F_ISSET(__gp, G_TMP_INUSE)) { \
+ bp = NULL; \
+ blen = 0; \
+ BINC_RET(sp, bp, blen, nlen); \
+ } else { \
+ BINC_RET(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \
+ bp = __gp->tmp_bp; \
+ blen = __gp->tmp_blen; \
+ F_SET(__gp, G_TMP_INUSE); \
+ } \
+}
+
+/*
+ * Add space to a GET_SPACE returned buffer. Two versions, one that
+ * returns, one that jumps to an error label.
+ */
+#define ADD_SPACE_GOTO(sp, bp, blen, nlen) { \
+ GS *__gp = (sp)->gp; \
+ if (bp == __gp->tmp_bp) { \
+ F_CLR(__gp, G_TMP_INUSE); \
+ BINC_GOTO(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \
+ bp = __gp->tmp_bp; \
+ blen = __gp->tmp_blen; \
+ F_SET(__gp, G_TMP_INUSE); \
+ } else \
+ BINC_GOTO(sp, bp, blen, nlen); \
+}
+#define ADD_SPACE_RET(sp, bp, blen, nlen) { \
+ GS *__gp = (sp)->gp; \
+ if (bp == __gp->tmp_bp) { \
+ F_CLR(__gp, G_TMP_INUSE); \
+ BINC_RET(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \
+ bp = __gp->tmp_bp; \
+ blen = __gp->tmp_blen; \
+ F_SET(__gp, G_TMP_INUSE); \
+ } else \
+ BINC_RET(sp, bp, blen, nlen); \
+}
+
+/* Free memory, optionally making pointers unusable. */
+#ifdef DEBUG
+#define FREE(p, sz) { \
+ memset(p, 0xff, sz); \
+ free(p); \
+}
+#else
+#define FREE(p, sz) free(p);
+#endif
+
+/* Free a GET_SPACE returned buffer. */
+#define FREE_SPACE(sp, bp, blen) { \
+ if (bp == sp->gp->tmp_bp) \
+ F_CLR(sp->gp, G_TMP_INUSE); \
+ else \
+ FREE(bp, blen); \
+}
+
+/*
+ * Malloc a buffer, casting the return pointer. Various versions.
+ *
+ * !!!
+ * The cast should be unnecessary, malloc(3) and friends return void *'s,
+ * which is all we need. However, some systems that nvi needs to run on
+ * don't do it right yet, resulting in the compiler printing out roughly
+ * a million warnings. After awhile, it seemed easier to put the casts
+ * in instead of explaining it all the time.
+ */
+#define CALLOC_NOMSG(sp, p, cast, nmemb, size) { \
+ p = (cast)calloc(nmemb, size); \
+}
+#define CALLOC(sp, p, cast, nmemb, size) { \
+ if ((p = (cast)calloc(nmemb, size)) == NULL) \
+ msgq(sp, M_SYSERR, NULL); \
+}
+#define CALLOC_RET(sp, p, cast, nmemb, size) { \
+ if ((p = (cast)calloc(nmemb, size)) == NULL) { \
+ msgq(sp, M_SYSERR, NULL); \
+ return (1); \
+ } \
+}
+#define MALLOC_NOMSG(sp, p, cast, size) { \
+ p = (cast)malloc(size); \
+}
+#define MALLOC(sp, p, cast, size) { \
+ if ((p = (cast)malloc(size)) == NULL) \
+ msgq(sp, M_SYSERR, NULL); \
+}
+#define MALLOC_RET(sp, p, cast, size) { \
+ if ((p = (cast)malloc(size)) == NULL) { \
+ msgq(sp, M_SYSERR, NULL); \
+ return (1); \
+ } \
+}
+#define REALLOC(sp, p, cast, size) { \
+ if ((p = (cast)realloc(p, size)) == NULL) \
+ msgq(sp, M_SYSERR, NULL); \
+}
+
+int binc __P((SCR *, void *, size_t *, size_t));
diff --git a/usr.bin/vi/msg.h b/usr.bin/vi/msg.h
new file mode 100644
index 000000000000..6b20bb4bb63b
--- /dev/null
+++ b/usr.bin/vi/msg.h
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 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.
+ *
+ * @(#)msg.h 8.8 (Berkeley) 11/18/93
+ */
+
+/*
+ * M_BERR -- Error: ring a bell if O_VERBOSE not set, else
+ * display in inverse video.
+ * M_ERR -- Error: display in inverse video.
+ * M_INFO -- Info: display in normal video.
+ * M_SYSERR -- M_ERR, but use standard error message.
+ * M_VINFO -- Info: display only if O_VERBOSE set.
+ *
+ * In historical vi, O_VERBOSE didn't exist, and O_TERSE made the
+ * error messages shorter. In this version, O_TERSE has no effect
+ * and O_VERBOSE results in informational displays about common
+ * errors.
+ */
+enum msgtype { M_BERR, M_ERR, M_INFO, M_SYSERR, M_VINFO };
+
+typedef struct _msgh MSGH; /* MESG list head structure. */
+LIST_HEAD(_msgh, _msg);
+
+struct _msg {
+ LIST_ENTRY(_msg) q; /* Linked list of messages. */
+ char *mbuf; /* Message buffer. */
+ size_t blen; /* Message buffer length. */
+ size_t len; /* Message length. */
+
+#define M_EMPTY 0x01 /* No message. */
+#define M_INV_VIDEO 0x02 /* Inverse video. */
+ u_int flags; /* Flags. */
+};
+
+/* Messages. */
+void msg_app __P((GS *, SCR *, int, char *, size_t));
+int msg_rpt __P((SCR *, int));
+void msgq __P((SCR *, enum msgtype, const char *, ...));
diff --git a/usr.bin/vi/nex/ex.c b/usr.bin/vi/nex/ex.c
new file mode 100644
index 000000000000..3f5beac4a734
--- /dev/null
+++ b/usr.bin/vi/nex/ex.c
@@ -0,0 +1,1523 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex.c 8.90 (Berkeley) 1/23/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+static inline EXCMDLIST const *
+ ex_comm_search __P((char *, size_t));
+static int ep_line __P((SCR *, EXF *, MARK *, char **, size_t *, int *));
+static int ep_range __P((SCR *, EXF *, EXCMDARG *, char **, size_t *));
+
+#define DEFCOM ".+1"
+
+/*
+ * ex --
+ * Read an ex command and execute it.
+ */
+int
+ex(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ TEXT *tp;
+ u_int saved_mode;
+ int eval;
+ char defcom[sizeof(DEFCOM)];
+
+ if (ex_init(sp, ep))
+ return (1);
+
+ if (sp->s_refresh(sp, ep))
+ return (ex_end(sp));
+
+ /* If reading from a file, messages should have line info. */
+ if (!F_ISSET(sp->gp, G_ISFROMTTY)) {
+ sp->if_lno = 1;
+ sp->if_name = strdup("input");
+ }
+ for (eval = 0;; ++sp->if_lno) {
+ /* Get the next command. */
+ switch (sp->s_get(sp, ep, &sp->tiq, ':', TXT_CR | TXT_PROMPT)) {
+ case INP_OK:
+ break;
+ case INP_EOF:
+ F_SET(sp, S_EXIT_FORCE);
+ goto ret;
+ case INP_ERR:
+ continue;
+ }
+
+ saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE);
+ tp = sp->tiq.cqh_first;
+ if (tp->len == 0) {
+ if (F_ISSET(sp->gp, G_ISFROMTTY)) {
+ (void)fputc('\r', stdout);
+ (void)fflush(stdout);
+ }
+ memmove(defcom, DEFCOM, sizeof(DEFCOM));
+ (void)ex_icmd(sp, ep, defcom, sizeof(DEFCOM) - 1);
+ } else {
+ if (F_ISSET(sp->gp, G_ISFROMTTY))
+ (void)fputc('\n', stdout);
+ (void)ex_icmd(sp, ep, tp->lb, tp->len);
+ }
+ (void)msg_rpt(sp, 0);
+
+ if (saved_mode != F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE))
+ break;
+
+ if (sp->s_refresh(sp, ep)) {
+ eval = 1;
+ break;
+ }
+ }
+ret: if (sp->if_name != NULL) {
+ FREE(sp->if_name, strlen(sp->if_name) + 1);
+ sp->if_name = NULL;
+ }
+ return (ex_end(sp) || eval);
+}
+
+/*
+ * ex_cfile --
+ * Execute ex commands from a file.
+ */
+int
+ex_cfile(sp, ep, filename)
+ SCR *sp;
+ EXF *ep;
+ char *filename;
+{
+ struct stat sb;
+ int fd, len, rval;
+ char *bp;
+
+ bp = NULL;
+ if ((fd = open(filename, O_RDONLY, 0)) < 0 || fstat(fd, &sb))
+ goto err;
+
+ /*
+ * XXX
+ * We'd like to test if the file is too big to malloc. Since we don't
+ * know what size or type off_t's or size_t's are, what the largest
+ * unsigned integral type is, or what random insanity the local C
+ * compiler will perpetrate, doing the comparison in a portable way
+ * is flatly impossible. Hope that malloc fails if the file is too
+ * large.
+ */
+ MALLOC(sp, bp, char *, (size_t)sb.st_size + 1);
+ if (bp == NULL)
+ goto err;
+
+ len = read(fd, bp, (int)sb.st_size);
+ if (len == -1 || len != sb.st_size) {
+ if (len != sb.st_size)
+ errno = EIO;
+err: rval = 1;
+ msgq(sp, M_SYSERR, filename);
+ } else {
+ bp[sb.st_size] = '\0'; /* XXX */
+
+ /* Run the command. Messages include file/line information. */
+ sp->if_lno = 1;
+ sp->if_name = strdup(filename);
+ rval = ex_icmd(sp, ep, bp, len);
+ FREE(sp->if_name, strlen(sp->if_name) + 1);
+ sp->if_name = NULL;
+ }
+
+ /*
+ * !!!
+ * THE UNDERLYING EXF MAY HAVE CHANGED.
+ */
+ if (bp != NULL)
+ FREE(bp, sb.st_size);
+ if (fd >= 0)
+ (void)close(fd);
+ return (rval);
+}
+
+/*
+ * ex_icmd --
+ * Call ex_cmd() after turning off interruptible bits.
+ */
+int
+ex_icmd(sp, ep, cmd, len)
+ SCR *sp;
+ EXF *ep;
+ char *cmd;
+ size_t len;
+{
+ /*
+ * Ex goes through here for each vi :colon command and for each ex
+ * command, however, globally executed commands don't go through
+ * here, instead, they call ex_cmd directly. So, reset all of the
+ * interruptible flags now.
+ */
+ F_CLR(sp, S_INTERRUPTED | S_INTERRUPTIBLE);
+
+ return (ex_cmd(sp, ep, cmd, len));
+}
+
+/* Special command structure for :s as a repeat substitution command. */
+static EXCMDLIST const cmd_subagain =
+ {"s", ex_subagain, E_ADDR2|E_NORC,
+ "s",
+ "[line [,line]] s [cgr] [count] [#lp]",
+ "repeat the last subsitution"};
+
+/*
+ * ex_cmd --
+ * Parse and execute a string containing ex commands.
+ */
+int
+ex_cmd(sp, ep, cmd, cmdlen)
+ SCR *sp;
+ EXF *ep;
+ char *cmd;
+ size_t cmdlen;
+{
+ CHAR_T vlit;
+ EX_PRIVATE *exp;
+ EXCMDARG exc;
+ EXCMDLIST const *cp;
+ MARK cur;
+ recno_t lno, num;
+ size_t arg1_len, len, save_cmdlen;
+ long flagoff;
+ u_int saved_mode;
+ int ch, cnt, delim, flags, namelen, nl, uselastcmd, tmp;
+ char *arg1, *save_cmd, *p, *t;
+
+ /* Init. */
+ nl = 0;
+loop: if (nl) {
+ nl = 0;
+ ++sp->if_lno;
+ }
+ arg1 = NULL;
+ save_cmdlen = 0;
+
+ /*
+ * It's possible that we've been interrupted during a
+ * command.
+ */
+ if (F_ISSET(sp, S_INTERRUPTED))
+ return (0);
+
+ /* Skip whitespace, separators, newlines. */
+ for (; cmdlen > 0; ++cmd, --cmdlen)
+ if ((ch = *cmd) == '\n')
+ ++sp->if_lno;
+ else if (!isblank(ch))
+ break;
+ if (cmdlen == 0)
+ return (0);
+
+ /* Command lines that start with a double-quote are comments. */
+ if (ch == '"') {
+ while (--cmdlen > 0 && *++cmd != '\n');
+ if (*cmd == '\n') {
+ ++cmd;
+ --cmdlen;
+ ++sp->if_lno;
+ }
+ goto loop;
+ }
+
+ /*
+ * !!!
+ * Permit extra colons at the start of the line. Historically,
+ * ex/vi allowed a single extra one. It's simpler not to count.
+ * The stripping is done here because, historically, any command
+ * could have preceding colons, e.g. ":g/pattern/:p" worked.
+ */
+ if (ch == ':')
+ while (--cmdlen > 0 && *++cmd == ':');
+
+ /* Skip whitespace. */
+ for (; cmdlen > 0; ++cmd, --cmdlen) {
+ ch = *cmd;
+ if (!isblank(ch))
+ break;
+ }
+
+ /* The last point at which an empty line means do nothing. */
+ if (cmdlen == 0)
+ return (0);
+
+ /* Initialize the structure passed to underlying functions. */
+ memset(&exc, 0, sizeof(EXCMDARG));
+ exp = EXP(sp);
+ if (argv_init(sp, ep, &exc))
+ goto err;
+
+ /* Parse command addresses. */
+ if (ep_range(sp, ep, &exc, &cmd, &cmdlen))
+ goto err;
+
+ /* Skip whitespace. */
+ for (; cmdlen > 0; ++cmd, --cmdlen) {
+ ch = *cmd;
+ if (!isblank(ch))
+ break;
+ }
+
+ /*
+ * If no command, ex does the last specified of p, l, or #, and vi
+ * moves to the line. Otherwise, determine the length of the command
+ * name by looking for the first non-alphabetic character. (There
+ * are a few non-alphabetic characters in command names, but they're
+ * all single character commands.) This isn't a great test, because
+ * it means that, for the command ":e +cut.c file", we'll report that
+ * the command "cut" wasn't known. However, it makes ":e+35 file" work
+ * correctly.
+ */
+#define SINGLE_CHAR_COMMANDS "!#&<=>@~"
+ if (cmdlen != 0 && cmd[0] != '|' && cmd[0] != '\n') {
+ if (strchr(SINGLE_CHAR_COMMANDS, *cmd)) {
+ p = cmd;
+ ++cmd;
+ --cmdlen;
+ namelen = 1;
+ } else {
+ for (p = cmd; cmdlen > 0; --cmdlen, ++cmd)
+ if (!isalpha(*cmd))
+ break;
+ if ((namelen = cmd - p) == 0) {
+ msgq(sp, M_ERR, "Unknown command name.");
+ goto err;
+ }
+ }
+
+ /*
+ * Search the table for the command.
+ *
+ * !!!
+ * Historic vi permitted the mark to immediately follow the
+ * 'k' in the 'k' command. Make it work.
+ *
+ * !!!
+ * Historic vi permitted pretty much anything to follow the
+ * substitute command, e.g. "s/e/E/|s|sgc3p" was fine. Make
+ * it work.
+ *
+ * Use of msgq below is safe, command names are all alphabetics.
+ */
+ if ((cp = ex_comm_search(p, namelen)) == NULL)
+ if (p[0] == 'k' && p[1] && !p[2]) {
+ cmd -= namelen - 1;
+ cmdlen += namelen - 1;
+ cp = &cmds[C_K];
+ } else if (p[0] == 's') {
+ cmd -= namelen - 1;
+ cmdlen += namelen - 1;
+ cp = &cmd_subagain;
+ } else {
+ msgq(sp, M_ERR,
+ "The %.*s command is unknown.", namelen, p);
+ goto err;
+ }
+
+ /* Some commands are either not implemented or turned off. */
+ if (F_ISSET(cp, E_NOPERM)) {
+ msgq(sp, M_ERR,
+ "The %s command is not currently supported.",
+ cp->name);
+ goto err;
+ }
+
+ /* Some commands aren't okay in globals. */
+ if (F_ISSET(sp, S_GLOBAL) && F_ISSET(cp, E_NOGLOBAL)) {
+ msgq(sp, M_ERR,
+ "The %s command can't be used as part of a global command.",
+ cp->name);
+ goto err;
+ }
+
+ /*
+ * Multiple < and > characters; another "feature". Note,
+ * The string passed to the underlying function may not be
+ * nul terminated in this case.
+ */
+ if ((cp == &cmds[C_SHIFTL] && *p == '<') ||
+ (cp == &cmds[C_SHIFTR] && *p == '>')) {
+ for (ch = *p; cmdlen > 0; --cmdlen, ++cmd)
+ if (*cmd != ch)
+ break;
+ if (argv_exp0(sp, ep, &exc, p, cmd - p))
+ goto err;
+ }
+
+ /*
+ * The visual command has a different syntax when called
+ * from ex than when called from a vi colon command. FMH.
+ */
+ if (cp == &cmds[C_VISUAL_EX] && IN_VI_MODE(sp))
+ cp = &cmds[C_VISUAL_VI];
+
+ uselastcmd = 0;
+ } else {
+ cp = exp->lastcmd;
+ uselastcmd = 1;
+ }
+
+ /* Initialize local flags to the command flags. */
+ LF_INIT(cp->flags);
+
+ /*
+ * File state must be checked throughout this code, because it is
+ * called when reading the .exrc file and similar things. There's
+ * this little chicken and egg problem -- if we read the file first,
+ * we won't know how to display it. If we read/set the exrc stuff
+ * first, we can't allow any command that requires file state.
+ * Historic vi generally took the easy way out and dropped core.
+ */
+ if (LF_ISSET(E_NORC) && ep == NULL) {
+ msgq(sp, M_ERR,
+ "The %s command requires that a file already have been read in.",
+ cp->name);
+ goto err;
+ }
+
+ /*
+ * There are three normal termination cases for an ex command. They
+ * are the end of the string (cmdlen), or unescaped (by literal next
+ * characters) newline or '|' characters. As we're past any addresses,
+ * we can now determine how long the command is, so we don't have to
+ * look for all the possible terminations. There are three exciting
+ * special cases:
+ *
+ * 1: The bang, global, vglobal and the filter versions of the read and
+ * write commands are delimited by newlines (they can contain shell
+ * pipes).
+ * 2: The ex, edit and visual in vi mode commands take ex commands as
+ * their first arguments.
+ * 3: The substitute command takes an RE as its first argument, and
+ * wants it to be specially delimited.
+ *
+ * Historically, '|' characters in the first argument of the ex, edit,
+ * and substitute commands did not delimit the command. And, in the
+ * filter cases for read and write, and the bang, global and vglobal
+ * commands, they did not delimit the command at all.
+ *
+ * For example, the following commands were legal:
+ *
+ * :edit +25|s/abc/ABC/ file.c
+ * :substitute s/|/PIPE/
+ * :read !spell % | columnate
+ * :global/pattern/p|l
+ *
+ * It's not quite as simple as it sounds, however. The command:
+ *
+ * :substitute s/a/b/|s/c/d|set
+ *
+ * was also legal, i.e. the historic ex parser (using the word loosely,
+ * since "parser" implies some regularity) delimited the RE's based on
+ * its delimiter and not anything so irretrievably vulgar as a command
+ * syntax.
+ *
+ * One thing that makes this easier is that we can ignore most of the
+ * command termination conditions for the commands that want to take
+ * the command up to the next newline. None of them are legal in .exrc
+ * files, so if we're here, we only dealing with a single line, and we
+ * can just eat it.
+ *
+ * Anyhow, the following code makes this all work. First, for the
+ * special cases we move past their special argument. Then, we do
+ * normal command processing on whatever is left. Barf-O-Rama.
+ */
+ arg1_len = 0;
+ save_cmd = cmd;
+ (void)term_key_ch(sp, K_VLNEXT, &vlit);
+ if (cp == &cmds[C_EDIT] ||
+ cp == &cmds[C_EX] || cp == &cmds[C_VISUAL_VI]) {
+ /*
+ * Move to the next non-whitespace character. As '+' must
+ * be the character after the command name, if there isn't
+ * one, we're done.
+ */
+ for (; cmdlen > 0; --cmdlen, ++cmd) {
+ ch = *cmd;
+ if (!isblank(ch))
+ break;
+ }
+ /*
+ * QUOTING NOTE:
+ *
+ * The historic implementation ignored all escape characters
+ * so there was no way to put a space or newline into the +cmd
+ * field. We do a simplistic job of fixing it by moving to the
+ * first whitespace character that isn't escaped by a literal
+ * next character. The literal next characters are stripped
+ * as they're no longer useful.
+ */
+ if (cmdlen > 0 && ch == '+') {
+ ++cmd;
+ --cmdlen;
+ for (arg1 = p = cmd; cmdlen > 0; --cmdlen, ++cmd) {
+ if ((ch = *cmd) == vlit && cmdlen > 1) {
+ --cmdlen;
+ ch = *++cmd;
+ } else if (isblank(ch))
+ break;
+ *p++ = ch;
+ }
+ arg1_len = cmd - arg1;
+
+ /* Reset, so the first argument isn't reparsed. */
+ save_cmd = cmd;
+ }
+ } else if (cp == &cmds[C_BANG] ||
+ cp == &cmds[C_GLOBAL] || cp == &cmds[C_VGLOBAL]) {
+ cmd += cmdlen;
+ cmdlen = 0;
+ } else if (cp == &cmds[C_READ] || cp == &cmds[C_WRITE]) {
+ /*
+ * Move to the next character. If it's a '!', it's a filter
+ * command and we want to eat it all, otherwise, we're done.
+ */
+ for (; cmdlen > 0; --cmdlen, ++cmd) {
+ ch = *cmd;
+ if (!isblank(ch))
+ break;
+ }
+ if (cmdlen > 0 && ch == '!') {
+ cmd += cmdlen;
+ cmdlen = 0;
+ }
+ } else if (cp == &cmds[C_SUBSTITUTE]) {
+ /*
+ * Move to the next non-whitespace character, we'll use it as
+ * the delimiter. If the character isn't an alphanumeric or
+ * a '|', it's the delimiter, so parse it. Otherwise, we're
+ * into something like ":s g", so use the special substitute
+ * command.
+ */
+ for (; cmdlen > 0; --cmdlen, ++cmd)
+ if (!isblank(cmd[0]))
+ break;
+
+ if (isalnum(cmd[0]) || cmd[0] == '|')
+ cp = &cmd_subagain;
+ else if (cmdlen > 0) {
+ /*
+ * QUOTING NOTE:
+ *
+ * Backslashes quote delimiter characters for RE's.
+ * The backslashes are NOT removed since they'll be
+ * used by the RE code. Move to the third delimiter
+ * that's not escaped (or the end of the command).
+ */
+ delim = *cmd;
+ ++cmd;
+ --cmdlen;
+ for (cnt = 2; cmdlen > 0 && cnt; --cmdlen, ++cmd)
+ if (cmd[0] == '\\' && cmdlen > 1) {
+ ++cmd;
+ --cmdlen;
+ } else if (cmd[0] == delim)
+ --cnt;
+ }
+ }
+ /*
+ * Use normal quoting and termination rules to find the end
+ * of this command.
+ *
+ * QUOTING NOTE:
+ *
+ * Historically, vi permitted ^V's to escape <newline>'s in the .exrc
+ * file. It was almost certainly a bug, but that's what bug-for-bug
+ * compatibility means, Grasshopper. Also, ^V's escape the command
+ * delimiters. Literal next quote characters in front of the newlines,
+ * '|' characters or literal next characters are stripped as as they're
+ * no longer useful.
+ */
+ for (p = cmd, cnt = 0; cmdlen > 0; --cmdlen, ++cmd) {
+ if ((ch = cmd[0]) == vlit && cmdlen > 1) {
+ ch = cmd[1];
+ if (ch == '\n' || ch == '|') {
+ if (ch == '\n')
+ ++sp->if_lno;
+ --cmdlen;
+ ++cmd;
+ ++cnt;
+ } else
+ ch = vlit;
+ } else if (ch == '\n' || ch == '|') {
+ if (ch == '\n')
+ nl = 1;
+ --cmdlen;
+ break;
+ }
+ *p++ = ch;
+ }
+
+ /*
+ * Save off the next command information, go back to the
+ * original start of the command.
+ */
+ p = cmd + 1;
+ cmd = save_cmd;
+ save_cmd = p;
+ save_cmdlen = cmdlen;
+ cmdlen = ((save_cmd - cmd) - 1) - cnt;
+
+ /*
+ * !!!
+ * The "set tags" command historically used a backslash, not the
+ * user's literal next character, to escape whitespace. Handle
+ * it here instead of complicating the argv_exp3() code. Note,
+ * this isn't a particularly complex trap, and if backslashes were
+ * legal in set commands, this would have to be much more complicated.
+ */
+ if (cp == &cmds[C_SET])
+ for (p = cmd, len = cmdlen; len > 0; --len, ++p)
+ if (*p == '\\')
+ *p = vlit;
+
+ /*
+ * Set the default addresses. It's an error to specify an address for
+ * a command that doesn't take them. If two addresses are specified
+ * for a command that only takes one, lose the first one. Two special
+ * cases here, some commands take 0 or 2 addresses. For most of them
+ * (the E_ADDR2_ALL flag), 0 defaults to the entire file. For one
+ * (the `!' command, the E_ADDR2_NONE flag), 0 defaults to no lines.
+ *
+ * Also, if the file is empty, some commands want to use an address of
+ * 0, i.e. the entire file is 0 to 0, and the default first address is
+ * 0. Otherwise, an entire file is 1 to N and the default line is 1.
+ * Note, we also add the E_ZERO flag to the command flags, for the case
+ * where the 0 address is only valid if it's a default address.
+ *
+ * Also, set a flag if we set the default addresses. Some commands
+ * (ex: z) care if the user specified an address of if we just used
+ * the current cursor.
+ */
+ switch (LF_ISSET(E_ADDR1|E_ADDR2|E_ADDR2_ALL|E_ADDR2_NONE)) {
+ case E_ADDR1: /* One address: */
+ switch (exc.addrcnt) {
+ case 0: /* Default cursor/empty file. */
+ exc.addrcnt = 1;
+ F_SET(&exc, E_ADDRDEF);
+ if (LF_ISSET(E_ZERODEF)) {
+ if (file_lline(sp, ep, &lno))
+ goto err;
+ if (lno == 0) {
+ exc.addr1.lno = 0;
+ LF_SET(E_ZERO);
+ } else
+ exc.addr1.lno = sp->lno;
+ } else
+ exc.addr1.lno = sp->lno;
+ exc.addr1.cno = sp->cno;
+ break;
+ case 1:
+ break;
+ case 2: /* Lose the first address. */
+ exc.addrcnt = 1;
+ exc.addr1 = exc.addr2;
+ }
+ break;
+ case E_ADDR2_NONE: /* Zero/two addresses: */
+ if (exc.addrcnt == 0) /* Default to nothing. */
+ break;
+ goto two;
+ case E_ADDR2_ALL: /* Zero/two addresses: */
+ if (exc.addrcnt == 0) { /* Default entire/empty file. */
+ exc.addrcnt = 2;
+ F_SET(&exc, E_ADDRDEF);
+ if (file_lline(sp, ep, &exc.addr2.lno))
+ goto err;
+ if (LF_ISSET(E_ZERODEF) && exc.addr2.lno == 0) {
+ exc.addr1.lno = 0;
+ LF_SET(E_ZERO);
+ } else
+ exc.addr1.lno = 1;
+ exc.addr1.cno = exc.addr2.cno = 0;
+ F_SET(&exc, E_ADDR2_ALL);
+ break;
+ }
+ /* FALLTHROUGH */
+ case E_ADDR2: /* Two addresses: */
+two: switch (exc.addrcnt) {
+ case 0: /* Default cursor/empty file. */
+ exc.addrcnt = 2;
+ F_SET(&exc, E_ADDRDEF);
+ if (LF_ISSET(E_ZERODEF) && sp->lno == 1) {
+ if (file_lline(sp, ep, &lno))
+ goto err;
+ if (lno == 0) {
+ exc.addr1.lno = exc.addr2.lno = 0;
+ LF_SET(E_ZERO);
+ } else
+ exc.addr1.lno = exc.addr2.lno = sp->lno;
+ } else
+ exc.addr1.lno = exc.addr2.lno = sp->lno;
+ exc.addr1.cno = exc.addr2.cno = sp->cno;
+ break;
+ case 1: /* Default to first address. */
+ exc.addrcnt = 2;
+ exc.addr2 = exc.addr1;
+ break;
+ case 2:
+ break;
+ }
+ break;
+ default:
+ if (exc.addrcnt) /* Error. */
+ goto usage;
+ }
+
+ flagoff = 0;
+ for (p = cp->syntax; *p != '\0'; ++p) {
+ /*
+ * The write command is sensitive to leading whitespace, e.g.
+ * "write !" is different from "write!". If not the write
+ * command, skip leading whitespace.
+ */
+ if (cp != &cmds[C_WRITE])
+ for (; cmdlen > 0; --cmdlen, ++cmd) {
+ ch = *cmd;
+ if (!isblank(ch))
+ break;
+ }
+
+ /*
+ * Quit when reach the end of the command, unless it's a
+ * command that does its own parsing, in which case we want
+ * to build a reasonable argv for it. This code guarantees
+ * that there will be an argv when the function gets called,
+ * so the correct test is for a length of 0, not for the
+ * argc > 0.
+ */
+ if (cmdlen == 0 && *p != '!' && *p != 'S' && *p != 's')
+ break;
+
+ switch (*p) {
+ case '!': /* ! */
+ if (*cmd == '!') {
+ ++cmd;
+ --cmdlen;
+ F_SET(&exc, E_FORCE);
+ }
+ break;
+ case '1': /* +, -, #, l, p */
+ /*
+ * !!!
+ * Historically, some flags were ignored depending
+ * on where they occurred in the command line. For
+ * example, in the command, ":3+++p--#", historic vi
+ * acted on the '#' flag, but ignored the '-' flags.
+ * It's unambiguous what the flags mean, so we just
+ * handle them regardless of the stupidity of their
+ * location.
+ */
+ for (; cmdlen; --cmdlen, ++cmd)
+ switch (*cmd) {
+ case '+':
+ ++flagoff;
+ break;
+ case '-':
+ --flagoff;
+ break;
+ case '#':
+ F_SET(&exc, E_F_HASH);
+ break;
+ case 'l':
+ F_SET(&exc, E_F_LIST);
+ break;
+ case 'p':
+ F_SET(&exc, E_F_PRINT);
+ break;
+ default:
+ goto end1;
+ }
+end1: break;
+ case '2': /* -, ., +, ^ */
+ case '3': /* -, ., +, ^, = */
+ for (; cmdlen; --cmdlen, ++cmd)
+ switch (*cmd) {
+ case '-':
+ F_SET(&exc, E_F_DASH);
+ break;
+ case '.':
+ F_SET(&exc, E_F_DOT);
+ break;
+ case '+':
+ F_SET(&exc, E_F_PLUS);
+ break;
+ case '^':
+ F_SET(&exc, E_F_CARAT);
+ break;
+ case '=':
+ if (*p == '3') {
+ F_SET(&exc, E_F_EQUAL);
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ goto end2;
+ }
+end2: break;
+ case 'b': /* buffer */
+ /*
+ * Digits can't be buffer names in ex commands, or the
+ * command "d2" would be a delete into buffer '2', and
+ * not a two-line deletion.
+ */
+ if (!isdigit(cmd[0])) {
+ exc.buffer = *cmd;
+ ++cmd;
+ --cmdlen;
+ F_SET(&exc, E_BUFFER);
+ }
+ break;
+ case 'c': /* count [01+a] */
+ ++p;
+ if (!isdigit(*cmd) &&
+ (*p != '+' || (*cmd != '+' && *cmd != '-')))
+ break;
+/* 8-bit XXX */ if ((lno = strtol(cmd, &t, 10)) == 0 && *p != '0') {
+ msgq(sp, M_ERR, "Count may not be zero.");
+ goto err;
+ }
+ cmdlen -= (t - cmd);
+ cmd = t;
+ /*
+ * Count as address offsets occur in commands taking
+ * two addresses. Historic vi practice was to use
+ * the count as an offset from the *second* address.
+ *
+ * Set a count flag; some underlying commands (see
+ * join) do different things with counts than with
+ * line addresses.
+ */
+ if (*p == 'a') {
+ exc.addr1 = exc.addr2;
+ exc.addr2.lno = exc.addr1.lno + lno - 1;
+ } else
+ exc.count = lno;
+ F_SET(&exc, E_COUNT);
+ break;
+ case 'f': /* file */
+ if (argv_exp2(sp, ep,
+ &exc, cmd, cmdlen, cp == &cmds[C_BANG]))
+ goto err;
+ goto countchk;
+ case 'l': /* line */
+ if (ep_line(sp, ep, &cur, &cmd, &cmdlen, &tmp))
+ goto err;
+ /* Line specifications are always required. */
+ if (!tmp) {
+ msgq(sp, M_ERR,
+ "%s: bad line specification", cmd);
+ goto err;
+ }
+ exc.lineno = cur.lno;
+ break;
+ case 'S': /* string, file exp. */
+ if (argv_exp1(sp, ep,
+ &exc, cmd, cmdlen, cp == &cmds[C_BANG]))
+ goto err;
+ goto addr2;
+ case 's': /* string */
+ if (argv_exp0(sp, ep, &exc, cmd, cmdlen))
+ goto err;
+ goto addr2;
+ case 'W': /* word string */
+ /*
+ * QUOTING NOTE:
+ *
+ * Literal next characters escape the following
+ * character. Quoting characters are stripped
+ * here since they are no longer useful.
+ *
+ * First there was the word.
+ */
+ for (p = t = cmd; cmdlen > 0; --cmdlen, ++cmd) {
+ if ((ch = *cmd) == vlit && cmdlen > 1) {
+ --cmdlen;
+ *p++ = *++cmd;
+ } else if (isblank(ch)) {
+ ++cmd;
+ --cmdlen;
+ break;
+ } else
+ *p++ = ch;
+ }
+ if (argv_exp0(sp, ep, &exc, t, p - t))
+ goto err;
+
+ /* Delete intervening whitespace. */
+ for (; cmdlen > 0; --cmdlen, ++cmd) {
+ ch = *cmd;
+ if (!isblank(ch))
+ break;
+ }
+ if (cmdlen == 0)
+ goto usage;
+
+ /* Followed by the string. */
+ for (p = t = cmd; cmdlen > 0; --cmdlen, ++cmd, ++p)
+ if ((ch = *cmd) == vlit && cmdlen > 1) {
+ --cmdlen;
+ *p = *++cmd;
+ } else
+ *p = ch;
+ if (argv_exp0(sp, ep, &exc, t, p - t))
+ goto err;
+ goto addr2;
+ case 'w': /* word */
+ if (argv_exp3(sp, ep, &exc, cmd, cmdlen))
+ goto err;
+countchk: if (*++p != 'N') { /* N */
+ /*
+ * If a number is specified, must either be
+ * 0 or that number, if optional, and that
+ * number, if required.
+ */
+ num = *p - '0';
+ if ((*++p != 'o' || exp->argsoff != 0) &&
+ exp->argsoff != num)
+ goto usage;
+ }
+ goto addr2;
+ default:
+ msgq(sp, M_ERR,
+ "Internal syntax table error (%s: %c).",
+ cp->name, *p);
+ }
+ }
+
+ /* Skip trailing whitespace. */
+ for (; cmdlen; --cmdlen) {
+ ch = *cmd++;
+ if (!isblank(ch))
+ break;
+ }
+
+ /*
+ * There shouldn't be anything left, and no more required
+ * fields, i.e neither 'l' or 'r' in the syntax string.
+ */
+ if (cmdlen || strpbrk(p, "lr")) {
+usage: msgq(sp, M_ERR, "Usage: %s.", cp->usage);
+ goto err;
+ }
+
+ /* Verify that the addresses are legal. */
+addr2: switch (exc.addrcnt) {
+ case 2:
+ if (file_lline(sp, ep, &lno))
+ goto err;
+ /*
+ * Historic ex/vi permitted commands with counts to go past
+ * EOF. So, for example, if the file only had 5 lines, the
+ * ex command "1,6>" would fail, but the command ">300"
+ * would succeed. Since we don't want to have to make all
+ * of the underlying commands handle random line numbers,
+ * fix it here.
+ */
+ if (exc.addr2.lno > lno)
+ if (F_ISSET(&exc, E_COUNT))
+ exc.addr2.lno = lno;
+ else {
+ if (lno == 0)
+ msgq(sp, M_ERR, "The file is empty.");
+ else
+ msgq(sp, M_ERR,
+ "Only %lu line%s in the file",
+ lno, lno > 1 ? "s" : "");
+ goto err;
+ }
+ /* FALLTHROUGH */
+ case 1:
+ num = exc.addr1.lno;
+ /*
+ * If it's a "default vi command", zero is okay. Historic
+ * vi allowed this, note, it's also the hack that allows
+ * "vi + nonexistent_file" to work.
+ */
+ if (num == 0 && (!IN_VI_MODE(sp) || uselastcmd != 1) &&
+ !LF_ISSET(E_ZERO)) {
+ msgq(sp, M_ERR,
+ "The %s command doesn't permit an address of 0.",
+ cp->name);
+ goto err;
+ }
+ if (file_lline(sp, ep, &lno))
+ goto err;
+ if (num > lno) {
+ if (lno == 0)
+ msgq(sp, M_ERR, "The file is empty.");
+ else
+ msgq(sp, M_ERR, "Only %lu line%s in the file",
+ lno, lno > 1 ? "s" : "");
+ goto err;
+ }
+ break;
+ }
+
+ /* If doing a default command, vi just moves to the line. */
+ if (IN_VI_MODE(sp) && uselastcmd) {
+ switch (exc.addrcnt) {
+ case 2:
+ sp->lno = exc.addr2.lno ? exc.addr2.lno : 1;
+ sp->cno = exc.addr2.cno;
+ break;
+ case 1:
+ sp->lno = exc.addr1.lno ? exc.addr1.lno : 1;
+ sp->cno = exc.addr1.cno;
+ break;
+ }
+ cmd = save_cmd;
+ cmdlen = save_cmdlen;
+ goto loop;
+ }
+
+ /* Reset "last" command. */
+ if (LF_ISSET(E_SETLAST))
+ exp->lastcmd = cp;
+
+ /* Final setup for the command. */
+ exc.cmd = cp;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "ex_cmd: %s", exc.cmd->name);
+ if (exc.addrcnt > 0) {
+ TRACE(sp, "\taddr1 %d", exc.addr1.lno);
+ if (exc.addrcnt > 1)
+ TRACE(sp, " addr2: %d", exc.addr2.lno);
+ TRACE(sp, "\n");
+ }
+ if (exc.lineno)
+ TRACE(sp, "\tlineno %d", exc.lineno);
+ if (exc.flags)
+ TRACE(sp, "\tflags %0x", exc.flags);
+ if (F_ISSET(&exc, E_BUFFER))
+ TRACE(sp, "\tbuffer %c", exc.buffer);
+ TRACE(sp, "\n");
+ if (exc.argc) {
+ for (cnt = 0; cnt < exc.argc; ++cnt)
+ TRACE(sp, "\targ %d: {%s}", cnt, exc.argv[cnt]);
+ TRACE(sp, "\n");
+ }
+#endif
+ /* Clear autoprint flag. */
+ F_CLR(exp, EX_AUTOPRINT);
+
+ /* Increment the command count if not called from vi. */
+ if (!IN_VI_MODE(sp))
+ ++sp->ccnt;
+
+ /*
+ * If file state and not doing a global command, log the start of
+ * an action.
+ */
+ if (ep != NULL && !F_ISSET(sp, S_GLOBAL))
+ (void)log_cursor(sp, ep);
+
+ /* Save the current mode. */
+ saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE);
+
+ /* Do the command. */
+ if ((cp->fn)(sp, ep, &exc))
+ goto err;
+
+#ifdef DEBUG
+ /* Make sure no function left the temporary space locked. */
+ if (F_ISSET(sp->gp, G_TMP_INUSE)) {
+ F_CLR(sp->gp, G_TMP_INUSE);
+ msgq(sp, M_ERR, "Error: ex: temporary buffer not released.");
+ goto err;
+ }
+#endif
+ if (saved_mode != F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE)) {
+ /*
+ * Only here if the mode of the underlying file changed, e.g.
+ * the user switched files or is exiting. There are two things
+ * that we might have to save. First, any "+cmd" field set up
+ * for an ex/edit command will have to be saved for later, also,
+ * any not yet executed part of the current ex command.
+ *
+ * :edit +25 file.c|s/abc/ABC/|1
+ *
+ * for example.
+ *
+ * The historic vi just hung, of course; we handle by
+ * pushing the keys onto the tty queue. If we're
+ * switching modes to vi, since the commands are intended
+ * as ex commands, add the extra characters to make it
+ * work.
+ *
+ * For the fun of it, if you want to see if a vi clone got
+ * the ex argument parsing right, try:
+ *
+ * echo 'foo|bar' > file1; echo 'foo/bar' > file2;
+ * vi
+ * :edit +1|s/|/PIPE/|w file1| e file2|1 | s/\//SLASH/|wq
+ */
+ if (arg1_len == NULL && save_cmdlen == 0)
+ return (0);
+ if (IN_VI_MODE(sp) && term_push(sp, "\n", 1, 0, 0))
+ goto err;
+ if (save_cmdlen != 0)
+ if (term_push(sp, save_cmd, save_cmdlen, 0, 0))
+ goto err;
+ if (arg1 != NULL) {
+ if (IN_VI_MODE(sp) && save_cmdlen != 0 &&
+ term_push(sp, "|", 1, 0, 0))
+ goto err;
+ if (term_push(sp, arg1, arg1_len, 0, 0))
+ goto err;
+ }
+ if (IN_VI_MODE(sp) && term_push(sp, ":", 1, 0, 0))
+ goto err;
+ return (0);
+ }
+
+ if (IN_EX_MODE(sp) && ep != NULL) {
+ /*
+ * The print commands have already handled the `print' flags.
+ * If so, clear them. Don't return, autoprint may still have
+ * stuff to print out.
+ */
+ if (LF_ISSET(E_F_PRCLEAR))
+ F_CLR(&exc, E_F_HASH | E_F_LIST | E_F_PRINT);
+
+ /*
+ * If the command was successful, and there was an explicit
+ * flag to display the new cursor line, or we're in ex mode,
+ * autoprint is set, and a change was made, display the line.
+ */
+ if (flagoff) {
+ if (flagoff < 0) {
+ if (sp->lno < -flagoff) {
+ msgq(sp, M_ERR,
+ "Flag offset before line 1.");
+ goto err;
+ }
+ } else {
+ if (file_lline(sp, ep, &lno))
+ goto err;
+ if (sp->lno + flagoff > lno) {
+ msgq(sp, M_ERR,
+ "Flag offset past end-of-file.");
+ goto err;
+ }
+ }
+ sp->lno += flagoff;
+ }
+
+ if (O_ISSET(sp, O_AUTOPRINT) &&
+ (F_ISSET(exp, EX_AUTOPRINT) || F_ISSET(cp, E_AUTOPRINT)))
+ LF_INIT(E_F_PRINT);
+ else
+ LF_INIT(F_ISSET(&exc, E_F_HASH | E_F_LIST | E_F_PRINT));
+
+ memset(&exc, 0, sizeof(EXCMDARG));
+ exc.addrcnt = 2;
+ exc.addr1.lno = exc.addr2.lno = sp->lno;
+ exc.addr1.cno = exc.addr2.cno = sp->cno;
+ switch (LF_ISSET(E_F_HASH | E_F_LIST | E_F_PRINT)) {
+ case E_F_HASH:
+ exc.cmd = &cmds[C_HASH];
+ ex_number(sp, ep, &exc);
+ break;
+ case E_F_LIST:
+ exc.cmd = &cmds[C_LIST];
+ ex_list(sp, ep, &exc);
+ break;
+ case E_F_PRINT:
+ exc.cmd = &cmds[C_PRINT];
+ ex_pr(sp, ep, &exc);
+ break;
+ }
+ }
+
+ cmd = save_cmd;
+ cmdlen = save_cmdlen;
+ goto loop;
+ /* NOTREACHED */
+
+ /*
+ * On error, we discard any keys we have left, as well as any keys
+ * that were mapped. The test of save_cmdlen isn't necessarily
+ * correct. If we fail early enough we don't know if the entire
+ * string was a single command or not. Try and guess, it's useful
+ * to know if part of the command was discarded.
+ */
+err: if (save_cmdlen == 0)
+ for (; cmdlen; --cmdlen)
+ if ((ch = *cmd++) == vlit && cmdlen > 1) {
+ --cmdlen;
+ ++cmd;
+ } else if (ch == '\n' || ch == '|') {
+ if (cmdlen > 1)
+ save_cmdlen = 1;
+ break;
+ }
+ if (save_cmdlen != 0)
+ msgq(sp, M_ERR,
+ "Ex command failed: remaining command input discarded.");
+ term_map_flush(sp, "Ex command failed");
+ return (1);
+}
+
+/*
+ * ep_range --
+ * Get a line range for ex commands.
+ */
+static int
+ep_range(sp, ep, excp, cmdp, cmdlenp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *excp;
+ char **cmdp;
+ size_t *cmdlenp;
+{
+ MARK cur, savecursor;
+ size_t cmdlen;
+ int savecursor_set, tmp;
+ char *cmd;
+
+ /* Percent character is all lines in the file. */
+ cmd = *cmdp;
+ cmdlen = *cmdlenp;
+ if (*cmd == '%') {
+ excp->addr1.lno = 1;
+ if (file_lline(sp, ep, &excp->addr2.lno))
+ return (1);
+
+ /* If an empty file, then the first line is 0, not 1. */
+ if (excp->addr2.lno == 0)
+ excp->addr1.lno = 0;
+ excp->addr1.cno = excp->addr2.cno = 0;
+ excp->addrcnt = 2;
+
+ ++*cmdp;
+ --*cmdlenp;
+ return (0);
+ }
+
+ /* Parse comma or semi-colon delimited line specs. */
+ for (savecursor_set = 0, excp->addrcnt = 0; cmdlen > 0;)
+ switch (*cmd) {
+ case ';': /* Semi-colon delimiter. */
+ /*
+ * Comma delimiters delimit; semi-colon delimiters
+ * change the current address for the 2nd address
+ * to be the first address. Trailing or multiple
+ * delimiters are discarded.
+ */
+ if (excp->addrcnt == 0)
+ goto done;
+ if (!savecursor_set) {
+ savecursor.lno = sp->lno;
+ savecursor.cno = sp->cno;
+ sp->lno = excp->addr1.lno;
+ sp->cno = excp->addr1.cno;
+ savecursor_set = 1;
+ }
+ ++cmd;
+ --cmdlen;
+ break;
+ case ',': /* Comma delimiter. */
+ /* If no addresses yet, defaults to ".". */
+ if (excp->addrcnt == 0) {
+ excp->addr1.lno = sp->lno;
+ excp->addr1.cno = sp->cno;
+ excp->addrcnt = 1;
+ }
+ /* FALLTHROUGH */
+ case ' ': /* Whitespace. */
+ case '\t': /* Whitespace. */
+ ++cmd;
+ --cmdlen;
+ break;
+ default:
+ if (ep_line(sp, ep, &cur, &cmd, &cmdlen, &tmp))
+ return (1);
+ if (!tmp)
+ goto done;
+
+ /*
+ * Extra addresses are discarded, starting with
+ * the first.
+ */
+ switch (excp->addrcnt) {
+ case 0:
+ excp->addr1 = cur;
+ excp->addrcnt = 1;
+ break;
+ case 1:
+ excp->addr2 = cur;
+ excp->addrcnt = 2;
+ break;
+ case 2:
+ excp->addr1 = excp->addr2;
+ excp->addr2 = cur;
+ break;
+ }
+ break;
+ }
+
+ /*
+ * XXX
+ * This is probably not right behavior for savecursor -- need
+ * to figure out what the historical ex did for ";,;,;5p" or
+ * similar stupidity.
+ */
+done: if (savecursor_set) {
+ sp->lno = savecursor.lno;
+ sp->cno = savecursor.cno;
+ }
+ if (excp->addrcnt == 2 &&
+ (excp->addr2.lno < excp->addr1.lno ||
+ excp->addr2.lno == excp->addr1.lno &&
+ excp->addr2.cno < excp->addr1.cno)) {
+ msgq(sp, M_ERR,
+ "The second address is smaller than the first.");
+ return (1);
+ }
+ *cmdp = cmd;
+ *cmdlenp = cmdlen;
+ return (0);
+}
+
+/*
+ * Get a single line address specifier.
+ */
+static int
+ep_line(sp, ep, cur, cmdp, cmdlenp, addr_found)
+ SCR *sp;
+ EXF *ep;
+ MARK *cur;
+ char **cmdp;
+ size_t *cmdlenp;
+ int *addr_found;
+{
+ MARK m, *mp;
+ long total;
+ u_int flags;
+ size_t cmdlen;
+ char *cmd, *endp;
+
+ *addr_found = 0;
+
+ cmd = *cmdp;
+ cmdlen = *cmdlenp;
+ switch (*cmd) {
+ case '$': /* Last line in the file. */
+ *addr_found = 1;
+ cur->cno = 0;
+ if (file_lline(sp, ep, &cur->lno))
+ return (1);
+ ++cmd;
+ --cmdlen;
+ break; /* Absolute line number. */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ *addr_found = 1;
+ /*
+ * The way the vi "previous context" mark worked was that
+ * "non-relative" motions set it. While vi wasn't totally
+ * consistent about this, ANY numeric address was considered
+ * non-relative, and set the value. Which is why we're
+ * hacking marks down here.
+ */
+ if (IN_VI_MODE(sp)) {
+ m.lno = sp->lno;
+ m.cno = sp->cno;
+ if (mark_set(sp, ep, ABSMARK1, &m, 1))
+ return (1);
+ }
+ cur->cno = 0;
+/* 8-bit XXX */ cur->lno = strtol(cmd, &endp, 10);
+ cmdlen -= (endp - cmd);
+ cmd = endp;
+ break;
+ case '\'': /* Use a mark. */
+ *addr_found = 1;
+ if (cmdlen == 1) {
+ msgq(sp, M_ERR, "No mark name supplied.");
+ return (1);
+ }
+ if ((mp = mark_get(sp, ep, cmd[1])) == NULL)
+ return (1);
+ *cur = *mp;
+ cmd += 2;
+ cmdlen -= 2;
+ break;
+ case '\\': /* Search: forward/backward. */
+ /*
+ * !!!
+ * I can't find any difference between // and \/ or
+ * between ?? and \?. Mark Horton doesn't remember
+ * there being any difference. C'est la vie.
+ */
+ if (cmdlen < 2 || cmd[1] != '/' && cmd[1] != '?') {
+ msgq(sp, M_ERR, "\\ not followed by / or ?.");
+ return (1);
+ }
+ ++cmd;
+ --cmdlen;
+ if (cmd[0] == '/')
+ goto forward;
+ if (cmd[0] == '?')
+ goto backward;
+ /* NOTREACHED */
+ case '/': /* Search forward. */
+forward: *addr_found = 1;
+ m.lno = sp->lno;
+ m.cno = sp->cno;
+ flags = SEARCH_MSG | SEARCH_PARSE | SEARCH_SET;
+ if (f_search(sp, ep, &m, &m, cmd, &endp, &flags))
+ return (1);
+ cur->lno = m.lno;
+ cur->cno = m.cno;
+ cmdlen -= (endp - cmd);
+ cmd = endp;
+ break;
+ case '?': /* Search backward. */
+backward: *addr_found = 1;
+ m.lno = sp->lno;
+ m.cno = sp->cno;
+ flags = SEARCH_MSG | SEARCH_PARSE | SEARCH_SET;
+ if (b_search(sp, ep, &m, &m, cmd, &endp, &flags))
+ return (1);
+ cur->lno = m.lno;
+ cur->cno = m.cno;
+ cmdlen -= (endp - cmd);
+ cmd = endp;
+ break;
+ case '.': /* Current position. */
+ *addr_found = 1;
+ cur->cno = sp->cno;
+
+ /* If an empty file, then '.' is 0, not 1. */
+ if (sp->lno == 1) {
+ if (file_lline(sp, ep, &cur->lno))
+ return (1);
+ if (cur->lno != 0)
+ cur->lno = 1;
+ } else
+ cur->lno = sp->lno;
+ ++cmd;
+ --cmdlen;
+ break;
+ }
+
+ /*
+ * Evaluate any offset. Offsets are +/- any number, or any number
+ * of +/- signs, or any combination thereof. If no address found
+ * yet, offset is relative to ".".
+ */
+ for (total = 0; cmdlen > 0 && (cmd[0] == '-' || cmd[0] == '+');) {
+ if (!*addr_found) {
+ cur->lno = sp->lno;
+ cur->cno = sp->cno;
+ *addr_found = 1;
+ }
+
+ if (cmdlen > 1 && isdigit(cmd[1])) {
+/* 8-bit XXX */ total += strtol(cmd, &endp, 10);
+ cmdlen -= (endp - cmd);
+ cmd = endp;
+ } else {
+ total += cmd[0] == '-' ? -1 : 1;
+ --cmdlen;
+ ++cmd;
+ }
+ }
+ if (total < 0 && -total > cur->lno) {
+ msgq(sp, M_ERR, "Reference to a line number less than 0.");
+ return (1);
+ }
+ cur->lno += total;
+
+ *cmdp = cmd;
+ *cmdlenp = cmdlen;
+ return (0);
+}
+
+/*
+ * ex_is_abbrev -
+ * The vi text input routine needs to know if ex thinks this is
+ * an [un]abbreviate command, so it can turn off abbreviations.
+ * Usual ranting in the vi/v_ntext:txt_abbrev() routine.
+ */
+int
+ex_is_abbrev(name, len)
+ char *name;
+ size_t len;
+{
+ EXCMDLIST const *cp;
+
+ return ((cp = ex_comm_search(name, len)) != NULL &&
+ (cp == &cmds[C_ABBR] || cp == &cmds[C_UNABBREVIATE]));
+}
+
+static inline EXCMDLIST const *
+ex_comm_search(name, len)
+ char *name;
+ size_t len;
+{
+ EXCMDLIST const *cp;
+
+ for (cp = cmds; cp->name != NULL; ++cp) {
+ if (cp->name[0] > name[0])
+ return (NULL);
+ if (cp->name[0] != name[0])
+ continue;
+ if (!memcmp(name, cp->name, len))
+ return (cp);
+ }
+ return (NULL);
+}
diff --git a/usr.bin/vi/nex/ex_abbrev.c b/usr.bin/vi/nex/ex_abbrev.c
new file mode 100644
index 000000000000..f28cbe45e490
--- /dev/null
+++ b/usr.bin/vi/nex/ex_abbrev.c
@@ -0,0 +1,105 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_abbrev.c 8.6 (Berkeley) 12/22/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+
+#include "vi.h"
+#include "seq.h"
+#include "excmd.h"
+
+/*
+ * ex_abbr -- :abbreviate [key replacement]
+ * Create an abbreviation or display abbreviations.
+ */
+int
+ex_abbr(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ switch (cmdp->argc) {
+ case 0:
+ if (seq_dump(sp, SEQ_ABBREV, 0) == 0)
+ msgq(sp, M_INFO, "No abbreviations to display.");
+ return (0);
+ case 2:
+ break;
+ default:
+ abort();
+ }
+
+ if (seq_set(sp, NULL, 0, cmdp->argv[0]->bp, cmdp->argv[0]->len,
+ cmdp->argv[1]->bp, cmdp->argv[1]->len, SEQ_ABBREV, 1))
+ return (1);
+ F_SET(sp->gp, G_ABBREV);
+ return (0);
+}
+
+/*
+ * ex_unabbr -- :unabbreviate key
+ * Delete an abbreviation.
+ */
+int
+ex_unabbr(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ ARGS *ap;
+
+ ap = cmdp->argv[0];
+ if (!F_ISSET(sp->gp, G_ABBREV) ||
+ seq_delete(sp, ap->bp, ap->len, SEQ_ABBREV)) {
+ msgq(sp, M_ERR, "\"%s\" is not an abbreviation.", ap->bp);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * abbr_save --
+ * Save the abbreviation sequences to a file.
+ */
+int
+abbr_save(sp, fp)
+ SCR *sp;
+ FILE *fp;
+{
+ return (seq_save(sp, fp, "abbreviate ", SEQ_ABBREV));
+}
diff --git a/usr.bin/vi/nex/ex_append.c b/usr.bin/vi/nex/ex_append.c
new file mode 100644
index 000000000000..c6edfd57db3b
--- /dev/null
+++ b/usr.bin/vi/nex/ex_append.c
@@ -0,0 +1,180 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_append.c 8.6 (Berkeley) 11/23/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+enum which {APPEND, CHANGE, INSERT};
+
+static int aci __P((SCR *, EXF *, EXCMDARG *, enum which));
+
+/*
+ * ex_append -- :[line] a[ppend][!]
+ * Append one or more lines of new text after the specified line,
+ * or the current line if no address is specified.
+ */
+int
+ex_append(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (aci(sp, ep, cmdp, APPEND));
+}
+
+/*
+ * ex_change -- :[line[,line]] c[hange][!] [count]
+ * Change one or more lines to the input text.
+ */
+int
+ex_change(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (aci(sp, ep, cmdp, CHANGE));
+}
+
+/*
+ * ex_insert -- :[line] i[nsert][!]
+ * Insert one or more lines of new text before the specified line,
+ * or the current line if no address is specified.
+ */
+int
+ex_insert(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (aci(sp, ep, cmdp, INSERT));
+}
+
+static int
+aci(sp, ep, cmdp, cmd)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+ enum which cmd;
+{
+ MARK m;
+ TEXT *tp;
+ recno_t cnt;
+ int rval, aiset;
+
+ /*
+ * The ! flag turns off autoindent for append, change and
+ * insert.
+ */
+ if (F_ISSET(cmdp, E_FORCE)) {
+ aiset = O_ISSET(sp, O_AUTOINDENT);
+ O_CLR(sp, O_AUTOINDENT);
+ } else
+ aiset = 0;
+
+ /*
+ * If doing a change, replace lines as long as possible.
+ * Then, append more lines or delete remaining lines.
+ * Inserts are the same as appends to the previous line.
+ */
+ rval = 0;
+ m = cmdp->addr1;
+ if (cmd == INSERT) {
+ --m.lno;
+ cmd = APPEND;
+ }
+ if (cmd == CHANGE)
+ for (;; ++m.lno) {
+ if (m.lno > cmdp->addr2.lno) {
+ cmd = APPEND;
+ --m.lno;
+ break;
+ }
+ switch (sp->s_get(sp, ep, &sp->tiq, 0,
+ TXT_BEAUTIFY | TXT_CR | TXT_NLECHO)) {
+ case INP_OK:
+ break;
+ case INP_EOF:
+ case INP_ERR:
+ rval = 1;
+ goto done;
+ }
+ tp = sp->tiq.cqh_first;
+ if (tp->len == 1 && tp->lb[0] == '.') {
+ for (cnt =
+ (cmdp->addr2.lno - m.lno) + 1; cnt--;)
+ if (file_dline(sp, ep, m.lno)) {
+ rval = 1;
+ goto done;
+ }
+ goto done;
+ }
+ if (file_sline(sp, ep, m.lno, tp->lb, tp->len)) {
+ rval = 1;
+ goto done;
+ }
+ }
+
+ if (cmd == APPEND)
+ for (;; ++m.lno) {
+ switch (sp->s_get(sp, ep, &sp->tiq, 0,
+ TXT_BEAUTIFY | TXT_CR | TXT_NLECHO)) {
+ case INP_OK:
+ break;
+ case INP_EOF:
+ case INP_ERR:
+ rval = 1;
+ goto done;
+ }
+ tp = sp->tiq.cqh_first;
+ if (tp->len == 1 && tp->lb[0] == '.')
+ break;
+ if (file_aline(sp, ep, 1, m.lno, tp->lb, tp->len)) {
+ rval = 1;
+ goto done;
+ }
+ }
+
+done: if (rval == 0)
+ sp->lno = m.lno;
+
+ if (aiset)
+ O_SET(sp, O_AUTOINDENT);
+
+ return (rval);
+}
diff --git a/usr.bin/vi/nex/ex_args.c b/usr.bin/vi/nex/ex_args.c
new file mode 100644
index 000000000000..2f96df727dd8
--- /dev/null
+++ b/usr.bin/vi/nex/ex_args.c
@@ -0,0 +1,274 @@
+/*-
+ * Copyright (c) 1991, 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
+static char sccsid[] = "@(#)ex_args.c 8.13 (Berkeley) 12/20/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_next -- :next [files]
+ * Edit the next file, optionally setting the list of files.
+ *
+ * !!!
+ * The :next command behaved differently from the :rewind command in
+ * historic vi. See nvi/docs/autowrite for details, but the basic
+ * idea was that it ignored the force flag if the autowrite flag was
+ * set. This implementation handles them all identically.
+ */
+int
+ex_next(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ ARGS **argv;
+ FREF *frp;
+ char *name;
+
+ MODIFY_CHECK(sp, ep, F_ISSET(cmdp, E_FORCE));
+
+ if (cmdp->argc) {
+ /* Mark all the current files as ignored. */
+ for (frp = sp->frefq.cqh_first;
+ frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next)
+ F_SET(frp, FR_IGNORE);
+
+ /* Add the new files into the file list. */
+ for (argv = cmdp->argv; argv[0]->len != 0; ++argv)
+ if (file_add(sp, NULL, argv[0]->bp, 0) == NULL)
+ return (1);
+
+ if ((frp = file_first(sp)) == NULL)
+ return (1);
+ } else if ((frp = file_next(sp, sp->a_frp)) == NULL) {
+ msgq(sp, M_ERR, "No more files to edit.");
+ return (1);
+ }
+
+ /*
+ * There's a tricky sequence, where the user edits two files, e.g.
+ * "x" and "y". While in "x", they do ":e y|:f foo", which changes
+ * the name of that FRP entry. Then, the :n command finds the file
+ * "y" with a name change. If the file name has been changed, get
+ * a new FREF for the original file name, and make it be the one that
+ * is displayed in the argument list, not the one with the name change.
+ */
+ if (frp->cname != NULL) {
+ F_SET(frp, FR_IGNORE);
+ name = frp->name == NULL ? frp->tname : frp->name;
+ if ((frp = file_add(sp, sp->a_frp, name, 0)) == NULL)
+ return (1);
+ }
+ if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE)))
+ return (1);
+ sp->a_frp = frp;
+ F_SET(sp, S_FSWITCH);
+ return (0);
+}
+
+/*
+ * ex_prev -- :prev
+ * Edit the previous file.
+ */
+int
+ex_prev(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ FREF *frp;
+ char *name;
+
+ MODIFY_CHECK(sp, ep, F_ISSET(cmdp, E_FORCE));
+
+ if ((frp = file_prev(sp, sp->a_frp)) == NULL) {
+ msgq(sp, M_ERR, "No previous files to edit.");
+ return (1);
+ }
+
+ /* See comment in ex_next(). */
+ if (frp->cname != NULL) {
+ F_SET(frp, FR_IGNORE);
+ name = frp->name == NULL ? frp->tname : frp->name;
+ if ((frp = file_add(sp, frp, name, 0)) == NULL)
+ return (1);
+ }
+ if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE)))
+ return (1);
+ sp->a_frp = frp;
+ F_SET(sp, S_FSWITCH);
+ return (0);
+}
+
+/*
+ * ex_rew -- :rew
+ * Re-edit the list of files.
+ */
+int
+ex_rew(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ FREF *frp, *tfrp;
+
+ /*
+ * !!!
+ * Historic practice -- you can rewind to the current file.
+ */
+ if ((frp = file_first(sp)) == NULL) {
+ msgq(sp, M_ERR, "No previous files to rewind.");
+ return (1);
+ }
+
+ MODIFY_CHECK(sp, ep, F_ISSET(cmdp, E_FORCE));
+
+ /*
+ * !!!
+ * Historic practice, turn off the edited bit. The :next and :prev
+ * code will discard any name changes, so ignore them here. Start
+ * at the beginning of the file, too.
+ */
+ for (tfrp = sp->frefq.cqh_first;
+ tfrp != (FREF *)&sp->frefq; tfrp = tfrp->q.cqe_next)
+ F_CLR(tfrp, FR_CHANGEWRITE | FR_CURSORSET | FR_EDITED);
+
+ if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE)))
+ return (1);
+ sp->a_frp = frp;
+ F_SET(sp, S_FSWITCH);
+ return (0);
+}
+
+/*
+ * ex_args -- :args
+ * Display the list of files.
+ */
+int
+ex_args(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ FREF *frp;
+ int cnt, col, iscur, len, nlen, sep;
+ char *name;
+
+ /*
+ * !!!
+ * Ignore files that aren't in the "argument" list unless they are the
+ * one we're currently editing. I'm not sure this is right, but the
+ * historic vi behavior of not showing the current file if it was the
+ * result of a ":e" command, or if the file name was changed was wrong.
+ * This is actually pretty tricky, don't modify it without thinking it
+ * through. There have been a lot of problems in here.
+ *
+ * Also, historic practice was to display the original name of the file
+ * even if the user had used a file command to change the file name.
+ * Confusing, at best. We show both names: the original as that's what
+ * the user will get in a next, prev or rewind, and the new one since
+ * that's what the user is actually editing now.
+ *
+ * When we find the "argument" FREF, i.e. the current location in the
+ * user's argument list, if it's not the same as the current FREF, we
+ * display the current FREF as following the argument in the list.
+ * This means that if the user edits three files, "x", "y" and "z", and
+ * then does a :e command in the file "x" to edit "z", "z" will appear
+ * in the list twice.
+ */
+ col = len = sep = 0;
+ for (cnt = 1, frp = sp->frefq.cqh_first;
+ frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next) {
+ iscur = 0;
+ /*
+ * If the last argument FREF structure, and we're editing
+ * it, set the current bit. Otherwise, we'll display it,
+ * then the file we're editing, and the latter will have
+ * the current bit set.
+ */
+ if (frp == sp->a_frp) {
+ if (frp == sp->frp && frp->cname == NULL)
+ iscur = 1;
+ } else if (F_ISSET(frp, FR_IGNORE))
+ continue;
+ name = frp->name == NULL ? frp->tname : frp->name;
+ /*
+ * Mistake. The user edited a temporary file (vi /tmp), then
+ * switched to another file (:e file). The argument FREF is
+ * pointing to the temporary file, but it doesn't have a name.
+ * Gracefully recover through the creative use of goto's.
+ */
+ if (name == NULL)
+ goto testcur;
+extra: nlen = strlen(name);
+ col += len = nlen + sep + (iscur ? 2 : 0);
+ if (col >= sp->cols - 1) {
+ col = len;
+ sep = 0;
+ (void)ex_printf(EXCOOKIE, "\n");
+ } else if (cnt != 1) {
+ sep = 1;
+ (void)ex_printf(EXCOOKIE, " ");
+ }
+ ++cnt;
+
+ if (iscur)
+ (void)ex_printf(EXCOOKIE, "[%s]", name);
+ else {
+ (void)ex_printf(EXCOOKIE, "%s", name);
+testcur: if (frp == sp->a_frp) {
+ if (frp != sp->frp)
+ name = FILENAME(sp->frp);
+ else
+ name = frp->cname;
+ iscur = 1;
+ goto extra;
+ }
+ }
+ }
+ /* This should never happen; left in because it's been known to. */
+ if (cnt == 1)
+ (void)ex_printf(EXCOOKIE, "No files.\n");
+ else
+ (void)ex_printf(EXCOOKIE, "\n");
+ return (0);
+}
diff --git a/usr.bin/vi/nex/ex_argv.c b/usr.bin/vi/nex/ex_argv.c
new file mode 100644
index 000000000000..6ad76c61dfbc
--- /dev/null
+++ b/usr.bin/vi/nex/ex_argv.c
@@ -0,0 +1,575 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)ex_argv.c 8.26 (Berkeley) 1/2/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+static int argv_alloc __P((SCR *, size_t));
+static int argv_fexp __P((SCR *, EXCMDARG *,
+ char *, size_t, char *, size_t *, char **, size_t *, int));
+static int argv_sexp __P((SCR *, char **, size_t *, size_t *));
+
+/*
+ * argv_init --
+ * Build a prototype arguments list.
+ */
+int
+argv_init(sp, ep, excp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *excp;
+{
+ EX_PRIVATE *exp;
+
+ exp = EXP(sp);
+ exp->argsoff = 0;
+ argv_alloc(sp, 1);
+
+ excp->argv = exp->args;
+ excp->argc = exp->argsoff;
+ return (0);
+}
+
+/*
+ * argv_exp0 --
+ * Put a string into an argv.
+ */
+int
+argv_exp0(sp, ep, excp, cmd, cmdlen)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *excp;
+ char *cmd;
+ size_t cmdlen;
+{
+ EX_PRIVATE *exp;
+
+ exp = EXP(sp);
+ argv_alloc(sp, cmdlen);
+ memmove(exp->args[exp->argsoff]->bp, cmd, cmdlen);
+ exp->args[exp->argsoff]->bp[cmdlen] = '\0';
+ exp->args[exp->argsoff]->len = cmdlen;
+ ++exp->argsoff;
+ excp->argv = exp->args;
+ excp->argc = exp->argsoff;
+ return (0);
+}
+
+/*
+ * argv_exp1 --
+ * Do file name expansion on a string, and leave it in a string.
+ */
+int
+argv_exp1(sp, ep, excp, cmd, cmdlen, is_bang)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *excp;
+ char *cmd;
+ size_t cmdlen;
+ int is_bang;
+{
+ EX_PRIVATE *exp;
+ size_t blen, len;
+ char *bp;
+
+ GET_SPACE_RET(sp, bp, blen, 512);
+
+ len = 0;
+ exp = EXP(sp);
+ if (argv_fexp(sp, excp, cmd, cmdlen, bp, &len, &bp, &blen, is_bang)) {
+ FREE_SPACE(sp, bp, blen);
+ return (1);
+ }
+
+ argv_alloc(sp, len);
+ memmove(exp->args[exp->argsoff]->bp, bp, len);
+ exp->args[exp->argsoff]->bp[len] = '\0';
+ exp->args[exp->argsoff]->len = len;
+ ++exp->argsoff;
+ excp->argv = exp->args;
+ excp->argc = exp->argsoff;
+
+ FREE_SPACE(sp, bp, blen);
+ return (0);
+}
+
+/*
+ * argv_exp2 --
+ * Do file name and shell expansion on a string, and break
+ * it up into an argv.
+ */
+int
+argv_exp2(sp, ep, excp, cmd, cmdlen, is_bang)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *excp;
+ char *cmd;
+ size_t cmdlen;
+ int is_bang;
+{
+ size_t blen, len, n;
+ int rval;
+ char *bp, *p;
+
+ GET_SPACE_RET(sp, bp, blen, 512);
+
+#define SHELLECHO "echo "
+#define SHELLOFFSET (sizeof(SHELLECHO) - 1)
+ memmove(bp, SHELLECHO, SHELLOFFSET);
+ p = bp + SHELLOFFSET;
+ len = SHELLOFFSET;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "file_argv: {%.*s}\n", (int)cmdlen, cmd);
+#endif
+
+ if (argv_fexp(sp, excp, cmd, cmdlen, p, &len, &bp, &blen, is_bang)) {
+ rval = 1;
+ goto err;
+ }
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "before shell: %d: {%s}\n", len, bp);
+#endif
+
+ /*
+ * Do shell word expansion -- it's very, very hard to figure out
+ * what magic characters the user's shell expects. If it's not
+ * pure vanilla, don't even try.
+ */
+ for (p = bp, n = len; n > 0; --n, ++p)
+ if (!isalnum(*p) && !isblank(*p) && *p != '/' && *p != '.')
+ break;
+ if (n > 0) {
+ if (argv_sexp(sp, &bp, &blen, &len)) {
+ rval = 1;
+ goto err;
+ }
+ p = bp;
+ } else {
+ p = bp + SHELLOFFSET;
+ len -= SHELLOFFSET;
+ }
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "after shell: %d: {%s}\n", len, bp);
+#endif
+
+ rval = argv_exp3(sp, ep, excp, p, len);
+
+err: FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
+
+/*
+ * argv_exp3 --
+ * Take a string and break it up into an argv.
+ */
+int
+argv_exp3(sp, ep, excp, cmd, cmdlen)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *excp;
+ char *cmd;
+ size_t cmdlen;
+{
+ CHAR_T vlit;
+ EX_PRIVATE *exp;
+ size_t len;
+ int ch, off;
+ char *ap, *p;
+
+ (void)term_key_ch(sp, K_VLNEXT, &vlit);
+ for (exp = EXP(sp); cmdlen > 0; ++exp->argsoff) {
+ /* Skip any leading whitespace. */
+ for (; cmdlen > 0; --cmdlen, ++cmd) {
+ ch = *cmd;
+ if (!isblank(ch))
+ break;
+ }
+ if (cmdlen == 0)
+ break;
+
+ /*
+ * Determine the length of this whitespace delimited
+ * argument.
+ *
+ * QUOTING NOTE:
+ *
+ * Skip any character preceded by the user's quoting
+ * character.
+ */
+ for (ap = cmd, len = 0; cmdlen > 0; ++cmd, --cmdlen, ++len)
+ if ((ch = *cmd) == vlit && cmdlen > 1) {
+ ++cmd;
+ --cmdlen;
+ } else if (isblank(ch))
+ break;
+
+ /*
+ * Copy the argument into place.
+ *
+ * QUOTING NOTE:
+ *
+ * Lose quote chars.
+ */
+ argv_alloc(sp, len);
+ off = exp->argsoff;
+ exp->args[off]->len = len;
+ for (p = exp->args[off]->bp; len > 0; --len, *p++ = *ap++)
+ if (*ap == vlit) {
+ ++ap;
+ --exp->args[off]->len;
+ }
+ *p = '\0';
+ }
+ excp->argv = exp->args;
+ excp->argc = exp->argsoff;
+
+#if defined(DEBUG) && 0
+ for (cnt = 0; cnt < exp->argsoff; ++cnt)
+ TRACE(sp, "arg %d: {%s}\n", cnt, exp->argv[cnt]);
+#endif
+ return (0);
+}
+
+/*
+ * argv_fexp --
+ * Do file name and bang command expansion.
+ */
+static int
+argv_fexp(sp, excp, cmd, cmdlen, p, lenp, bpp, blenp, is_bang)
+ SCR *sp;
+ EXCMDARG *excp;
+ char *cmd, *p, **bpp;
+ size_t cmdlen, *lenp, *blenp;
+ int is_bang;
+{
+ EX_PRIVATE *exp;
+ char *bp, *t;
+ size_t blen, len, tlen;
+
+ /* Replace file name characters. */
+ for (bp = *bpp, blen = *blenp, len = *lenp; cmdlen > 0; --cmdlen, ++cmd)
+ switch (*cmd) {
+ case '!':
+ if (!is_bang)
+ goto ins_ch;
+ exp = EXP(sp);
+ if (exp->lastbcomm == NULL) {
+ msgq(sp, M_ERR,
+ "No previous command to replace \"!\".");
+ return (1);
+ }
+ len += tlen = strlen(exp->lastbcomm);
+ ADD_SPACE_RET(sp, bp, blen, len);
+ memmove(p, exp->lastbcomm, tlen);
+ p += tlen;
+ F_SET(excp, E_MODIFY);
+ break;
+ case '%':
+ if (sp->frp->cname == NULL && sp->frp->name == NULL) {
+ msgq(sp, M_ERR,
+ "No filename to substitute for %%.");
+ return (1);
+ }
+ tlen = strlen(t = FILENAME(sp->frp));
+ len += tlen;
+ ADD_SPACE_RET(sp, bp, blen, len);
+ memmove(p, t, tlen);
+ p += tlen;
+ F_SET(excp, E_MODIFY);
+ break;
+ case '#':
+ /*
+ * Try the alternate file name first, then the
+ * previously edited file.
+ */
+ if (sp->alt_name == NULL && (sp->p_frp == NULL ||
+ sp->frp->cname == NULL && sp->frp->name == NULL)) {
+ msgq(sp, M_ERR,
+ "No filename to substitute for #.");
+ return (1);
+ }
+ if (sp->alt_name != NULL)
+ t = sp->alt_name;
+ else
+ t = FILENAME(sp->frp);
+ len += tlen = strlen(t);
+ ADD_SPACE_RET(sp, bp, blen, len);
+ memmove(p, t, tlen);
+ p += tlen;
+ F_SET(excp, E_MODIFY);
+ break;
+ case '\\':
+ /*
+ * QUOTING NOTE:
+ *
+ * Strip any backslashes that protected the file
+ * expansion characters.
+ */
+ if (cmdlen > 1 && cmd[1] == '%' || cmd[1] == '#')
+ ++cmd;
+ /* FALLTHROUGH */
+ default:
+ins_ch: ++len;
+ ADD_SPACE_RET(sp, bp, blen, len);
+ *p++ = *cmd;
+ }
+
+ /* Nul termination. */
+ ++len;
+ ADD_SPACE_RET(sp, bp, blen, len);
+ *p = '\0';
+
+ /* Return the new string length, buffer, buffer length. */
+ *lenp = len - 1;
+ *bpp = bp;
+ *blenp = blen;
+ return (0);
+}
+
+/*
+ * argv_alloc --
+ * Make more space for arguments.
+ */
+static int
+argv_alloc(sp, len)
+ SCR *sp;
+ size_t len;
+{
+ ARGS *ap;
+ EX_PRIVATE *exp;
+ int cnt, off;
+
+ /*
+ * Allocate room for another argument, always leaving
+ * enough room for an ARGS structure with a length of 0.
+ */
+#define INCREMENT 20
+ exp = EXP(sp);
+ off = exp->argsoff;
+ if (exp->argscnt == 0 || off + 2 >= exp->argscnt - 1) {
+ cnt = exp->argscnt + INCREMENT;
+ REALLOC(sp, exp->args, ARGS **, cnt * sizeof(ARGS *));
+ if (exp->args == NULL) {
+ (void)argv_free(sp);
+ goto mem;
+ }
+ memset(&exp->args[off], 0, INCREMENT * sizeof(ARGS *));
+ exp->argscnt = cnt;
+ }
+
+ /* First argument. */
+ if (exp->args[off] == NULL) {
+ CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS));
+ if (exp->args[off] == NULL)
+ goto mem;
+ }
+
+ /* First argument buffer. */
+ ap = exp->args[off];
+ ap->len = 0;
+ if (ap->blen < len + 1) {
+ ap->blen = len + 1;
+ REALLOC(sp, ap->bp, CHAR_T *, ap->blen * sizeof(CHAR_T));
+ if (ap->bp == NULL) {
+ ap->bp = NULL;
+ ap->blen = 0;
+ F_CLR(ap, A_ALLOCATED);
+mem: msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ F_SET(ap, A_ALLOCATED);
+ }
+
+ /* Second argument. */
+ if (exp->args[++off] == NULL) {
+ CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS));
+ if (exp->args[off] == NULL)
+ goto mem;
+ }
+ /* 0 length serves as end-of-argument marker. */
+ exp->args[off]->len = 0;
+ return (0);
+}
+
+/*
+ * argv_free --
+ * Free up argument structures.
+ */
+int
+argv_free(sp)
+ SCR *sp;
+{
+ EX_PRIVATE *exp;
+ int off;
+
+ exp = EXP(sp);
+ if (exp->args != NULL) {
+ for (off = 0; off < exp->argscnt; ++off) {
+ if (exp->args[off] == NULL)
+ continue;
+ if (F_ISSET(exp->args[off], A_ALLOCATED))
+ free(exp->args[off]->bp);
+ FREE(exp->args[off], sizeof(ARGS));
+ }
+ FREE(exp->args, exp->argscnt * sizeof(ARGS *));
+ }
+ exp->args = NULL;
+ exp->argscnt = 0;
+ exp->argsoff = 0;
+ return (0);
+}
+
+/*
+ * argv_sexp --
+ * Fork a shell, pipe a command through it, and read the output into
+ * a buffer.
+ */
+static int
+argv_sexp(sp, bpp, blenp, lenp)
+ SCR *sp;
+ char **bpp;
+ size_t *blenp, *lenp;
+{
+ FILE *ifp;
+ pid_t pid;
+ size_t blen, len;
+ int ch, rval, output[2];
+ char *bp, *p, *sh, *sh_path;
+
+ bp = *bpp;
+ blen = *blenp;
+
+ sh_path = O_STR(sp, O_SHELL);
+ if ((sh = strrchr(sh_path, '/')) == NULL)
+ sh = sh_path;
+ else
+ ++sh;
+
+ /*
+ * There are two different processes running through this code.
+ * They are named the utility and the parent. The utility reads
+ * from standard input and writes to the parent. The parent reads
+ * from the utility and writes into the buffer. The parent reads
+ * from output[0], and the utility writes to output[1].
+ */
+ if (pipe(output) < 0) {
+ msgq(sp, M_SYSERR, "pipe");
+ return (1);
+ }
+ if ((ifp = fdopen(output[0], "r")) == NULL) {
+ msgq(sp, M_SYSERR, "fdopen");
+ goto err;
+ }
+
+ /*
+ * Do the minimal amount of work possible, the shell is going
+ * to run briefly and then exit. Hopefully.
+ */
+ switch (pid = vfork()) {
+ case -1: /* Error. */
+ msgq(sp, M_SYSERR, "vfork");
+err: (void)close(output[0]);
+ (void)close(output[1]);
+ return (1);
+ case 0: /* Utility. */
+ /* Redirect stdout/stderr to the write end of the pipe. */
+ (void)dup2(output[1], STDOUT_FILENO);
+ (void)dup2(output[1], STDERR_FILENO);
+
+ /* Close the utility's file descriptors. */
+ (void)close(output[0]);
+ (void)close(output[1]);
+
+ /* Assumes that all shells have -c. */
+ execl(sh_path, sh, "-c", bp, NULL);
+ msgq(sp, M_ERR,
+ "Error: execl: %s: %s", sh_path, strerror(errno));
+ _exit(127);
+ default:
+ /* Close the pipe end the parent won't use. */
+ (void)close(output[1]);
+ break;
+ }
+
+ rval = 0;
+
+ /*
+ * Copy process output into a buffer.
+ *
+ * !!!
+ * Historic vi apparently discarded leading \n and \r's from
+ * the shell output stream. We don't on the grounds that any
+ * shell that does that is broken.
+ */
+ for (p = bp, len = 0, ch = EOF;
+ (ch = getc(ifp)) != EOF; *p++ = ch, --blen, ++len)
+ if (blen < 5) {
+ ADD_SPACE_GOTO(sp, bp, blen, *blenp * 2);
+ p = bp + len;
+ blen = *blenp - len;
+ }
+
+ /* Delete the final newline, nul terminate the string. */
+ if (p > bp && p[-1] == '\n' || p[-1] == '\r') {
+ --len;
+ *--p = '\0';
+ } else
+ *p = '\0';
+ *lenp = len;
+
+ if (ferror(ifp)) {
+ msgq(sp, M_ERR, "I/O error: %s", sh);
+binc_err: rval = 1;
+ }
+ (void)fclose(ifp);
+
+ *bpp = bp; /* *blenp is already updated. */
+
+ /* Wait for the process. */
+ return (proc_wait(sp, (long)pid, sh, 0) | rval);
+}
diff --git a/usr.bin/vi/nex/ex_at.c b/usr.bin/vi/nex/ex_at.c
new file mode 100644
index 000000000000..51d001f304a7
--- /dev/null
+++ b/usr.bin/vi/nex/ex_at.c
@@ -0,0 +1,99 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_at.c 8.16 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_at -- :@[@ | buffer]
+ * :*[* | buffer]
+ *
+ * Execute the contents of the buffer.
+ */
+int
+ex_at(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ CB *cbp;
+ EX_PRIVATE *exp;
+ TEXT *tp;
+ int name, lmode;
+
+ exp = EXP(sp);
+
+ /* Historically, @@ and ** execute the last buffer. */
+ name = cmdp->buffer;
+ if (name == cmdp->cmd->name[0]) {
+ if (!exp->at_lbuf_set) {
+ msgq(sp, M_ERR, "No previous buffer to execute.");
+ return (1);
+ }
+ name = exp->at_lbuf;
+ }
+
+ CBNAME(sp, cbp, name);
+ if (cbp == NULL) {
+ msgq(sp, M_ERR, "Buffer %s is empty.", charname(sp, name));
+ return (1);
+ }
+
+ /* Save for reuse. */
+ exp->at_lbuf = name;
+ exp->at_lbuf_set = 1;
+
+ /*
+ * If the buffer was cut in line mode or had portions of more
+ * than one line, <newlines> are appended to each line as it
+ * is pushed onto the stack.
+ */
+ tp = cbp->textq.cqh_last;
+ lmode = F_ISSET(cbp, CB_LMODE) || tp->q.cqe_prev != (void *)&cbp->textq;
+ for (; tp != (void *)&cbp->textq; tp = tp->q.cqe_prev)
+ if ((lmode || tp->q.cqe_prev != (void *)&cbp->textq) &&
+ term_push(sp, "\n", 1, 0, 0) ||
+ term_push(sp, tp->lb, tp->len, 0, CH_QUOTED))
+ return (1);
+ return (0);
+}
diff --git a/usr.bin/vi/nex/ex_bang.c b/usr.bin/vi/nex/ex_bang.c
new file mode 100644
index 000000000000..4f7222b4e78c
--- /dev/null
+++ b/usr.bin/vi/nex/ex_bang.c
@@ -0,0 +1,192 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_bang.c 8.22 (Berkeley) 12/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "sex/sex_screen.h"
+
+/*
+ * ex_bang -- :[line [,line]] ! command
+ *
+ * Pass the rest of the line after the ! character to the program named by
+ * the O_SHELL option.
+ *
+ * Historical vi did NOT do shell expansion on the arguments before passing
+ * them, only file name expansion. This means that the O_SHELL program got
+ * "$t" as an argument if that is what the user entered. Also, there's a
+ * special expansion done for the bang command. Any exclamation points in
+ * the user's argument are replaced by the last, expanded ! command.
+ *
+ * There's some fairly amazing slop in this routine to make the different
+ * ways of getting here display the right things. It took a long time to
+ * get get right (wrong?), so be careful.
+ */
+int
+ex_bang(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ enum filtertype ftype;
+ ARGS *ap;
+ EX_PRIVATE *exp;
+ MARK rm;
+ recno_t lno;
+ size_t blen;
+ int rval;
+ char *bp, *msg;
+
+
+ ap = cmdp->argv[0];
+ if (ap->len == 0) {
+ msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage);
+ return (1);
+ }
+
+ /* Swap commands. */
+ exp = EXP(sp);
+ if (exp->lastbcomm != NULL)
+ FREE(exp->lastbcomm, strlen(exp->lastbcomm) + 1);
+ if ((exp->lastbcomm = strdup(ap->bp)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+
+ /*
+ * If the command was modified by the expansion, we redisplay it.
+ * Redisplaying it in vi mode is tricky, and handled separately
+ * in each case below. If we're in ex mode, it's easy, so we just
+ * do it here.
+ */
+ bp = NULL;
+ if (F_ISSET(cmdp, E_MODIFY)) {
+ if (IN_EX_MODE(sp)) {
+ (void)ex_printf(EXCOOKIE, "!%s\n", ap->bp);
+ (void)ex_fflush(EXCOOKIE);
+ }
+ /*
+ * Vi: Display the command if modified. Historic vi displayed
+ * the command if it was modified due to file name and/or bang
+ * expansion. If piping lines, it was immediately overwritten
+ * by any error or line change reporting. We don't the user to
+ * have to page through the responses, so we only post it until
+ * it's erased by something else. Otherwise, pass it on to the
+ * ex_exec_proc routine to display after the screen has been
+ * cleaned up.
+ */
+ if (IN_VI_MODE(sp)) {
+ GET_SPACE_RET(sp, bp, blen, ap->len + 2);
+ bp[0] = '!';
+ memmove(bp + 1, ap->bp, ap->len + 1);
+ }
+ }
+
+ /*
+ * If addresses were specified, pipe lines from the file through
+ * the command.
+ */
+ if (cmdp->addrcnt != 0) {
+ if (bp != NULL) {
+ (void)sp->s_busy(sp, bp);
+ FREE_SPACE(sp, bp, blen);
+ }
+ /*
+ * !!!
+ * Historical vi permitted "!!" in an empty file. When it
+ * happens, we get called with two addresses of 1,1 and a
+ * bad attitude.
+ */
+ ftype = FILTER;
+ if (cmdp->addr1.lno == 1 && cmdp->addr2.lno == 1) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0) {
+ cmdp->addr1.lno = cmdp->addr2.lno = 0;
+ ftype = FILTER_READ;
+ }
+ }
+ if (filtercmd(sp, ep,
+ &cmdp->addr1, &cmdp->addr2, &rm, ap->bp, ftype))
+ return (1);
+ sp->lno = rm.lno;
+ F_SET(exp, EX_AUTOPRINT);
+ return (0);
+ }
+
+ /*
+ * If no addresses were specified, run the command. If the file
+ * has been modified and autowrite is set, write the file back.
+ * If the file has been modified, autowrite is not set and the
+ * warn option is set, tell the user about the file.
+ */
+ msg = "\n";
+ if (F_ISSET(ep, F_MODIFIED))
+ if (O_ISSET(sp, O_AUTOWRITE)) {
+ if (file_write(sp, ep, NULL, NULL, NULL, FS_ALL)) {
+ rval = 1;
+ goto ret;
+ }
+ } else if (O_ISSET(sp, O_WARN))
+ if (IN_VI_MODE(sp) && F_ISSET(cmdp, E_MODIFY))
+ msg = "\nFile modified since last write.\n";
+ else
+ msg = "File modified since last write.\n";
+
+ /* Run the command. */
+ rval = ex_exec_proc(sp, ap->bp, bp, msg);
+
+ /* Ex terminates with a bang. */
+ if (IN_EX_MODE(sp))
+ (void)write(STDOUT_FILENO, "!\n", 2);
+
+ /* Vi requires user permission to continue. */
+ if (IN_VI_MODE(sp))
+ F_SET(sp, S_CONTINUE);
+
+ /* Free the extra space. */
+ret: if (bp != NULL)
+ FREE_SPACE(sp, bp, blen);
+
+ return (rval);
+}
diff --git a/usr.bin/vi/nex/ex_cd.c b/usr.bin/vi/nex/ex_cd.c
new file mode 100644
index 000000000000..6b51bc51c15e
--- /dev/null
+++ b/usr.bin/vi/nex/ex_cd.c
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_cd.c 8.3 (Berkeley) 12/2/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_cd -- :cd[!] [directory]
+ * Change directories.
+ */
+int
+ex_cd(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ char *dir, buf[MAXPATHLEN];
+
+ switch (cmdp->argc) {
+ case 0:
+ if ((dir = getenv("HOME")) == NULL) {
+ msgq(sp, M_ERR,
+ "Environment variable HOME not set.");
+ return (1);
+ }
+ break;
+ case 1:
+ dir = cmdp->argv[0]->bp;
+ break;
+ default:
+ abort();
+ }
+
+ if (chdir(dir) < 0) {
+ msgq(sp, M_SYSERR, dir);
+ return (1);
+ }
+ if (getcwd(buf, sizeof(buf)) != NULL)
+ msgq(sp, M_INFO, "New directory: %s", buf);
+ return (0);
+}
diff --git a/usr.bin/vi/nex/ex_delete.c b/usr.bin/vi/nex/ex_delete.c
new file mode 100644
index 000000000000..5b737e8f547d
--- /dev/null
+++ b/usr.bin/vi/nex/ex_delete.c
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_delete.c 8.6 (Berkeley) 1/11/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_delete: [line [,line]] d[elete] [buffer] [count] [flags]
+ *
+ * Delete lines from the file.
+ */
+int
+ex_delete(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ recno_t lno;
+
+ /* Yank the lines. */
+ if (cut(sp, ep, NULL, F_ISSET(cmdp, E_BUFFER) ? &cmdp->buffer : NULL,
+ &cmdp->addr1, &cmdp->addr2, CUT_DELETE | CUT_LINEMODE))
+ return (1);
+
+ /* Delete the lines. */
+ if (delete(sp, ep, &cmdp->addr1, &cmdp->addr2, 1))
+ return (1);
+
+ /* Set the cursor to the line after the last line deleted. */
+ sp->lno = cmdp->addr1.lno;
+
+ /* Or the last line in the file if deleted to the end of the file. */
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (sp->lno > lno)
+ sp->lno = lno;
+ return (0);
+}
diff --git a/usr.bin/vi/nex/ex_digraph.c b/usr.bin/vi/nex/ex_digraph.c
new file mode 100644
index 000000000000..20f1bf327ded
--- /dev/null
+++ b/usr.bin/vi/nex/ex_digraph.c
@@ -0,0 +1,313 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_digraph.c 8.4 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#ifndef NO_DIGRAPH
+#include <sys/types.h>
+
+#include <curses.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+static void do_digraph __P((SCR *, EXF *, int, u_char *));
+
+/* This stuff is used to build the default digraphs table. */
+static u_char digtable[][4] = {
+# ifdef CS_IBMPC
+ "C,\200", "u\"\1", "e'\2", "a^\3",
+ "a\"\4", "a`\5", "a@\6", "c,\7",
+ "e^\10", "e\"\211", "e`\12", "i\"\13",
+ "i^\14", "i`\15", "A\"\16", "A@\17",
+ "E'\20", "ae\21", "AE\22", "o^\23",
+ "o\"\24", "o`\25", "u^\26", "u`\27",
+ "y\"\30", "O\"\31", "U\"\32", "a'\240",
+ "i'!", "o'\"", "u'#", "n~$",
+ "N~%", "a-&", "o-'", "~?(",
+ "~!-", "\"<.", "\">/",
+# ifdef CS_SPECIAL
+ "2/+", "4/,", "^+;", "^q<",
+ "^c=", "^r>", "^t?", "pp]",
+ "^^^", "oo_", "*a`", "*ba",
+ "*pc", "*Sd", "*se", "*uf",
+ "*tg", "*Ph", "*Ti", "*Oj",
+ "*dk", "*Hl", "*hm", "*En",
+ "*No", "eqp", "pmq", "ger",
+ "les", "*It", "*iu", "*/v",
+ "*=w", "sq{", "^n|", "^2}",
+ "^3~", "^_\377",
+# endif /* CS_SPECIAL */
+# endif /* CS_IBMPC */
+# ifdef CS_LATIN1
+ "~!!", "a-*", "\">+", "o-:",
+ "\"<>", "~??",
+
+ "A`@", "A'A", "A^B", "A~C",
+ "A\"D", "A@E", "AEF", "C,G",
+ "E`H", "E'I", "E^J", "E\"K",
+ "I`L", "I'M", "I^N", "I\"O",
+ "-DP", "N~Q", "O`R", "O'S",
+ "O^T", "O~U", "O\"V", "O/X",
+ "U`Y", "U'Z", "U^[", "U\"\\",
+ "Y'_",
+
+ "a``", "a'a", "a^b", "a~c",
+ "a\"d", "a@e", "aef", "c,g",
+ "e`h", "e'i", "e^j", "e\"k",
+ "i`l", "i'm", "i^n", "i\"o",
+ "-dp", "n~q", "o`r", "o's",
+ "o^t", "o~u", "o\"v", "o/x",
+ "u`y", "u'z", "u^{", "u\"|",
+ "y'~",
+# endif /* CS_LATIN1 */
+ ""
+};
+
+int
+digraph_init(sp)
+ SCR *sp;
+{
+ int i;
+
+ for (i = 0; *digtable[i]; i++)
+ do_digraph(sp, NULL, 0, digtable[i]);
+ do_digraph(sp, NULL, 0, NULL);
+ return (0);
+}
+
+int
+ex_digraph(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ do_digraph(sp, ep, F_ISSET(cmdp, E_FORCE), cmdp->argv[0]->bp);
+ return (0);
+}
+
+static struct _DIG
+{
+ struct _DIG *next;
+ char key1;
+ char key2;
+ char dig;
+ char save;
+} *digs;
+
+int
+digraph(sp, key1, key2)
+ SCR *sp;
+ char key1; /* the underlying character */
+ char key2; /* the second character */
+{
+ int new_key;
+ register struct _DIG *dp;
+
+ /* if digraphs are disabled, then just return the new char */
+ if (O_ISSET(sp, O_DIGRAPH))
+ {
+ return key2;
+ }
+
+ /* remember the new key, so we can return it if this isn't a digraph */
+ new_key = key2;
+
+ /* sort key1 and key2, so that their original order won't matter */
+ if (key1 > key2)
+ {
+ key2 = key1;
+ key1 = new_key;
+ }
+
+ /* scan through the digraph chart */
+ for (dp = digs;
+ dp && (dp->key1 != key1 || dp->key2 != key2);
+ dp = dp->next)
+ {
+ }
+
+ /* if this combination isn't in there, just use the new key */
+ if (!dp)
+ {
+ return new_key;
+ }
+
+ /* else use the digraph key */
+ return dp->dig;
+}
+
+/* this function lists or defines digraphs */
+static void
+do_digraph(sp, ep, bang, extra)
+ SCR *sp;
+ EXF *ep;
+ int bang;
+ u_char *extra;
+{
+ int dig;
+ register struct _DIG *dp;
+ struct _DIG *prev;
+ static int user_defined = 0; /* boolean: are all later digraphs user-defined? */
+ char listbuf[8];
+
+ /* if "extra" is NULL, then we've reached the end of the built-ins */
+ if (!extra)
+ {
+ user_defined = 1;
+ return;
+ }
+
+ /* if no args, then display the existing digraphs */
+ if (*extra < ' ')
+ {
+ listbuf[0] = listbuf[1] = listbuf[2] = listbuf[5] = ' ';
+ listbuf[7] = '\0';
+ for (dig = 0, dp = digs; dp; dp = dp->next)
+ {
+ if (dp->save || bang)
+ {
+ dig += 7;
+ if (dig >= sp->cno)
+ {
+ addch('\n');
+ refresh();
+ dig = 7;
+ }
+ listbuf[3] = dp->key1;
+ listbuf[4] = dp->key2;
+ listbuf[6] = dp->dig;
+ addstr(listbuf);
+ }
+ }
+ addch('\n');
+ refresh();
+ return;
+ }
+
+ /* make sure we have at least two characters */
+ if (!extra[1])
+ {
+ msgq(sp, M_ERR,
+ "Digraphs must be composed of two characters");
+ return;
+ }
+
+ /* sort key1 and key2, so that their original order won't matter */
+ if (extra[0] > extra[1])
+ {
+ dig = extra[0];
+ extra[0] = extra[1];
+ extra[1] = dig;
+ }
+
+ /* locate the new digraph character */
+ for (dig = 2; extra[dig] == ' ' || extra[dig] == '\t'; dig++)
+ {
+ }
+ dig = extra[dig];
+ if (!bang && dig)
+ {
+ dig |= 0x80;
+ }
+
+ /* search for the digraph */
+ for (prev = (struct _DIG *)0, dp = digs;
+ dp && (dp->key1 != extra[0] || dp->key2 != extra[1]);
+ prev = dp, dp = dp->next)
+ {
+ }
+
+ /* deleting the digraph? */
+ if (!dig)
+ {
+ if (!dp)
+ {
+#ifndef CRUNCH
+ msgq(sp, M_ERR,
+ "%c%c not a digraph", extra[0], extra[1]);
+#endif
+ return;
+ }
+ if (prev)
+ prev->next = dp->next;
+ else
+ digs = dp->next;
+ free(dp);
+ return;
+ }
+
+ /* if necessary, create a new digraph struct for the new digraph */
+ if (dig && !dp)
+ {
+ MALLOC(sp, dp, struct _DIG *, sizeof(struct _DIG));
+ if (dp == NULL)
+ return;
+ if (prev)
+ prev->next = dp;
+ else
+ digs = dp;
+ dp->next = (struct _DIG *)0;
+ }
+
+ /* assign it the new digraph value */
+ dp->key1 = extra[0];
+ dp->key2 = extra[1];
+ dp->dig = dig;
+ dp->save = user_defined;
+}
+
+void
+digraph_save(sp, fd)
+ SCR *sp;
+ int fd;
+{
+ static char buf[] = "digraph! XX Y\n";
+ register struct _DIG *dp;
+
+ for (dp = digs; dp; dp = dp->next)
+ {
+ if (dp->save)
+ {
+ buf[9] = dp->key1;
+ buf[10] = dp->key2;
+ buf[12] = dp->dig;
+ write(fd, buf, (unsigned)14);
+ }
+ }
+}
+#endif
diff --git a/usr.bin/vi/nex/ex_display.c b/usr.bin/vi/nex/ex_display.c
new file mode 100644
index 000000000000..0cfef42f675b
--- /dev/null
+++ b/usr.bin/vi/nex/ex_display.c
@@ -0,0 +1,145 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_display.c 8.14 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <string.h>
+
+#include "vi.h"
+#include "tag.h"
+#include "excmd.h"
+
+static int bdisplay __P((SCR *, EXF *));
+static void db __P((SCR *, CB *, char *));
+
+/*
+ * ex_display -- :display b[uffers] | s[creens] | t[ags]
+ *
+ * Display buffers, tags or screens.
+ */
+int
+ex_display(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ switch (cmdp->argv[0]->bp[0]) {
+ case 'b':
+ return (bdisplay(sp, ep));
+ case 's':
+ return (ex_sdisplay(sp, ep));
+ case 't':
+ return (ex_tagdisplay(sp, ep));
+ }
+ msgq(sp, M_ERR,
+ "Unknown display argument %s, use b[uffers], s[creens], or t[ags].",
+ cmdp->argv[0]);
+ return (1);
+}
+
+/*
+ * bdisplay --
+ *
+ * Display buffers.
+ */
+static int
+bdisplay(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ CB *cbp;
+
+ if (sp->gp->cutq.lh_first == NULL && sp->gp->dcbp == NULL) {
+ (void)ex_printf(EXCOOKIE, "No cut buffers to display.");
+ return (0);
+ }
+
+ /* Buffers can be infinitely long, make it interruptible. */
+ F_SET(sp, S_INTERRUPTIBLE);
+
+ /* Display regular cut buffers. */
+ for (cbp = sp->gp->cutq.lh_first; cbp != NULL; cbp = cbp->q.le_next) {
+ if (isdigit(cbp->name))
+ continue;
+ if (cbp->textq.cqh_first != (void *)&cbp->textq)
+ db(sp, cbp, NULL);
+ if (F_ISSET(sp, S_INTERRUPTED))
+ return (0);
+ }
+ /* Display numbered buffers. */
+ for (cbp = sp->gp->cutq.lh_first; cbp != NULL; cbp = cbp->q.le_next) {
+ if (!isdigit(cbp->name))
+ continue;
+ if (cbp->textq.cqh_first != (void *)&cbp->textq)
+ db(sp, cbp, NULL);
+ if (F_ISSET(sp, S_INTERRUPTED))
+ return (0);
+ }
+ /* Display default buffer. */
+ if ((cbp = sp->gp->dcbp) != NULL)
+ db(sp, cbp, "default buffer");
+ return (0);
+}
+
+/*
+ * db --
+ * Display a buffer.
+ */
+static void
+db(sp, cbp, name)
+ SCR *sp;
+ CB *cbp;
+ char *name;
+{
+ TEXT *tp;
+ size_t len;
+ char *p;
+
+ (void)ex_printf(EXCOOKIE, "********** %s%s\n",
+ name == NULL ? charname(sp, cbp->name) : name,
+ F_ISSET(cbp, CB_LMODE) ? " (line mode)" : "");
+ for (tp = cbp->textq.cqh_first;
+ tp != (void *)&cbp->textq; tp = tp->q.cqe_next) {
+ for (len = tp->len, p = tp->lb; len--;) {
+ (void)ex_printf(EXCOOKIE, "%s", charname(sp, *p++));
+ if (F_ISSET(sp, S_INTERRUPTED))
+ return;
+ }
+ (void)ex_printf(EXCOOKIE, "\n");
+ }
+}
diff --git a/usr.bin/vi/nex/ex_edit.c b/usr.bin/vi/nex/ex_edit.c
new file mode 100644
index 000000000000..eeafedaed778
--- /dev/null
+++ b/usr.bin/vi/nex/ex_edit.c
@@ -0,0 +1,114 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_edit.c 8.14 (Berkeley) 1/22/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_edit -- :e[dit][!] [+cmd] [file]
+ * :vi[sual][!] [+cmd] [file]
+ *
+ * Edit a file; if none specified, re-edit the current file. The second
+ * form of the command can only be executed while in vi mode. See the
+ * hack in ex.c:ex_cmd().
+ *
+ * !!!
+ * Historic vi didn't permit the '+' command form without specifying
+ * a file name as well.
+ */
+int
+ex_edit(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ ARGS *ap;
+ FREF *frp;
+
+ frp = sp->frp;
+ switch (cmdp->argc) {
+ case 0:
+ /*
+ * If the name has been changed, we edit that file, not the
+ * original name. If the user was editing a temporary file,
+ * create another one. The reason for this is that we do
+ * special exit processing of temporary files, and reusing
+ * them is tricky.
+ */
+ if (frp->cname != NULL) {
+ if ((frp = file_add(sp, frp, frp->cname, 1)) == NULL)
+ return (1);
+ set_alt_name(sp, sp->frp->cname);
+ } else if (frp->name == NULL)
+ if ((frp = file_add(sp, frp, NULL, 1)) == NULL)
+ return (1);
+ break;
+ case 1:
+ ap = cmdp->argv[0];
+ if ((frp = file_add(sp, sp->frp, ap->bp, 1)) == NULL)
+ return (1);
+ set_alt_name(sp, ap->bp);
+ break;
+ default:
+ abort();
+ }
+
+ /*
+ * Check for modifications.
+ *
+ * !!!
+ * Contrary to POSIX 1003.2-1992, autowrite did not affect :edit.
+ */
+ if (F_ISSET(ep, F_MODIFIED) &&
+ ep->refcnt <= 1 && !F_ISSET(cmdp, E_FORCE)) {
+ msgq(sp, M_ERR,
+ "Modified since last write; write or use ! to override.");
+ return (1);
+ }
+
+ /* Switch files. */
+ if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE)))
+ return (1);
+ F_SET(sp, S_FSWITCH);
+ return (0);
+}
diff --git a/usr.bin/vi/nex/ex_equal.c b/usr.bin/vi/nex/ex_equal.c
new file mode 100644
index 000000000000..f0a18bef41f4
--- /dev/null
+++ b/usr.bin/vi/nex/ex_equal.c
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_equal.c 8.4 (Berkeley) 11/2/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_equal -- :address =
+ * Print out the line number matching the specified address, or the
+ * last line number in the file if no address specified.
+ */
+int
+ex_equal(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ recno_t lno;
+
+ if (F_ISSET(cmdp, E_ADDRDEF)) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ (void)ex_printf(EXCOOKIE, "%ld\n", lno);
+ } else
+ (void)ex_printf(EXCOOKIE, "%ld\n", cmdp->addr1.lno);
+ return (0);
+}
diff --git a/usr.bin/vi/nex/ex_exit.c b/usr.bin/vi/nex/ex_exit.c
new file mode 100644
index 000000000000..b0f929a5bbe3
--- /dev/null
+++ b/usr.bin/vi/nex/ex_exit.c
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_exit.c 8.7 (Berkeley) 12/10/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_quit -- :quit[!]
+ * Quit.
+ */
+int
+ex_quit(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ int force;
+
+ force = F_ISSET(cmdp, E_FORCE);
+
+ /* Check for modifications. */
+ if (F_ISSET(ep, F_MODIFIED) && ep->refcnt <= 1 && !force) {
+ msgq(sp, M_ERR,
+ "Modified since last write; write or use ! to override.");
+ return (1);
+ }
+
+ /*
+ * !!!
+ * Historic practice: quit! or two quit's done in succession
+ * (where ZZ counts as a quit) didn't check for other files.
+ *
+ * Also check for related screens; if they exist, quit, the
+ * user will get the message on the last screen.
+ */
+ if (!force && sp->ccnt != sp->q_ccnt + 1 &&
+ ep->refcnt <= 1 && file_unedited(sp) != NULL) {
+ sp->q_ccnt = sp->ccnt;
+ msgq(sp, M_ERR,
+ "More files; use \":n\" to go to the next file, \":q!\" to quit.");
+ return (1);
+ }
+
+ F_SET(sp, force ? S_EXIT_FORCE : S_EXIT);
+ return (0);
+}
diff --git a/usr.bin/vi/nex/ex_file.c b/usr.bin/vi/nex/ex_file.c
new file mode 100644
index 000000000000..d556d5214e0e
--- /dev/null
+++ b/usr.bin/vi/nex/ex_file.c
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_file.c 8.7 (Berkeley) 12/2/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_file -- :f[ile] [name]
+ * Status line and change the file's name.
+ */
+int
+ex_file(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ FREF *frp;
+ char *p, *t;
+
+ switch (cmdp->argc) {
+ case 0:
+ break;
+ case 1:
+ frp = sp->frp;
+
+ /* Make sure can allocate enough space. */
+ if ((p = strdup(cmdp->argv[0]->bp)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+
+ /* If already have a file name, it becomes the alternate. */
+ if ((t = FILENAME(frp)) != NULL)
+ set_alt_name(sp, t);
+
+ /* Free any previously changed name. */
+ if (frp->cname != NULL)
+ free(frp->cname);
+ frp->cname = p;
+
+ /* The read-only bit follows the file name; clear it. */
+ F_CLR(frp, FR_RDONLY);
+
+ /* Have to force a write if the file exists, next time. */
+ F_CLR(frp, FR_CHANGEWRITE);
+ break;
+ default:
+ abort();
+ }
+ status(sp, ep, sp->lno, 1);
+ return (0);
+}
diff --git a/usr.bin/vi/nex/ex_global.c b/usr.bin/vi/nex/ex_global.c
new file mode 100644
index 000000000000..4f942a39f680
--- /dev/null
+++ b/usr.bin/vi/nex/ex_global.c
@@ -0,0 +1,403 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_global.c 8.29 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "interrupt.h"
+
+enum which {GLOBAL, VGLOBAL};
+
+static int global __P((SCR *, EXF *, EXCMDARG *, enum which));
+static void global_intr __P((int));
+
+/*
+ * ex_global -- [line [,line]] g[lobal][!] /pattern/ [commands]
+ * Exec on lines matching a pattern.
+ */
+int
+ex_global(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (global(sp, ep,
+ cmdp, F_ISSET(cmdp, E_FORCE) ? VGLOBAL : GLOBAL));
+}
+
+/*
+ * ex_vglobal -- [line [,line]] v[global] /pattern/ [commands]
+ * Exec on lines not matching a pattern.
+ */
+int
+ex_vglobal(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (global(sp, ep, cmdp, VGLOBAL));
+}
+
+static int
+global(sp, ep, cmdp, cmd)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+ enum which cmd;
+{
+ DECLARE_INTERRUPTS;
+ RANGE *rp;
+ EX_PRIVATE *exp;
+ recno_t elno, lno;
+ regmatch_t match[1];
+ regex_t *re, lre;
+ size_t clen, len;
+ int delim, eval, reflags, replaced, rval;
+ char *cb, *ptrn, *p, *t;
+
+ /*
+ * Skip leading white space. Historic vi allowed any non-
+ * alphanumeric to serve as the global command delimiter.
+ */
+ for (p = cmdp->argv[0]->bp; isblank(*p); ++p);
+ if (*p == '\0' || isalnum(*p)) {
+ msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage);
+ return (1);
+ }
+ delim = *p++;
+
+ /*
+ * Get the pattern string, toss escaped characters.
+ *
+ * QUOTING NOTE:
+ * Only toss an escaped character if it escapes a delimiter.
+ */
+ for (ptrn = t = p;;) {
+ if (p[0] == '\0' || p[0] == delim) {
+ if (p[0] == delim)
+ ++p;
+ /*
+ * !!!
+ * Nul terminate the pattern string -- it's passed
+ * to regcomp which doesn't understand anything else.
+ */
+ *t = '\0';
+ break;
+ }
+ if (p[0] == '\\' && p[1] == delim)
+ ++p;
+ *t++ = *p++;
+ }
+
+ /* If the pattern string is empty, use the last one. */
+ if (*ptrn == '\0') {
+ if (!F_ISSET(sp, S_SRE_SET)) {
+ msgq(sp, M_ERR, "No previous regular expression.");
+ return (1);
+ }
+ re = &sp->sre;
+ } else {
+ /* Set RE flags. */
+ reflags = 0;
+ if (O_ISSET(sp, O_EXTENDED))
+ reflags |= REG_EXTENDED;
+ if (O_ISSET(sp, O_IGNORECASE))
+ reflags |= REG_ICASE;
+
+ /* Convert vi-style RE's to POSIX 1003.2 RE's. */
+ if (re_conv(sp, &ptrn, &replaced))
+ return (1);
+
+ /* Compile the RE. */
+ re = &lre;
+ eval = regcomp(re, ptrn, reflags);
+
+ /* Free up any allocated memory. */
+ if (replaced)
+ free(ptrn);
+
+ if (eval) {
+ re_error(sp, eval, re);
+ return (1);
+ }
+
+ /*
+ * Set saved RE. Historic practice is that
+ * globals set direction as well as the RE.
+ */
+ sp->sre = lre;
+ sp->searchdir = FORWARD;
+ F_SET(sp, S_SRE_SET);
+ }
+
+ /* Get a copy of the command string. */
+ if ((clen = strlen(p)) == 0) {
+ msgq(sp, M_ERR, "No command string specified.");
+ return (1);
+ }
+ MALLOC_RET(sp, cb, char *, clen);
+ memmove(cb, p, clen);
+
+ /*
+ * The global commands sets the substitute RE as well as
+ * the everything-else RE.
+ */
+ sp->subre = sp->sre;
+ F_SET(sp, S_SUBRE_SET);
+
+ /* Set the global flag, and set up interrupts. */
+ F_SET(sp, S_GLOBAL);
+ SET_UP_INTERRUPTS(global_intr);
+
+ /*
+ * For each line... The semantics of global matching are that we first
+ * have to decide which lines are going to get passed to the command,
+ * and then pass them to the command, ignoring other changes. There's
+ * really no way to do this in a single pass, since arbitrary line
+ * creation, deletion and movement can be done in the ex command. For
+ * example, a good vi clone test is ":g/X/mo.-3", or "g/X/.,.+1d".
+ * What we do is create linked list of lines that are tracked through
+ * each ex command. There's a callback routine which the DB interface
+ * routines call when a line is created or deleted. This doesn't help
+ * the layering much.
+ */
+ exp = EXP(sp);
+ for (rval = 0, lno = cmdp->addr1.lno,
+ elno = cmdp->addr2.lno; lno <= elno; ++lno) {
+ /* Someone's unhappy, time to stop. */
+ if (F_ISSET(sp, S_INTERRUPTED))
+ goto interrupted;
+
+ /* Get the line and search for a match. */
+ if ((t = file_gline(sp, ep, lno, &len)) == NULL) {
+ GETLINE_ERR(sp, lno);
+ goto err;
+ }
+ match[0].rm_so = 0;
+ match[0].rm_eo = len;
+ switch(eval = regexec(re, t, 1, match, REG_STARTEND)) {
+ case 0:
+ if (cmd == VGLOBAL)
+ continue;
+ break;
+ case REG_NOMATCH:
+ if (cmd == GLOBAL)
+ continue;
+ break;
+ default:
+ re_error(sp, eval, re);
+ goto err;
+ }
+
+ /* If follows the last entry, extend the last entry's range. */
+ if ((rp = exp->rangeq.cqh_last) != (void *)&exp->rangeq &&
+ rp->stop == lno - 1) {
+ ++rp->stop;
+ continue;
+ }
+
+ /* Allocate a new range, and append it to the list. */
+ CALLOC(sp, rp, RANGE *, 1, sizeof(RANGE));
+ if (rp == NULL)
+ goto err;
+ rp->start = rp->stop = lno;
+ CIRCLEQ_INSERT_TAIL(&exp->rangeq, rp, q);
+ }
+
+ exp = EXP(sp);
+ exp->range_lno = OOBLNO;
+ for (;;) {
+ /*
+ * Start at the beginning of the range each time, it may have
+ * been changed (or exhausted) if lines were inserted/deleted.
+ */
+ if ((rp = exp->rangeq.cqh_first) == (void *)&exp->rangeq)
+ break;
+ if (rp->start > rp->stop) {
+ CIRCLEQ_REMOVE(&exp->rangeq, exp->rangeq.cqh_first, q);
+ free(rp);
+ continue;
+ }
+
+ /*
+ * Execute the command, setting the cursor to the line so that
+ * relative addressing works. This means that the cursor moves
+ * to the last line sent to the command, by default, even if
+ * the command fails.
+ */
+ exp->range_lno = sp->lno = rp->start++;
+ if (ex_cmd(sp, ep, cb, clen))
+ goto err;
+
+ /* Someone's unhappy, time to stop. */
+ if (F_ISSET(sp, S_INTERRUPTED)) {
+interrupted: msgq(sp, M_INFO, "Interrupted.");
+ break;
+ }
+ }
+
+ /* Set the cursor to the new value, making sure it exists. */
+ if (exp->range_lno != OOBLNO) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ sp->lno =
+ lno < exp->range_lno ? (lno ? lno : 1) : exp->range_lno;
+ }
+ if (0) {
+err: rval = 1;
+ }
+
+interrupt_err:
+ F_CLR(sp, S_GLOBAL);
+ TEAR_DOWN_INTERRUPTS;
+
+ /* Free any remaining ranges and the command buffer. */
+ while ((rp = exp->rangeq.cqh_first) != (void *)&exp->rangeq) {
+ CIRCLEQ_REMOVE(&exp->rangeq, exp->rangeq.cqh_first, q);
+ free(rp);
+ }
+ free(cb);
+ return (rval);
+}
+
+/*
+ * global_insdel --
+ * Update the ranges based on an insertion or deletion.
+ */
+void
+global_insdel(sp, ep, op, lno)
+ SCR *sp;
+ EXF *ep;
+ enum operation op;
+ recno_t lno;
+{
+ EX_PRIVATE *exp;
+ RANGE *nrp, *rp;
+
+ exp = EXP(sp);
+
+ switch (op) {
+ case LINE_APPEND:
+ return;
+ case LINE_DELETE:
+ for (rp = exp->rangeq.cqh_first;
+ rp != (void *)&exp->rangeq; rp = nrp) {
+ nrp = rp->q.cqe_next;
+ /* If range less than the line, ignore it. */
+ if (rp->stop < lno)
+ continue;
+ /* If range greater than the line, decrement range. */
+ if (rp->start > lno) {
+ --rp->start;
+ --rp->stop;
+ continue;
+ }
+ /* Lno is inside the range, decrement the end point. */
+ if (rp->start > --rp->stop) {
+ CIRCLEQ_REMOVE(&exp->rangeq, rp, q);
+ free(rp);
+ }
+ }
+ break;
+ case LINE_INSERT:
+ for (rp = exp->rangeq.cqh_first;
+ rp != (void *)&exp->rangeq; rp = rp->q.cqe_next) {
+ /* If range less than the line, ignore it. */
+ if (rp->stop < lno)
+ continue;
+ /* If range greater than the line, increment range. */
+ if (rp->start >= lno) {
+ ++rp->start;
+ ++rp->stop;
+ continue;
+ }
+ /*
+ * Lno is inside the range, so the range must be split.
+ * Since we're inserting a new element, neither range
+ * can be exhausted.
+ */
+ CALLOC(sp, nrp, RANGE *, 1, sizeof(RANGE));
+ if (nrp == NULL) {
+ F_SET(sp, S_INTERRUPTED);
+ return;
+ }
+ nrp->start = lno + 1;
+ nrp->stop = rp->stop + 1;
+ rp->stop = lno - 1;
+ CIRCLEQ_INSERT_AFTER(&exp->rangeq, rp, nrp, q);
+ rp = nrp;
+ }
+ break;
+ case LINE_RESET:
+ return;
+ }
+ /*
+ * If the command deleted/inserted lines, the cursor moves to
+ * the line after the deleted/inserted line.
+ */
+ exp->range_lno = lno;
+}
+
+/*
+ * global_intr --
+ * Set the interrupt bit in any screen that is running an interruptible
+ * global.
+ *
+ * XXX
+ * In the future this may be a problem. The user should be able to move to
+ * another screen and keep typing while this runs. If so, and the user has
+ * more than one global running, it will be hard to decide which one to
+ * stop.
+ */
+static void
+global_intr(signo)
+ int signo;
+{
+ SCR *sp;
+
+ for (sp = __global_list->dq.cqh_first;
+ sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
+ if (F_ISSET(sp, S_GLOBAL) && F_ISSET(sp, S_INTERRUPTIBLE))
+ F_SET(sp, S_INTERRUPTED);
+}
diff --git a/usr.bin/vi/nex/ex_init.c b/usr.bin/vi/nex/ex_init.c
new file mode 100644
index 000000000000..26acf2f44c92
--- /dev/null
+++ b/usr.bin/vi/nex/ex_init.c
@@ -0,0 +1,189 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_init.c 8.11 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "tag.h"
+
+/*
+ * ex_screen_copy --
+ * Copy ex screen.
+ */
+int
+ex_screen_copy(orig, sp)
+ SCR *orig, *sp;
+{
+ EX_PRIVATE *oexp, *nexp;
+
+ /* Create the private ex structure. */
+ CALLOC_RET(orig, nexp, EX_PRIVATE *, 1, sizeof(EX_PRIVATE));
+ sp->ex_private = nexp;
+
+ /* Initialize queues. */
+ TAILQ_INIT(&nexp->tagq);
+ TAILQ_INIT(&nexp->tagfq);
+ CIRCLEQ_INIT(&nexp->rangeq);
+
+ if (orig == NULL) {
+ nexp->at_lbuf_set = 0;
+ } else {
+ oexp = EXP(orig);
+
+ nexp->at_lbuf = oexp->at_lbuf;
+ nexp->at_lbuf_set = oexp->at_lbuf_set;
+
+ if (oexp->lastbcomm != NULL &&
+ (nexp->lastbcomm = strdup(oexp->lastbcomm)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return(1);
+ }
+
+ if (ex_tagcopy(orig, sp))
+ return (1);
+ }
+
+ nexp->lastcmd = &cmds[C_PRINT];
+ return (0);
+}
+
+/*
+ * ex_screen_end --
+ * End a vi screen.
+ */
+int
+ex_screen_end(sp)
+ SCR *sp;
+{
+ EX_PRIVATE *exp;
+ int rval;
+
+ rval = 0;
+ exp = EXP(sp);
+
+ if (argv_free(sp))
+ rval = 1;
+
+ if (exp->ibp != NULL)
+ FREE(exp->ibp, exp->ibp_len);
+
+ if (exp->lastbcomm != NULL)
+ FREE(exp->lastbcomm, strlen(exp->lastbcomm) + 1);
+
+ /* Free private memory. */
+ FREE(exp, sizeof(EX_PRIVATE));
+ sp->ex_private = NULL;
+
+ return (rval);
+}
+
+/*
+ * ex_init --
+ * Initialize ex.
+ */
+int
+ex_init(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ size_t len;
+
+ /*
+ * The default address is the last line of the file. If the address
+ * set bit is on for this file, load the address, ensuring that it
+ * exists.
+ */
+ if (F_ISSET(sp->frp, FR_CURSORSET)) {
+ sp->lno = sp->frp->lno;
+ sp->cno = sp->frp->cno;
+
+ if (file_gline(sp, ep, sp->lno, &len) == NULL) {
+ if (file_lline(sp, ep, &sp->lno))
+ return (1);
+ if (sp->lno == 0)
+ sp->lno = 1;
+ sp->cno = 0;
+ } else if (sp->cno >= len)
+ sp->cno = 0;
+ } else {
+ if (file_lline(sp, ep, &sp->lno))
+ return (1);
+ if (sp->lno == 0)
+ sp->lno = 1;
+ sp->cno = 0;
+ }
+
+ /* Display the status line. */
+ return (status(sp, ep, sp->lno, 0));
+}
+
+/*
+ * ex_end --
+ * End ex session.
+ */
+int
+ex_end(sp)
+ SCR *sp;
+{
+ /* Save the cursor location. */
+ sp->frp->lno = sp->lno;
+ sp->frp->cno = sp->cno;
+ F_SET(sp->frp, FR_CURSORSET);
+
+ return (0);
+}
+
+/*
+ * ex_optchange --
+ * Handle change of options for vi.
+ */
+int
+ex_optchange(sp, opt)
+ SCR *sp;
+ int opt;
+{
+ switch (opt) {
+ case O_TAGS:
+ return (ex_tagalloc(sp, O_STR(sp, O_TAGS)));
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/nex/ex_join.c b/usr.bin/vi/nex/ex_join.c
new file mode 100644
index 000000000000..33bace97500e
--- /dev/null
+++ b/usr.bin/vi/nex/ex_join.c
@@ -0,0 +1,189 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_join.c 8.8 (Berkeley) 12/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_join -- :[line [,line]] j[oin][!] [count] [flags]
+ * Join lines.
+ */
+int
+ex_join(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ recno_t from, to;
+ size_t blen, clen, len, tlen;
+ int echar, extra, first;
+ char *bp, *p, *tbp;
+
+ from = cmdp->addr1.lno;
+ to = cmdp->addr2.lno;
+
+ /* Check for no lines to join. */
+ if ((p = file_gline(sp, ep, from + 1, &len)) == NULL) {
+ msgq(sp, M_ERR, "No following lines to join.");
+ return (1);
+ }
+
+ GET_SPACE_RET(sp, bp, blen, 256);
+
+ /*
+ * The count for the join command was off-by-one,
+ * historically, to other counts for other commands.
+ */
+ if (F_ISSET(cmdp, E_COUNT))
+ ++cmdp->addr2.lno;
+
+ /*
+ * If only a single address specified, or, the same address
+ * specified twice, the from/two addresses will be the same.
+ */
+ if (cmdp->addr1.lno == cmdp->addr2.lno)
+ ++cmdp->addr2.lno;
+
+ clen = tlen = 0;
+ for (first = 1, from = cmdp->addr1.lno,
+ to = cmdp->addr2.lno; from <= to; ++from) {
+ /*
+ * Get next line. Historic versions of vi allowed "10J" while
+ * less than 10 lines from the end-of-file, so we do too.
+ */
+ if ((p = file_gline(sp, ep, from, &len)) == NULL) {
+ cmdp->addr2.lno = from - 1;
+ break;
+ }
+
+ /* Empty lines just go away. */
+ if (len == 0)
+ continue;
+
+ /*
+ * Get more space if necessary. Note, tlen isn't the length
+ * of the new line, it's roughly the amount of space needed.
+ * tbp - bp is the length of the new line.
+ */
+ tlen += len + 2;
+ ADD_SPACE_RET(sp, bp, blen, tlen);
+ tbp = bp + clen;
+
+ /*
+ * Historic practice:
+ *
+ * If force specified, join without modification.
+ * If the current line ends with whitespace, strip leading
+ * whitespace from the joined line.
+ * If the next line starts with a ), do nothing.
+ * If the current line ends with ., ? or !, insert two spaces.
+ * Else, insert one space.
+ *
+ * Echar is the last character in the last line joined.
+ */
+ extra = 0;
+ if (!first && !F_ISSET(cmdp, E_FORCE)) {
+ if (isblank(echar))
+ for (; len && isblank(*p); --len, ++p);
+ else if (p[0] != ')') {
+ if (strchr(".?!", echar)) {
+ *tbp++ = ' ';
+ ++clen;
+ extra = 1;
+ }
+ *tbp++ = ' ';
+ ++clen;
+ for (; len && isblank(*p); --len, ++p);
+ }
+ }
+
+ if (len != 0) {
+ memmove(tbp, p, len);
+ tbp += len;
+ clen += len;
+ echar = p[len - 1];
+ } else
+ echar = ' ';
+
+ /*
+ * Historic practice for vi was to put the cursor at the first
+ * inserted whitespace character, if there was one, or the
+ * first character of the joined line, if there wasn't, or the
+ * last character of the line if joined to an empty line. If
+ * a count was specified, the cursor was moved as described
+ * for the first line joined, ignoring subsequent lines. If
+ * the join was a ':' command, the cursor was placed at the
+ * first non-blank character of the line unless the cursor was
+ * "attracted" to the end of line when the command was executed
+ * in which case it moved to the new end of line. There are
+ * probably several more special cases, but frankly, my dear,
+ * I don't give a damn. This implementation puts the cursor
+ * on the first inserted whitespace character, the first
+ * character of the joined line, or the last character of the
+ * line regardless. Note, if the cursor isn't on the joined
+ * line (possible with : commands), it is reset to the starting
+ * line.
+ */
+ if (first) {
+ sp->cno = (tbp - bp) - (1 + extra);
+ first = 0;
+ } else
+ sp->cno = (tbp - bp) - len - (1 + extra);
+ }
+ sp->lno = cmdp->addr1.lno;
+
+ /* Delete the joined lines. */
+ for (from = cmdp->addr1.lno, to = cmdp->addr2.lno; to > from; --to)
+ if (file_dline(sp, ep, to))
+ goto err;
+
+ /* Reset the original line. */
+ if (file_sline(sp, ep, from, bp, tbp - bp)) {
+err: FREE_SPACE(sp, bp, blen);
+ return (1);
+ }
+ FREE_SPACE(sp, bp, blen);
+
+ sp->rptlines[L_JOINED] += (cmdp->addr2.lno - cmdp->addr1.lno) + 1;
+ return (0);
+}
diff --git a/usr.bin/vi/nex/ex_map.c b/usr.bin/vi/nex/ex_map.c
new file mode 100644
index 000000000000..0154c34699be
--- /dev/null
+++ b/usr.bin/vi/nex/ex_map.c
@@ -0,0 +1,158 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_map.c 8.6 (Berkeley) 12/2/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "seq.h"
+#include "excmd.h"
+
+/*
+ * ex_map -- :map[!] [input] [replacement]
+ * Map a key/string or display mapped keys.
+ *
+ * Historical note:
+ * Historic vi maps were fairly bizarre, and likely to differ in
+ * very subtle and strange ways from this implementation. Two
+ * things worth noting are that vi would often hang or drop core
+ * if the map was strange enough (ex: map X "xy$@x^V), or, simply
+ * not work. One trick worth remembering is that if you put a
+ * mark at the start of the map, e.g. map X mx"xy ...), or if you
+ * put the map in a .exrc file, things would often work much better.
+ * No clue why.
+ */
+int
+ex_map(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ enum seqtype stype;
+ CHAR_T *input;
+ size_t nlen;
+ int key;
+ char *name, buf[10];
+
+ stype = F_ISSET(cmdp, E_FORCE) ? SEQ_INPUT : SEQ_COMMAND;
+
+ switch (cmdp->argc) {
+ case 0:
+ if (seq_dump(sp, stype, 1) == 0)
+ msgq(sp, M_INFO, "No %s map entries.",
+ stype == SEQ_INPUT ? "input" : "command");
+ return (0);
+ case 2:
+ input = cmdp->argv[0]->bp;
+ break;
+ default:
+ abort();
+ }
+
+ /*
+ * If the mapped string is #[0-9] (and wasn't quoted in any
+ * way, then map to a function key.
+ */
+ nlen = 0;
+ if (input[0] == '#' && isdigit(input[1]) && !input[2]) {
+ key = atoi(input + 1);
+ nlen = snprintf(buf, sizeof(buf), "f%d", key);
+#ifdef notdef
+ if (FKEY[key]) { /* CCC */
+ input = FKEY[key];
+ name = buf;
+ } else {
+ msgq(sp, M_ERR, "This terminal has no %s key.", buf);
+ return (1);
+ }
+#else
+ name = NULL;
+#endif
+ } else {
+ name = NULL;
+
+ /* Some single keys may not be remapped in command mode. */
+ if (stype == SEQ_COMMAND && input[1] == '\0')
+ switch (term_key_val(sp, input[0])) {
+ case K_COLON:
+ case K_CR:
+ case K_ESCAPE:
+ case K_NL:
+ msgq(sp, M_ERR,
+ "The %s character may not be remapped.",
+ charname(sp, input[0]));
+ return (1);
+ }
+ }
+ return (seq_set(sp, name, nlen, input, cmdp->argv[0]->len,
+ cmdp->argv[1]->bp, cmdp->argv[1]->len, stype, 1));
+}
+
+/*
+ * ex_unmap -- (:unmap[!] key)
+ * Unmap a key.
+ */
+int
+ex_unmap(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ if (seq_delete(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len,
+ F_ISSET(cmdp, E_FORCE) ? SEQ_INPUT : SEQ_COMMAND)) {
+ msgq(sp, M_INFO, "\"%s\" isn't mapped.", cmdp->argv[0]->bp);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * map_save --
+ * Save the mapped sequences to a file.
+ */
+int
+map_save(sp, fp)
+ SCR *sp;
+ FILE *fp;
+{
+ if (seq_save(sp, fp, "map ", SEQ_COMMAND))
+ return (1);
+ return (seq_save(sp, fp, "map! ", SEQ_INPUT));
+}
diff --git a/usr.bin/vi/nex/ex_mark.c b/usr.bin/vi/nex/ex_mark.c
new file mode 100644
index 000000000000..34efe21648b1
--- /dev/null
+++ b/usr.bin/vi/nex/ex_mark.c
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_mark.c 8.4 (Berkeley) 12/2/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+int
+ex_mark(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ if (cmdp->argv[0]->len != 1) {
+ msgq(sp, M_ERR, "Mark names must be a single character.");
+ return (1);
+ }
+ return (mark_set(sp, ep, cmdp->argv[0]->bp[0], &cmdp->addr1, 1));
+}
diff --git a/usr.bin/vi/nex/ex_mkexrc.c b/usr.bin/vi/nex/ex_mkexrc.c
new file mode 100644
index 000000000000..b26c7f814532
--- /dev/null
+++ b/usr.bin/vi/nex/ex_mkexrc.c
@@ -0,0 +1,120 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_mkexrc.c 8.8 (Berkeley) 12/2/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "seq.h"
+#include "pathnames.h"
+
+/*
+ * ex_mkexrc -- :mkexrc[!] [file]
+ *
+ * Create (or overwrite) a .exrc file with the current info.
+ */
+int
+ex_mkexrc(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ struct stat sb;
+ FILE *fp;
+ int fd, sverrno;
+ char *fname;
+
+ switch (cmdp->argc) {
+ case 0:
+ fname = _PATH_EXRC;
+ break;
+ case 1:
+ fname = cmdp->argv[0]->bp;
+ set_alt_name(sp, fname);
+ break;
+ default:
+ abort();
+ }
+
+ if (!F_ISSET(cmdp, E_FORCE) && !stat(fname, &sb)) {
+ msgq(sp, M_ERR,
+ "%s exists, not written; use ! to override.", fname);
+ return (1);
+ }
+
+ /* Create with max permissions of rw-r--r--. */
+ if ((fd = open(fname, O_CREAT | O_TRUNC | O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) {
+ msgq(sp, M_SYSERR, fname);
+ return (1);
+ }
+
+ if ((fp = fdopen(fd, "w")) == NULL) {
+ sverrno = errno;
+ (void)close(fd);
+ errno = sverrno;
+ goto e2;
+ }
+
+ if (abbr_save(sp, fp) || ferror(fp))
+ goto e1;
+ if (map_save(sp, fp) || ferror(fp))
+ goto e1;
+ if (opts_save(sp, fp) || ferror(fp))
+ goto e1;
+#ifndef NO_DIGRAPH
+ digraph_save(sp, fd);
+#endif
+ if (fclose(fp))
+ goto e2;
+
+ msgq(sp, M_INFO, "New .exrc file: %s. ", fname);
+ return (0);
+
+e1: sverrno = errno;
+ (void)fclose(fp);
+ errno = sverrno;
+e2: msgq(sp, M_ERR, "%s: incomplete: %s", fname, strerror(errno));
+ return (1);
+}
diff --git a/usr.bin/vi/nex/ex_move.c b/usr.bin/vi/nex/ex_move.c
new file mode 100644
index 000000000000..965618b0834f
--- /dev/null
+++ b/usr.bin/vi/nex/ex_move.c
@@ -0,0 +1,133 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_move.c 8.6 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+enum which {COPY, MOVE};
+static int cm __P((SCR *, EXF *, EXCMDARG *, enum which));
+
+/*
+ * ex_copy -- :[line [,line]] co[py] line [flags]
+ * Copy selected lines.
+ */
+int
+ex_copy(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (cm(sp, ep, cmdp, COPY));
+}
+
+/*
+ * ex_move -- :[line [,line]] co[py] line
+ * Move selected lines.
+ */
+int
+ex_move(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (cm(sp, ep, cmdp, MOVE));
+}
+
+static int
+cm(sp, ep, cmdp, cmd)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+ enum which cmd;
+{
+ CB cb;
+ MARK fm1, fm2, m, tm;
+ recno_t diff;
+ int rval;
+
+ fm1 = cmdp->addr1;
+ fm2 = cmdp->addr2;
+ tm.lno = cmdp->lineno;
+ tm.cno = 0;
+
+ /* Make sure the destination is valid. */
+ if (cmd == MOVE && tm.lno >= fm1.lno && tm.lno < fm2.lno) {
+ msgq(sp, M_ERR, "Destination line is inside move range.");
+ return (1);
+ }
+
+ /* Save the text to a cut buffer. */
+ memset(&cb, 0, sizeof(cb));
+ CIRCLEQ_INIT(&cb.textq);
+ if (cut(sp, ep, &cb, NULL, &fm1, &fm2, CUT_LINEMODE))
+ return (1);
+
+ /* If we're not copying, delete the old text and adjust tm. */
+ if (cmd == MOVE) {
+ if (delete(sp, ep, &fm1, &fm2, 1)) {
+ rval = 1;
+ goto err;
+ }
+ if (tm.lno >= fm1.lno)
+ tm.lno -= (fm2.lno - fm1.lno) + 1;
+ }
+
+ /* Add the new text. */
+ if (put(sp, ep, &cb, NULL, &tm, &m, 1)) {
+ rval = 1;
+ goto err;
+ }
+
+ /*
+ * Move and copy put the cursor on the last line moved or copied.
+ * The returned cursor from the put routine is the first line put,
+ * not the last, because that's the semantics of vi.
+ */
+ diff = (fm2.lno - fm1.lno) + 1;
+ sp->lno = m.lno + (diff - 1);
+ sp->cno = 0;
+
+ sp->rptlines[cmd == COPY ? L_COPIED : L_MOVED] += diff;
+ rval = 0;
+
+err: (void)text_lfree(&cb.textq);
+ return (rval);
+}
diff --git a/usr.bin/vi/nex/ex_open.c b/usr.bin/vi/nex/ex_open.c
new file mode 100644
index 000000000000..77b418a41d6f
--- /dev/null
+++ b/usr.bin/vi/nex/ex_open.c
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)ex_open.c 8.2 (Berkeley) 10/3/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_open -- :[line] o[pen] [/pattern/] [flags]
+ *
+ * Switch to single line "open" mode.
+ */
+int
+ex_open(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ /* If open option off, disallow open command. */
+ if (!O_ISSET(sp, O_OPEN)) {
+ msgq(sp, M_ERR,
+ "The open command requires that the open option be set.");
+ return (1);
+ }
+
+ msgq(sp, M_ERR, "The open command is not yet implemented.");
+ return (1);
+}
diff --git a/usr.bin/vi/nex/ex_preserve.c b/usr.bin/vi/nex/ex_preserve.c
new file mode 100644
index 000000000000..a7f35485698d
--- /dev/null
+++ b/usr.bin/vi/nex/ex_preserve.c
@@ -0,0 +1,80 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)ex_preserve.c 8.4 (Berkeley) 11/8/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_preserve -- :pre[serve]
+ * Push the file to recovery.
+ */
+int
+ex_preserve(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ recno_t lno;
+
+ if (!F_ISSET(ep, F_RCV_ON)) {
+ msgq(sp, M_ERR, "Preservation of this file not possible.");
+ return (1);
+ }
+
+ /* If recovery not initialized, do so. */
+ if (F_ISSET(ep, F_FIRSTMODIFY) && rcv_init(sp, ep))
+ return (1);
+
+ /* Force the file to be read in, in case it hasn't yet. */
+ if (file_lline(sp, ep, &lno))
+ return (1);
+
+ /* Sync to disk. */
+ if (rcv_sync(sp, ep))
+ return (1);
+
+ /* Preserve the recovery files. */
+ F_SET(ep, F_RCV_NORM);
+
+ msgq(sp, M_INFO, "File preserved.");
+ return (0);
+}
diff --git a/usr.bin/vi/nex/ex_print.c b/usr.bin/vi/nex/ex_print.c
new file mode 100644
index 000000000000..d25e9ff563b1
--- /dev/null
+++ b/usr.bin/vi/nex/ex_print.c
@@ -0,0 +1,196 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_print.c 8.6 (Berkeley) 11/2/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_list -- :[line [,line]] l[ist] [count] [flags]
+ *
+ * Display the addressed lines such that the output is unambiguous.
+ */
+int
+ex_list(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ if (ex_print(sp, ep,
+ &cmdp->addr1, &cmdp->addr2, cmdp->flags | E_F_LIST))
+ return (1);
+ sp->lno = cmdp->addr2.lno;
+ sp->cno = cmdp->addr2.cno;
+ return (0);
+}
+
+/*
+ * ex_number -- :[line [,line]] nu[mber] [count] [flags]
+ *
+ * Display the addressed lines with a leading line number.
+ */
+int
+ex_number(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ if (ex_print(sp, ep,
+ &cmdp->addr1, &cmdp->addr2, cmdp->flags | E_F_HASH))
+ return (1);
+ sp->lno = cmdp->addr2.lno;
+ sp->cno = cmdp->addr2.cno;
+ return (0);
+}
+
+/*
+ * ex_pr -- :[line [,line]] p[rint] [count] [flags]
+ *
+ * Display the addressed lines.
+ */
+int
+ex_pr(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ if (ex_print(sp, ep, &cmdp->addr1, &cmdp->addr2, cmdp->flags))
+ return (1);
+ sp->lno = cmdp->addr2.lno;
+ sp->cno = cmdp->addr2.cno;
+ return (0);
+}
+
+/*
+ * ex_print --
+ * Print the selected lines.
+ */
+int
+ex_print(sp, ep, fp, tp, flags)
+ SCR *sp;
+ EXF *ep;
+ MARK *fp, *tp;
+ register int flags;
+{
+ register int ch, col, rlen;
+ recno_t from, to;
+ size_t len;
+ int cnt;
+ char *p, buf[10];
+
+ F_SET(sp, S_INTERRUPTIBLE);
+ for (from = fp->lno, to = tp->lno; from <= to; ++from) {
+ /* Display the line number. */
+ if (LF_ISSET(E_F_HASH))
+ col = ex_printf(EXCOOKIE, "%7ld ", from);
+ else
+ col = 0;
+
+ /*
+ * Display the line. The format for E_F_PRINT isn't very good,
+ * especially in handling end-of-line tabs, but they're almost
+ * backward compatible.
+ */
+ if ((p = file_gline(sp, ep, from, &len)) == NULL) {
+ GETLINE_ERR(sp, from);
+ return (1);
+ }
+
+#define WCHECK(ch) { \
+ if (col == sp->cols) { \
+ (void)ex_printf(EXCOOKIE, "\n"); \
+ col = 0; \
+ } \
+ (void)ex_printf(EXCOOKIE, "%c", ch); \
+ ++col; \
+}
+ for (rlen = len; rlen--;) {
+ ch = *p++;
+ if (LF_ISSET(E_F_LIST))
+ if (ch != '\t' && isprint(ch)) {
+ WCHECK(ch);
+ } else if (ch & 0x80) {
+ (void)snprintf(buf,
+ sizeof(buf), "\\%03o", ch);
+ len = strlen(buf);
+ for (cnt = 0; cnt < len; ++cnt)
+ WCHECK(buf[cnt]);
+ } else {
+ WCHECK('^');
+ WCHECK(ch + 0x40);
+ }
+ else {
+ ch &= 0x7f;
+ if (ch == '\t') {
+ while (col < sp->cols &&
+ ++col % O_VAL(sp, O_TABSTOP))
+ (void)ex_printf(EXCOOKIE, " ");
+ if (col == sp->cols) {
+ col = 0;
+ (void)ex_printf(EXCOOKIE, "\n");
+ }
+ } else if (isprint(ch)) {
+ WCHECK(ch);
+ } else if (ch == '\n') {
+ col = 0;
+ (void)ex_printf(EXCOOKIE, "\n");
+ } else {
+ WCHECK('^');
+ WCHECK(ch + 0x40);
+ }
+ }
+ }
+ if (LF_ISSET(E_F_LIST)) {
+ WCHECK('$');
+ } else if (len == 0) {
+ /*
+ * If the line is empty, output a space
+ * to overwrite the colon prompt.
+ */
+ WCHECK(' ');
+ }
+ (void)ex_printf(EXCOOKIE, "\n");
+
+ if (F_ISSET(sp, S_INTERRUPTED))
+ break;
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/nex/ex_put.c b/usr.bin/vi/nex/ex_put.c
new file mode 100644
index 000000000000..3bc687fd1496
--- /dev/null
+++ b/usr.bin/vi/nex/ex_put.c
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_put.c 8.4 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_put -- [line] pu[t] [buffer]
+ *
+ * Append a cut buffer into the file.
+ */
+int
+ex_put(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ MARK m;
+
+ m.lno = sp->lno;
+ m.cno = sp->cno;
+ if (put(sp, ep, NULL, F_ISSET(cmdp, E_BUFFER) ? &cmdp->buffer : NULL,
+ &cmdp->addr1, &m, 1))
+ return (1);
+ return (0);
+}
diff --git a/usr.bin/vi/nex/ex_read.c b/usr.bin/vi/nex/ex_read.c
new file mode 100644
index 000000000000..9328b93c329a
--- /dev/null
+++ b/usr.bin/vi/nex/ex_read.c
@@ -0,0 +1,223 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_read.c 8.21 (Berkeley) 1/23/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_read -- :read [file]
+ * :read [! cmd]
+ * Read from a file or utility.
+ */
+int
+ex_read(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ struct stat sb;
+ FILE *fp;
+ MARK rm;
+ recno_t nlines;
+ size_t blen, len;
+ int rval;
+ char *bp, *name;
+
+ /*
+ * If "read !", it's a pipe from a utility.
+ *
+ * !!!
+ * Historical vi wouldn't undo a filter read, for no apparent
+ * reason.
+ */
+ if (F_ISSET(cmdp, E_FORCE)) {
+ /* Expand the user's argument. */
+ if (argv_exp1(sp, ep,
+ cmdp, cmdp->argv[0]->bp, cmdp->argv[0]->len, 0))
+ return (1);
+
+ /* If argc still 1, there wasn't anything to expand. */
+ if (cmdp->argc == 1) {
+ msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage);
+ return (1);
+ }
+
+ /* Redisplay the user's argument if it's changed. */
+ if (F_ISSET(cmdp, E_MODIFY) && IN_VI_MODE(sp)) {
+ len = cmdp->argv[1]->len;
+ GET_SPACE_RET(sp, bp, blen, len + 2);
+ bp[0] = '!';
+ memmove(bp + 1, cmdp->argv[1], cmdp->argv[1]->len + 1);
+ (void)sp->s_busy(sp, bp);
+ FREE_SPACE(sp, bp, blen);
+ }
+
+ if (filtercmd(sp, ep,
+ &cmdp->addr1, NULL, &rm, cmdp->argv[1]->bp, FILTER_READ))
+ return (1);
+ sp->lno = rm.lno;
+ return (0);
+ }
+
+ /* Expand the user's argument. */
+ if (argv_exp2(sp, ep,
+ cmdp, cmdp->argv[0]->bp, cmdp->argv[0]->len, 0))
+ return (1);
+
+ switch (cmdp->argc) {
+ case 1:
+ /*
+ * No arguments, read the current file.
+ * Doesn't set the alternate file name.
+ */
+ name = FILENAME(sp->frp);
+ break;
+ case 2:
+ /*
+ * One argument, read it.
+ * Sets the alternate file name.
+ */
+ name = cmdp->argv[1]->bp;
+ set_alt_name(sp, name);
+ break;
+ default:
+ /* If expanded to more than one argument, object. */
+ msgq(sp, M_ERR,
+ "%s expanded into too many file names", cmdp->argv[0]->bp);
+ msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage);
+ return (1);
+ }
+
+ /*
+ * !!!
+ * Historically, vi did not permit reads from non-regular files,
+ * nor did it distinguish between "read !" and "read!", so there
+ * was no way to "force" it.
+ */
+ if ((fp = fopen(name, "r")) == NULL || fstat(fileno(fp), &sb)) {
+ msgq(sp, M_SYSERR, name);
+ return (1);
+ }
+ if (!S_ISREG(sb.st_mode)) {
+ (void)fclose(fp);
+ msgq(sp, M_ERR, "Only regular files may be read.");
+ return (1);
+ }
+
+ rval = ex_readfp(sp, ep, name, fp, &cmdp->addr1, &nlines, 1);
+
+ /*
+ * Set the cursor to the first line read in, if anything read
+ * in, otherwise, the address. (Historic vi set it to the
+ * line after the address regardless, but since that line may
+ * not exist we don't bother.)
+ */
+ sp->lno = cmdp->addr1.lno;
+ if (nlines)
+ ++sp->lno;
+
+ F_SET(EXP(sp), EX_AUTOPRINT);
+ return (rval);
+}
+
+/*
+ * ex_readfp --
+ * Read lines into the file.
+ */
+int
+ex_readfp(sp, ep, name, fp, fm, nlinesp, success_msg)
+ SCR *sp;
+ EXF *ep;
+ char *name;
+ FILE *fp;
+ MARK *fm;
+ recno_t *nlinesp;
+ int success_msg;
+{
+ EX_PRIVATE *exp;
+ u_long ccnt;
+ size_t len;
+ recno_t lno, nlines;
+ int rval;
+
+ /*
+ * Add in the lines from the output. Insertion starts at the line
+ * following the address.
+ */
+ ccnt = 0;
+ rval = 0;
+ exp = EXP(sp);
+ for (lno = fm->lno; !ex_getline(sp, fp, &len); ++lno) {
+ if (file_aline(sp, ep, 1, lno, exp->ibp, len)) {
+ rval = 1;
+ break;
+ }
+ ccnt += len;
+ }
+
+ if (ferror(fp)) {
+ msgq(sp, M_SYSERR, name);
+ rval = 1;
+ }
+
+ if (fclose(fp)) {
+ msgq(sp, M_SYSERR, name);
+ return (1);
+ }
+
+ if (rval)
+ return (1);
+
+ /* Return the number of lines read in. */
+ nlines = lno - fm->lno;
+ if (nlinesp != NULL)
+ *nlinesp = nlines;
+
+ if (success_msg)
+ msgq(sp, M_INFO, "%s: %lu line%s, %lu characters.",
+ name, nlines, nlines == 1 ? "" : "s", ccnt);
+
+ return (0);
+}
diff --git a/usr.bin/vi/nex/ex_screen.c b/usr.bin/vi/nex/ex_screen.c
new file mode 100644
index 000000000000..0fd000d4898a
--- /dev/null
+++ b/usr.bin/vi/nex/ex_screen.c
@@ -0,0 +1,134 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)ex_screen.c 8.10 (Berkeley) 12/2/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_split -- :s[plit] [file ...]
+ * Split the screen, optionally setting the file list.
+ */
+int
+ex_split(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (sp->s_split(sp, cmdp->argc ? cmdp->argv : NULL));
+}
+
+/*
+ * ex_bg -- :bg
+ * Hide the screen.
+ */
+int
+ex_bg(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (sp->s_bg(sp));
+}
+
+/*
+ * ex_fg -- :fg [file]
+ * Show the screen.
+ */
+int
+ex_fg(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (sp->s_fg(sp, cmdp->argc ? cmdp->argv[0]->bp : NULL));
+}
+
+/*
+ * ex_resize -- :resize [change]
+ * Change the screen size.
+ */
+int
+ex_resize(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ if (!F_ISSET(cmdp, E_COUNT))
+ cmdp->count = 1;
+ return (sp->s_rabs(sp, cmdp->count));
+}
+
+/*
+ * ex_sdisplay --
+ * Display the list of screens.
+ */
+int
+ex_sdisplay(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ SCR *tsp;
+ int cnt, col, len, sep;
+
+ if ((tsp = sp->gp->hq.cqh_first) == (void *)&sp->gp->hq) {
+ (void)ex_printf(EXCOOKIE,
+ "No backgrounded screens to display.\n");
+ return (0);
+ }
+
+ col = len = sep = 0;
+ for (cnt = 1; tsp != (void *)&sp->gp->hq; tsp = tsp->q.cqe_next) {
+ col += len = strlen(FILENAME(tsp->frp)) + sep;
+ if (col >= sp->cols - 1) {
+ col = len;
+ sep = 0;
+ (void)ex_printf(EXCOOKIE, "\n");
+ } else if (cnt != 1) {
+ sep = 1;
+ (void)ex_printf(EXCOOKIE, " ");
+ }
+ (void)ex_printf(EXCOOKIE, "%s", FILENAME(tsp->frp));
+ ++cnt;
+ }
+ (void)ex_printf(EXCOOKIE, "\n");
+ return (0);
+}
diff --git a/usr.bin/vi/nex/ex_script.c b/usr.bin/vi/nex/ex_script.c
new file mode 100644
index 000000000000..606e479d6af4
--- /dev/null
+++ b/usr.bin/vi/nex/ex_script.c
@@ -0,0 +1,570 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_script.c 8.10 (Berkeley) 12/22/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "script.h"
+
+/*
+ * XXX
+ */
+int openpty __P((int *, int *, char *, struct termios *, struct winsize *));
+
+static int sscr_getprompt __P((SCR *, EXF *));
+static int sscr_init __P((SCR *, EXF *));
+static int sscr_matchprompt __P((SCR *, char *, size_t, size_t *));
+static int sscr_setprompt __P((SCR *, char *, size_t));
+
+/*
+ * ex_script -- : sc[ript][!] [file]
+ *
+ * Switch to script mode.
+ */
+int
+ex_script(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ /* Vi only command. */
+ if (!IN_VI_MODE(sp)) {
+ msgq(sp, M_ERR,
+ "The script command is only available in vi mode.");
+ return (1);
+ }
+
+ /* Switch to the new file. */
+ if (cmdp->argc != 0 && ex_edit(sp, ep, cmdp))
+ return (1);
+
+ /*
+ * Create the shell, figure out the prompt.
+ *
+ * !!!
+ * The files just switched, use sp->ep.
+ */
+ if (sscr_init(sp, sp->ep))
+ return (1);
+
+ return (0);
+}
+
+/*
+ * sscr_init --
+ * Create a pty setup for a shell.
+ */
+static int
+sscr_init(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ SCRIPT *sc;
+ char *sh, *sh_path;
+
+ MALLOC_RET(sp, sc, SCRIPT *, sizeof(SCRIPT));
+ sp->script = sc;
+ sc->sh_prompt = NULL;
+ sc->sh_prompt_len = 0;
+
+ /*
+ * There are two different processes running through this code.
+ * They are the shell and the parent.
+ */
+ sc->sh_master = sc->sh_slave = -1;
+
+ if (tcgetattr(STDIN_FILENO, &sc->sh_term) == -1) {
+ msgq(sp, M_SYSERR, "tcgetattr");
+ goto err;
+ }
+
+ /*
+ * Turn off output postprocessing and echo.
+ */
+ sc->sh_term.c_oflag &= ~OPOST;
+ sc->sh_term.c_cflag &= ~(ECHO|ECHOE|ECHONL|ECHOK);
+
+ if (ioctl(STDIN_FILENO, TIOCGWINSZ, &sc->sh_win) == -1) {
+ msgq(sp, M_SYSERR, "tcgetattr");
+ goto err;
+ }
+
+ if (openpty(&sc->sh_master,
+ &sc->sh_slave, sc->sh_name, &sc->sh_term, &sc->sh_win) == -1) {
+ msgq(sp, M_SYSERR, "openpty");
+ goto err;
+ }
+
+ /*
+ * Don't use vfork() here, because the signal semantics
+ * differ from implementation to implementation.
+ */
+ switch (sc->sh_pid = fork()) {
+ case -1: /* Error. */
+ msgq(sp, M_SYSERR, "fork");
+err: if (sc->sh_master != -1)
+ (void)close(sc->sh_master);
+ if (sc->sh_slave != -1)
+ (void)close(sc->sh_slave);
+ return (1);
+ case 0: /* Utility. */
+ /*
+ * The utility has default signal behavior. Don't bother
+ * using sigaction(2) 'cause we want the default behavior.
+ */
+ (void)signal(SIGINT, SIG_DFL);
+ (void)signal(SIGQUIT, SIG_DFL);
+
+ /*
+ * XXX
+ * So that shells that do command line editing turn it off.
+ */
+ (void)putenv("TERM=emacs");
+ (void)putenv("TERMCAP=emacs:");
+ (void)putenv("EMACS=t");
+
+ (void)setsid();
+#ifdef TIOCSCTTY
+ /*
+ * 4.4BSD allocates a controlling terminal using the TIOCSCTTY
+ * ioctl, not by opening a terminal device file. POSIX 1003.1
+ * doesn't define a portable way to do this. If TIOCSCTTY is
+ * not available, hope that the open does it.
+ */
+ (void)ioctl(sc->sh_slave, TIOCSCTTY, 0);
+#endif
+ (void)close(sc->sh_master);
+ (void)dup2(sc->sh_slave, STDIN_FILENO);
+ (void)dup2(sc->sh_slave, STDOUT_FILENO);
+ (void)dup2(sc->sh_slave, STDERR_FILENO);
+ (void)close(sc->sh_slave);
+
+ /* Assumes that all shells have -i. */
+ sh_path = O_STR(sp, O_SHELL);
+ if ((sh = strrchr(sh_path, '/')) == NULL)
+ sh = sh_path;
+ else
+ ++sh;
+ execl(sh_path, sh, "-i", NULL);
+ msgq(sp, M_ERR,
+ "Error: execl: %s: %s", sh_path, strerror(errno));
+ _exit(127);
+ default:
+ break;
+ }
+
+ if (sscr_getprompt(sp, ep))
+ return (1);
+
+ F_SET(sp, S_REDRAW | S_SCRIPT);
+ return (0);
+
+}
+
+/*
+ * sscr_getprompt --
+ * Eat lines printed by the shell until a line with no trailing
+ * carriage return comes; set the prompt from that line.
+ */
+static int
+sscr_getprompt(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ struct timeval tv;
+ SCRIPT *sc;
+ fd_set fdset;
+ recno_t lline;
+ size_t llen, len;
+ u_int value;
+ int nr;
+ char *endp, *p, *t, buf[1024];
+
+ FD_ZERO(&fdset);
+ endp = buf;
+ len = sizeof(buf);
+
+ /* Wait up to a second for characters to read. */
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ sc = sp->script;
+ FD_SET(sc->sh_master, &fdset);
+ switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) {
+ case -1: /* Error or interrupt. */
+ msgq(sp, M_SYSERR, "select");
+ goto prompterr;
+ case 0: /* Timeout */
+ msgq(sp, M_ERR, "Error: timed out.");
+ goto prompterr;
+ case 1: /* Characters to read. */
+ break;
+ }
+
+ /* Read the characters. */
+more: len = sizeof(buf) - (endp - buf);
+ switch (nr = read(sc->sh_master, endp, len)) {
+ case 0: /* EOF. */
+ msgq(sp, M_ERR, "Error: shell: EOF");
+ goto prompterr;
+ case -1: /* Error or interrupt. */
+ msgq(sp, M_SYSERR, "shell");
+ goto prompterr;
+ default:
+ endp += nr;
+ break;
+ }
+
+ /* If any complete lines, push them into the file. */
+ for (p = t = buf; p < endp; ++p) {
+ value = term_key_val(sp, *p);
+ if (value == K_CR || value == K_NL) {
+ if (file_lline(sp, ep, &lline) ||
+ file_aline(sp, ep, 0, lline, t, p - t))
+ goto prompterr;
+ t = p + 1;
+ }
+ }
+ if (p > buf) {
+ memmove(buf, t, endp - t);
+ endp = buf + (endp - t);
+ }
+ if (endp == buf)
+ goto more;
+
+ /* Wait up 1/10 of a second to make sure that we got it all. */
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+ switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) {
+ case -1: /* Error or interrupt. */
+ msgq(sp, M_SYSERR, "select");
+ goto prompterr;
+ case 0: /* Timeout */
+ break;
+ case 1: /* Characters to read. */
+ goto more;
+ }
+
+ /* Timed out, so theoretically we have a prompt. */
+ llen = endp - buf;
+ endp = buf;
+
+ /* Append the line into the file. */
+ if (file_lline(sp, ep, &lline) ||
+ file_aline(sp, ep, 0, lline, buf, llen)) {
+prompterr: sscr_end(sp);
+ return (1);
+ }
+
+ return (sscr_setprompt(sp, buf, llen));
+}
+
+/*
+ * sscr_exec --
+ * Take a line and hand it off to the shell.
+ */
+int
+sscr_exec(sp, ep, lno)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+{
+ SCRIPT *sc;
+ recno_t last_lno;
+ size_t blen, len, last_len, tlen;
+ int matchprompt, nw, rval;
+ char *bp, *p;
+
+ /* If there's a prompt on the last line, append the command. */
+ if (file_lline(sp, ep, &last_lno))
+ return (1);
+ if ((p = file_gline(sp, ep, last_lno, &last_len)) == NULL) {
+ GETLINE_ERR(sp, last_lno);
+ return (1);
+ }
+ if (sscr_matchprompt(sp, p, last_len, &tlen) && tlen == 0) {
+ matchprompt = 1;
+ GET_SPACE_RET(sp, bp, blen, last_len + 128);
+ memmove(bp, p, last_len);
+ } else
+ matchprompt = 0;
+
+ /* Get something to execute. */
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ goto err1;
+ if (lno == 0)
+ goto empty;
+ else
+ GETLINE_ERR(sp, lno);
+ goto err1;
+ }
+
+ /* Empty lines aren't interesting. */
+ if (len == 0)
+ goto empty;
+
+ /* Delete any prompt. */
+ if (sscr_matchprompt(sp, p, len, &tlen)) {
+ if (tlen == len) {
+empty: msgq(sp, M_BERR, "Nothing to execute.");
+ goto err1;
+ }
+ p += (len - tlen);
+ len = tlen;
+ }
+
+ /* Push the line to the shell. */
+ sc = sp->script;
+ if ((nw = write(sc->sh_master, p, len)) != len)
+ goto err2;
+ rval = 0;
+ if (write(sc->sh_master, "\n", 1) != 1) {
+err2: if (nw == 0)
+ errno = EIO;
+ msgq(sp, M_SYSERR, "shell");
+ goto err1;
+ }
+
+ if (matchprompt) {
+ ADD_SPACE_RET(sp, bp, blen, last_len + len);
+ memmove(bp + last_len, p, len);
+ if (file_sline(sp, ep, last_lno, bp, last_len + len))
+err1: rval = 1;
+ }
+ if (matchprompt)
+ FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
+
+/*
+ * sscr_input --
+ * Take a line from the shell and insert it into the file.
+ */
+int
+sscr_input(sp)
+ SCR *sp;
+{
+ struct timeval tv;
+ SCRIPT *sc;
+ EXF *ep;
+ recno_t lno;
+ size_t blen, len, tlen;
+ u_int value;
+ int nr, rval;
+ char *bp, *endp, *p, *t;
+
+ /* Find out where the end of the file is. */
+ ep = sp->ep;
+ if (file_lline(sp, ep, &lno))
+ return (1);
+
+#define MINREAD 1024
+ GET_SPACE_RET(sp, bp, blen, MINREAD);
+ endp = bp;
+
+ /* Read the characters. */
+ rval = 1;
+ sc = sp->script;
+more: switch (nr = read(sc->sh_master, endp, MINREAD)) {
+ case 0: /* EOF; shell just exited. */
+ sscr_end(sp);
+ F_CLR(sp, S_SCRIPT);
+ rval = 0;
+ goto ret;
+ case -1: /* Error or interrupt. */
+ msgq(sp, M_SYSERR, "shell");
+ goto ret;
+ default:
+ endp += nr;
+ break;
+ }
+
+ /* Append the lines into the file. */
+ for (p = t = bp; p < endp; ++p) {
+ value = term_key_val(sp, *p);
+ if (value == K_CR || value == K_NL) {
+ len = p - t;
+ if (file_aline(sp, ep, 1, lno++, t, len))
+ goto ret;
+ t = p + 1;
+ }
+ }
+ if (p > t) {
+ len = p - t;
+ /*
+ * If the last thing from the shell isn't another prompt, wait
+ * up to 1/10 of a second for more stuff to show up, so that
+ * we don't break the output into two separate lines. Don't
+ * want to hang indefinitely because some program is hanging,
+ * confused the shell, or whatever.
+ */
+ if (!sscr_matchprompt(sp, t, len, &tlen) || tlen != 0) {
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+ FD_SET(sc->sh_master, &sp->rdfd);
+ FD_CLR(STDIN_FILENO, &sp->rdfd);
+ if (select(sc->sh_master + 1,
+ &sp->rdfd, NULL, NULL, &tv) == 1) {
+ memmove(bp, t, len);
+ endp = bp + len;
+ goto more;
+ }
+ }
+ if (sscr_setprompt(sp, t, len))
+ return (1);
+ if (file_aline(sp, ep, 1, lno++, t, len))
+ goto ret;
+ }
+
+ /* The cursor moves to EOF. */
+ sp->lno = lno;
+ sp->cno = len ? len - 1 : 0;
+ rval = sp->s_refresh(sp, ep);
+
+ret: FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
+
+/*
+ * sscr_setprompt --
+ *
+ * Set the prompt to the last line we got from the shell.
+ *
+ */
+static int
+sscr_setprompt(sp, buf, len)
+ SCR *sp;
+ char* buf;
+ size_t len;
+{
+ SCRIPT *sc;
+
+ sc = sp->script;
+ if (sc->sh_prompt)
+ FREE(sc->sh_prompt, sc->sh_prompt_len);
+ MALLOC(sp, sc->sh_prompt, char *, len + 1);
+ if (sc->sh_prompt == NULL) {
+ sscr_end(sp);
+ return (1);
+ }
+ memmove(sc->sh_prompt, buf, len);
+ sc->sh_prompt_len = len;
+ sc->sh_prompt[len] = '\0';
+ return (0);
+}
+
+/*
+ * sscr_matchprompt --
+ * Check to see if a line matches the prompt. Nul's indicate
+ * parts that can change, in both content and size.
+ */
+static int
+sscr_matchprompt(sp, lp, line_len, lenp)
+ SCR *sp;
+ char *lp;
+ size_t line_len, *lenp;
+{
+ SCRIPT *sc;
+ size_t prompt_len;
+ char *pp;
+
+ sc = sp->script;
+ if (line_len < (prompt_len = sc->sh_prompt_len))
+ return (0);
+
+ for (pp = sc->sh_prompt;
+ prompt_len && line_len; --prompt_len, --line_len) {
+ if (*pp == '\0') {
+ for (; prompt_len && *pp == '\0'; --prompt_len, ++pp);
+ if (!prompt_len)
+ return (0);
+ for (; line_len && *lp != *pp; --line_len, ++lp);
+ if (!line_len)
+ return (0);
+ }
+ if (*pp++ != *lp++)
+ break;
+ }
+
+ if (prompt_len)
+ return (0);
+ if (lenp != NULL)
+ *lenp = line_len;
+ return (1);
+}
+
+/*
+ * sscr_end --
+ * End the pipe to a shell.
+ */
+int
+sscr_end(sp)
+ SCR *sp;
+{
+ SCRIPT *sc;
+ int rval;
+
+ if ((sc = sp->script) == NULL)
+ return (0);
+
+ /* Turn off the script flag. */
+ F_CLR(sp, S_SCRIPT);
+
+ /* Close down the parent's file descriptors. */
+ if (sc->sh_master != -1)
+ (void)close(sc->sh_master);
+ if (sc->sh_slave != -1)
+ (void)close(sc->sh_slave);
+
+ /* This should have killed the child. */
+ rval = proc_wait(sp, (long)sc->sh_pid, "script-shell", 0);
+
+ /* Free memory. */
+ FREE(sc->sh_prompt, sc->sh_prompt_len);
+ FREE(sc, sizeof(SCRIPT));
+ sp->script = NULL;
+
+ return (rval);
+}
diff --git a/usr.bin/vi/nex/ex_set.c b/usr.bin/vi/nex/ex_set.c
new file mode 100644
index 000000000000..a463524e5fa9
--- /dev/null
+++ b/usr.bin/vi/nex/ex_set.c
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_set.c 8.2 (Berkeley) 10/3/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+int
+ex_set(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ switch(cmdp->argc) {
+ case 0:
+ opts_dump(sp, CHANGED_DISPLAY);
+ break;
+ default:
+ opts_set(sp, cmdp->argv);
+ break;
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/nex/ex_shell.c b/usr.bin/vi/nex/ex_shell.c
new file mode 100644
index 000000000000..282cc8f4fbdc
--- /dev/null
+++ b/usr.bin/vi/nex/ex_shell.c
@@ -0,0 +1,136 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_shell.c 8.17 (Berkeley) 12/23/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <curses.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "../svi/svi_screen.h"
+
+/*
+ * ex_shell -- :sh[ell]
+ * Invoke the program named in the SHELL environment variable
+ * with the argument -i.
+ */
+int
+ex_shell(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ char buf[MAXPATHLEN];
+
+ (void)snprintf(buf, sizeof(buf), "%s -i", O_STR(sp, O_SHELL));
+ return (ex_exec_proc(sp, buf, "\n", NULL));
+}
+
+/*
+ * ex_exec_proc --
+ * Run a separate process.
+ */
+int
+ex_exec_proc(sp, cmd, p1, p2)
+ SCR *sp;
+ char *cmd, *p1, *p2;
+{
+ struct sigaction act, oact;
+ struct stat osb, sb;
+ struct termios term;
+ const char *name;
+ pid_t pid;
+ int isig, rval;
+
+ /* Clear the rest of the screen. */
+ if (sp->s_clear(sp))
+ return (1);
+
+ /* Save ex/vi terminal settings, and restore the original ones. */
+ EX_LEAVE(sp, isig, act, oact, sb, osb, term);
+
+ /* Put out various messages. */
+ if (p1 != NULL)
+ (void)write(STDOUT_FILENO, p1, strlen(p1));
+ if (p2 != NULL)
+ (void)write(STDOUT_FILENO, p2, strlen(p2));
+
+
+ switch (pid = vfork()) {
+ case -1: /* Error. */
+ msgq(sp, M_SYSERR, "vfork");
+ rval = 1;
+ goto err;
+ case 0: /* Utility. */
+ /*
+ * The utility has default signal behavior. Don't bother
+ * using sigaction(2) 'cause we want the default behavior.
+ */
+ (void)signal(SIGINT, SIG_DFL);
+ (void)signal(SIGQUIT, SIG_DFL);
+
+ if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL)
+ name = O_STR(sp, O_SHELL);
+ else
+ ++name;
+ execl(O_STR(sp, O_SHELL), name, "-c", cmd, NULL);
+ msgq(sp, M_ERR, "Error: execl: %s: %s",
+ O_STR(sp, O_SHELL), strerror(errno));
+ _exit(127);
+ /* NOTREACHED */
+ }
+
+ rval = proc_wait(sp, (long)pid, cmd, 0);
+
+ /* Restore ex/vi terminal settings. */
+err: EX_RETURN(sp, isig, act, oact, sb, osb, term);
+
+ /*
+ * XXX
+ * EX_LEAVE/EX_RETURN only give us 1-second resolution on the tty
+ * changes. A fast '!' command, e.g. ":!pwd" can beat us to the
+ * refresh. When there's better resolution from the stat(2) timers,
+ * this can go away.
+ */
+ F_SET(sp, S_REFRESH);
+
+ return (rval);
+}
diff --git a/usr.bin/vi/nex/ex_shift.c b/usr.bin/vi/nex/ex_shift.c
new file mode 100644
index 000000000000..8a0b858cbb6f
--- /dev/null
+++ b/usr.bin/vi/nex/ex_shift.c
@@ -0,0 +1,193 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_shift.c 8.12 (Berkeley) 12/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+enum which {LEFT, RIGHT};
+static int shift __P((SCR *, EXF *, EXCMDARG *, enum which));
+
+int
+ex_shiftl(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (shift(sp, ep, cmdp, LEFT));
+}
+
+int
+ex_shiftr(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (shift(sp, ep, cmdp, RIGHT));
+}
+
+static int
+shift(sp, ep, cmdp, rl)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+ enum which rl;
+{
+ recno_t from, to;
+ size_t blen, len, newcol, newidx, oldcol, oldidx, sw;
+ int curset;
+ char *p, *bp, *tbp;
+
+ if (O_VAL(sp, O_SHIFTWIDTH) == 0) {
+ msgq(sp, M_INFO, "shiftwidth option set to 0");
+ return (0);
+ }
+
+ /*
+ * The historic version of vi permitted the user to string any number
+ * of '>' or '<' characters together, resulting in an indent of the
+ * appropriate levels. There's a special hack in ex_cmd() so that
+ * cmdp->argv[0] points to the string of '>' or '<' characters.
+ *
+ * Q: What's the difference between the people adding features
+ * to vi and the Girl Scouts?
+ * A: The Girl Scouts have mint cookies and adult supervision.
+ */
+ for (p = cmdp->argv[0]->bp, sw = 0; *p == '>' || *p == '<'; ++p)
+ sw += O_VAL(sp, O_SHIFTWIDTH);
+
+ GET_SPACE_RET(sp, bp, blen, 256);
+
+ curset = 0;
+ for (from = cmdp->addr1.lno, to = cmdp->addr2.lno; from <= to; ++from) {
+ if ((p = file_gline(sp, ep, from, &len)) == NULL)
+ goto err;
+ if (!len) {
+ if (sp->lno == from)
+ curset = 1;
+ continue;
+ }
+
+ /*
+ * Calculate the old indent amount and the number of
+ * characters it used.
+ */
+ for (oldidx = 0, oldcol = 0; oldidx < len; ++oldidx)
+ if (p[oldidx] == ' ')
+ ++oldcol;
+ else if (p[oldidx] == '\t')
+ oldcol += O_VAL(sp, O_TABSTOP) -
+ oldcol % O_VAL(sp, O_TABSTOP);
+ else
+ break;
+
+ /* Calculate the new indent amount. */
+ if (rl == RIGHT)
+ newcol = oldcol + sw;
+ else {
+ newcol = oldcol < sw ? 0 : oldcol - sw;
+ if (newcol == oldcol) {
+ if (sp->lno == from)
+ curset = 1;
+ continue;
+ }
+ }
+
+ /* Get a buffer that will hold the new line. */
+ ADD_SPACE_RET(sp, bp, blen, newcol + len);
+
+ /*
+ * Build a new indent string and count the number of
+ * characters it uses.
+ */
+ for (tbp = bp, newidx = 0;
+ newcol >= O_VAL(sp, O_TABSTOP); ++newidx) {
+ *tbp++ = '\t';
+ newcol -= O_VAL(sp, O_TABSTOP);
+ }
+ for (; newcol > 0; --newcol, ++newidx)
+ *tbp++ = ' ';
+
+ /* Add the original line. */
+ memmove(tbp, p + oldidx, len - oldidx);
+
+ /* Set the replacement line. */
+ if (file_sline(sp, ep, from, bp, (tbp + (len - oldidx)) - bp)) {
+err: FREE_SPACE(sp, bp, blen);
+ return (1);
+ }
+
+ /*
+ * !!!
+ * The shift command in historic vi had the usual bizarre
+ * collection of cursor semantics. If called from vi, the
+ * cursor was repositioned to the first non-blank character
+ * of the lowest numbered line shifted. If called from ex,
+ * the cursor was repositioned to the first non-blank of the
+ * highest numbered line shifted. Here, if the cursor isn't
+ * part of the set of lines that are moved, move it to the
+ * first non-blank of the last line shifted. (This makes
+ * ":3>>" in vi work reasonably.) If the cursor is part of
+ * the shifted lines, it doesn't get moved at all. This
+ * permits shifting of marked areas, i.e. ">'a." shifts the
+ * marked area twice, something that couldn't be done with
+ * historic vi.
+ */
+ if (sp->lno == from) {
+ curset = 1;
+ if (newidx > oldidx)
+ sp->cno += newidx - oldidx;
+ else if (sp->cno >= oldidx - newidx)
+ sp->cno -= oldidx - newidx;
+ }
+ }
+ if (!curset) {
+ sp->lno = to;
+ sp->cno = 0;
+ (void)nonblank(sp, ep, to, &sp->cno);
+ }
+
+ FREE_SPACE(sp, bp, blen);
+
+ sp->rptlines[rl == RIGHT ? L_RSHIFT : L_LSHIFT] +=
+ cmdp->addr2.lno - cmdp->addr1.lno + 1;
+ return (0);
+}
diff --git a/usr.bin/vi/nex/ex_source.c b/usr.bin/vi/nex/ex_source.c
new file mode 100644
index 000000000000..bf2a9f1b275e
--- /dev/null
+++ b/usr.bin/vi/nex/ex_source.c
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_source.c 8.3 (Berkeley) 12/2/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_source -- :source file
+ * Execute ex commands from a file.
+ */
+int
+ex_source(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (ex_cfile(sp, ep, cmdp->argv[0]->bp));
+}
diff --git a/usr.bin/vi/nex/ex_stop.c b/usr.bin/vi/nex/ex_stop.c
new file mode 100644
index 000000000000..26ec067c622a
--- /dev/null
+++ b/usr.bin/vi/nex/ex_stop.c
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)ex_stop.c 8.4 (Berkeley) 10/28/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "sex/sex_screen.h"
+
+/*
+ * ex_stop -- :stop[!]
+ * :suspend[!]
+ * Suspend execution.
+ */
+int
+ex_stop(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ /* For some strange reason, the force flag turns off autowrite. */
+ if (F_ISSET(ep, F_MODIFIED) && O_ISSET(sp, O_AUTOWRITE) &&
+ !F_ISSET(cmdp, E_FORCE)) {
+ if (file_write((sp), (ep), NULL, NULL, NULL, FS_ALL))
+ return (1);
+ if (sex_refresh(sp, ep))
+ return (1);
+ }
+ return (sp->s_suspend(sp));
+}
diff --git a/usr.bin/vi/nex/ex_subst.c b/usr.bin/vi/nex/ex_subst.c
new file mode 100644
index 000000000000..6d132c56dd24
--- /dev/null
+++ b/usr.bin/vi/nex/ex_subst.c
@@ -0,0 +1,984 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_substitute.c 8.33 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "interrupt.h"
+
+#define SUB_FIRST 0x01 /* The 'r' flag isn't reasonable. */
+#define SUB_MUSTSETR 0x02 /* The 'r' flag is required. */
+
+static int checkmatchsize __P((SCR *, regex_t *));
+static inline int regsub __P((SCR *,
+ char *, char **, size_t *, size_t *));
+static void subst_intr __P((int));
+static int substitute __P((SCR *, EXF *,
+ EXCMDARG *, char *, regex_t *, u_int));
+
+/*
+ * ex_substitute --
+ * [line [,line]] s[ubstitute] [[/;]pat[/;]/repl[/;] [cgr] [count] [#lp]]
+ *
+ * Substitute on lines matching a pattern.
+ */
+int
+ex_substitute(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ regex_t *re, lre;
+ size_t blen, len;
+ u_int flags;
+ int delim, eval, reflags, replaced;
+ char *bp, *ptrn, *rep, *p, *t;
+
+ /*
+ * Skip leading white space.
+ *
+ * !!!
+ * Historic vi allowed any non-alphanumeric to serve as the
+ * substitution command delimiter.
+ *
+ * !!!
+ * If the arguments are empty, it's the same as &, i.e. we
+ * repeat the last substitution.
+ */
+ for (p = cmdp->argv[0]->bp,
+ len = cmdp->argv[0]->len; len > 0; --len, ++p) {
+ if (!isblank(*p))
+ break;
+ }
+ if (len == 0)
+ return (ex_subagain(sp, ep, cmdp));
+ delim = *p++;
+ if (isalnum(delim))
+ return (substitute(sp, ep,
+ cmdp, p, &sp->subre, SUB_MUSTSETR));
+
+ /*
+ * Get the pattern string, toss escaped characters.
+ *
+ * !!!
+ * Historic vi accepted any of the following forms:
+ *
+ * :s/abc/def/ change "abc" to "def"
+ * :s/abc/def change "abc" to "def"
+ * :s/abc/ delete "abc"
+ * :s/abc delete "abc"
+ *
+ * QUOTING NOTE:
+ *
+ * Only toss an escape character if it escapes a delimiter.
+ * This means that "s/A/\\\\f" replaces "A" with "\\f". It
+ * would be nice to be more regular, i.e. for each layer of
+ * escaping a single escape character is removed, but that's
+ * not how the historic vi worked.
+ */
+ for (ptrn = t = p;;) {
+ if (p[0] == '\0' || p[0] == delim) {
+ if (p[0] == delim)
+ ++p;
+ /*
+ * !!!
+ * Nul terminate the pattern string -- it's passed
+ * to regcomp which doesn't understand anything else.
+ */
+ *t = '\0';
+ break;
+ }
+ if (p[0] == '\\' && p[1] == delim)
+ ++p;
+ *t++ = *p++;
+ }
+
+ /* If the pattern string is empty, use the last one. */
+ if (*ptrn == NULL) {
+ if (!F_ISSET(sp, S_SUBRE_SET)) {
+ msgq(sp, M_ERR,
+ "No previous regular expression.");
+ return (1);
+ }
+ re = &sp->subre;
+ flags = 0;
+ } else {
+ /* Set RE flags. */
+ reflags = 0;
+ if (O_ISSET(sp, O_EXTENDED))
+ reflags |= REG_EXTENDED;
+ if (O_ISSET(sp, O_IGNORECASE))
+ reflags |= REG_ICASE;
+
+ /* Convert vi-style RE's to POSIX 1003.2 RE's. */
+ if (re_conv(sp, &ptrn, &replaced))
+ return (1);
+
+ /* Compile the RE. */
+ eval = regcomp(&lre, (char *)ptrn, reflags);
+
+ /* Free up any allocated memory. */
+ if (replaced)
+ FREE_SPACE(sp, ptrn, 0);
+
+ if (eval) {
+ re_error(sp, eval, &lre);
+ return (1);
+ }
+
+ /*
+ * Set saved RE.
+ *
+ * !!!
+ * Historic practice is that substitutes set the search
+ * direction as well as both substitute and search RE's.
+ */
+ sp->searchdir = FORWARD;
+ sp->sre = lre;
+ F_SET(sp, S_SRE_SET);
+ sp->subre = lre;
+ F_SET(sp, S_SUBRE_SET);
+
+ re = &lre;
+ flags = SUB_FIRST;
+ }
+
+ /*
+ * Get the replacement string.
+ *
+ * The special character ~ (\~ if O_MAGIC not set) inserts the
+ * previous replacement string into this replacement string.
+ *
+ * The special character & (\& if O_MAGIC not set) matches the
+ * entire RE. No handling of & is required here, it's done by
+ * regsub().
+ *
+ * QUOTING NOTE:
+ *
+ * Only toss an escape character if it escapes a delimiter or
+ * an escape character, or if O_MAGIC is set and it escapes a
+ * tilde.
+ */
+ if (*p == '\0') {
+ if (sp->repl != NULL)
+ FREE(sp->repl, sp->repl_len);
+ sp->repl = NULL;
+ sp->repl_len = 0;
+ } else {
+ /*
+ * Count ~'s to figure out how much space we need. We could
+ * special case nonexistent last patterns or whether or not
+ * O_MAGIC is set, but it's probably not worth the effort.
+ */
+ for (rep = p, len = 0;
+ p[0] != '\0' && p[0] != delim; ++p, ++len)
+ if (p[0] == '~')
+ len += sp->repl_len;
+ GET_SPACE_RET(sp, bp, blen, len);
+ for (t = bp, len = 0, p = rep;;) {
+ if (p[0] == '\0' || p[0] == delim) {
+ if (p[0] == delim)
+ ++p;
+ break;
+ }
+ if (p[0] == '\\') {
+ if (p[1] == '\\' || p[1] == delim)
+ ++p;
+ else if (p[1] == '~') {
+ ++p;
+ if (!O_ISSET(sp, O_MAGIC))
+ goto tilde;
+ }
+ } else if (p[0] == '~' && O_ISSET(sp, O_MAGIC)) {
+tilde: ++p;
+ memmove(t, sp->repl, sp->repl_len);
+ t += sp->repl_len;
+ len += sp->repl_len;
+ continue;
+ }
+ *t++ = *p++;
+ ++len;
+ }
+ if (sp->repl != NULL)
+ FREE(sp->repl, sp->repl_len);
+ if ((sp->repl = malloc(len)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ FREE_SPACE(sp, bp, blen);
+ return (1);
+ }
+ memmove(sp->repl, bp, len);
+ sp->repl_len = len;
+ FREE_SPACE(sp, bp, blen);
+ }
+
+ if (checkmatchsize(sp, &sp->subre))
+ return (1);
+ return (substitute(sp, ep, cmdp, p, re, flags));
+}
+
+/*
+ * ex_subagain --
+ * [line [,line]] & [cgr] [count] [#lp]]
+ *
+ * Substitute using the last substitute RE and replacement pattern.
+ */
+int
+ex_subagain(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ if (!F_ISSET(sp, S_SUBRE_SET)) {
+ msgq(sp, M_ERR, "No previous regular expression.");
+ return (1);
+ }
+ return (substitute(sp, ep, cmdp, cmdp->argv[0]->bp, &sp->subre, 0));
+}
+
+/*
+ * ex_subtilde --
+ * [line [,line]] ~ [cgr] [count] [#lp]]
+ *
+ * Substitute using the last RE and last substitute replacement pattern.
+ */
+int
+ex_subtilde(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ if (!F_ISSET(sp, S_SRE_SET)) {
+ msgq(sp, M_ERR, "No previous regular expression.");
+ return (1);
+ }
+ return (substitute(sp, ep, cmdp, cmdp->argv[0]->bp, &sp->sre, 0));
+}
+
+/*
+ * The nasty part of the substitution is what happens when the replacement
+ * string contains newlines. It's a bit tricky -- consider the information
+ * that has to be retained for "s/f\(o\)o/^M\1^M\1/". The solution here is
+ * to build a set of newline offsets which we use to break the line up later,
+ * when the replacement is done. Don't change it unless you're pretty damned
+ * confident.
+ */
+#define NEEDNEWLINE(sp) { \
+ if (sp->newl_len == sp->newl_cnt) { \
+ sp->newl_len += 25; \
+ REALLOC(sp, sp->newl, size_t *, \
+ sp->newl_len * sizeof(size_t)); \
+ if (sp->newl == NULL) { \
+ sp->newl_len = 0; \
+ return (1); \
+ } \
+ } \
+}
+
+#define BUILD(sp, l, len) { \
+ if (lbclen + (len) > lblen) { \
+ lblen += MAX(lbclen + (len), 256); \
+ REALLOC(sp, lb, char *, lblen); \
+ if (lb == NULL) { \
+ lbclen = 0; \
+ return (1); \
+ } \
+ } \
+ memmove(lb + lbclen, l, len); \
+ lbclen += len; \
+}
+
+#define NEEDSP(sp, len, pnt) { \
+ if (lbclen + (len) > lblen) { \
+ lblen += MAX(lbclen + (len), 256); \
+ REALLOC(sp, lb, char *, lblen); \
+ if (lb == NULL) { \
+ lbclen = 0; \
+ return (1); \
+ } \
+ pnt = lb + lbclen; \
+ } \
+}
+
+/*
+ * substitute --
+ * Do the substitution. This stuff is *really* tricky. There are
+ * lots of special cases, and general nastiness. Don't mess with it
+ * unless you're pretty confident.
+ */
+static int
+substitute(sp, ep, cmdp, s, re, flags)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+ char *s;
+ regex_t *re;
+ u_int flags;
+{
+ DECLARE_INTERRUPTS;
+ MARK from, to;
+ recno_t elno, lno;
+ size_t blen, cnt, last, lbclen, lblen, len, llen, offset, saved_offset;
+ int didsub, do_eol_match, eflags, empty_ok, eval;
+ int linechanged, matched, quit, rval;
+ int cflag, gflag, lflag, nflag, pflag, rflag;
+ char *bp, *lb;
+
+ /*
+ * Historic vi permitted the '#', 'l' and 'p' options in vi mode, but
+ * it only displayed the last change. I'd disallow them, but they are
+ * useful in combination with the [v]global commands. In the current
+ * model the problem is combining them with the 'c' flag -- the screen
+ * would have to flip back and forth between the confirm screen and the
+ * ex print screen, which would be pretty awful. We do display all
+ * changes, though, for what that's worth.
+ *
+ * !!!
+ * Historic vi was fairly strict about the order of "options", the
+ * count, and "flags". I'm somewhat fuzzy on the difference between
+ * options and flags, anyway, so this is a simpler approach, and we
+ * just take it them in whatever order the user gives them. (The ex
+ * usage statement doesn't reflect this.)
+ */
+ cflag = gflag = lflag = nflag = pflag = rflag = 0;
+ for (lno = OOBLNO; *s != '\0'; ++s)
+ switch (*s) {
+ case ' ':
+ case '\t':
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (lno != OOBLNO)
+ goto usage;
+ errno = 0;
+ lno = strtoul(s, &s, 10);
+ if (*s == '\0') /* Loop increment correction. */
+ --s;
+ if (errno == ERANGE) {
+ if (lno == LONG_MAX)
+ msgq(sp, M_ERR, "Count overflow.");
+ else if (lno == LONG_MIN)
+ msgq(sp, M_ERR, "Count underflow.");
+ else
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ /*
+ * In historic vi, the count was inclusive from the
+ * second address.
+ */
+ cmdp->addr1.lno = cmdp->addr2.lno;
+ cmdp->addr2.lno += lno - 1;
+ break;
+ case '#':
+ nflag = 1;
+ break;
+ case 'c':
+ cflag = 1;
+ break;
+ case 'g':
+ gflag = 1;
+ break;
+ case 'l':
+ lflag = 1;
+ break;
+ case 'p':
+ pflag = 1;
+ break;
+ case 'r':
+ if (LF_ISSET(SUB_FIRST)) {
+ msgq(sp, M_ERR,
+ "Regular expression specified; r flag meaningless.");
+ return (1);
+ }
+ if (!F_ISSET(sp, S_SUBRE_SET)) {
+ msgq(sp, M_ERR,
+ "No previous regular expression.");
+ return (1);
+ }
+ rflag = 1;
+ break;
+ default:
+ goto usage;
+ }
+
+ if (*s != '\0' || !rflag && LF_ISSET(SUB_MUSTSETR)) {
+usage: msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage);
+ return (1);
+ }
+
+ if (IN_VI_MODE(sp) && cflag && (lflag || nflag || pflag)) {
+ msgq(sp, M_ERR,
+ "The #, l and p flags may not be combined with the c flag in vi mode.");
+ return (1);
+ }
+
+ if (!F_ISSET(sp, S_GLOBAL))
+ SET_UP_INTERRUPTS(subst_intr);
+
+ /*
+ * bp: if interactive, line cache
+ * blen: if interactive, line cache length
+ * lb: build buffer pointer.
+ * lbclen: current length of built buffer.
+ * lblen; length of build buffer.
+ */
+ bp = lb = NULL;
+ blen = lbclen = lblen = 0;
+
+ /* For each line... */
+ for (matched = quit = 0, lno = cmdp->addr1.lno,
+ elno = cmdp->addr2.lno; !quit && lno <= elno; ++lno) {
+
+ /* Someone's unhappy, time to stop. */
+ if (F_ISSET(sp, S_INTERRUPTED)) {
+ if (!F_ISSET(sp, S_GLOBAL))
+ msgq(sp, M_INFO, "Interrupted.");
+ break;
+ }
+
+ /* Get the line. */
+ if ((s = file_gline(sp, ep, lno, &llen)) == NULL) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+
+ /*
+ * Make a local copy if doing confirmation -- when calling
+ * the confirm routine we're likely to lose the cached copy.
+ */
+ if (cflag) {
+ if (bp == NULL) {
+ GET_SPACE_RET(sp, bp, blen, llen);
+ } else
+ ADD_SPACE_RET(sp, bp, blen, llen);
+ memmove(bp, s, llen);
+ s = bp;
+ }
+
+ /* Start searching from the beginning. */
+ offset = 0;
+ len = llen;
+
+ /* Reset the build buffer offset. */
+ lbclen = 0;
+
+ /* Reset empty match flag. */
+ empty_ok = 1;
+
+ /*
+ * We don't want to have to do a setline if the line didn't
+ * change -- keep track of whether or not this line changed.
+ * If doing confirmations, don't want to keep setting the
+ * line if change is refused -- keep track of substitutions.
+ */
+ didsub = linechanged = 0;
+
+ /* New line, do an EOL match. */
+ do_eol_match = 1;
+
+ /* It's not nul terminated, but we pretend it is. */
+ eflags = REG_STARTEND;
+
+ /*
+ * The search area is from s + offset to the EOL.
+ *
+ * Generally, sp->match[0].rm_so is the offset of the start
+ * of the match from the start of the search, and offset is
+ * the offset of the start of the last search.
+ */
+nextmatch: sp->match[0].rm_so = 0;
+ sp->match[0].rm_eo = len;
+
+ /* Get the next match. */
+ eval = regexec(re,
+ (char *)s + offset, re->re_nsub + 1, sp->match, eflags);
+
+ /*
+ * There wasn't a match or if there was an error, deal with
+ * it. If there was a previous match in this line, resolve
+ * the changes into the database. Otherwise, just move on.
+ */
+ if (eval == REG_NOMATCH)
+ goto endmatch;
+ if (eval != 0) {
+ re_error(sp, eval, re);
+ goto ret1;
+ }
+ matched = 1;
+
+ /* Only the first search can match an anchored expression. */
+ eflags |= REG_NOTBOL;
+
+ /*
+ * !!!
+ * It's possible to match 0-length strings -- for example, the
+ * command s;a*;X;, when matched against the string "aabb" will
+ * result in "XbXbX", i.e. the matches are "aa", the space
+ * between the b's and the space between the b's and the end of
+ * the string. There is a similar space between the beginning
+ * of the string and the a's. The rule that we use (because vi
+ * historically used it) is that any 0-length match, occurring
+ * immediately after a match, is ignored. Otherwise, the above
+ * example would have resulted in "XXbXbX". Another example is
+ * incorrectly using " *" to replace groups of spaces with one
+ * space.
+ *
+ * The way we do this is that if we just had a successful match,
+ * the starting offset does not skip characters, and the match
+ * is empty, ignore the match and move forward. If there's no
+ * more characters in the string, we were attempting to match
+ * after the last character, so quit.
+ */
+ if (!empty_ok &&
+ sp->match[0].rm_so == 0 && sp->match[0].rm_eo == 0) {
+ empty_ok = 1;
+ if (len == 0)
+ goto endmatch;
+ BUILD(sp, s + offset, 1)
+ ++offset;
+ --len;
+ goto nextmatch;
+ }
+
+ /* Confirm change. */
+ if (cflag) {
+ /*
+ * Set the cursor position for confirmation. Note,
+ * if we matched on a '$', the cursor may be past
+ * the end of line.
+ *
+ * XXX
+ * We may want to "fix" this in the confirm routine,
+ * if the confirm routine should be able to display
+ * a cursor past EOL.
+ */
+ from.lno = to.lno = lno;
+ from.cno = sp->match[0].rm_so + offset;
+ to.cno = sp->match[0].rm_eo;
+ if (llen == 0)
+ from.cno = to.cno = 0;
+ else {
+ if (to.cno >= llen)
+ to.cno = llen - 1;
+ if (from.cno >= llen)
+ from.cno = llen - 1;
+ }
+ switch (sp->s_confirm(sp, ep, &from, &to)) {
+ case CONF_YES:
+ break;
+ case CONF_NO:
+ didsub = 0;
+ BUILD(sp, s +offset, sp->match[0].rm_eo);
+ goto skip;
+ case CONF_QUIT:
+ /* Set the quit flag. */
+ quit = 1;
+
+ /* If interruptible, pass the info back. */
+ if (F_ISSET(sp, S_INTERRUPTIBLE))
+ F_SET(sp, S_INTERRUPTED);
+
+ /*
+ * If any changes, resolve them, otherwise
+ * return to the main loop.
+ */
+ goto endmatch;
+ }
+ }
+
+ /* Copy the bytes before the match into the build buffer. */
+ BUILD(sp, s + offset, sp->match[0].rm_so);
+
+ /*
+ * Cursor moves to last line changed, unless doing confirm,
+ * in which case don't move it.
+ *
+ * !!!
+ * Historic vi just put the cursor on the first non-blank
+ * of the last line changed. This might be better.
+ */
+ if (!cflag) {
+ sp->lno = lno;
+ sp->cno = sp->match[0].rm_so + offset;
+ }
+
+ /* Substitute the matching bytes. */
+ didsub = 1;
+ if (regsub(sp, s + offset, &lb, &lbclen, &lblen))
+ goto ret1;
+
+ /* Set the change flag so we know this line was modified. */
+ linechanged = 1;
+
+ /* Move past the matched bytes. */
+skip: offset += sp->match[0].rm_eo;
+ len -= sp->match[0].rm_eo;
+
+ /* A match cannot be followed by an empty pattern. */
+ empty_ok = 0;
+
+ /*
+ * If doing a global change with confirmation, we have to
+ * update the screen. The basic idea is to store the line
+ * so the screen update routines can find it, and restart.
+ */
+ if (didsub && cflag && gflag) {
+ /*
+ * The new search offset will be the end of the
+ * modified line.
+ */
+ saved_offset = lbclen;
+
+ /* Copy the rest of the line. */
+ if (len)
+ BUILD(sp, s + offset, len)
+
+ /* Set the new offset. */
+ offset = saved_offset;
+
+ /* Store inserted lines, adjusting the build buffer. */
+ last = 0;
+ if (sp->newl_cnt) {
+ for (cnt = 0;
+ cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) {
+ if (file_iline(sp, ep, lno,
+ lb + last, sp->newl[cnt] - last))
+ goto ret1;
+ last = sp->newl[cnt] + 1;
+ ++sp->rptlines[L_ADDED];
+ }
+ lbclen -= last;
+ offset -= last;
+ sp->newl_cnt = 0;
+ }
+
+ /* Store and retrieve the line. */
+ if (file_sline(sp, ep, lno, lb + last, lbclen))
+ goto ret1;
+ if ((s = file_gline(sp, ep, lno, &llen)) == NULL) {
+ GETLINE_ERR(sp, lno);
+ goto ret1;
+ }
+ ADD_SPACE_RET(sp, bp, blen, llen)
+ memmove(bp, s, llen);
+ s = bp;
+ len = llen - offset;
+
+ /* Restart the build. */
+ lbclen = 0;
+ BUILD(sp, s, offset);
+
+ /*
+ * If we haven't already done the after-the-string
+ * match, do one. Set REG_NOTEOL so the '$' pattern
+ * only matches once.
+ */
+ if (!do_eol_match)
+ goto endmatch;
+ if (offset == len) {
+ do_eol_match = 0;
+ eflags |= REG_NOTEOL;
+ }
+ goto nextmatch;
+ }
+
+ /*
+ * If it's a global:
+ *
+ * If at the end of the string, do a test for the after
+ * the string match. Set REG_NOTEOL so the '$' pattern
+ * only matches once.
+ */
+ if (gflag && do_eol_match) {
+ if (len == 0) {
+ do_eol_match = 0;
+ eflags |= REG_NOTEOL;
+ }
+ goto nextmatch;
+ }
+
+endmatch: if (!linechanged)
+ continue;
+
+ /* Copy any remaining bytes into the build buffer. */
+ if (len)
+ BUILD(sp, s + offset, len)
+
+ /* Store inserted lines, adjusting the build buffer. */
+ last = 0;
+ if (sp->newl_cnt) {
+ for (cnt = 0;
+ cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) {
+ if (file_iline(sp, ep,
+ lno, lb + last, sp->newl[cnt] - last))
+ goto ret1;
+ last = sp->newl[cnt] + 1;
+ ++sp->rptlines[L_ADDED];
+ }
+ lbclen -= last;
+ sp->newl_cnt = 0;
+ }
+
+ /* Store the changed line. */
+ if (file_sline(sp, ep, lno, lb + last, lbclen))
+ goto ret1;
+
+ /* Update changed line counter. */
+ ++sp->rptlines[L_CHANGED];
+
+ /* Display as necessary. */
+ if (lflag || nflag || pflag) {
+ from.lno = to.lno = lno;
+ from.cno = to.cno = 0;
+ if (lflag)
+ ex_print(sp, ep, &from, &to, E_F_LIST);
+ if (nflag)
+ ex_print(sp, ep, &from, &to, E_F_HASH);
+ if (pflag)
+ ex_print(sp, ep, &from, &to, E_F_PRINT);
+ }
+ }
+
+ /*
+ * If not in a global command, and nothing matched, say so.
+ * Else, if none of the lines displayed, put something up.
+ */
+ if (!matched) {
+ if (!F_ISSET(sp, S_GLOBAL))
+ msgq(sp, M_INFO, "No match found.");
+ } else if (!lflag && !nflag && !pflag)
+ F_SET(EXP(sp), EX_AUTOPRINT);
+
+ rval = 0;
+ if (0) {
+ret1: rval = 1;
+ }
+
+interrupt_err:
+ if (!F_ISSET(sp, S_GLOBAL))
+ TEAR_DOWN_INTERRUPTS;
+
+ if (bp != NULL)
+ FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
+
+/*
+ * regsub --
+ * Do the substitution for a regular expression.
+ */
+static inline int
+regsub(sp, ip, lbp, lbclenp, lblenp)
+ SCR *sp;
+ char *ip; /* Input line. */
+ char **lbp;
+ size_t *lbclenp, *lblenp;
+{
+ enum { C_NOTSET, C_LOWER, C_ONELOWER, C_ONEUPPER, C_UPPER } conv;
+ size_t lbclen, lblen; /* Local copies. */
+ size_t mlen; /* Match length. */
+ size_t rpl; /* Remaining replacement length. */
+ char *rp; /* Replacement pointer. */
+ int ch;
+ int no; /* Match replacement offset. */
+ char *p, *t; /* Buffer pointers. */
+ char *lb; /* Local copies. */
+
+ lb = *lbp; /* Get local copies. */
+ lbclen = *lbclenp;
+ lblen = *lblenp;
+
+ /*
+ * QUOTING NOTE:
+ *
+ * There are some special sequences that vi provides in the
+ * replacement patterns.
+ * & string the RE matched (\& if nomagic set)
+ * \# n-th regular subexpression
+ * \E end \U, \L conversion
+ * \e end \U, \L conversion
+ * \l convert the next character to lower-case
+ * \L convert to lower-case, until \E, \e, or end of replacement
+ * \u convert the next character to upper-case
+ * \U convert to upper-case, until \E, \e, or end of replacement
+ *
+ * Otherwise, since this is the lowest level of replacement, discard
+ * all escape characters. This (hopefully) follows historic practice.
+ */
+#define ADDCH(ch) { \
+ CHAR_T __ch = (ch); \
+ u_int __value = term_key_val(sp, __ch); \
+ if (__value == K_CR || __value == K_NL) { \
+ NEEDNEWLINE(sp); \
+ sp->newl[sp->newl_cnt++] = lbclen; \
+ } else if (conv != C_NOTSET) { \
+ switch (conv) { \
+ case C_ONELOWER: \
+ conv = C_NOTSET; \
+ /* FALLTHROUGH */ \
+ case C_LOWER: \
+ if (isupper(__ch)) \
+ __ch = tolower(__ch); \
+ break; \
+ case C_ONEUPPER: \
+ conv = C_NOTSET; \
+ /* FALLTHROUGH */ \
+ case C_UPPER: \
+ if (islower(__ch)) \
+ __ch = toupper(__ch); \
+ break; \
+ default: \
+ abort(); \
+ } \
+ } \
+ NEEDSP(sp, 1, p); \
+ *p++ = __ch; \
+ ++lbclen; \
+}
+ conv = C_NOTSET;
+ for (rp = sp->repl, rpl = sp->repl_len, p = lb + lbclen; rpl--;) {
+ switch (ch = *rp++) {
+ case '&':
+ if (O_ISSET(sp, O_MAGIC)) {
+ no = 0;
+ goto subzero;
+ }
+ break;
+ case '\\':
+ if (rpl == 0)
+ break;
+ --rpl;
+ switch (ch = *rp) {
+ case '&':
+ if (!O_ISSET(sp, O_MAGIC)) {
+ ++rp;
+ no = 0;
+ goto subzero;
+ }
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ no = *rp++ - '0';
+subzero: if (sp->match[no].rm_so == -1 ||
+ sp->match[no].rm_eo == -1)
+ continue;
+ mlen =
+ sp->match[no].rm_eo - sp->match[no].rm_so;
+ for (t = ip + sp->match[no].rm_so; mlen--; ++t)
+ ADDCH(*t);
+ continue;
+ case 'e':
+ case 'E':
+ ++rp;
+ conv = C_NOTSET;
+ continue;
+ case 'l':
+ ++rp;
+ conv = C_ONELOWER;
+ continue;
+ case 'L':
+ ++rp;
+ conv = C_LOWER;
+ continue;
+ case 'u':
+ ++rp;
+ conv = C_ONEUPPER;
+ continue;
+ case 'U':
+ ++rp;
+ conv = C_UPPER;
+ continue;
+ default:
+ ++rp;
+ break;
+ }
+ }
+ ADDCH(ch);
+ }
+
+ *lbp = lb; /* Update caller's information. */
+ *lbclenp = lbclen;
+ *lblenp = lblen;
+ return (0);
+}
+
+static int
+checkmatchsize(sp, re)
+ SCR *sp;
+ regex_t *re;
+{
+ /* Build nsub array as necessary. */
+ if (sp->matchsize < re->re_nsub + 1) {
+ sp->matchsize = re->re_nsub + 1;
+ REALLOC(sp, sp->match,
+ regmatch_t *, sp->matchsize * sizeof(regmatch_t));
+ if (sp->match == NULL) {
+ sp->matchsize = 0;
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * subst_intr --
+ * Set the interrupt bit in any screen that is interruptible.
+ *
+ * XXX
+ * In the future this may be a problem. The user should be able to move to
+ * another screen and keep typing while this runs. If so, and the user has
+ * more than one substitute running, it will be hard to decide which one to
+ * stop.
+ */
+static void
+subst_intr(signo)
+ int signo;
+{
+ SCR *sp;
+
+ for (sp = __global_list->dq.cqh_first;
+ sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
+ if (F_ISSET(sp, S_INTERRUPTIBLE))
+ F_SET(sp, S_INTERRUPTED);
+}
diff --git a/usr.bin/vi/nex/ex_tag.c b/usr.bin/vi/nex/ex_tag.c
new file mode 100644
index 000000000000..c47ab0de9c44
--- /dev/null
+++ b/usr.bin/vi/nex/ex_tag.c
@@ -0,0 +1,859 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * David Hitz of Auspex Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_tag.c 8.31 (Berkeley) 1/22/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "tag.h"
+
+static char *binary_search __P((char *, char *, char *));
+static int compare __P((char *, char *, char *));
+static char *linear_search __P((char *, char *, char *));
+static int search __P((SCR *, char *, char *, char **));
+static int tag_get __P((SCR *, char *, char **, char **, char **));
+
+/*
+ * ex_tagfirst --
+ * The tag code can be entered from main, i.e. "vi -t tag".
+ */
+int
+ex_tagfirst(sp, tagarg)
+ SCR *sp;
+ char *tagarg;
+{
+ FREF *frp;
+ MARK m;
+ long tl;
+ u_int flags;
+ int sval;
+ char *p, *tag, *name, *search;
+
+ /* Taglength may limit the number of characters. */
+ if ((tl = O_VAL(sp, O_TAGLENGTH)) != 0 && strlen(tagarg) > tl)
+ tagarg[tl] = '\0';
+
+ /* Get the tag information. */
+ if (tag_get(sp, tagarg, &tag, &name, &search))
+ return (1);
+
+ /* Create the file entry. */
+ if ((frp = file_add(sp, NULL, name, 0)) == NULL)
+ return (1);
+ if (file_init(sp, frp, NULL, 0))
+ return (1);
+
+ /*
+ * !!!
+ * Historic vi accepted a line number as well as a search
+ * string, and people are apparently still using the format.
+ */
+ if (isdigit(search[0])) {
+ m.lno = atoi(search);
+ m.cno = 0;
+ } else {
+ /*
+ * Search for the tag; cheap fallback for C functions if
+ * the name is the same but the arguments have changed.
+ */
+ m.lno = 1;
+ m.cno = 0;
+ flags = SEARCH_FILE | SEARCH_TAG | SEARCH_TERM;
+ sval = f_search(sp, sp->ep, &m, &m, search, NULL, &flags);
+ if (sval && (p = strrchr(search, '(')) != NULL) {
+ p[1] = '\0';
+ sval = f_search(sp, sp->ep,
+ &m, &m, search, NULL, &flags);
+ }
+ if (sval)
+ msgq(sp, M_ERR, "%s: search pattern not found.", tag);
+ }
+
+ /* Set up the screen. */
+ frp->lno = m.lno;
+ frp->cno = m.cno;
+ F_SET(frp, FR_CURSORSET);
+
+ /* Might as well make this the default tag. */
+ if ((EXP(sp)->tlast = strdup(tagarg)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * ex_tagpush -- :tag [file]
+ * Move to a new tag.
+ *
+ * The tags stacks in nvi are a bit tricky. Each tag contains a file name,
+ * search string, and line/column numbers. The search string is only used
+ * for the first access and for user display. The first record on the stack
+ * is the place where we first did a tag, so it has no search string. The
+ * second record is the first tag, and so on. Note, this means that the
+ * "current" tag is always on the stack. Each tag has a line/column which is
+ * the location from which the user tagged the following TAG entry, and which
+ * is used as the return location.
+ */
+int
+ex_tagpush(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ enum {TC_CHANGE, TC_CURRENT} which;
+ EX_PRIVATE *exp;
+ FREF *frp;
+ MARK m;
+ TAG *tp;
+ u_int flags;
+ int sval;
+ long tl;
+ char *name, *p, *search, *tag;
+
+ exp = EXP(sp);
+ switch (cmdp->argc) {
+ case 1:
+ if (exp->tlast != NULL)
+ FREE(exp->tlast, strlen(exp->tlast) + 1);
+ if ((exp->tlast = strdup(cmdp->argv[0]->bp)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ break;
+ case 0:
+ if (exp->tlast == NULL) {
+ msgq(sp, M_ERR, "No previous tag entered.");
+ return (1);
+ }
+ break;
+ default:
+ abort();
+ }
+
+ /* Taglength may limit the number of characters. */
+ if ((tl = O_VAL(sp, O_TAGLENGTH)) != 0 && strlen(exp->tlast) > tl)
+ exp->tlast[tl] = '\0';
+
+ /* Get the tag information. */
+ if (tag_get(sp, exp->tlast, &tag, &name, &search))
+ return (1);
+
+ /* Get a new FREF structure. */
+ if ((frp = file_add(sp, sp->frp, name, 1)) == NULL) {
+ FREE(tag, strlen(tag));
+ return (1);
+ }
+
+ /*
+ * Get a tag structure -- if this is the first tag, push it on the
+ * stack as a placeholder and get another tag structure. Set the
+ * line/column of the most recent element on the stack to be the
+ * current values, including the file pointer. Then push the new
+ * TAG onto the stack with the new file and search string for user
+ * display.
+ */
+ CALLOC(sp, tp, TAG *, 1, sizeof(TAG));
+ if (tp != NULL && exp->tagq.tqh_first == NULL) {
+ TAILQ_INSERT_HEAD(&exp->tagq, tp, q);
+ CALLOC(sp, tp, TAG *, 1, sizeof(TAG));
+ }
+ if (exp->tagq.tqh_first != NULL) {
+ exp->tagq.tqh_first->frp = sp->frp;
+ exp->tagq.tqh_first->lno = sp->lno;
+ exp->tagq.tqh_first->cno = sp->cno;
+ }
+ if (tp != NULL) {
+ if ((tp->search = strdup(search)) == NULL)
+ msgq(sp, M_SYSERR, NULL);
+ else
+ tp->slen = strlen(search);
+ tp->frp = frp;
+ TAILQ_INSERT_HEAD(&exp->tagq, tp, q);
+ }
+
+ /* Switch to the new file. */
+ if (sp->frp == frp)
+ which = TC_CURRENT;
+ else {
+ MODIFY_CHECK(sp, sp->ep, F_ISSET(cmdp, E_FORCE));
+
+ if (file_init(sp, frp, NULL, 0)) {
+ if (tp != NULL)
+ FREE(tp, sizeof(TAG));
+ FREE(tag, strlen(tag));
+ return (1);
+ }
+ which = TC_CHANGE;
+ }
+
+ /*
+ * !!!
+ * Historic vi accepted a line number as well as a search
+ * string, and people are apparently still using the format.
+ */
+ if (isdigit(search[0])) {
+ m.lno = atoi(search);
+ m.cno = 0;
+ sval = 0;
+ } else {
+ /*
+ * Search for the tag; cheap fallback for C functions
+ * if the name is the same but the arguments have changed.
+ */
+ m.lno = 1;
+ m.cno = 0;
+ flags = SEARCH_FILE | SEARCH_TAG | SEARCH_TERM;
+ sval = f_search(sp, sp->ep, &m, &m, search, NULL, &flags);
+ if (sval && (p = strrchr(search, '(')) != NULL) {
+ p[1] = '\0';
+ sval = f_search(sp, sp->ep,
+ &m, &m, search, NULL, &flags);
+ p[1] = '(';
+ }
+ if (sval)
+ msgq(sp, M_ERR, "%s: search pattern not found.", tag);
+ }
+ free(tag);
+
+ switch (which) {
+ case TC_CHANGE:
+ frp->lno = m.lno;
+ frp->cno = m.cno;
+ F_SET(frp, FR_CURSORSET);
+ F_SET(sp, S_FSWITCH);
+ break;
+ case TC_CURRENT:
+ if (sval)
+ return (1);
+ sp->lno = m.lno;
+ sp->cno = m.cno;
+ break;
+ }
+ return (0);
+}
+
+/* Free a tag or tagf structure from a queue. */
+#define FREETAG(tp) { \
+ TAILQ_REMOVE(&exp->tagq, (tp), q); \
+ if ((tp)->search != NULL) \
+ free((tp)->search); \
+ FREE((tp), sizeof(TAGF)); \
+}
+#define FREETAGF(tfp) { \
+ TAILQ_REMOVE(&exp->tagfq, (tfp), q); \
+ free((tfp)->name); \
+ FREE((tfp), sizeof(TAGF)); \
+}
+
+/*
+ * ex_tagpop -- :tagp[op][!] [number | file]
+ * Pop the tag stack.
+ */
+int
+ex_tagpop(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ EX_PRIVATE *exp;
+ TAG *ntp, *tp;
+ recno_t lno;
+ long off, saved_off;
+ size_t arglen, cno;
+ char *arg, *p, *t;
+
+ /* Check for an empty stack. */
+ exp = EXP(sp);
+ if (exp->tagq.tqh_first == NULL) {
+ msgq(sp, M_INFO, "The tags stack is empty.");
+ return (1);
+ }
+
+ switch (cmdp->argc) {
+ case 0: /* Pop one tag. */
+ tp = exp->tagq.tqh_first;
+ FREETAG(tp);
+ break;
+ case 1: /* Name or number. */
+ arg = cmdp->argv[0]->bp;
+ saved_off = strtol(arg, &p, 10);
+ if (*p == '\0') {
+ if (saved_off < 1)
+ return (0);
+ for (tp = exp->tagq.tqh_first, off = saved_off;
+ tp != NULL && off-- > 1; tp = tp->q.tqe_next);
+ if (tp == NULL) {
+ msgq(sp, M_ERR,
+"Less than %d entries on the tags stack; use :display to see the tags stack.",
+ saved_off);
+ return (1);
+ }
+ for (off = saved_off; off-- > 1;) {
+ tp = exp->tagq.tqh_first;
+ FREETAG(tp);
+ }
+ } else {
+ arglen = strlen(arg);
+ for (tp = exp->tagq.tqh_first;
+ tp != NULL; tp = tp->q.tqe_next) {
+ /* Use the user's original file name. */
+ p = tp->frp->name;
+ if ((t = strrchr(p, '/')) == NULL)
+ t = p;
+ else
+ ++t;
+ if (!strncmp(arg, t, arglen)) {
+ ntp = tp;
+ break;
+ }
+ }
+ if (tp == NULL) {
+ msgq(sp, M_ERR,
+"No file named %s on the tags stack; use :display to see the tags stack.",
+ arg);
+ return (1);
+ }
+ for (;;) {
+ tp = exp->tagq.tqh_first;
+ if (tp == ntp)
+ break;
+ FREETAG(tp);
+ }
+ }
+ break;
+ default:
+ abort();
+ }
+
+ /* Update the cursor from the saved TAG information. */
+ tp = exp->tagq.tqh_first;
+ if (tp->frp == sp->frp) {
+ sp->lno = tp->lno;
+ sp->cno = tp->cno;
+ } else {
+ MODIFY_CHECK(sp, ep, F_ISSET(cmdp, E_FORCE));
+
+ if (file_init(sp, tp->frp, NULL, 0))
+ return (1);
+
+ tp->frp->lno = tp->lno;
+ tp->frp->cno = tp->cno;
+ F_SET(sp->frp, FR_CURSORSET);
+
+ F_SET(sp, S_FSWITCH);
+ }
+
+ /* If returning to the first tag, the stack is now empty. */
+ if (tp->q.tqe_next == NULL)
+ FREETAG(tp);
+ return (0);
+}
+
+/*
+ * ex_tagtop -- :tagt[op][!]
+ * Clear the tag stack.
+ */
+int
+ex_tagtop(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ EX_PRIVATE *exp;
+ TAG *tp, tmp;
+ int found;
+
+ /* Pop to oldest saved information. */
+ exp = EXP(sp);
+ for (found = 0; (tp = exp->tagq.tqh_first) != NULL; found = 1) {
+ if (exp->tagq.tqh_first == NULL)
+ tmp = *tp;
+ FREETAG(tp);
+ }
+
+ if (!found) {
+ msgq(sp, M_INFO, "The tags stack is empty.");
+ return (1);
+ }
+
+ /* If not switching files, it's easy; else do the work. */
+ if (tmp.frp == sp->frp) {
+ sp->lno = tmp.lno;
+ sp->cno = tmp.cno;
+ } else {
+ MODIFY_CHECK(sp, sp->ep, F_ISSET(cmdp, E_FORCE));
+
+ if (file_init(sp, tmp.frp, NULL, 0))
+ return (1);
+
+ tmp.frp->lno = tmp.lno;
+ tmp.frp->cno = tmp.cno;
+
+ F_SET(sp->frp, FR_CURSORSET);
+
+ F_SET(sp, S_FSWITCH);
+ }
+ return (0);
+}
+
+/*
+ * ex_tagdisplay --
+ * Display the list of tags.
+ */
+int
+ex_tagdisplay(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ EX_PRIVATE *exp;
+ TAG *tp;
+ size_t len, maxlen;
+ int cnt;
+ char *name;
+
+ exp = EXP(sp);
+ if ((tp = exp->tagq.tqh_first) == NULL) {
+ (void)ex_printf(EXCOOKIE, "No tags to display.\n");
+ return (0);
+ }
+
+ /*
+ * Figure out the formatting. MNOC is the maximum
+ * number of file name columns before we split the line.
+ */
+#define MNOC 15
+ for (maxlen = 0,
+ tp = exp->tagq.tqh_first; tp != NULL; tp = tp->q.tqe_next) {
+ len = strlen(name = tp->frp->name); /* The original name. */
+ if (maxlen < len && len < MNOC)
+ maxlen = len;
+ }
+
+ for (cnt = 1, tp = exp->tagq.tqh_first; tp != NULL;
+ ++cnt, tp = tp->q.tqe_next) {
+ len = strlen(name = tp->frp->name); /* The original name. */
+ if (len > maxlen || len + tp->slen > sp->cols)
+ if (tp == NULL || tp->search == NULL)
+ (void)ex_printf(EXCOOKIE,
+ "%2d %s\n", cnt, name);
+ else
+ (void)ex_printf(EXCOOKIE,
+ "%2d %s\n** %*.*s %s\n", cnt, name,
+ (int)maxlen, (int)maxlen, "", tp->search);
+ else
+ if (tp == NULL || tp->search == NULL)
+ (void)ex_printf(EXCOOKIE, "%2d %*.*s\n",
+ cnt, (int)maxlen, (int)len, name);
+ else
+ (void)ex_printf(EXCOOKIE, "%2d %*.*s %s\n",
+ cnt, (int)maxlen, (int)len, name,
+ tp->search);
+ }
+ return (0);
+}
+
+/*
+ * ex_tagalloc --
+ * Create a new list of tag files.
+ */
+int
+ex_tagalloc(sp, str)
+ SCR *sp;
+ char *str;
+{
+ EX_PRIVATE *exp;
+ TAGF *tp;
+ size_t len;
+ char *p, *t;
+
+ /* Free current queue. */
+ exp = EXP(sp);
+ while ((tp = exp->tagfq.tqh_first) != NULL)
+ FREETAGF(tp);
+
+ /* Create new queue. */
+ for (p = t = str;; ++p) {
+ if (*p == '\0' || isblank(*p)) {
+ if ((len = p - t) > 1) {
+ MALLOC_RET(sp, tp, TAGF *, sizeof(TAGF));
+ MALLOC(sp, tp->name, char *, len + 1);
+ if (tp->name == NULL) {
+ FREE(tp, sizeof(TAGF));
+ return (1);
+ }
+ memmove(tp->name, t, len);
+ tp->name[len] = '\0';
+ tp->flags = 0;
+ TAILQ_INSERT_TAIL(&exp->tagfq, tp, q);
+ }
+ t = p + 1;
+ }
+ if (*p == '\0')
+ break;
+ }
+ return (0);
+}
+ /* Free previous queue. */
+/*
+ * ex_tagfree --
+ * Free the tags file list.
+ */
+int
+ex_tagfree(sp)
+ SCR *sp;
+{
+ EX_PRIVATE *exp;
+ TAG *tp;
+ TAGF *tfp;
+
+ /* Free up tag information. */
+ exp = EXP(sp);
+ while ((tp = exp->tagq.tqh_first) != NULL)
+ FREETAG(tp);
+ while ((tfp = exp->tagfq.tqh_first) != NULL)
+ FREETAGF(tfp);
+ FREE(exp->tlast, strlen(exp->tlast) + 1);
+ return (0);
+}
+
+/*
+ * ex_tagcopy --
+ * Copy a screen's tag structures.
+ */
+int
+ex_tagcopy(orig, sp)
+ SCR *orig, *sp;
+{
+ EX_PRIVATE *oexp, *nexp;
+ TAG *ap, *tp;
+ TAGF *atfp, *tfp;
+
+ /* Copy tag stack. */
+ oexp = EXP(orig);
+ nexp = EXP(sp);
+ for (ap = oexp->tagq.tqh_first; ap != NULL; ap = ap->q.tqe_next) {
+ MALLOC(sp, tp, TAG *, sizeof(TAG));
+ if (tp == NULL)
+ goto nomem;
+ *tp = *ap;
+ if (ap->search != NULL &&
+ (tp->search = strdup(ap->search)) == NULL)
+ goto nomem;
+ TAILQ_INSERT_TAIL(&nexp->tagq, tp, q);
+ }
+
+ /* Copy list of tag files. */
+ for (atfp = oexp->tagfq.tqh_first;
+ atfp != NULL; atfp = atfp->q.tqe_next) {
+ MALLOC(sp, tfp, TAGF *, sizeof(TAGF));
+ if (tfp == NULL)
+ goto nomem;
+ *tfp = *atfp;
+ if ((tfp->name = strdup(atfp->name)) == NULL)
+ goto nomem;
+ TAILQ_INSERT_TAIL(&nexp->tagfq, tfp, q);
+ }
+
+ /* Copy the last tag. */
+ if (oexp->tlast != NULL &&
+ (nexp->tlast = strdup(oexp->tlast)) == NULL) {
+nomem: msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * tag_get --
+ * Get a tag from the tags files.
+ */
+static int
+tag_get(sp, tag, tagp, filep, searchp)
+ SCR *sp;
+ char *tag, **tagp, **filep, **searchp;
+{
+ EX_PRIVATE *exp;
+ TAGF *tfp;
+ int dne;
+ char *p;
+
+ /*
+ * Find the tag, only display missing file messages once, and
+ * then only if we didn't find the tag.
+ */
+ dne = 0;
+ exp = EXP(sp);
+ for (p = NULL, tfp = exp->tagfq.tqh_first;
+ tfp != NULL && p == NULL; tfp = tfp->q.tqe_next) {
+ errno = 0;
+ F_CLR(tfp, TAGF_DNE);
+ if (search(sp, tfp->name, tag, &p))
+ if (errno == ENOENT) {
+ if (!F_ISSET(tfp, TAGF_DNE_WARN)) {
+ dne = 1;
+ F_SET(tfp, TAGF_DNE);
+ }
+ } else
+ msgq(sp, M_SYSERR, tfp->name);
+ }
+
+ if (p == NULL) {
+ msgq(sp, M_ERR, "%s: tag not found.", tag);
+ if (dne)
+ for (tfp = exp->tagfq.tqh_first;
+ tfp != NULL; tfp = tfp->q.tqe_next)
+ if (F_ISSET(tfp, TAGF_DNE)) {
+ errno = ENOENT;
+ msgq(sp, M_SYSERR, tfp->name);
+ F_SET(tfp, TAGF_DNE_WARN);
+ }
+ return (1);
+ }
+
+ /*
+ * Set the return pointers; tagp points to the tag, and, incidentally
+ * the allocated string, filep points to the nul-terminated file name,
+ * searchp points to the nul-terminated search string.
+ */
+ for (*tagp = p; *p && !isblank(*p); ++p);
+ if (*p == '\0')
+ goto malformed;
+ for (*p++ = '\0'; isblank(*p); ++p);
+ for (*filep = p; *p && !isblank(*p); ++p);
+ if (*p == '\0')
+ goto malformed;
+ for (*p++ = '\0'; isblank(*p); ++p);
+ *searchp = p;
+ if (*p == '\0') {
+malformed: free(*tagp);
+ msgq(sp, M_ERR, "%s: corrupted tag in %s.", tag, tfp->name);
+ return (1);
+ }
+ return (0);
+}
+
+#define EQUAL 0
+#define GREATER 1
+#define LESS (-1)
+
+/*
+ * search --
+ * Search a file for a tag.
+ */
+static int
+search(sp, name, tname, tag)
+ SCR *sp;
+ char *name, *tname, **tag;
+{
+ struct stat sb;
+ int fd, len;
+ char *endp, *back, *front, *map, *p;
+
+ if ((fd = open(name, O_RDONLY, 0)) < 0)
+ return (1);
+
+ /*
+ * XXX
+ * We'd like to test if the file is too big to mmap. Since we don't
+ * know what size or type off_t's or size_t's are, what the largest
+ * unsigned integral type is, or what random insanity the local C
+ * compiler will perpetrate, doing the comparison in a portable way
+ * is flatly impossible. Hope that malloc fails if the file is too
+ * large.
+ */
+ if (fstat(fd, &sb) || (map = mmap(NULL, (size_t)sb.st_size,
+ PROT_READ, MAP_PRIVATE, fd, (off_t)0)) == (caddr_t)-1) {
+ (void)close(fd);
+ return (1);
+ }
+ front = map;
+ back = front + sb.st_size;
+
+ front = binary_search(tname, front, back);
+ front = linear_search(tname, front, back);
+
+ if (front == NULL || (endp = strchr(front, '\n')) == NULL) {
+ *tag = NULL;
+ goto done;
+ }
+
+ len = endp - front;
+ MALLOC(sp, p, char *, len + 1);
+ if (p == NULL) {
+ *tag = NULL;
+ goto done;
+ }
+ memmove(p, front, len);
+ p[len] = '\0';
+ *tag = p;
+
+done: if (munmap(map, (size_t)sb.st_size))
+ msgq(sp, M_SYSERR, "munmap");
+ if (close(fd))
+ msgq(sp, M_SYSERR, "close");
+ return (0);
+}
+
+/*
+ * Binary search for "string" in memory between "front" and "back".
+ *
+ * This routine is expected to return a pointer to the start of a line at
+ * *or before* the first word matching "string". Relaxing the constraint
+ * this way simplifies the algorithm.
+ *
+ * Invariants:
+ * front points to the beginning of a line at or before the first
+ * matching string.
+ *
+ * back points to the beginning of a line at or after the first
+ * matching line.
+ *
+ * Base of the Invariants.
+ * front = NULL;
+ * back = EOF;
+ *
+ * Advancing the Invariants:
+ *
+ * p = first newline after halfway point from front to back.
+ *
+ * If the string at "p" is not greater than the string to match,
+ * p is the new front. Otherwise it is the new back.
+ *
+ * Termination:
+ *
+ * The definition of the routine allows it return at any point,
+ * since front is always at or before the line to print.
+ *
+ * In fact, it returns when the chosen "p" equals "back". This
+ * implies that there exists a string is least half as long as
+ * (back - front), which in turn implies that a linear search will
+ * be no more expensive than the cost of simply printing a string or two.
+ *
+ * Trying to continue with binary search at this point would be
+ * more trouble than it's worth.
+ */
+#define SKIP_PAST_NEWLINE(p, back) while (p < back && *p++ != '\n');
+
+static char *
+binary_search(string, front, back)
+ register char *string, *front, *back;
+{
+ register char *p;
+
+ p = front + (back - front) / 2;
+ SKIP_PAST_NEWLINE(p, back);
+
+ while (p != back) {
+ if (compare(string, p, back) == GREATER)
+ front = p;
+ else
+ back = p;
+ p = front + (back - front) / 2;
+ SKIP_PAST_NEWLINE(p, back);
+ }
+ return (front);
+}
+
+/*
+ * Find the first line that starts with string, linearly searching from front
+ * to back.
+ *
+ * Return NULL for no such line.
+ *
+ * This routine assumes:
+ *
+ * o front points at the first character in a line.
+ * o front is before or at the first line to be printed.
+ */
+static char *
+linear_search(string, front, back)
+ char *string, *front, *back;
+{
+ while (front < back) {
+ switch (compare(string, front, back)) {
+ case EQUAL: /* Found it. */
+ return (front);
+ break;
+ case LESS: /* No such string. */
+ return (NULL);
+ break;
+ case GREATER: /* Keep going. */
+ break;
+ }
+ SKIP_PAST_NEWLINE(front, back);
+ }
+ return (NULL);
+}
+
+/*
+ * Return LESS, GREATER, or EQUAL depending on how the string1 compares
+ * with string2 (s1 ??? s2).
+ *
+ * o Matches up to len(s1) are EQUAL.
+ * o Matches up to len(s2) are GREATER.
+ *
+ * The string "s1" is null terminated. The string s2 is '\t', space, (or
+ * "back") terminated.
+ *
+ * !!!
+ * Reasonably modern ctags programs use tabs as separators, not spaces.
+ * However, historic programs did use spaces, and, I got complaints.
+ */
+static int
+compare(s1, s2, back)
+ register char *s1, *s2, *back;
+{
+ for (; *s1 && s2 < back && (*s2 != '\t' && *s2 != ' '); ++s1, ++s2)
+ if (*s1 != *s2)
+ return (*s1 < *s2 ? LESS : GREATER);
+ return (*s1 ? GREATER : s2 < back &&
+ (*s2 != '\t' && *s2 != ' ') ? LESS : EQUAL);
+}
diff --git a/usr.bin/vi/nex/ex_undo.c b/usr.bin/vi/nex/ex_undo.c
new file mode 100644
index 000000000000..c8dedd8d401a
--- /dev/null
+++ b/usr.bin/vi/nex/ex_undo.c
@@ -0,0 +1,99 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_undo.c 8.4 (Berkeley) 12/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_undol -- U
+ * Undo changes to this line.
+ */
+int
+ex_undol(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ if (log_setline(sp, ep))
+ return (1);
+
+ sp->cno = 0;
+ return (0);
+}
+
+/*
+ * ex_undo -- u
+ * Undo the last change.
+ */
+int
+ex_undo(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ MARK m;
+
+ /*
+ * !!!
+ * Multiple undo isn't available in ex, as there's no '.' command.
+ * Whether 'u' is undo or redo is toggled each time, unless there
+ * was a change since the last undo, in which case it's an undo.
+ */
+ if (!F_ISSET(ep, F_UNDO)) {
+ F_SET(ep, F_UNDO);
+ ep->lundo = FORWARD;
+ }
+ switch (ep->lundo) {
+ case BACKWARD:
+ if (log_forward(sp, ep, &m))
+ return (1);
+ ep->lundo = FORWARD;
+ break;
+ case FORWARD:
+ if (log_backward(sp, ep, &m))
+ return (1);
+ ep->lundo = BACKWARD;
+ break;
+ case NOTSET:
+ abort();
+ }
+ sp->lno = m.lno;
+ sp->cno = m.cno;
+ return (0);
+}
diff --git a/usr.bin/vi/nex/ex_usage.c b/usr.bin/vi/nex/ex_usage.c
new file mode 100644
index 000000000000..da84995d2993
--- /dev/null
+++ b/usr.bin/vi/nex/ex_usage.c
@@ -0,0 +1,163 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_usage.c 8.11 (Berkeley) 12/17/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+/*
+ * ex_help -- :help
+ * Display help message.
+ */
+int
+ex_help(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ (void)ex_printf(EXCOOKIE,
+ "To see the list of vi commands, enter \":viusage<CR>\"\n");
+ (void)ex_printf(EXCOOKIE,
+ "To see the list of ex commands, enter \":exusage<CR>\"\n");
+ (void)ex_printf(EXCOOKIE,
+ "For an ex command usage statement enter \":exusage [cmd]<CR>\"\n");
+ (void)ex_printf(EXCOOKIE,
+ "For a vi key usage statement enter \":viusage [key]<CR>\"\n");
+ (void)ex_printf(EXCOOKIE, "To exit, enter \":q!\"\n");
+ return (0);
+}
+
+/*
+ * ex_usage -- :exusage [cmd]
+ * Display ex usage strings.
+ */
+int
+ex_usage(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ ARGS *ap;
+ EXCMDLIST const *cp;
+
+ switch (cmdp->argc) {
+ case 1:
+ ap = cmdp->argv[0];
+ for (cp = cmds; cp->name != NULL &&
+ memcmp(ap->bp, cp->name, ap->len); ++cp);
+ if (cp->name == NULL)
+ (void)ex_printf(EXCOOKIE,
+ "The %.*s command is unknown.",
+ (int)ap->len, ap->bp);
+ else {
+ (void)ex_printf(EXCOOKIE,
+ "Command: %s\n Usage: %s\n", cp->help, cp->usage);
+ /*
+ * !!!
+ * The "visual" command has two modes, one from ex,
+ * one from the vi colon line. Don't ask.
+ */
+ if (cp != &cmds[C_VISUAL_EX] &&
+ cp != &cmds[C_VISUAL_VI])
+ break;
+ if (cp == &cmds[C_VISUAL_EX])
+ cp = &cmds[C_VISUAL_VI];
+ else
+ cp = &cmds[C_VISUAL_EX];
+ (void)ex_printf(EXCOOKIE,
+ "Command: %s\n Usage: %s\n", cp->help, cp->usage);
+ }
+ break;
+ case 0:
+ for (cp = cmds; cp->name != NULL; ++cp)
+ (void)ex_printf(EXCOOKIE,
+ "%*s: %s\n", MAXCMDNAMELEN, cp->name, cp->help);
+ break;
+ default:
+ abort();
+ }
+ return (0);
+}
+
+/*
+ * ex_viusage -- :viusage [key]
+ * Display vi usage strings.
+ */
+int
+ex_viusage(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ VIKEYS const *kp;
+ int key;
+
+ switch (cmdp->argc) {
+ case 1:
+ key = cmdp->argv[0]->bp[0];
+ if (key > MAXVIKEY)
+ goto nokey;
+
+ /* Special case: '[' and ']' commands. */
+ if ((key == '[' || key == ']') && cmdp->argv[0]->bp[1] != key)
+ goto nokey;
+
+ kp = &vikeys[key];
+ if (kp->func == NULL)
+nokey: (void)ex_printf(EXCOOKIE,
+ "The %s key has no current meaning",
+ charname(sp, key));
+ else
+ (void)ex_printf(EXCOOKIE,
+ " Key:%s%s\nUsage: %s\n",
+ isblank(*kp->help) ? "" : " ", kp->help, kp->usage);
+ break;
+ case 0:
+ for (key = 0; key <= MAXVIKEY; ++key) {
+ kp = &vikeys[key];
+ if (kp->help != NULL)
+ (void)ex_printf(EXCOOKIE, "%s\n", kp->help);
+ }
+ break;
+ default:
+ abort();
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/nex/ex_util.c b/usr.bin/vi/nex/ex_util.c
new file mode 100644
index 000000000000..fa7e70b9dfcc
--- /dev/null
+++ b/usr.bin/vi/nex/ex_util.c
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)ex_util.c 8.4 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_getline --
+ * Return a line from the terminal.
+ */
+int
+ex_getline(sp, fp, lenp)
+ SCR *sp;
+ FILE *fp;
+ size_t *lenp;
+{
+ EX_PRIVATE *exp;
+ size_t off;
+ int ch;
+ char *p;
+
+ exp = EXP(sp);
+ for (off = 0, p = exp->ibp;; ++off) {
+ ch = getc(fp);
+ if (off >= exp->ibp_len) {
+ BINC_RET(sp, exp->ibp, exp->ibp_len, off + 1);
+ p = exp->ibp + off;
+ }
+ if (ch == EOF || ch == '\n') {
+ if (ch == EOF && !off)
+ return (1);
+ *lenp = off;
+ return (0);
+ }
+ *p++ = ch;
+ }
+ /* NOTREACHED */
+}
diff --git a/usr.bin/vi/nex/ex_version.c b/usr.bin/vi/nex/ex_version.c
new file mode 100644
index 000000000000..230632d2de3c
--- /dev/null
+++ b/usr.bin/vi/nex/ex_version.c
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (c) 1991, 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
+static char sccsid[] = "@(#)ex_version.c 8.32 (Berkeley) 1/23/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_version -- :version
+ * Display the program version.
+ */
+int
+ex_version(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ static time_t then = 759365451;
+
+ (void)ex_printf(EXCOOKIE,
+"Version 1.03, %sThe CSRG, University of California, Berkeley.\n",
+ ctime(&then));
+ return (0);
+}
diff --git a/usr.bin/vi/nex/ex_visual.c b/usr.bin/vi/nex/ex_visual.c
new file mode 100644
index 000000000000..d53152bde8e8
--- /dev/null
+++ b/usr.bin/vi/nex/ex_visual.c
@@ -0,0 +1,122 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_visual.c 8.7 (Berkeley) 11/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_visual -- :[line] vi[sual] [^-.+] [window_size] [flags]
+ *
+ * Switch to visual mode.
+ */
+int
+ex_visual(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ size_t len;
+ int pos;
+ char buf[256];
+
+ /* If open option off, disallow visual command. */
+ if (!O_ISSET(sp, O_OPEN)) {
+ msgq(sp, M_ERR,
+ "The visual command requires that the open option be set.");
+ return (1);
+ }
+
+ /* If a line specified, move to that line. */
+ if (cmdp->addrcnt)
+ sp->lno = cmdp->addr1.lno;
+
+ /*
+ * Push a command based on the line position flags. If no
+ * flag specified, the line goes at the top of the screen.
+ */
+ switch (F_ISSET(cmdp, E_F_CARAT | E_F_DASH | E_F_DOT | E_F_PLUS)) {
+ case E_F_CARAT:
+ pos = '^';
+ break;
+ case E_F_DASH:
+ pos = '-';
+ break;
+ case E_F_DOT:
+ pos = '.';
+ break;
+ case E_F_PLUS:
+ default:
+ pos = '+';
+ break;
+ }
+
+ if (F_ISSET(cmdp, E_COUNT))
+ len = snprintf(buf, sizeof(buf),
+ "%luz%c%lu", sp->lno, pos, cmdp->count);
+ else
+ len = snprintf(buf, sizeof(buf), "%luz%c", sp->lno, pos);
+ (void)term_push(sp, buf, len, 0, CH_NOMAP | CH_QUOTED);
+
+ /*
+ * !!!
+ * Historically, if no line address was specified, the [p#l] flags
+ * caused the cursor to be moved to the last line of the file, which
+ * was then positioned as described above. This seems useless, so
+ * I haven't implemented it.
+ */
+ switch (F_ISSET(cmdp, E_F_HASH | E_F_LIST | E_F_PRINT)) {
+ case E_F_HASH:
+ O_SET(sp, O_NUMBER);
+ break;
+ case E_F_LIST:
+ O_SET(sp, O_LIST);
+ break;
+ case E_F_PRINT:
+ break;
+ }
+
+ /* Switch modes. */
+ F_CLR(sp, S_SCREENS);
+ F_SET(sp, sp->saved_vi_mode);
+
+ return (0);
+}
diff --git a/usr.bin/vi/nex/ex_write.c b/usr.bin/vi/nex/ex_write.c
new file mode 100644
index 000000000000..5a51b6dff914
--- /dev/null
+++ b/usr.bin/vi/nex/ex_write.c
@@ -0,0 +1,277 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_write.c 8.19 (Berkeley) 12/18/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+enum which {WQ, WRITE, XIT};
+
+static int exwr __P((SCR *, EXF *, EXCMDARG *, enum which));
+
+/*
+ * ex_wq -- :wq[!] [>>] [file]
+ * Write to a file.
+ */
+int
+ex_wq(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ int force;
+
+ if (exwr(sp, ep, cmdp, WQ))
+ return (1);
+
+ force = F_ISSET(cmdp, E_FORCE);
+ if (!force && ep->refcnt <= 1 && file_unedited(sp) != NULL) {
+ msgq(sp, M_ERR,
+ "More files to edit; use \":n\" to go to the next file");
+ return (1);
+ }
+
+ F_SET(sp, force ? S_EXIT_FORCE : S_EXIT);
+ return (0);
+}
+
+/*
+ * ex_write -- :write[!] [>>] [file]
+ * :write [!] [cmd]
+ * Write to a file.
+ */
+int
+ex_write(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (exwr(sp, ep, cmdp, WRITE));
+}
+
+
+/*
+ * ex_xit -- :x[it]! [file]
+ *
+ * Write out any modifications and quit.
+ */
+int
+ex_xit(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ int force;
+
+ if (F_ISSET((ep), F_MODIFIED) && exwr(sp, ep, cmdp, XIT))
+ return (1);
+
+ force = F_ISSET(cmdp, E_FORCE);
+ if (!force && ep->refcnt <= 1 && file_unedited(sp) != NULL) {
+ msgq(sp, M_ERR,
+ "More files to edit; use \":n\" to go to the next file");
+ return (1);
+ }
+
+ F_SET(sp, force ? S_EXIT_FORCE : S_EXIT);
+ return (0);
+}
+
+/*
+ * exwr --
+ * The guts of the ex write commands.
+ */
+static int
+exwr(sp, ep, cmdp, cmd)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+ enum which cmd;
+{
+ EX_PRIVATE *exp;
+ MARK rm;
+ int flags;
+ char *name, *p;
+
+ /* All write commands can have an associated '!'. */
+ LF_INIT(FS_POSSIBLE);
+ if (F_ISSET(cmdp, E_FORCE))
+ LF_SET(FS_FORCE);
+
+ /* Skip any leading whitespace. */
+ if (cmdp->argc != 0)
+ for (p = cmdp->argv[0]->bp; *p && isblank(*p); ++p);
+
+ /* If no arguments, just write the file back. */
+ if (cmdp->argc == 0 || *p == '\0') {
+ if (F_ISSET(cmdp, E_ADDR2_ALL))
+ LF_SET(FS_ALL);
+ return (file_write(sp, ep,
+ &cmdp->addr1, &cmdp->addr2, NULL, flags));
+ }
+
+ /* If "write !" it's a pipe to a utility. */
+ exp = EXP(sp);
+ if (cmd == WRITE && *p == '!') {
+ for (++p; *p && isblank(*p); ++p);
+ if (*p == '\0') {
+ msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage);
+ return (1);
+ }
+ /* Expand the argument. */
+ if (argv_exp1(sp, ep, cmdp, p, strlen(p), 0))
+ return (1);
+ if (filtercmd(sp, ep, &cmdp->addr1, &cmdp->addr2,
+ &rm, cmdp->argv[1]->bp, FILTER_WRITE))
+ return (1);
+ sp->lno = rm.lno;
+ return (0);
+ }
+
+ /* If "write >>" it's an append to a file. */
+ if (cmd != XIT && p[0] == '>' && p[1] == '>') {
+ LF_SET(FS_APPEND);
+
+ /* Skip ">>" and whitespace. */
+ for (p += 2; *p && isblank(*p); ++p);
+ }
+
+ /* Build an argv so we get an argument count and file expansion. */
+ if (argv_exp2(sp, ep, cmdp, p, strlen(p), 0))
+ return (1);
+
+ switch (cmdp->argc) {
+ case 1:
+ /*
+ * Nothing to expand, write the current file.
+ * XXX
+ * Should never happen, already checked this case.
+ */
+ name = NULL;
+ break;
+ case 2:
+ /* One new argument, write it. */
+ name = cmdp->argv[exp->argsoff - 1]->bp;
+ set_alt_name(sp, name);
+ break;
+ default:
+ /* If expanded to more than one argument, object. */
+ msgq(sp, M_ERR, "%s expanded into too many file names",
+ cmdp->argv[0]->bp);
+ msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage);
+ return (1);
+ }
+
+ if (F_ISSET(cmdp, E_ADDR2_ALL))
+ LF_SET(FS_ALL);
+ return (file_write(sp, ep, &cmdp->addr1, &cmdp->addr2, name, flags));
+}
+
+/*
+ * ex_writefp --
+ * Write a range of lines to a FILE *.
+ */
+int
+ex_writefp(sp, ep, name, fp, fm, tm, nlno, nch)
+ SCR *sp;
+ EXF *ep;
+ char *name;
+ FILE *fp;
+ MARK *fm, *tm;
+ u_long *nlno, *nch;
+{
+ register u_long ccnt, fline, tline;
+ size_t len;
+ char *p;
+
+ fline = fm->lno;
+ tline = tm->lno;
+
+ if (nlno != NULL) {
+ *nch = 0;
+ *nlno = 0;
+ }
+ ccnt = 0;
+
+ /*
+ * The vi filter code has multiple processes running simultaneously,
+ * and one of them calls ex_writefp(). The "unsafe" function calls
+ * in this code are to file_gline() and msgq(). File_gline() is safe,
+ * see the comment in filter.c:filtercmd() for details. We don't call
+ * msgq if the multiple process bit in the EXF is set.
+ *
+ * !!!
+ * Historic vi permitted files of 0 length to be written. However,
+ * since the way vi got around dealing with "empty" files was to
+ * always have a line in the file no matter what, it wrote them as
+ * files of a single, empty line. We write empty files.
+ *
+ * "Alex, I'll take vi trivia for $1000."
+ */
+ if (tline != 0)
+ for (; fline <= tline; ++fline) {
+ if ((p = file_gline(sp, ep, fline, &len)) == NULL)
+ break;
+ if (fwrite(p, 1, len, fp) != len) {
+ msgq(sp, M_SYSERR, name);
+ (void)fclose(fp);
+ return (1);
+ }
+ ccnt += len;
+ if (putc('\n', fp) != '\n')
+ break;
+ ++ccnt;
+ }
+ if (fclose(fp)) {
+ if (!F_ISSET(ep, F_MULTILOCK))
+ msgq(sp, M_SYSERR, name);
+ return (1);
+ }
+ if (nlno != NULL) {
+ *nch = ccnt;
+ *nlno = tm->lno == 0 ? 0 : tm->lno - fm->lno + 1;
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/nex/ex_yank.c b/usr.bin/vi/nex/ex_yank.c
new file mode 100644
index 000000000000..0897034bf022
--- /dev/null
+++ b/usr.bin/vi/nex/ex_yank.c
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)ex_yank.c 8.3 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_yank -- :[line [,line]] ya[nk] [buffer] [count]
+ *
+ * Yank the lines into a buffer.
+ */
+int
+ex_yank(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (cut(sp, ep, NULL,
+ F_ISSET(cmdp, E_BUFFER) ? &cmdp->buffer : NULL,
+ &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE));
+}
diff --git a/usr.bin/vi/nex/ex_z.c b/usr.bin/vi/nex/ex_z.c
new file mode 100644
index 000000000000..2842a22855a5
--- /dev/null
+++ b/usr.bin/vi/nex/ex_z.c
@@ -0,0 +1,160 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)ex_z.c 8.4 (Berkeley) 12/2/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_z -- :[line] z [^-.+=] [count] [flags]
+ *
+ * Adjust window.
+ */
+int
+ex_z(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ recno_t cnt, equals, lno;
+ int eofcheck;
+
+ /*
+ * !!!
+ * If no count specified, use either two times the size of the
+ * scrolling region, or the size of the window option. POSIX
+ * 1003.2 claims that the latter is correct, but historic ex/vi
+ * documentation and practice appear to use the scrolling region.
+ * I'm using the window size as it means that the entire screen
+ * is used instead of losing a line to roundoff. Note, we drop
+ * a line from the cnt if using the window size to leave room for
+ * the next ex prompt.
+ */
+ if (F_ISSET(cmdp, E_COUNT))
+ cnt = cmdp->count;
+ else
+#ifdef HISTORIC_PRACTICE
+ cnt = O_VAL(sp, O_SCROLL) * 2;
+#else
+ cnt = O_VAL(sp, O_WINDOW) - 1;
+#endif
+
+ equals = 0;
+ eofcheck = 0;
+ lno = cmdp->addr1.lno;
+
+ switch (F_ISSET(cmdp,
+ E_F_CARAT | E_F_DASH | E_F_DOT | E_F_EQUAL | E_F_PLUS)) {
+ case E_F_CARAT: /* Display cnt * 2 before the line. */
+ eofcheck = 1;
+ if (lno > cnt * 2)
+ cmdp->addr1.lno = (lno - cnt * 2) + 1;
+ else
+ cmdp->addr1.lno = 1;
+ cmdp->addr2.lno = (cmdp->addr1.lno + cnt) - 1;
+ break;
+ case E_F_DASH: /* Line goes at the bottom of the screen. */
+ cmdp->addr1.lno = lno > cnt ? (lno - cnt) + 1 : 1;
+ cmdp->addr2.lno = lno;
+ break;
+ case E_F_DOT: /* Line goes in the middle of the screen. */
+ /*
+ * !!!
+ * Historically, the "middleness" of the line overrode the
+ * count, so that "3z.19" or "3z.20" would display the first
+ * 12 lines of the file, i.e. (N - 1) / 2 lines before and
+ * after the specified line.
+ */
+ eofcheck = 1;
+ cnt = (cnt - 1) / 2;
+ cmdp->addr1.lno = lno > cnt ? lno - cnt : 1;
+ cmdp->addr2.lno = lno + cnt;
+ break;
+ case E_F_EQUAL: /* Center with hyphens. */
+ /*
+ * !!!
+ * Strangeness. The '=' flag is like the '.' flag (see the
+ * above comment, it applies here as well) but with a special
+ * little hack. Print out lines of hyphens before and after
+ * the specified line. Additionally, the cursor remains set
+ * on that line.
+ */
+ eofcheck = 1;
+ cnt = (cnt - 1) / 2;
+ cmdp->addr1.lno = lno > cnt ? lno - cnt : 1;
+ cmdp->addr2.lno = lno - 1;
+ if (ex_pr(sp, ep, cmdp))
+ return (1);
+ (void)ex_printf(EXCOOKIE,
+ "%s", "----------------------------------------\n");
+ cmdp->addr2.lno = cmdp->addr1.lno = equals = lno;
+ if (ex_pr(sp, ep, cmdp))
+ return (1);
+ (void)ex_printf(EXCOOKIE,
+ "%s", "----------------------------------------\n");
+ cmdp->addr1.lno = lno + 1;
+ cmdp->addr2.lno = (lno + cnt) - 1;
+ break;
+ default:
+ /* If no line specified, move to the next one. */
+ if (F_ISSET(cmdp, E_ADDRDEF))
+ ++lno;
+ /* FALLTHROUGH */
+ case E_F_PLUS: /* Line goes at the top of the screen. */
+ eofcheck = 1;
+ cmdp->addr1.lno = lno;
+ cmdp->addr2.lno = (lno + cnt) - 1;
+ break;
+ }
+
+ if (eofcheck) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (cmdp->addr2.lno > lno)
+ cmdp->addr2.lno = lno;
+ }
+
+ if (ex_pr(sp, ep, cmdp))
+ return (1);
+ if (equals)
+ sp->lno = equals;
+ return (0);
+}
diff --git a/usr.bin/vi/nex/excmd.c b/usr.bin/vi/nex/excmd.c
new file mode 100644
index 000000000000..27ebc39eebe8
--- /dev/null
+++ b/usr.bin/vi/nex/excmd.c
@@ -0,0 +1,431 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)excmd.c 8.36 (Berkeley) 1/22/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * This array maps ex command names to command functions.
+ *
+ * The order in which command names are listed below is important --
+ * ambiguous abbreviations are resolved to be the first possible match,
+ * e.g. "r" means "read", not "rewind", because "read" is listed before
+ * "rewind".
+ *
+ * The syntax of the ex commands is unbelievably irregular, and a special
+ * case from beginning to end. Each command has an associated "syntax
+ * script" which describes the "arguments" that are possible. The script
+ * syntax is as follows:
+ *
+ * ! -- ! flag
+ * 1 -- flags: [+-]*[pl#][+-]*
+ * 2 -- flags: [-.+^]
+ * 3 -- flags: [-.+^=]
+ * b -- buffer
+ * c[01+a] -- count (0-N, 1-N, signed 1-N, address offset)
+ * f[N#][or] -- file (a number or N, optional or required)
+ * l -- line
+ * S -- string with file name expansion
+ * s -- string
+ * W -- word string
+ * w[N#][or] -- word (a number or N, optional or required)
+ */
+EXCMDLIST const cmds[] = {
+/* C_BANG */
+ {"!", ex_bang, E_ADDR2_NONE|E_NORC,
+ "S",
+ "[line [,line]] ! command",
+ "filter lines through commands or run commands"},
+/* C_HASH */
+ {"#", ex_number, E_ADDR2|E_F_PRCLEAR|E_NORC|E_SETLAST,
+ "ca1",
+ "[line [,line]] # [count] [l]",
+ "display numbered lines"},
+/* C_SUBAGAIN */
+ {"&", ex_subagain, E_ADDR2|E_NORC,
+ "s",
+ "[line [,line]] & [cgr] [count] [#lp]",
+ "repeat the last subsitution"},
+/* C_STAR */
+ {"*", ex_at, 0,
+ "b",
+ "* [buffer]",
+ "execute a buffer"},
+/* C_SHIFTL */
+ {"<", ex_shiftl, E_ADDR2|E_AUTOPRINT|E_NORC,
+ "ca1",
+ "[line [,line]] <[<...] [count] [flags]",
+ "shift lines left"},
+/* C_EQUAL */
+ {"=", ex_equal, E_ADDR1|E_NORC,
+ "1",
+ "[line] = [flags]",
+ "display line number"},
+/* C_SHIFTR */
+ {">", ex_shiftr, E_ADDR2|E_AUTOPRINT|E_NORC,
+ "ca1",
+ "[line [,line]] >[>...] [count] [flags]",
+ "shift lines right"},
+/* C_AT */
+ {"@", ex_at, 0,
+ "b",
+ "@ [buffer]",
+ "execute a buffer"},
+/* C_APPEND */
+ {"append", ex_append, E_ADDR1|E_NORC|E_ZERO|E_ZERODEF,
+ "!",
+ "[line] a[ppend][!]",
+ "append input to a line"},
+/* C_ABBR */
+ {"abbreviate", ex_abbr, E_NOGLOBAL,
+ "W",
+ "ab[brev] word replace",
+ "specify an input abbreviation"},
+/* C_ARGS */
+ {"args", ex_args, E_NOGLOBAL|E_NORC,
+ "",
+ "ar[gs]",
+ "display file argument list"},
+/* C_BG */
+ {"bg", ex_bg, E_NOGLOBAL|E_NORC,
+ "",
+ "bg",
+ "background the current screen"},
+/* C_CHANGE */
+ {"change", ex_change, E_ADDR2|E_NORC|E_ZERODEF,
+ "!ca",
+ "[line [,line]] c[hange][!] [count]",
+ "change lines to input"},
+/* C_CD */
+ {"cd", ex_cd, E_NOGLOBAL,
+ "!f1o",
+ "cd[!] [directory]",
+ "change the current directory"},
+/* C_CHDIR */
+ {"chdir", ex_cd, E_NOGLOBAL,
+ "!f1o",
+ "chd[ir][!] [directory]",
+ "change the current directory"},
+/* C_COPY */
+ {"copy", ex_copy, E_ADDR2|E_AUTOPRINT|E_NORC,
+ "l1",
+ "[line [,line]] co[py] line [flags]",
+ "copy lines elsewhere in the file"},
+/* C_DELETE */
+ {"delete", ex_delete, E_ADDR2|E_AUTOPRINT|E_NORC,
+ "bca1",
+ "[line [,line]] d[elete] [buffer] [count] [flags]",
+ "delete lines from the file"},
+/* C_DISPLAY */
+ {"display", ex_display, E_NOGLOBAL|E_NORC,
+ "w1r",
+ "display b[uffers] | s[creens] | t[ags]",
+ "display buffers, screens or tags"},
+/* C_DIGRAPH */
+ {"digraph", ex_digraph, E_NOGLOBAL|E_NOPERM|E_NORC,
+ "",
+ "digraph",
+ "specify digraphs (not implemented)"},
+/* C_EDIT */
+ {"edit", ex_edit, E_NOGLOBAL|E_NORC,
+ "!f1o",
+ "e[dit][!] [+cmd] [file]",
+ "begin editing another file"},
+/* C_EX */
+ {"ex", ex_edit, E_NOGLOBAL|E_NORC,
+ "!f1o",
+ "ex[!] [+cmd] [file]",
+ "begin editing another file"},
+/* C_EXUSAGE */
+ {"exusage", ex_usage, E_NOGLOBAL|E_NORC,
+ "w1o",
+ "[exu]sage [command]",
+ "display ex command usage statement"},
+/* C_FILE */
+ {"file", ex_file, E_NOGLOBAL|E_NORC,
+ "f1o",
+ "f[ile] [name]",
+ "display (and optionally set) file name"},
+/* C_FG */
+ {"fg", ex_fg, E_NOGLOBAL|E_NORC,
+ "f1o",
+ "fg [file]",
+ "switch the current screen and a backgrounded screen"},
+/* C_GLOBAL */
+ {"global", ex_global, E_ADDR2_ALL|E_NOGLOBAL|E_NORC,
+ "!s",
+ "[line [,line]] g[lobal][!] [;/]pattern[;/] [commands]",
+ "execute a global command on lines matching a pattern"},
+/* C_HELP */
+ {"help", ex_help, E_NOGLOBAL|E_NORC,
+ "",
+ "he[lp]",
+ "display help statement"},
+/* C_INSERT */
+ {"insert", ex_insert, E_ADDR1|E_NORC,
+ "!",
+ "[line] i[nsert][!]",
+ "insert input before a line"},
+/* C_JOIN */
+ {"join", ex_join, E_ADDR2|E_AUTOPRINT|E_NORC,
+ "!ca1",
+ "[line [,line]] j[oin][!] [count] [flags]",
+ "join lines into a single line"},
+/* C_K */
+ {"k", ex_mark, E_ADDR1|E_NORC,
+ "w1r",
+ "[line] k key",
+ "mark a line position"},
+/* C_LIST */
+ {"list", ex_list, E_ADDR2|E_F_PRCLEAR|E_NORC|E_SETLAST,
+ "ca1",
+ "[line [,line]] l[ist] [count] [#]",
+ "display lines in an unambiguous form"},
+/* C_MOVE */
+ {"move", ex_move, E_ADDR2|E_AUTOPRINT|E_NORC,
+ "l",
+ "[line [,line]] m[ove] line",
+ "move lines elsewhere in the file"},
+/* C_MARK */
+ {"mark", ex_mark, E_ADDR1|E_NORC,
+ "w1r",
+ "[line] ma[rk] key",
+ "mark a line position"},
+/* C_MAP */
+ {"map", ex_map, 0,
+ "!W",
+ "map[!] [keys replace]",
+ "map input or commands to one or more keys"},
+/* C_MKEXRC */
+ {"mkexrc", ex_mkexrc, E_NOGLOBAL|E_NORC,
+ "!f1r",
+ "mkexrc[!] file",
+ "write a .exrc file"},
+/* C_NEXT */
+ {"next", ex_next, E_NOGLOBAL|E_NORC,
+ "!fN",
+ "n[ext][!] [file ...]",
+ "edit (and optionally specify) the next file"},
+/* C_NUMBER */
+ {"number", ex_number, E_ADDR2|E_F_PRCLEAR|E_NORC|E_SETLAST,
+ "ca1",
+ "[line [,line]] nu[mber] [count] [l]",
+ "change display to number lines"},
+/* C_OPEN */
+ {"open", ex_open, E_ADDR1,
+ "s",
+ "[line] o[pen] [/pattern/] [flags]",
+ "enter \"open\" mode (not implemented)"},
+/* C_PRINT */
+ {"print", ex_pr, E_ADDR2|E_F_PRCLEAR|E_NORC|E_SETLAST,
+ "ca1",
+ "[line [,line]] p[rint] [count] [#l]",
+ "display lines"},
+/* C_PRESERVE */
+ {"preserve", ex_preserve, E_NOGLOBAL|E_NORC,
+ "",
+ "pre[serve]",
+ "preserve an edit session for recovery"},
+/* C_PREVIOUS */
+ {"previous", ex_prev, E_NOGLOBAL|E_NORC,
+ "!",
+ "prev[ious][!]",
+ "edit the previous file in the file argument list"},
+/* C_PUT */
+ {"put", ex_put, E_ADDR1|E_AUTOPRINT|E_NORC|E_ZERO,
+ "b",
+ "[line] pu[t] [buffer]",
+ "append a cut buffer to the line"},
+/* C_QUIT */
+ {"quit", ex_quit, E_NOGLOBAL,
+ "!",
+ "q[uit][!]",
+ "exit ex/vi"},
+/* C_READ */
+ {"read", ex_read, E_ADDR1|E_NORC|E_ZERO|E_ZERODEF,
+ "!s",
+ "[line] r[ead] [!cmd | [file]]",
+ "append input from a command or file to the line"},
+/* C_RESIZE */
+ {"resize", ex_resize, E_NOGLOBAL|E_NORC,
+ "c+",
+ "resize [change]",
+ "grow or shrink the current screen"},
+/* C_REWIND */
+ {"rewind", ex_rew, E_NOGLOBAL|E_NORC,
+ "!",
+ "rew[ind][!]",
+ "re-edit all the files in the file argument list"},
+/* C_SUBSTITUTE */
+ {"substitute", ex_substitute, E_ADDR2|E_NORC,
+ "s",
+"[line [,line]] s[ubstitute] [[/;]pat[/;]/repl[/;] [cgr] [count] [#lp]]",
+ "substitute on lines matching a pattern"},
+/* C_SCRIPT */
+ {"script", ex_script, E_NOGLOBAL|E_NORC,
+ "!f1o",
+ "sc[ript][!] [file]",
+ "run a shell in a screen"},
+/* C_SET */
+ {"set", ex_set, E_NOGLOBAL,
+ "wN",
+ "se[t] [option[=[value]]...] [nooption ...] [option? ...] [all]",
+ "set options (use \":set all\" to see all options)"},
+/* C_SHELL */
+ {"shell", ex_shell, E_NOGLOBAL|E_NORC,
+ "",
+ "sh[ell]",
+ "suspend editing and run a shell"},
+/* C_SOURCE */
+ {"source", ex_source, E_NOGLOBAL,
+ "f1r",
+ "so[urce] file",
+ "read a file of ex commands"},
+/* C_SPLIT */
+ {"split", ex_split, E_NOGLOBAL|E_NORC,
+ "fNo",
+ "sp[lit] [file ...]",
+ "split the current screen into two screens"},
+/* C_STOP */
+ {"stop", ex_stop, E_NOGLOBAL|E_NORC,
+ "!",
+ "st[op][!]",
+ "suspend the edit session"},
+/* C_SUSPEND */
+ {"suspend", ex_stop, E_NOGLOBAL|E_NORC,
+ "!",
+ "su[spend][!]",
+ "suspend the edit session"},
+/* C_T */
+ {"t", ex_copy, E_ADDR2|E_AUTOPRINT|E_NORC,
+ "l1",
+ "[line [,line]] t line [flags]",
+ "move lines elsewhere in the file"},
+/* C_TAG */
+ {"tag", ex_tagpush, E_NOGLOBAL,
+ "!w1o",
+ "ta[g][!] [string]",
+ "edit the file containing the tag"},
+/* C_TAGPOP */
+ {"tagpop", ex_tagpop, E_NOGLOBAL|E_NORC,
+ "!w1o",
+ "tagp[op][!] [number | file]",
+ "return to a previous tag"},
+/* C_TAGTOP */
+ {"tagtop", ex_tagtop, E_NOGLOBAL|E_NORC,
+ "!",
+ "tagt[op][!]",
+ "return to the first tag"},
+/* C_UNDOL */
+ {"Undo", ex_undol, E_AUTOPRINT|E_NOGLOBAL|E_NORC,
+ "",
+ "U[ndo]",
+ "undo all the changes to this line"},
+/* C_UNDO */
+ {"undo", ex_undo, E_AUTOPRINT|E_NOGLOBAL|E_NORC,
+ "",
+ "u[ndo]",
+ "undo the most recent change"},
+/* C_UNABBREVIATE */
+ {"unabbreviate",ex_unabbr, E_NOGLOBAL,
+ "w1r",
+ "una[bbrev] word",
+ "delete an abbreviation"},
+/* C_UNMAP */
+ {"unmap", ex_unmap, E_NOGLOBAL,
+ "!w1r",
+ "unm[ap][!] word",
+ "delete an input or command map"},
+/* C_VGLOBAL */
+ {"vglobal", ex_vglobal, E_ADDR2_ALL|E_NOGLOBAL|E_NORC,
+ "s",
+ "[line [,line]] v[global] [;/]pattern[;/] [commands]",
+ "execute a global command on lines NOT matching a pattern"},
+/* C_VERSION */
+ {"version", ex_version, E_NOGLOBAL|E_NORC,
+ "",
+ "version",
+ "display the program version information"},
+/* C_VISUAL_EX */
+ {"visual", ex_visual, E_ADDR1|E_NOGLOBAL|E_NORC|E_ZERODEF,
+ "2c11",
+ "[line] vi[sual] [-|.|+|^] [window_size] [flags]",
+ "enter visual (vi) mode from ex mode"},
+/* C_VISUAL_VI */
+ {"visual", ex_edit, E_NOGLOBAL|E_NORC,
+ "!f1o",
+ "vi[sual][!] [+cmd] [file]",
+ "edit another file (from vi mode only)"},
+/* C_VIUSAGE */
+ {"viusage", ex_viusage, E_NOGLOBAL|E_NORC,
+ "w1o",
+ "[viu]sage [key]",
+ "display vi key usage statement"},
+/* C_WRITE */
+ {"write", ex_write, E_ADDR2_ALL|E_NOGLOBAL|E_NORC|E_ZERODEF,
+ "!s",
+ "[line [,line]] w[rite][!] [!cmd | [>>] [file]]",
+ "write the file"},
+/* C_WQ */
+ {"wq", ex_wq, E_ADDR2_ALL|E_NOGLOBAL|E_NORC|E_ZERODEF,
+ "!s",
+ "[line [,line]] wq[!] [>>] [file]",
+ "write the file and exit"},
+/* C_XIT */
+ {"xit", ex_xit, E_ADDR2_ALL|E_NOGLOBAL|E_NORC|E_ZERODEF,
+ "!f1o",
+ "[line [,line]] x[it][!] [file]",
+ "exit"},
+/* C_YANK */
+ {"yank", ex_yank, E_ADDR2|E_NORC,
+ "bca",
+ "[line [,line]] ya[nk] [buffer] [count]",
+ "copy lines to a cut buffer"},
+/* C_Z */
+ {"z", ex_z, E_ADDR1|E_NOGLOBAL|E_NORC,
+ "3c01",
+ "[line] z [-|.|+|^|=] [count] [flags]",
+ "display different screens of the file"},
+/* C_SUBTILDE */
+ {"~", ex_subtilde, E_ADDR2|E_NORC,
+ "s",
+ "[line [,line]] ~ [cgr] [count] [#lp]",
+ "replace previous RE with previous replacement string,"},
+ {NULL},
+};
diff --git a/usr.bin/vi/nex/excmd.h.stub b/usr.bin/vi/nex/excmd.h.stub
new file mode 100644
index 000000000000..ce2715c8b086
--- /dev/null
+++ b/usr.bin/vi/nex/excmd.h.stub
@@ -0,0 +1,340 @@
+/*-
+ * Copyright (c) 1992, 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.
+ *
+ * @(#)excmd.h.stub 8.44 (Berkeley) 12/29/93
+ */
+
+/* Ex command structure. */
+typedef struct _excmdlist {
+ char *name; /* Command name. */
+ /* Underlying function. */
+ int (*fn) __P((SCR *, EXF *, EXCMDARG *));
+
+#define E_ADDR1 0x0000001 /* One address. */
+#define E_ADDR2 0x0000002 /* Two address. */
+#define E_ADDR2_ALL 0x0000004 /* Zero/two addresses; zero == all. */
+#define E_ADDR2_NONE 0x0000008 /* Zero/two addresses; zero == none. */
+#define E_ADDRDEF 0x0000010 /* Default addresses used. */
+#define E_AUTOPRINT 0x0000020 /* Command always sets autoprint. */
+#define E_BUFFER 0x0000040 /* Buffer name supplied. */
+#define E_COUNT 0x0000080 /* Count supplied. */
+#define E_FORCE 0x0000100 /* ! */
+
+#define E_F_CARAT 0x0000200 /* ^ flag. */
+#define E_F_DASH 0x0000400 /* - flag. */
+#define E_F_DOT 0x0000800 /* . flag. */
+#define E_F_EQUAL 0x0001000 /* = flag. */
+#define E_F_HASH 0x0002000 /* # flag. */
+#define E_F_LIST 0x0004000 /* l flag. */
+#define E_F_PLUS 0x0008000 /* + flag. */
+#define E_F_PRINT 0x0010000 /* p flag. */
+
+#define E_F_PRCLEAR 0x0020000 /* Clear the print (#, l, p) flags. */
+#define E_MODIFY 0x0040000 /* File name expansion modified arg. */
+#define E_NOGLOBAL 0x0080000 /* Not in a global. */
+#define E_NOPERM 0x0100000 /* Permission denied for now. */
+#define E_NORC 0x0200000 /* Not from a .exrc or EXINIT. */
+#define E_SETLAST 0x0400000 /* Reset last command. */
+#define E_ZERO 0x0800000 /* 0 is a legal addr1. */
+#define E_ZERODEF 0x1000000 /* 0 is default addr1 of empty files. */
+ u_long flags;
+ char *syntax; /* Syntax script. */
+ char *usage; /* Usage line. */
+ char *help; /* Help line. */
+} EXCMDLIST;
+#define MAXCMDNAMELEN 12 /* Longest command name. */
+extern EXCMDLIST const cmds[]; /* List of ex commands. */
+
+/* Structure passed around to functions implementing ex commands. */
+struct _excmdarg {
+ EXCMDLIST const *cmd; /* Command entry in command table. */
+ CHAR_T buffer; /* Named buffer. */
+ recno_t lineno; /* Line number. */
+ long count; /* Signed, specified count. */
+ int addrcnt; /* Number of addresses (0, 1 or 2). */
+ MARK addr1; /* 1st address. */
+ MARK addr2; /* 2nd address. */
+ ARGS **argv; /* Array of arguments. */
+ int argc; /* Count of arguments. */
+ u_int flags; /* Selected flags from EXCMDLIST. */
+};
+
+/* Global ranges. */
+typedef struct _range RANGE;
+struct _range {
+ CIRCLEQ_ENTRY(_range) q; /* Linked list of ranges. */
+ recno_t start, stop; /* Start/stop of the range. */
+};
+
+/* Ex private, per-screen memory. */
+typedef struct _ex_private {
+ ARGS **args; /* Arguments. */
+ int argscnt; /* Argument count. */
+ int argsoff; /* Offset into arguments. */
+
+ CHAR_T at_lbuf; /* Last executed at buffer's name. */
+ int at_lbuf_set; /* If at_lbuf is set. */
+
+ char *ibp; /* Line input buffer. */
+ size_t ibp_len; /* Line input buffer length. */
+
+ EXCMDLIST const *lastcmd; /* Last command. */
+
+ CHAR_T *lastbcomm; /* Last bang command. */
+
+ TAILQ_HEAD(_tagh, _tag) tagq; /* Tag stack. */
+ TAILQ_HEAD(_tagfh, _tagf) tagfq;/* Tag stack. */
+ char *tlast; /* Saved last tag. */
+
+ /* Linked list of ranges. */
+ CIRCLEQ_HEAD(_rangeh, _range) rangeq;
+ recno_t range_lno; /* Range set line number. */
+
+#define EX_AUTOPRINT 0x01 /* Autoprint flag. */
+ u_int flags;
+} EX_PRIVATE;
+#define EXP(sp) ((EX_PRIVATE *)((sp)->ex_private))
+
+/* Macro to set up a command structure. */
+#define SETCMDARG(s, cmd_id, naddr, lno1, lno2, force, arg) { \
+ ARGS *__ap[2], __a; \
+ memset(&s, 0, sizeof(EXCMDARG)); \
+ s.cmd = &cmds[cmd_id]; \
+ s.addrcnt = (naddr); \
+ s.addr1.lno = (lno1); \
+ s.addr2.lno = (lno2); \
+ s.addr1.cno = s.addr2.cno = 1; \
+ if (force) \
+ s.flags |= E_FORCE; \
+ if ((__a.bp = arg) == NULL) { \
+ s.argc = 0; \
+ __a.len = 0; \
+ } else { \
+ s.argc = 1; \
+ __a.len = strlen(arg); \
+ } \
+ __ap[0] = &__a; \
+ __ap[1] = NULL; \
+ s.argv = __ap; \
+}
+
+/*
+ * :next, :prev, :rewind, :tag, :tagpush, :tagpop modifications check.
+ * If force is set, the autowrite is skipped.
+ */
+#define MODIFY_CHECK(sp, ep, force) { \
+ if (F_ISSET((ep), F_MODIFIED)) \
+ if (O_ISSET((sp), O_AUTOWRITE)) { \
+ if (!(force) && \
+ file_write((sp), (ep), NULL, NULL, NULL, \
+ FS_ALL | FS_POSSIBLE)) \
+ return (1); \
+ } else if (ep->refcnt <= 1 && !(force)) { \
+ msgq(sp, M_ERR, \
+ "Modified since last write; write or use ! to override."); \
+ return (1); \
+ } \
+}
+
+/*
+ * Macros to set and restore the terminal values, and note if the screen
+ * was modified. Specific to their uses in ex/filter.c and ex/ex_shell.c.
+ *
+ * The old terminal values almost certainly turn on VINTR, VQUIT and VSUSP.
+ * We don't want to interrupt the parent(s), so we ignore VINTR. VQUIT is
+ * ignored by main() because nvi never wants to catch it. A VSUSP handler
+ * have been installed by the screen code.
+ */
+#define EX_LEAVE(sp, isig, act, oact, sb, osb, term) \
+ if (F_ISSET(sp->gp, G_ISFROMTTY)) { \
+ (act).sa_handler = SIG_IGN; \
+ sigemptyset(&(act).sa_mask); \
+ (act).sa_flags = 0; \
+ if ((isig) = !sigaction(SIGINT, &(act), &(oact))) { \
+ if (tcgetattr(STDIN_FILENO, &(term))) { \
+ msgq(sp, M_SYSERR, "tcgetattr"); \
+ rval = 1; \
+ goto err; \
+ } \
+ if (tcsetattr(STDIN_FILENO, TCSANOW | TCSASOFT, \
+ &sp->gp->original_termios)) { \
+ msgq(sp, M_SYSERR, "tcsetattr"); \
+ rval = 1; \
+ goto err; \
+ } \
+ } \
+ /* \
+ * The process may write to the terminal. Save the \
+ * access time (read) and modification time (write) \
+ * of the tty; if they have changed when we restore \
+ * the modes, will have to refresh the screen. \
+ */ \
+ sb.st_mtime = 1; \
+ osb.st_mtime = 0; \
+ (void)fstat(STDIN_FILENO, &osb); \
+ }
+
+#define EX_RETURN(sp, isig, act, oact, sb, osb, term) \
+ if (F_ISSET(sp->gp, G_ISFROMTTY) && (isig)) { \
+ if (sigaction(SIGINT, &(oact), NULL)) { \
+ msgq(sp, M_SYSERR, "signal"); \
+ rval = 1; \
+ } \
+ if (tcsetattr(STDIN_FILENO, \
+ TCSANOW | TCSASOFT, &(term))) { \
+ msgq(sp, M_SYSERR, "tcsetattr"); \
+ rval = 1; \
+ } \
+ /* If the terminal was used, refresh the screen. */ \
+ (void)fstat(STDIN_FILENO, &(sb)); \
+ if ((sb).st_mtime != (osb).st_mtime || \
+ (sb).st_atime != (osb).st_atime) \
+ F_SET(sp, S_REFRESH); \
+ }
+
+/*
+ * Filter actions:
+ *
+ * FILTER Filter text through the utility.
+ * FILTER_READ Read from the utility into the file.
+ * FILTER_WRITE Write to the utility, display its output.
+ */
+enum filtertype { FILTER, FILTER_READ, FILTER_WRITE };
+int filtercmd __P((SCR *, EXF *,
+ MARK *, MARK *, MARK *, char *, enum filtertype));
+
+/* Argument expansion routines. */
+int argv_init __P((SCR *, EXF *, EXCMDARG *));
+int argv_exp0 __P((SCR *, EXF *, EXCMDARG *, char *, size_t));
+int argv_exp1 __P((SCR *, EXF *, EXCMDARG *, char *, size_t, int));
+int argv_exp2 __P((SCR *, EXF *, EXCMDARG *, char *, size_t, int));
+int argv_exp3 __P((SCR *, EXF *, EXCMDARG *, char *, size_t));
+int argv_free __P((SCR *));
+
+/* Ex function prototypes. */
+int ex __P((SCR *, EXF *));
+int ex_cfile __P((SCR *, EXF *, char *));
+int ex_cmd __P((SCR *, EXF *, char *, size_t));
+int ex_end __P((SCR *));
+int ex_exec_proc __P((SCR *, char *, char *, char *));
+int ex_gb __P((SCR *, EXF *, TEXTH *, int, u_int));
+int ex_getline __P((SCR *, FILE *, size_t *));
+int ex_icmd __P((SCR *, EXF *, char *, size_t));
+int ex_init __P((SCR *, EXF *));
+int ex_is_abbrev __P((char *, size_t));
+int ex_optchange __P((SCR *, int));
+int ex_print __P((SCR *, EXF *, MARK *, MARK *, int));
+int ex_readfp __P((SCR *, EXF *, char *, FILE *, MARK *, recno_t *, int));
+void ex_refresh __P((SCR *, EXF *));
+int ex_screen_copy __P((SCR *, SCR *));
+int ex_screen_end __P((SCR *));
+int ex_sdisplay __P((SCR *, EXF *));
+int ex_suspend __P((SCR *));
+int ex_tdisplay __P((SCR *, EXF *));
+int ex_writefp __P((SCR *, EXF *,
+ char *, FILE *, MARK *, MARK *, u_long *, u_long *));
+void global_insdel __P((SCR *, EXF *, enum operation, recno_t));
+int proc_wait __P((SCR *, long, const char *, int));
+int sscr_end __P((SCR *));
+int sscr_exec __P((SCR *, EXF *, recno_t));
+int sscr_input __P((SCR *));
+
+#define EXPROTO(type, name) \
+ type name __P((SCR *, EXF *, EXCMDARG *))
+
+EXPROTO(int, ex_abbr);
+EXPROTO(int, ex_append);
+EXPROTO(int, ex_args);
+EXPROTO(int, ex_at);
+EXPROTO(int, ex_bang);
+EXPROTO(int, ex_bg);
+EXPROTO(int, ex_cd);
+EXPROTO(int, ex_change);
+EXPROTO(int, ex_color);
+EXPROTO(int, ex_copy);
+EXPROTO(int, ex_debug);
+EXPROTO(int, ex_delete);
+EXPROTO(int, ex_digraph);
+EXPROTO(int, ex_display);
+EXPROTO(int, ex_edit);
+EXPROTO(int, ex_equal);
+EXPROTO(int, ex_fg);
+EXPROTO(int, ex_file);
+EXPROTO(int, ex_global);
+EXPROTO(int, ex_help);
+EXPROTO(int, ex_insert);
+EXPROTO(int, ex_join);
+EXPROTO(int, ex_list);
+EXPROTO(int, ex_map);
+EXPROTO(int, ex_mark);
+EXPROTO(int, ex_mkexrc);
+EXPROTO(int, ex_move);
+EXPROTO(int, ex_next);
+EXPROTO(int, ex_number);
+EXPROTO(int, ex_open);
+EXPROTO(int, ex_pr);
+EXPROTO(int, ex_preserve);
+EXPROTO(int, ex_prev);
+EXPROTO(int, ex_put);
+EXPROTO(int, ex_quit);
+EXPROTO(int, ex_read);
+EXPROTO(int, ex_resize);
+EXPROTO(int, ex_rew);
+EXPROTO(int, ex_script);
+EXPROTO(int, ex_set);
+EXPROTO(int, ex_shell);
+EXPROTO(int, ex_shiftl);
+EXPROTO(int, ex_shiftr);
+EXPROTO(int, ex_source);
+EXPROTO(int, ex_split);
+EXPROTO(int, ex_stop);
+EXPROTO(int, ex_subagain);
+EXPROTO(int, ex_substitute);
+EXPROTO(int, ex_subtilde);
+EXPROTO(int, ex_tagpop);
+EXPROTO(int, ex_tagpush);
+EXPROTO(int, ex_tagtop);
+EXPROTO(int, ex_unabbr);
+EXPROTO(int, ex_undo);
+EXPROTO(int, ex_undol);
+EXPROTO(int, ex_unmap);
+EXPROTO(int, ex_usage);
+EXPROTO(int, ex_validate);
+EXPROTO(int, ex_version);
+EXPROTO(int, ex_vglobal);
+EXPROTO(int, ex_visual);
+EXPROTO(int, ex_viusage);
+EXPROTO(int, ex_wq);
+EXPROTO(int, ex_write);
+EXPROTO(int, ex_xit);
+EXPROTO(int, ex_yank);
+EXPROTO(int, ex_z);
diff --git a/usr.bin/vi/nex/filter.c b/usr.bin/vi/nex/filter.c
new file mode 100644
index 000000000000..d50e1e665d8d
--- /dev/null
+++ b/usr.bin/vi/nex/filter.c
@@ -0,0 +1,393 @@
+/*-
+ * Copyright (c) 1991, 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
+static char sccsid[] = "@(#)filter.c 8.26 (Berkeley) 1/2/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "pathnames.h"
+
+static int filter_ldisplay __P((SCR *, FILE *));
+
+/*
+ * filtercmd --
+ * Run a range of lines through a filter utility and optionally
+ * replace the original text with the stdout/stderr output of
+ * the utility.
+ */
+int
+filtercmd(sp, ep, fm, tm, rp, cmd, ftype)
+ SCR *sp;
+ EXF *ep;
+ MARK *fm, *tm, *rp;
+ char *cmd;
+ enum filtertype ftype;
+{
+ struct sigaction act, oact;
+ struct stat osb, sb;
+ struct termios term;
+ FILE *ifp, *ofp; /* GCC: can't be uninitialized. */
+ pid_t parent_writer_pid, utility_pid;
+ recno_t lno, nread;
+ int input[2], isig, output[2], rval;
+ char *name;
+
+ /* Set return cursor position; guard against a line number of zero. */
+ *rp = *fm;
+ if (fm->lno == 0)
+ rp->lno = 1;
+
+ /*
+ * There are three different processes running through this code.
+ * They are the utility, the parent-writer and the parent-reader.
+ * The parent-writer is the process that writes from the file to
+ * the utility, the parent reader is the process that reads from
+ * the utility.
+ *
+ * Input and output are named from the utility's point of view.
+ * The utility reads from input[0] and the parent(s) write to
+ * input[1]. The parent(s) read from output[0] and the utility
+ * writes to output[1].
+ *
+ * In the FILTER_READ case, the utility isn't expected to want
+ * input. Redirect its input from /dev/null. Otherwise open
+ * up utility input pipe.
+ */
+ ifp = ofp = NULL;
+ input[0] = input[1] = output[0] = output[1] = -1;
+ if (ftype == FILTER_READ) {
+ if ((input[0] = open(_PATH_DEVNULL, O_RDONLY, 0)) < 0) {
+ msgq(sp, M_ERR,
+ "filter: %s: %s", _PATH_DEVNULL, strerror(errno));
+ return (1);
+ }
+ } else {
+ if (pipe(input) < 0) {
+ msgq(sp, M_SYSERR, "pipe");
+ goto err;
+ }
+ if ((ifp = fdopen(input[1], "w")) == NULL) {
+ msgq(sp, M_SYSERR, "fdopen");
+ goto err;
+ }
+ }
+
+ /* Open up utility output pipe. */
+ if (pipe(output) < 0) {
+ msgq(sp, M_SYSERR, "pipe");
+ goto err;
+ }
+ if ((ofp = fdopen(output[0], "r")) == NULL) {
+ msgq(sp, M_SYSERR, "fdopen");
+ goto err;
+ }
+
+ /*
+ * Save ex/vi terminal settings, and restore the original ones.
+ * Restoration so that users can do things like ":r! cat /dev/tty".
+ */
+ EX_LEAVE(sp, isig, act, oact, sb, osb, term);
+
+ /* Fork off the utility process. */
+ switch (utility_pid = vfork()) {
+ case -1: /* Error. */
+ msgq(sp, M_SYSERR, "vfork");
+err: if (input[0] != -1)
+ (void)close(input[0]);
+ if (ifp != NULL)
+ (void)fclose(ifp);
+ else if (input[1] != -1)
+ (void)close(input[1]);
+ if (ofp != NULL)
+ (void)fclose(ofp);
+ else if (output[0] != -1)
+ (void)close(output[0]);
+ if (output[1] != -1)
+ (void)close(output[1]);
+ rval = 1;
+ goto ret;
+ case 0: /* Utility. */
+ /*
+ * The utility has default signal behavior. Don't bother
+ * using sigaction(2) 'cause we want the default behavior.
+ */
+ (void)signal(SIGINT, SIG_DFL);
+ (void)signal(SIGQUIT, SIG_DFL);
+
+ /*
+ * Redirect stdin from the read end of the input pipe,
+ * and redirect stdout/stderr to the write end of the
+ * output pipe.
+ */
+ (void)dup2(input[0], STDIN_FILENO);
+ (void)dup2(output[1], STDOUT_FILENO);
+ (void)dup2(output[1], STDERR_FILENO);
+
+ /* Close the utility's file descriptors. */
+ (void)close(input[0]);
+ (void)close(input[1]);
+ (void)close(output[0]);
+ (void)close(output[1]);
+
+ if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL)
+ name = O_STR(sp, O_SHELL);
+ else
+ ++name;
+
+ execl(O_STR(sp, O_SHELL), name, "-c", cmd, NULL);
+ msgq(sp, M_ERR, "Error: execl: %s: %s",
+ O_STR(sp, O_SHELL), strerror(errno));
+ _exit (127);
+ /* NOTREACHED */
+ default: /* Parent-reader, parent-writer. */
+ /* Close the pipe ends neither parent will use. */
+ (void)close(input[0]);
+ (void)close(output[1]);
+ break;
+ }
+
+ /*
+ * FILTER_READ:
+ *
+ * Reading is the simple case -- we don't need a parent writer,
+ * so the parent reads the output from the read end of the output
+ * pipe until it finishes, then waits for the child. Ex_readfp
+ * appends to the MARK, and closes ofp.
+ *
+ * !!!
+ * Set the return cursor to the last line read in. Historically,
+ * this behaves differently from ":r file" command, which leaves
+ * the cursor at the first line read in. Check to make sure that
+ * it's not past EOF because we were reading into an empty file.
+ */
+ if (ftype == FILTER_READ) {
+ rval = ex_readfp(sp, ep, "filter", ofp, fm, &nread, 0);
+ sp->rptlines[L_ADDED] += nread;
+ if (fm->lno == 0)
+ rp->lno = nread;
+ else
+ rp->lno += nread;
+ goto uwait;
+ }
+
+ /*
+ * FILTER, FILTER_WRITE
+ *
+ * Here we need both a reader and a writer. Temporary files are
+ * expensive and we'd like to avoid disk I/O. Using pipes has the
+ * obvious starvation conditions. It's done as follows:
+ *
+ * fork
+ * child
+ * write lines out
+ * exit
+ * parent
+ * FILTER:
+ * read lines into the file
+ * delete old lines
+ * FILTER_WRITE
+ * read and display lines
+ * wait for child
+ *
+ * XXX
+ * We get away without locking the underlying database because we know
+ * that none of the records that we're reading will be modified until
+ * after we've read them. This depends on the fact that the current
+ * B+tree implementation doesn't balance pages or similar things when
+ * it inserts new records. When the DB code has locking, we should
+ * treat vi as if it were multiple applications sharing a database, and
+ * do the required locking. If necessary a work-around would be to do
+ * explicit locking in the line.c:file_gline() code, based on the flag
+ * set here.
+ */
+ rval = 0;
+ F_SET(ep, F_MULTILOCK);
+ switch (parent_writer_pid = fork()) {
+ case -1: /* Error. */
+ rval = 1;
+ msgq(sp, M_SYSERR, "fork");
+ (void)close(input[1]);
+ (void)close(output[0]);
+ break;
+ case 0: /* Parent-writer. */
+ /*
+ * Write the selected lines to the write end of the
+ * input pipe. Ifp is closed by ex_writefp.
+ */
+ (void)close(output[0]);
+ _exit(ex_writefp(sp, ep, "filter", ifp, fm, tm, NULL, NULL));
+
+ /* NOTREACHED */
+ default: /* Parent-reader. */
+ (void)close(input[1]);
+ if (ftype == FILTER_WRITE)
+ /*
+ * Read the output from the read end of the output
+ * pipe and display it. Filter_ldisplay closes ofp.
+ */
+ rval = filter_ldisplay(sp, ofp);
+ else {
+ /*
+ * Read the output from the read end of the output
+ * pipe. Ex_readfp appends to the MARK and closes
+ * ofp.
+ */
+ rval = ex_readfp(sp, ep, "filter", ofp, tm, &nread, 0);
+ sp->rptlines[L_ADDED] += nread;
+ }
+
+ /* Wait for the parent-writer. */
+ rval |= proc_wait(sp,
+ (long)parent_writer_pid, "parent-writer", 1);
+
+ /* Delete any lines written to the utility. */
+ if (ftype == FILTER && rval == 0) {
+ for (lno = tm->lno; lno >= fm->lno; --lno)
+ if (file_dline(sp, ep, lno)) {
+ rval = 1;
+ break;
+ }
+ if (rval == 0)
+ sp->rptlines[L_DELETED] +=
+ (tm->lno - fm->lno) + 1;
+ }
+ /*
+ * If the filter had no output, we may have just deleted
+ * the cursor. Don't do any real error correction, we'll
+ * try and recover later.
+ */
+ if (rp->lno > 1 && file_gline(sp, ep, rp->lno, NULL) == NULL)
+ --rp->lno;
+ break;
+ }
+ F_CLR(ep, F_MULTILOCK);
+
+uwait: rval |= proc_wait(sp, (long)utility_pid, cmd, 0);
+
+ /* Restore ex/vi terminal settings. */
+ret: EX_RETURN(sp, isig, act, oact, sb, osb, term);
+
+ return (rval);
+}
+
+/*
+ * proc_wait --
+ * Wait for one of the processes.
+ *
+ * !!!
+ * The pid_t type varies in size from a short to a long depending on the
+ * system. It has to be cast into something or the standard promotion
+ * rules get you. I'm using a long based on the belief that nobody is
+ * going to make it unsigned and it's unlikely to be a quad.
+ */
+int
+proc_wait(sp, pid, cmd, okpipe)
+ SCR *sp;
+ long pid;
+ const char *cmd;
+ int okpipe;
+{
+ extern const char *const sys_siglist[];
+ size_t len;
+ int pstat;
+
+ /* Wait for the utility to finish. */
+ (void)waitpid((pid_t)pid, &pstat, 0);
+
+ /*
+ * Display the utility's exit status. Ignore SIGPIPE from the
+ * parent-writer, as that only means that the utility chose to
+ * exit before reading all of its input.
+ */
+ if (WIFSIGNALED(pstat) && (!okpipe || WTERMSIG(pstat) != SIGPIPE)) {
+ for (; isblank(*cmd); ++cmd);
+ len = strlen(cmd);
+ msgq(sp, M_ERR, "%.*s%s: received signal: %s%s.",
+ MIN(len, 20), cmd, len > 20 ? "..." : "",
+ sys_siglist[WTERMSIG(pstat)],
+ WCOREDUMP(pstat) ? "; core dumped" : "");
+ return (1);
+ }
+ if (WIFEXITED(pstat) && WEXITSTATUS(pstat)) {
+ for (; isblank(*cmd); ++cmd);
+ len = strlen(cmd);
+ msgq(sp, M_ERR, "%.*s%s: exited with status %d",
+ MIN(len, 20), cmd, len > 20 ? "..." : "",
+ WEXITSTATUS(pstat));
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * filter_ldisplay --
+ * Display a line output from a utility.
+ *
+ * XXX
+ * This should probably be combined with some of the ex_print()
+ * routines into a single display routine.
+ */
+static int
+filter_ldisplay(sp, fp)
+ SCR *sp;
+ FILE *fp;
+{
+ EX_PRIVATE *exp;
+ size_t len;
+
+ exp = EXP(sp);
+ while (!ex_getline(sp, fp, &len)) {
+ (void)ex_printf(EXCOOKIE, "%.*s\n", (int)len, exp->ibp);
+ if (ferror(sp->stdfp)) {
+ msgq(sp, M_SYSERR, NULL);
+ (void)fclose(fp);
+ return (1);
+ }
+ }
+ if (fclose(fp)) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/nex/script.h b/usr.bin/vi/nex/script.h
new file mode 100644
index 000000000000..a04f14459165
--- /dev/null
+++ b/usr.bin/vi/nex/script.h
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 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.
+ *
+ * @(#)script.h 8.1 (Berkeley) 12/19/93
+ */
+
+struct _script {
+ pid_t sh_pid; /* Shell pid. */
+ int sh_master; /* Master pty fd. */
+ int sh_slave; /* Slave pty fd. */
+ char *sh_prompt; /* Prompt. */
+ size_t sh_prompt_len; /* Prompt length. */
+ char sh_name[64]; /* Pty name */
+ struct winsize sh_win; /* Window size. */
+ struct termios sh_term; /* Terminal information. */
+};
diff --git a/usr.bin/vi/nex/tag.h b/usr.bin/vi/nex/tag.h
new file mode 100644
index 000000000000..f75d9b3fd466
--- /dev/null
+++ b/usr.bin/vi/nex/tag.h
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 1992, 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.
+ *
+ * @(#)tag.h 8.11 (Berkeley) 11/22/93
+ */
+
+struct _tagf { /* Tag file. */
+ TAILQ_ENTRY(_tagf) q; /* Linked list of tag files. */
+ char *name; /* Tag file name. */
+
+#define TAGF_DNE 0x01 /* Didn't exist. */
+#define TAGF_DNE_WARN 0x02 /* DNE error reported. */
+ u_char flags;
+};
+
+struct _tag { /* Tag stack. */
+ TAILQ_ENTRY(_tag) q; /* Linked list of tags. */
+ FREF *frp; /* Saved file name. */
+ recno_t lno; /* Saved line number. */
+ size_t cno; /* Saved column number. */
+ char *search; /* Search string. */
+ size_t slen; /* Search string length. */
+};
+
+int ex_tagalloc __P((SCR *, char *));
+int ex_tagcopy __P((SCR *, SCR *));
+int ex_tagdisplay __P((SCR *, EXF *));
+int ex_tagfirst __P((SCR *, char *));
+int ex_tagfree __P((SCR *));
diff --git a/usr.bin/vi/nvi/getc.c b/usr.bin/vi/nvi/getc.c
new file mode 100644
index 000000000000..d4ac2304b914
--- /dev/null
+++ b/usr.bin/vi/nvi/getc.c
@@ -0,0 +1,252 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)getc.c 8.6 (Berkeley) 10/26/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * Character stream routines --
+ * These routines return the file a character at a time. There are two
+ * special cases. First, the end of a line, end of a file, start of a
+ * file and empty lines are returned as special cases, and no character
+ * is returned. Second, empty lines include lines that have only white
+ * space in them, because the vi search functions don't care about white
+ * space, and this makes it easier for them to be consistent.
+ */
+
+/*
+ * cs_init --
+ * Initialize character stream routines.
+ */
+int
+cs_init(sp, ep, csp)
+ SCR *sp;
+ EXF *ep;
+ VCS *csp;
+{
+ recno_t lno;
+
+ if ((csp->cs_bp =
+ file_gline(sp, ep, csp->cs_lno, &csp->cs_len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ msgq(sp, M_BERR, "Empty file.");
+ else
+ GETLINE_ERR(sp, csp->cs_lno);
+ return (1);
+ }
+ if (csp->cs_len == 0 || v_isempty(csp->cs_bp, csp->cs_len)) {
+ csp->cs_cno = 0;
+ csp->cs_flags = CS_EMP;
+ } else {
+ csp->cs_flags = 0;
+ csp->cs_ch = csp->cs_bp[csp->cs_cno];
+ }
+ return (0);
+}
+
+/*
+ * cs_next --
+ * Retrieve the next character.
+ */
+int
+cs_next(sp, ep, csp)
+ SCR *sp;
+ EXF *ep;
+ VCS *csp;
+{
+ recno_t slno;
+
+ switch (csp->cs_flags) {
+ case CS_EMP: /* EMP; get next line. */
+ case CS_EOL: /* EOL; get next line. */
+ slno = csp->cs_lno; /* Save current line. */
+ if ((csp->cs_bp =
+ file_gline(sp, ep, ++csp->cs_lno, &csp->cs_len)) == NULL) {
+ csp->cs_lno = slno;
+ if (file_lline(sp, ep, &slno))
+ return (1);
+ if (slno > csp->cs_lno) {
+ GETLINE_ERR(sp, csp->cs_lno);
+ return (1);
+ }
+ csp->cs_flags = CS_EOF;
+ } else if (csp->cs_len == 0 ||
+ v_isempty(csp->cs_bp, csp->cs_len)) {
+ csp->cs_cno = 0;
+ csp->cs_flags = CS_EMP;
+ } else {
+ csp->cs_flags = 0;
+ csp->cs_ch = csp->cs_bp[csp->cs_cno = 0];
+ }
+ break;
+ case 0:
+ if (csp->cs_cno == csp->cs_len - 1)
+ csp->cs_flags = CS_EOL;
+ else
+ csp->cs_ch = csp->cs_bp[++csp->cs_cno];
+ break;
+ case CS_EOF: /* EOF; only returned once. */
+ default:
+ abort();
+ /* NOTREACHED */
+ }
+ return (0);
+}
+
+/*
+ * cs_fspace --
+ * If on a space, eat forward until something other than a
+ * whitespace character.
+ *
+ * XXX
+ * Semantics of checking the current character were coded for the fword()
+ * function -- once the other word routines are converted, they may have
+ * to change.
+ */
+int
+cs_fspace(sp, ep, csp)
+ SCR *sp;
+ EXF *ep;
+ VCS *csp;
+{
+ if (csp->cs_flags != 0 || !isblank(csp->cs_ch))
+ return (0);
+ for (;;) {
+ if (cs_next(sp, ep, csp))
+ return (1);
+ if (csp->cs_flags != 0 || !isblank(csp->cs_ch))
+ break;
+ }
+ return (0);
+}
+
+/*
+ * cs_fblank --
+ * Eat forward to the next non-whitespace character.
+ */
+int
+cs_fblank(sp, ep, csp)
+ SCR *sp;
+ EXF *ep;
+ VCS *csp;
+{
+ for (;;) {
+ if (cs_next(sp, ep, csp))
+ return (1);
+ if (csp->cs_flags == CS_EOL || csp->cs_flags == CS_EMP ||
+ csp->cs_flags == 0 && isblank(csp->cs_ch))
+ continue;
+ break;
+ }
+ return (0);
+}
+
+/*
+ * cs_prev --
+ * Retrieve the previous character.
+ */
+int
+cs_prev(sp, ep, csp)
+ SCR *sp;
+ EXF *ep;
+ VCS *csp;
+{
+ recno_t slno;
+
+ switch (csp->cs_flags) {
+ case CS_EMP: /* EMP; get previous line. */
+ case CS_EOL: /* EOL; get previous line. */
+ if (csp->cs_lno == 1) { /* SOF. */
+ csp->cs_flags = CS_SOF;
+ break;
+ }
+ slno = csp->cs_lno; /* Save current line. */
+ if ((csp->cs_bp = /* Line should exist. */
+ file_gline(sp, ep, --csp->cs_lno, &csp->cs_len)) == NULL) {
+ GETLINE_ERR(sp, csp->cs_lno);
+ csp->cs_lno = slno;
+ return (1);
+ }
+ if (csp->cs_len == 0 || v_isempty(csp->cs_bp, csp->cs_len)) {
+ csp->cs_cno = 0;
+ csp->cs_flags = CS_EMP;
+ } else {
+ csp->cs_flags = 0;
+ csp->cs_cno = csp->cs_len - 1;
+ csp->cs_ch = csp->cs_bp[csp->cs_cno];
+ }
+ break;
+ case 0:
+ if (csp->cs_cno == 0)
+ csp->cs_flags = CS_EOL;
+ else
+ csp->cs_ch = csp->cs_bp[--csp->cs_cno];
+ break;
+ case CS_SOF: /* SOF; only returned once. */
+ default:
+ abort();
+ /* NOTREACHED */
+ }
+ return (0);
+}
+
+/*
+ * cs_bblank --
+ * Eat backward to the next non-whitespace character.
+ */
+int
+cs_bblank(sp, ep, csp)
+ SCR *sp;
+ EXF *ep;
+ VCS *csp;
+{
+ for (;;) {
+ if (cs_prev(sp, ep, csp))
+ return (1);
+ if (csp->cs_flags == CS_EOL || csp->cs_flags == CS_EMP ||
+ csp->cs_flags == 0 && isblank(csp->cs_ch))
+ continue;
+ break;
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/nvi/v_again.c b/usr.bin/vi/nvi/v_again.c
new file mode 100644
index 000000000000..7e9ea5650d83
--- /dev/null
+++ b/usr.bin/vi/nvi/v_again.c
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_again.c 8.2 (Berkeley) 11/13/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+/*
+ * v_again -- &
+ * Repeat the previous substitution.
+ */
+int
+v_again(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ EXCMDARG cmd;
+
+ SETCMDARG(cmd, C_SUBAGAIN, 2, fm->lno, fm->lno, 1, "");
+ return (sp->s_ex_cmd(sp, ep, &cmd, rp));
+}
diff --git a/usr.bin/vi/nvi/v_at.c b/usr.bin/vi/nvi/v_at.c
new file mode 100644
index 000000000000..3b21fc3186d5
--- /dev/null
+++ b/usr.bin/vi/nvi/v_at.c
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (c) 1991, 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
+static char sccsid[] = "@(#)v_at.c 8.3 (Berkeley) 8/25/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+int
+v_at(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ EXCMDARG cmd;
+
+ SETCMDARG(cmd, C_AT, 0, OOBLNO, OOBLNO, 0, NULL);
+ cmd.buffer = vp->buffer;
+ return (sp->s_ex_cmd(sp, ep, &cmd, rp));
+}
diff --git a/usr.bin/vi/nvi/v_ch.c b/usr.bin/vi/nvi/v_ch.c
new file mode 100644
index 000000000000..40807d1db6b1
--- /dev/null
+++ b/usr.bin/vi/nvi/v_ch.c
@@ -0,0 +1,273 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_ch.c 8.2 (Berkeley) 12/20/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+#define NOPREV { \
+ msgq(sp, M_BERR, "No previous F, f, T or t search."); \
+ return (1); \
+}
+
+#define NOTFOUND(ch) { \
+ msgq(sp, M_BERR, "%s not found.", charname(sp, ch)); \
+ return (1); \
+}
+
+/*
+ * v_chrepeat -- [count];
+ * Repeat the last F, f, T or t search.
+ */
+int
+v_chrepeat(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ vp->character = sp->lastckey;
+
+ switch (sp->csearchdir) {
+ case CNOTSET:
+ NOPREV;
+ case FSEARCH:
+ return (v_chF(sp, ep, vp, fm, tm, rp));
+ case fSEARCH:
+ return (v_chf(sp, ep, vp, fm, tm, rp));
+ case TSEARCH:
+ return (v_chT(sp, ep, vp, fm, tm, rp));
+ case tSEARCH:
+ return (v_cht(sp, ep, vp, fm, tm, rp));
+ default:
+ abort();
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * v_chrrepeat -- [count],
+ * Repeat the last F, f, T or t search in the reverse direction.
+ */
+int
+v_chrrepeat(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ int rval;
+ enum cdirection savedir;
+
+ vp->character = sp->lastckey;
+ savedir = sp->csearchdir;
+
+ switch (sp->csearchdir) {
+ case CNOTSET:
+ NOPREV;
+ case FSEARCH:
+ rval = v_chf(sp, ep, vp, fm, tm, rp);
+ break;
+ case fSEARCH:
+ rval = v_chF(sp, ep, vp, fm, tm, rp);
+ break;
+ case TSEARCH:
+ rval = v_cht(sp, ep, vp, fm, tm, rp);
+ break;
+ case tSEARCH:
+ rval = v_chT(sp, ep, vp, fm, tm, rp);
+ break;
+ default:
+ abort();
+ }
+ sp->csearchdir = savedir;
+ return (rval);
+}
+
+/*
+ * v_cht -- [count]tc
+ * Search forward in the line for the next occurrence of the character.
+ * Place the cursor on it if a motion command, to its left if its not.
+ */
+int
+v_cht(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ int rval;
+
+ rval = v_chf(sp, ep, vp, fm, tm, rp);
+ if (!rval)
+ --rp->cno; /* XXX: Motion interaction with v_chf. */
+ sp->csearchdir = tSEARCH;
+ return (rval);
+}
+
+/*
+ * v_chf -- [count]fc
+ * Search forward in the line for the next occurrence of the character.
+ * Place the cursor to its right if a motion command, on it if its not.
+ */
+int
+v_chf(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ size_t len;
+ recno_t lno;
+ u_long cnt;
+ int key;
+ char *endp, *p, *startp;
+
+ /*
+ * !!!
+ * If it's a dot command, it doesn't reset the key for which
+ * we're searching, e.g. in "df1|f2|.|;", the ';' searches
+ * for a '2'.
+ */
+ key = vp->character;
+ if (!F_ISSET(vp, VC_ISDOT))
+ sp->lastckey = key;
+ sp->csearchdir = fSEARCH;
+
+ if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ NOTFOUND(key);
+ GETLINE_ERR(sp, fm->lno);
+ return (1);
+ }
+
+ if (len == 0)
+ NOTFOUND(key);
+
+ startp = p;
+ endp = p + len;
+ p += fm->cno;
+ for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ while (++p < endp && *p != key);
+ if (p == endp)
+ NOTFOUND(key);
+ }
+ rp->lno = fm->lno;
+ rp->cno = p - startp;
+ if (F_ISSET(vp, VC_C | VC_D | VC_Y))
+ ++rp->cno;
+ return (0);
+}
+
+/*
+ * v_chT -- [count]Tc
+ * Search backward in the line for the next occurrence of the character.
+ * Place the cursor to its right.
+ */
+int
+v_chT(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ int rval;
+
+ rval = v_chF(sp, ep, vp, fm, tm, rp);
+ if (!rval)
+ ++rp->cno;
+ sp->csearchdir = TSEARCH;
+ return (0);
+}
+
+/*
+ * v_chF -- [count]Fc
+ * Search backward in the line for the next occurrence of the character.
+ * Place the cursor on it.
+ */
+int
+v_chF(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t lno;
+ size_t len;
+ u_long cnt;
+ int key;
+ char *p, *endp;
+
+ /*
+ * !!!
+ * If it's a dot command, it doesn't reset the key for which
+ * we're searching, e.g. in "df1|f2|.|;", the ';' searches
+ * for a '2'.
+ */
+ key = vp->character;
+ if (!F_ISSET(vp, VC_ISDOT))
+ sp->lastckey = key;
+ sp->csearchdir = FSEARCH;
+
+ if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ NOTFOUND(key);
+ GETLINE_ERR(sp, fm->lno);
+ return (1);
+ }
+
+ if (len == 0)
+ NOTFOUND(key);
+
+ endp = p - 1;
+ p += fm->cno;
+ for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ while (--p > endp && *p != key);
+ if (p == endp)
+ NOTFOUND(key);
+ }
+ rp->lno = fm->lno;
+ rp->cno = (p - endp) - 1;
+ return (0);
+}
diff --git a/usr.bin/vi/nvi/v_delete.c b/usr.bin/vi/nvi/v_delete.c
new file mode 100644
index 000000000000..241ddf654f2e
--- /dev/null
+++ b/usr.bin/vi/nvi/v_delete.c
@@ -0,0 +1,147 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_delete.c 8.7 (Berkeley) 1/11/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_Delete -- [buffer][count]D
+ * Delete line command.
+ */
+int
+v_Delete(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t lno;
+ size_t len;
+
+ if (file_gline(sp, ep, fm->lno, &len) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ return (0);
+ GETLINE_ERR(sp, fm->lno);
+ return (1);
+ }
+
+ if (len == 0)
+ return (0);
+
+ tm->lno = fm->lno;
+ tm->cno = len;
+
+ /* Yank the lines. */
+ if (cut(sp, ep, NULL,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, CUT_DELETE))
+ return (1);
+ if (delete(sp, ep, fm, tm, 0))
+ return (1);
+
+ rp->lno = fm->lno;
+ rp->cno = fm->cno ? fm->cno - 1 : 0;
+ return (0);
+}
+
+/*
+ * v_delete -- [buffer][count]d[count]motion
+ * Delete a range of text.
+ */
+int
+v_delete(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t nlines;
+ size_t len;
+ int lmode;
+
+ /* Yank the lines. */
+ lmode = F_ISSET(vp, VC_LMODE) ? CUT_LINEMODE : 0;
+ if (cut(sp, ep, NULL,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ fm, tm, lmode | CUT_DELETE))
+ return (1);
+ if (delete(sp, ep, fm, tm, lmode))
+ return (1);
+
+ /* Check for deleting the file. */
+ if (file_lline(sp, ep, &nlines))
+ return (1);
+ if (nlines == 0) {
+ rp->lno = 1;
+ rp->cno = 0;
+ return (0);
+ }
+
+ /*
+ * If deleting lines, leave the cursor at the lowest line deleted,
+ * else, leave the cursor where it started. Always correct for EOL.
+ *
+ * The historic vi would delete the line the cursor was on (even if
+ * not in line mode) if the motion from the cursor was past the EOF
+ * and the cursor didn't originate on the last line of the file. A
+ * strange special case. We never delete the line the cursor is on.
+ * We'd have to pass a flag down to the delete() routine which would
+ * have to special case it.
+ */
+ if (lmode) {
+ rp->lno = MIN(fm->lno, tm->lno);
+ if (rp->lno > nlines)
+ rp->lno = nlines;
+ rp->cno = 0;
+ (void)nonblank(sp, ep, rp->lno, &rp->cno);
+ return (0);
+ }
+
+ rp->lno = fm->lno;
+ if (file_gline(sp, ep, rp->lno, &len) == NULL) {
+ GETLINE_ERR(sp, rp->lno);
+ return (1);
+ }
+ if (fm->cno >= len)
+ rp->cno = len ? len - 1 : 0;
+ else
+ rp->cno = fm->cno;
+ return (0);
+}
diff --git a/usr.bin/vi/nvi/v_ex.c b/usr.bin/vi/nvi/v_ex.c
new file mode 100644
index 000000000000..631cd342d010
--- /dev/null
+++ b/usr.bin/vi/nvi/v_ex.c
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_ex.c 8.1 (Berkeley) 6/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_ex --
+ * Run ex.
+ */
+int
+v_ex(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ return (sp->s_ex_run(sp, ep, rp));
+}
diff --git a/usr.bin/vi/nvi/v_exit.c b/usr.bin/vi/nvi/v_exit.c
new file mode 100644
index 000000000000..f308c480d3d7
--- /dev/null
+++ b/usr.bin/vi/nvi/v_exit.c
@@ -0,0 +1,79 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_exit.c 8.5 (Berkeley) 12/10/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+/*
+ * v_exit -- ZZ
+ * Save the file and exit.
+ */
+int
+v_exit(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ if (F_ISSET(ep, F_MODIFIED) &&
+ file_write(sp, ep, NULL, NULL, NULL, FS_ALL))
+ return (1);
+
+ /*
+ * !!!
+ * Historic practice: quit! or two quit's done in succession
+ * (where ZZ counts as a quit) didn't check for other files.
+ *
+ * Also check for related screens; if they exist, quit, the
+ * user will get the message on the last screen.
+ */
+ if (sp->ccnt != sp->q_ccnt + 1 &&
+ ep->refcnt <= 1 && file_unedited(sp) != NULL) {
+ sp->q_ccnt = sp->ccnt;
+ msgq(sp, M_ERR,
+ "More files to edit; use \":n\" to go to the next file");
+ return (1);
+ }
+
+ F_SET(sp, S_EXIT);
+ return (0);
+}
diff --git a/usr.bin/vi/nvi/v_exmode.c b/usr.bin/vi/nvi/v_exmode.c
new file mode 100644
index 000000000000..150ec52ccf9c
--- /dev/null
+++ b/usr.bin/vi/nvi/v_exmode.c
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_exmode.c 8.3 (Berkeley) 11/13/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_exmode --
+ * Put the editor in EX mode.
+ */
+int
+v_exmode(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ sp->saved_vi_mode = F_ISSET(sp, S_VI_CURSES | S_VI_XAW);
+ F_CLR(sp, S_SCREENS);
+ F_SET(sp, S_EX);
+ return (0);
+}
diff --git a/usr.bin/vi/nvi/v_filter.c b/usr.bin/vi/nvi/v_filter.c
new file mode 100644
index 000000000000..52ff2ae77b8d
--- /dev/null
+++ b/usr.bin/vi/nvi/v_filter.c
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_filter.c 8.10 (Berkeley) 12/2/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "vi.h"
+#include "vcmd.h"
+#include "excmd.h"
+
+/*
+ * v_filter -- [count]!motion command(s)
+ * Run range through shell commands, replacing text.
+ */
+int
+v_filter(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ EXCMDARG cmd;
+ TEXT *tp;
+
+ /*
+ * !!!
+ * Historical vi permitted "!!" in an empty file. This is
+ * handled as a special case in the ex_bang routine. Don't
+ * modify this setup without understanding that one. In
+ * particular, note that we're manipulating the ex argument
+ * structures behind ex's back.
+ */
+ SETCMDARG(cmd, C_BANG, 2, fm->lno, tm->lno, 0, NULL);
+ EXP(sp)->argsoff = 0; /* XXX */
+ if (F_ISSET(vp, VC_ISDOT)) {
+ if (argv_exp1(sp, ep, &cmd, "!", 1, 1))
+ return (1);
+ } else {
+ /* Get the command from the user. */
+ if (sp->s_get(sp, ep, &sp->tiq,
+ '!', TXT_BS | TXT_CR | TXT_ESCAPE | TXT_PROMPT) != INP_OK)
+ return (1);
+ /*
+ * Len is 0 if backspaced over the prompt,
+ * 1 if only CR entered.
+ */
+ tp = sp->tiq.cqh_first;
+ if (tp->len <= 1)
+ return (0);
+
+ if (argv_exp1(sp, ep, &cmd, tp->lb + 1, tp->len - 1, 1))
+ return (1);
+ }
+ cmd.argc = EXP(sp)->argsoff; /* XXX */
+ cmd.argv = EXP(sp)->args; /* XXX */
+ return (sp->s_ex_cmd(sp, ep, &cmd, rp));
+}
diff --git a/usr.bin/vi/nvi/v_increment.c b/usr.bin/vi/nvi/v_increment.c
new file mode 100644
index 000000000000..dfae29ea463e
--- /dev/null
+++ b/usr.bin/vi/nvi/v_increment.c
@@ -0,0 +1,153 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_increment.c 8.6 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+static char * const fmt[] = {
+#define DEC 0
+ "%ld",
+#define SDEC 1
+ "%+ld",
+#define HEXC 2
+ "%#0.*lX",
+#define HEXL 3
+ "%#0.*lx",
+#define OCTAL 4
+ "%#0.*lo",
+};
+
+/*
+ * v_increment -- [count]#[#+-]
+ * Increment/decrement a keyword number.
+ */
+int
+v_increment(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ VI_PRIVATE *vip;
+ u_long ulval;
+ long lval;
+ size_t blen, len, nlen;
+ int rval;
+ char *bp, *ntype, *p, nbuf[100];
+
+ vip = VIP(sp);
+
+ /* Do repeat operations. */
+ if (vp->character == '#')
+ vp->character = vip->inc_lastch;
+
+ /* Get new value. */
+ if (F_ISSET(vp, VC_C1SET))
+ vip->inc_lastval = vp->count;
+
+ if (vp->character != '+' && vp->character != '-') {
+ msgq(sp, M_ERR, "usage: %s.", vp->kp->usage);
+ return (1);
+ }
+ vip->inc_lastch = vp->character;
+
+ /* Figure out the resulting type and number. */
+ p = vp->keyword;
+ len = vp->klen;
+ if (len > 1 && p[0] == '0') {
+ if (vp->character == '+') {
+ ulval = strtoul(vp->keyword, NULL, 0);
+ if (ULONG_MAX - ulval < vip->inc_lastval)
+ goto overflow;
+ ulval += vip->inc_lastval;
+ } else {
+ ulval = strtoul(vp->keyword, NULL, 0);
+ if (ulval < vip->inc_lastval)
+ goto underflow;
+ ulval -= vip->inc_lastval;
+ }
+ ntype = fmt[OCTAL];
+ if (len > 2)
+ if (p[1] == 'X')
+ ntype = fmt[HEXC];
+ else if (p[1] == 'x')
+ ntype = fmt[HEXL];
+ nlen = snprintf(nbuf, sizeof(nbuf), ntype, len, ulval);
+ } else {
+ if (vp->character == '+') {
+ lval = strtol(vp->keyword, NULL, 0);
+ if (lval > 0 && LONG_MAX - lval < vip->inc_lastval) {
+overflow: msgq(sp, M_ERR, "Resulting number too large.");
+ return (1);
+ }
+ lval += vip->inc_lastval;
+ } else {
+ lval = strtol(vp->keyword, NULL, 0);
+ if (lval < 0 && -(LONG_MIN - lval) < vip->inc_lastval) {
+underflow: msgq(sp, M_ERR, "Resulting number too small.");
+ return (1);
+ }
+ lval -= vip->inc_lastval;
+ }
+ ntype = lval != 0 &&
+ (*vp->keyword == '+' || *vp->keyword == '-') ?
+ fmt[SDEC] : fmt[DEC];
+ nlen = snprintf(nbuf, sizeof(nbuf), ntype, lval);
+ }
+
+ if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, fm->lno);
+ return (1);
+ }
+
+ GET_SPACE_RET(sp, bp, blen, len + nlen);
+ memmove(bp, p, fm->cno);
+ memmove(bp + fm->cno, nbuf, nlen);
+ memmove(bp + fm->cno + nlen,
+ p + fm->cno + vp->klen, len - fm->cno - vp->klen);
+ len = len - vp->klen + nlen;
+
+ rval = file_sline(sp, ep, fm->lno, bp, len);
+ FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
diff --git a/usr.bin/vi/nvi/v_init.c b/usr.bin/vi/nvi/v_init.c
new file mode 100644
index 000000000000..f0d2facf4ea3
--- /dev/null
+++ b/usr.bin/vi/nvi/v_init.c
@@ -0,0 +1,228 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_init.c 8.18 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "vcmd.h"
+#include "excmd.h"
+
+static int v_comment __P((SCR *, EXF *));
+
+/*
+ * v_screen_copy --
+ * Copy vi screen.
+ */
+int
+v_screen_copy(orig, sp)
+ SCR *orig, *sp;
+{
+ VI_PRIVATE *ovip, *nvip;
+
+ /* Create the private vi structure. */
+ CALLOC_RET(orig, nvip, VI_PRIVATE *, 1, sizeof(VI_PRIVATE));
+ sp->vi_private = nvip;
+
+ if (orig == NULL) {
+ nvip->inc_lastch = '+';
+ nvip->inc_lastval = 1;
+ } else {
+ ovip = VIP(orig);
+
+ /* User can replay the last input, but nothing else. */
+ if (ovip->rep_len != 0) {
+ MALLOC(orig, nvip->rep, char *, ovip->rep_len);
+ if (nvip->rep != NULL) {
+ memmove(nvip->rep, ovip->rep, ovip->rep_len);
+ nvip->rep_len = ovip->rep_len;
+ }
+ }
+
+ nvip->inc_lastch = ovip->inc_lastch;
+ nvip->inc_lastval = ovip->inc_lastval;
+
+ if (ovip->paragraph != NULL &&
+ (nvip->paragraph = strdup(ovip->paragraph)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * v_screen_end --
+ * End a vi screen.
+ */
+int
+v_screen_end(sp)
+ SCR *sp;
+{
+ VI_PRIVATE *vip;
+
+ vip = VIP(sp);
+
+ if (vip->rep != NULL)
+ FREE(vip->rep, vip->rep_len);
+
+ if (vip->paragraph != NULL)
+ FREE(vip->paragraph, vip->paragraph_len);
+
+ /* Free private memory. */
+ FREE(vip, sizeof(VI_PRIVATE));
+ sp->vi_private = NULL;
+
+ return (0);
+}
+
+/*
+ * v_init --
+ * Initialize vi.
+ */
+int
+v_init(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ size_t len;
+
+ /*
+ * The default address is line 1, column 0. If the address set
+ * bit is on for this file, load the address, ensuring that it
+ * exists.
+ */
+ if (F_ISSET(sp->frp, FR_CURSORSET)) {
+ sp->lno = sp->frp->lno;
+ sp->cno = sp->frp->cno;
+
+ if (file_gline(sp, ep, sp->lno, &len) == NULL) {
+ if (sp->lno != 1 || sp->cno != 0) {
+ if (file_lline(sp, ep, &sp->lno))
+ return (1);
+ if (sp->lno == 0)
+ sp->lno = 1;
+ sp->cno = 0;
+ }
+ } else if (sp->cno >= len)
+ sp->cno = 0;
+
+ } else {
+ sp->lno = 1;
+ sp->cno = 0;
+
+ if (O_ISSET(sp, O_COMMENT) && v_comment(sp, ep))
+ return (1);
+ }
+
+ /* Reset strange attraction. */
+ sp->rcm = 0;
+ sp->rcmflags = 0;
+
+ /* Make ex display to a special function. */
+ if ((sp->stdfp = fwopen(sp, sp->s_ex_write)) == NULL) {
+ msgq(sp, M_SYSERR, "ex output");
+ return (1);
+ }
+#ifdef MAKE_EX_OUTPUT_LINE_BUFFERED
+ (void)setvbuf(sp->stdfp, NULL, _IOLBF, 0);
+#endif
+
+ /* Display the status line. */
+ return (status(sp, ep, sp->lno, 0));
+}
+
+/*
+ * v_end --
+ * End vi session.
+ */
+int
+v_end(sp)
+ SCR *sp;
+{
+ /* Close down ex output file descriptor. */
+ (void)fclose(sp->stdfp);
+
+ return (0);
+}
+
+/*
+ * v_optchange --
+ * Handle change of options for vi.
+ */
+int
+v_optchange(sp, opt)
+ SCR *sp;
+ int opt;
+{
+ switch (opt) {
+ case O_PARAGRAPHS:
+ case O_SECTIONS:
+ return (v_buildparagraph(sp));
+ }
+ return (0);
+}
+
+/*
+ * v_comment --
+ * Skip the first comment.
+ */
+static int
+v_comment(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ recno_t lno;
+ size_t len;
+ char *p;
+
+ for (lno = 1;
+ (p = file_gline(sp, ep, lno, &len)) != NULL && len == 0; ++lno);
+ if (p == NULL || len <= 1 || memcmp(p, "/*", 2))
+ return (0);
+ do {
+ for (; len; --len, ++p)
+ if (p[0] == '*' && len > 1 && p[1] == '/') {
+ sp->lno = lno;
+ return (0);
+ }
+ } while ((p = file_gline(sp, ep, ++lno, &len)) != NULL);
+ return (0);
+}
diff --git a/usr.bin/vi/nvi/v_join.c b/usr.bin/vi/nvi/v_join.c
new file mode 100644
index 000000000000..c3f81d6d1a5c
--- /dev/null
+++ b/usr.bin/vi/nvi/v_join.c
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_join.c 8.3 (Berkeley) 8/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+/*
+ * v_join -- [count]J
+ * Join lines together.
+ */
+int
+v_join(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ EXCMDARG cmd;
+ int lno;
+
+ /*
+ * YASC.
+ * The general rule is that '#J' joins # lines, counting the current
+ * line. However, 'J' and '1J' are the same as '2J', i.e. join the
+ * current and next lines. This doesn't map well into the ex command
+ * (which takes two line numbers), so we handle it here. Note that
+ * we never test for EOF -- historically going past the end of file
+ * worked just fine.
+ */
+ lno = fm->lno + 1;
+ if (F_ISSET(vp, VC_C1SET) && vp->count > 2)
+ lno = fm->lno + (vp->count - 1);
+
+ SETCMDARG(cmd, C_JOIN, 2, fm->lno, lno, 0, NULL);
+ return (sp->s_ex_cmd(sp, ep, &cmd, rp));
+}
diff --git a/usr.bin/vi/nvi/v_left.c b/usr.bin/vi/nvi/v_left.c
new file mode 100644
index 000000000000..cc80b379b06d
--- /dev/null
+++ b/usr.bin/vi/nvi/v_left.c
@@ -0,0 +1,166 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_left.c 8.3 (Berkeley) 12/16/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_left -- [count]^H, [count]h
+ * Move left by columns.
+ */
+int
+v_left(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t cnt;
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+
+ if (fm->cno == 0) {
+ msgq(sp, M_BERR, "Already in the first column.");
+ return (1);
+ }
+
+ rp->lno = fm->lno;
+ if (fm->cno > cnt)
+ rp->cno = fm->cno - cnt;
+ else
+ rp->cno = 0;
+ return (0);
+}
+
+/*
+ * v_cfirst -- [count]_
+ *
+ * Move to the first non-blank column on a line.
+ */
+int
+v_cfirst(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t cnt;
+
+ /*
+ * A count moves down count - 1 rows, so, "3_" is the same as "2j_".
+ *
+ * !!!
+ * Historically, if the _ is a motion, it is always a line motion,
+ * and the line motion flag is set.
+ */
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ if (cnt != 1) {
+ --vp->count;
+ if (v_down(sp, ep, vp, fm, tm, rp))
+ return (1);
+ if (F_ISSET(vp, VC_C | VC_D | VC_Y))
+ F_SET(vp, VC_LMODE);
+ } else
+ rp->lno = fm->lno;
+ rp->cno = 0;
+ if (nonblank(sp, ep, rp->lno, &rp->cno))
+ return (1);
+ return (0);
+}
+
+/*
+ * v_first -- ^
+ * Move to the first non-blank column on this line.
+ */
+int
+v_first(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ /*
+ * Yielding to none in our quest for compatibility with every
+ * historical blemish of vi, no matter how strange it might be,
+ * we permit the user to enter a count and then ignore it.
+ */
+ rp->cno = 0;
+ if (nonblank(sp, ep, fm->lno, &rp->cno))
+ return (1);
+ rp->lno = fm->lno;
+ return (0);
+}
+
+/*
+ * v_ncol -- [count]|
+ * Move to column count or the first column on this line. If the
+ * requested column is past EOL, move to EOL. The nasty part is
+ * that we have to know character column widths to make this work.
+ */
+int
+v_ncol(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ if (F_ISSET(vp, VC_C1SET) && vp->count > 1)
+ rp->cno =
+ sp->s_chposition(sp, ep, fm->lno, (size_t)--vp->count);
+ else
+ rp->cno = 0;
+ rp->lno = fm->lno;
+ return (0);
+}
+
+/*
+ * v_zero -- 0
+ * Move to the first column on this line.
+ */
+int
+v_zero(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ rp->lno = fm->lno;
+ rp->cno = 0;
+ return (0);
+}
diff --git a/usr.bin/vi/nvi/v_mark.c b/usr.bin/vi/nvi/v_mark.c
new file mode 100644
index 000000000000..714242d9331e
--- /dev/null
+++ b/usr.bin/vi/nvi/v_mark.c
@@ -0,0 +1,87 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_mark.c 8.3 (Berkeley) 10/31/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_mark -- m[a-z]
+ * Set a mark.
+ */
+int
+v_mark(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ rp->lno = fm->lno;
+ rp->cno = fm->cno;
+ return (mark_set(sp, ep, vp->character, fm, 1));
+}
+
+/*
+ * v_gomark -- '['`a-z], or `['`a-z]
+ * Move to a mark.
+ *
+ * The single quote form moves to the first nonblank character of a line
+ * containing a mark. The back quote form moves to a mark, setting both
+ * row and column. We use a single routine for both forms, taking care
+ * of the nonblank behavior using a flag for the command.
+ *
+ * Although not commonly known, the "'`" and "'`" forms are historically
+ * valid. The behavior is determined by the first character, so "`'" is
+ * the same as "``". Remember this fact -- you'll be amazed at how many
+ * people don't know it and will be delighted that you are able to tell
+ * them.
+ */
+int
+v_gomark(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ MARK *mp;
+
+ if ((mp = mark_get(sp, ep, vp->character)) == NULL)
+ return (1);
+ *rp = *mp;
+ return (0);
+}
diff --git a/usr.bin/vi/nvi/v_match.c b/usr.bin/vi/nvi/v_match.c
new file mode 100644
index 000000000000..963cca551fe8
--- /dev/null
+++ b/usr.bin/vi/nvi/v_match.c
@@ -0,0 +1,152 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_match.c 8.7 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_match -- %
+ * Search to matching character.
+ */
+int
+v_match(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ register int cnt, matchc, startc;
+ VCS cs;
+ recno_t lno;
+ size_t len, off;
+ int (*gc)__P((SCR *, EXF *, VCS *));
+ char *p;
+
+ if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ goto nomatch;
+ GETLINE_ERR(sp, fm->lno);
+ return (1);
+ }
+
+ /*
+ * !!!
+ * Historical practice was to search in the forward direction only.
+ */
+ for (off = fm->cno;; ++off) {
+ if (off >= len) {
+nomatch: msgq(sp, M_BERR, "No match character on this line.");
+ return (1);
+ }
+ switch (startc = p[off]) {
+ case '(':
+ matchc = ')';
+ gc = cs_next;
+ break;
+ case ')':
+ matchc = '(';
+ gc = cs_prev;
+ break;
+ case '[':
+ matchc = ']';
+ gc = cs_next;
+ break;
+ case ']':
+ matchc = '[';
+ gc = cs_prev;
+ break;
+ case '{':
+ matchc = '}';
+ gc = cs_next;
+ break;
+ case '}':
+ matchc = '{';
+ gc = cs_prev;
+ break;
+ default:
+ continue;
+ }
+ break;
+ }
+
+ cs.cs_lno = fm->lno;
+ cs.cs_cno = off;
+ if (cs_init(sp, ep, &cs))
+ return (1);
+ for (cnt = 1;;) {
+ if (gc(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags != 0) {
+ if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF)
+ break;
+ continue;
+ }
+ if (cs.cs_ch == startc)
+ ++cnt;
+ else if (cs.cs_ch == matchc && --cnt == 0)
+ break;
+ }
+ if (cnt) {
+ msgq(sp, M_BERR, "Matching character not found.");
+ return (1);
+ }
+ rp->lno = cs.cs_lno;
+ rp->cno = cs.cs_cno;
+
+ /*
+ * Movement commands go one space further. Increment the return
+ * MARK or from MARK depending on the direction of the search.
+ */
+ if (F_ISSET(vp, VC_C | VC_D | VC_Y)) {
+ if (file_gline(sp, ep, rp->lno, &len) == NULL) {
+ GETLINE_ERR(sp, rp->lno);
+ return (1);
+ }
+ if (len)
+ if (gc == cs_next)
+ ++rp->cno;
+ else
+ ++fm->cno;
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/nvi/v_ntext.c b/usr.bin/vi/nvi/v_ntext.c
new file mode 100644
index 000000000000..1c17ffc5bdd0
--- /dev/null
+++ b/usr.bin/vi/nvi/v_ntext.c
@@ -0,0 +1,1728 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)v_ntext.c 8.80 (Berkeley) 1/13/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "seq.h"
+#include "vcmd.h"
+#include "excmd.h"
+
+static int txt_abbrev __P((SCR *, TEXT *, ARG_CHAR_T, int, int *, int *));
+static void txt_ai_resolve __P((SCR *, TEXT *));
+static TEXT *txt_backup __P((SCR *, EXF *, TEXTH *, TEXT *, u_int));
+static void txt_err __P((SCR *, EXF *, TEXTH *));
+static int txt_hex __P((SCR *, TEXT *, int *, ARG_CHAR_T));
+static int txt_indent __P((SCR *, TEXT *));
+static int txt_margin __P((SCR *, TEXT *, int *, ARG_CHAR_T));
+static int txt_outdent __P((SCR *, TEXT *));
+static void txt_showmatch __P((SCR *, EXF *));
+static int txt_resolve __P((SCR *, EXF *, TEXTH *));
+
+/* Cursor character (space is hard to track on the screen). */
+#if defined(DEBUG) && 0
+#undef CURSOR_CH
+#define CURSOR_CH '+'
+#endif
+
+/* Local version of BINC. */
+#define TBINC(sp, lp, llen, nlen) { \
+ if ((nlen) > llen && binc(sp, &(lp), &(llen), nlen)) \
+ goto err; \
+}
+
+/*
+ * newtext --
+ * Read in text from the user.
+ *
+ * !!!
+ * Historic vi always used:
+ *
+ * ^D: autoindent deletion
+ * ^H: last character deletion
+ * ^W: last word deletion
+ * ^V: quote the next character
+ *
+ * regardless of the user's choices for these characters. The user's erase
+ * and kill characters worked in addition to these characters. Ex was not
+ * completely consistent with this, as it did map the scroll command to the
+ * user's EOF character.
+ *
+ * This implementation does not use fixed characters, but uses whatever the
+ * user specified as described by the termios structure. I'm getting away
+ * with something here, but I think I'm unlikely to get caught.
+ *
+ * !!!
+ * Historic vi did a special screen optimization for tab characters. For
+ * the keystrokes "iabcd<esc>0C<tab>", the tab would overwrite the rest of
+ * the string when it was displayed. Because this implementation redisplays
+ * the entire line on each keystroke, the "bcd" gets pushed to the right as
+ * we ignore that the user has "promised" to change the rest of the characters.
+ * Users have noticed, but this isn't worth fixing, and, the way that the
+ * historic vi did it results in an even worse bug. Given the keystrokes
+ * "iabcd<esc>0R<tab><esc>", the "bcd" disappears, and magically reappears
+ * on the second <esc> key.
+ */
+int
+v_ntext(sp, ep, tiqh, tm, lp, len, rp, prompt, ai_line, flags)
+ SCR *sp;
+ EXF *ep;
+ TEXTH *tiqh;
+ MARK *tm; /* To MARK. */
+ const char *lp; /* Input line. */
+ const size_t len; /* Input line length. */
+ MARK *rp; /* Return MARK. */
+ int prompt; /* Prompt to display. */
+ recno_t ai_line; /* Line number to use for autoindent count. */
+ u_int flags; /* TXT_ flags. */
+{
+ /* State of abbreviation checks. */
+ enum { A_NOTSET, A_SPACE, A_NOTSPACE } abb;
+ /* State of the "[^0]^D" sequences. */
+ enum { C_NOTSET, C_CARATSET, C_NOCHANGE, C_ZEROSET } carat_st;
+ /* State of the hex input character. */
+ enum { H_NOTSET, H_NEXTCHAR, H_INHEX } hex;
+ /* State of quotation. */
+ enum { Q_NOTSET, Q_NEXTCHAR, Q_THISCHAR } quoted;
+ CH ikey; /* Input character structure. */
+ CHAR_T ch; /* Input character. */
+ GS *gp; /* Global pointer. */
+ TEXT *tp, *ntp, ait; /* Input and autoindent text structures. */
+ size_t rcol; /* 0-N: insert offset in the replay buffer. */
+ size_t col; /* Current column. */
+ u_long margin; /* Wrapmargin value. */
+ u_int iflags; /* Input flags. */
+ int ab_cnt, ab_turnoff; /* Abbreviation count, if turned off. */
+ int eval; /* Routine return value. */
+ int replay; /* If replaying a set of input. */
+ int showmatch; /* Showmatch set on this character. */
+ int testnr; /* Test first character for nul replay. */
+ int max, tmp;
+ char *p;
+
+ /*
+ * Set the input flag, so tabs get displayed correctly
+ * and everyone knows that the text buffer is in use.
+ */
+ F_SET(sp, S_INPUT);
+
+ /* Local initialization. */
+ eval = 0;
+ gp = sp->gp;
+
+ /*
+ * Get one TEXT structure with some initial buffer space, reusing
+ * the last one if it's big enough. (All TEXT bookkeeping fields
+ * default to 0 -- text_init() handles this.) If changing a line,
+ * copy it into the TEXT buffer.
+ */
+ if (tiqh->cqh_first != (void *)tiqh) {
+ tp = tiqh->cqh_first;
+ if (tp->q.cqe_next != (void *)tiqh || tp->lb_len < len + 32) {
+ text_lfree(tiqh);
+ goto newtp;
+ }
+ tp->ai = tp->insert = tp->offset = tp->owrite = 0;
+ if (lp != NULL) {
+ tp->len = len;
+ memmove(tp->lb, lp, len);
+ } else
+ tp->len = 0;
+ } else {
+newtp: if ((tp = text_init(sp, lp, len, len + 32)) == NULL)
+ return (1);
+ CIRCLEQ_INSERT_HEAD(tiqh, tp, q);
+ }
+
+ /* Set the starting line number. */
+ tp->lno = sp->lno;
+
+ /*
+ * Set the insert and overwrite counts. If overwriting characters,
+ * do insertion afterward. If not overwriting characters, assume
+ * doing insertion. If change is to a mark, emphasize it with an
+ * END_CH.
+ */
+ if (len) {
+ if (LF_ISSET(TXT_OVERWRITE)) {
+ tp->owrite = tm->cno - sp->cno;
+ tp->insert = len - tm->cno;
+ } else
+ tp->insert = len - sp->cno;
+
+ if (LF_ISSET(TXT_EMARK))
+ tp->lb[tm->cno - 1] = END_CH;
+ }
+
+ /*
+ * Many of the special cases in this routine are to handle autoindent
+ * support. Somebody decided that it would be a good idea if "^^D"
+ * and "0^D" deleted all of the autoindented characters. In an editor
+ * that takes single character input from the user, this wasn't a very
+ * good idea. Note also that "^^D" resets the next lines' autoindent,
+ * but "0^D" doesn't.
+ *
+ * We assume that autoindent only happens on empty lines, so insert
+ * and overwrite will be zero. If doing autoindent, figure out how
+ * much indentation we need and fill it in. Update input column and
+ * screen cursor as necessary.
+ */
+ if (LF_ISSET(TXT_AUTOINDENT) && ai_line != OOBLNO) {
+ if (txt_auto(sp, ep, ai_line, NULL, 0, tp))
+ return (1);
+ sp->cno = tp->ai;
+ } else {
+ /*
+ * The cc and S commands have a special feature -- leading
+ * <blank> characters are handled as autoindent characters.
+ * Beauty!
+ */
+ if (LF_ISSET(TXT_AICHARS)) {
+ tp->offset = 0;
+ tp->ai = sp->cno;
+ } else
+ tp->offset = sp->cno;
+ }
+
+ /* If getting a command buffer from the user, there may be a prompt. */
+ if (LF_ISSET(TXT_PROMPT)) {
+ tp->lb[sp->cno++] = prompt;
+ ++tp->len;
+ ++tp->offset;
+ }
+
+ /*
+ * If appending after the end-of-line, add a space into the buffer
+ * and move the cursor right. This space is inserted, i.e. pushed
+ * along, and then deleted when the line is resolved. Assumes that
+ * the cursor is already positioned at the end of the line. This
+ * avoids the nastiness of having the cursor reside on a magical
+ * column, i.e. a column that doesn't really exist. The only down
+ * side is that we may wrap lines or scroll the screen before it's
+ * strictly necessary. Not a big deal.
+ */
+ if (LF_ISSET(TXT_APPENDEOL)) {
+ tp->lb[sp->cno] = CURSOR_CH;
+ ++tp->len;
+ ++tp->insert;
+ }
+
+ /*
+ * Historic practice is that the wrapmargin value was a distance
+ * from the RIGHT-HAND column, not the left. It's more useful to
+ * us as a distance from the left-hand column.
+ *
+ * !!!
+ * Replay commands are not affected by wrapmargin values. What
+ * I found surprising was that people actually depend on it, as
+ * in this gem of a macro which centers lines:
+ *
+ * map #c $mq81a ^V^[81^V|D`qld0:s/ / /g^V^M$p
+ *
+ * XXX
+ * Setting margin causes a significant performance hit. Normally
+ * we don't update the screen if there are keys waiting, but we
+ * have to if margin is set, otherwise the screen routines don't
+ * know where the cursor is.
+ */
+ if (LF_ISSET(TXT_REPLAY) || !LF_ISSET(TXT_WRAPMARGIN))
+ margin = 0;
+ else if ((margin = O_VAL(sp, O_WRAPMARGIN)) != 0)
+ margin = sp->cols - margin;
+
+ /* Initialize abbreviations checks. */
+ if (F_ISSET(gp, G_ABBREV) && LF_ISSET(TXT_MAPINPUT)) {
+ abb = A_NOTSPACE;
+ ab_cnt = ab_turnoff = 0;
+ } else
+ abb = A_NOTSET;
+
+ /*
+ * Set up the dot command. Dot commands are done by saving the
+ * actual characters and replaying the input. We have to push
+ * the characters onto the key stack and then handle them normally,
+ * otherwise things like wrapmargin will fail.
+ *
+ * XXX
+ * It would be nice if we could swallow backspaces and such, but
+ * it's not all that easy to do. Another possibility would be to
+ * recognize full line insertions, which could be performed quickly,
+ * without replay.
+ */
+nullreplay:
+ rcol = 0;
+ if (replay = LF_ISSET(TXT_REPLAY)) {
+ /*
+ * !!!
+ * Historically, it wasn't an error to replay non-existent
+ * input. This test is necessary, we get here by the user
+ * doing an input command followed by a nul.
+ *
+ * !!!
+ * Historically, vi did not remap or reabbreviate replayed
+ * input. It did, however, beep at you if you changed an
+ * abbreviation and then replayed the input. We're not that
+ * compatible.
+ */
+ if (VIP(sp)->rep == NULL)
+ return (0);
+ if (term_push(sp, VIP(sp)->rep, VIP(sp)->rep_cnt, 0, CH_NOMAP))
+ return (1);
+ testnr = 0;
+ abb = A_NOTSET;
+ LF_CLR(TXT_RECORD);
+ } else
+ testnr = 1;
+
+ iflags = LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT);
+ for (gp, showmatch = 0,
+ carat_st = C_NOTSET, hex = H_NOTSET, quoted = Q_NOTSET;;) {
+ /*
+ * Reset the line and update the screen. (The txt_showmatch()
+ * code refreshes the screen for us.) Don't refresh unless
+ * we're about to wait on a character or we need to know where
+ * the cursor really is.
+ */
+ if (showmatch || margin || !KEYS_WAITING(sp)) {
+ if (sp->s_change(sp, ep, tp->lno, LINE_RESET))
+ goto err;
+ if (showmatch) {
+ showmatch = 0;
+ txt_showmatch(sp, ep);
+ } else if (sp->s_refresh(sp, ep))
+ goto err;
+ }
+
+ /* Get the next character. */
+next_ch: if (term_key(sp, &ikey, iflags) != INP_OK)
+ goto err;
+ ch = ikey.ch;
+
+ /* Abbreviation check. See comment in txt_abbrev(). */
+#define MAX_ABBREVIATION_EXPANSION 256
+ if (ikey.flags & CH_ABBREVIATED) {
+ if (++ab_cnt > MAX_ABBREVIATION_EXPANSION) {
+ term_ab_flush(sp,
+ "Abbreviation exceeded maximum number of characters");
+ ab_cnt = 0;
+ continue;
+ }
+ } else
+ ab_cnt = 0;
+
+ /*
+ * !!!
+ * Historic feature. If the first character of the input is
+ * a nul, replay the previous input. This isn't documented
+ * anywhere, and is a great test of vi clones.
+ */
+ if (ch == '\0' && testnr) {
+ LF_SET(TXT_REPLAY);
+ goto nullreplay;
+ }
+ testnr = 0;
+
+ /*
+ * Check to see if the character fits into the input (and
+ * replay, if necessary) buffers. It isn't necessary to
+ * have tp->len bytes, since it doesn't consider overwrite
+ * characters, but not worth fixing.
+ */
+ if (LF_ISSET(TXT_RECORD)) {
+ TBINC(sp, VIP(sp)->rep, VIP(sp)->rep_len, rcol + 1);
+ VIP(sp)->rep[rcol++] = ch;
+ }
+ TBINC(sp, tp->lb, tp->lb_len, tp->len + 1);
+
+ /*
+ * If the character was quoted, replace the last character
+ * (the literal mark) with the new character. If quoted
+ * by someone else, simply insert the character.
+ *
+ * !!!
+ * Extension -- if the quoted character is HEX_CH, enter hex
+ * mode. If the user enters "<HEX_CH>[isxdigit()]*" we will
+ * try to use the value as a character. Anything else resets
+ * hex mode.
+ */
+ if (ikey.flags & CH_QUOTED)
+ goto ins_ch;
+ if (quoted == Q_THISCHAR) {
+ --sp->cno;
+ ++tp->owrite;
+ quoted = Q_NOTSET;
+
+ if (ch == HEX_CH)
+ hex = H_NEXTCHAR;
+ goto ins_ch;
+ }
+
+ switch (ikey.value) {
+ case K_CR:
+ case K_NL: /* New line. */
+#define LINE_RESOLVE { \
+ /* \
+ * Handle abbreviations. If there was one, \
+ * discard the replay characters. \
+ */ \
+ if (abb == A_NOTSPACE && !replay) { \
+ if (txt_abbrev(sp, tp, ch, \
+ LF_ISSET(TXT_INFOLINE), &tmp, \
+ &ab_turnoff)) \
+ goto err; \
+ if (tmp) { \
+ if (LF_ISSET(TXT_RECORD)) \
+ rcol -= tmp; \
+ goto next_ch; \
+ } \
+ } \
+ if (abb != A_NOTSET) \
+ abb = A_SPACE; \
+ /* Handle hex numbers. */ \
+ if (hex == H_INHEX) { \
+ if (txt_hex(sp, tp, &tmp, ch)) \
+ goto err; \
+ if (tmp) { \
+ hex = H_NOTSET; \
+ goto next_ch; \
+ } \
+ } \
+ /* \
+ * The 'R' command returns any overwriteable \
+ * characters in the first line to the original \
+ * characters.
+ */ \
+ if (LF_ISSET(TXT_REPLACE) && tp->owrite && \
+ tp == tiqh->cqh_first) { \
+ memmove(tp->lb + sp->cno, \
+ lp + sp->cno, tp->owrite); \
+ tp->insert += tp->owrite; \
+ tp->owrite = 0; \
+ } \
+ /* Delete any appended cursor. */ \
+ if (LF_ISSET(TXT_APPENDEOL)) { \
+ --tp->len; \
+ --tp->insert; \
+ } \
+}
+ LINE_RESOLVE;
+
+ /* CR returns from the vi command line. */
+ if (LF_ISSET(TXT_CR)) {
+ /*
+ * If a script window and not the colon
+ * line, push a <cr> so it gets executed.
+ */
+ if (F_ISSET(sp, S_SCRIPT) &&
+ !LF_ISSET(TXT_INFOLINE))
+ (void)term_push(sp,
+ "\r", 1, 0, CH_NOMAP);
+ goto k_escape;
+ }
+
+ /*
+ * Historic practice was to delete any <blank>
+ * characters following the inserted newline.
+ * This affects the 'R', 'c', and 's' commands.
+ */
+ for (p = tp->lb + sp->cno + tp->owrite;
+ tp->insert && isblank(*p);
+ ++p, ++tp->owrite, --tp->insert);
+
+ /*
+ * Move any remaining insert characters into
+ * a new TEXT structure.
+ */
+ if ((ntp = text_init(sp,
+ tp->lb + sp->cno + tp->owrite,
+ tp->insert, tp->insert + 32)) == NULL)
+ goto err;
+
+ /* Set bookkeeping for the new line. */
+ ntp->lno = tp->lno + 1;
+ ntp->insert = tp->insert;
+
+ /*
+ * Note if the user inserted any characters on this
+ * line. Done before calling txt_ai_resolve() because
+ * it changes the value of sp->cno without making the
+ * corresponding changes to tp->ai.
+ */
+ tmp = sp->cno <= tp->ai;
+
+ /*
+ * Resolve autoindented characters for the old line.
+ * Reset the autoindent line value. 0^D keeps the ai
+ * line from changing, ^D changes the level, even if
+ * there are no characters in the old line. Note,
+ * if using the current tp structure, use the cursor
+ * as the length, the user may have erased autoindent
+ * characters.
+ */
+ if (LF_ISSET(TXT_AUTOINDENT)) {
+ txt_ai_resolve(sp, tp);
+
+ if (carat_st == C_NOCHANGE) {
+ if (txt_auto(sp, ep,
+ OOBLNO, &ait, ait.ai, ntp))
+ goto err;
+ FREE_SPACE(sp, ait.lb, ait.lb_len);
+ } else
+ if (txt_auto(sp, ep,
+ OOBLNO, tp, sp->cno, ntp))
+ goto err;
+ carat_st = C_NOTSET;
+ }
+
+ /*
+ * If the user hasn't entered any characters, delete
+ * any autoindent characters.
+ *
+ * !!!
+ * Historic vi didn't get the insert test right, if
+ * there were characters after the cursor, entering
+ * a <cr> left the autoindent characters on the line.
+ */
+ if (tmp)
+ sp->cno = 0;
+
+ /* Reset bookkeeping for the old line. */
+ tp->len = sp->cno;
+ tp->ai = tp->insert = tp->owrite = 0;
+
+ /* New cursor position. */
+ sp->cno = ntp->ai;
+
+ /* New lines are TXT_APPENDEOL if nothing to insert. */
+ if (ntp->insert == 0) {
+ TBINC(sp, tp->lb, tp->lb_len, tp->len + 1);
+ LF_SET(TXT_APPENDEOL);
+ ntp->lb[sp->cno] = CURSOR_CH;
+ ++ntp->insert;
+ ++ntp->len;
+ }
+
+ /* Update the old line. */
+ if (sp->s_change(sp, ep, tp->lno, LINE_RESET))
+ goto err;
+
+ /*
+ * Swap old and new TEXT's, and insert the new TEXT
+ * into the queue. (DON'T insert until the old line
+ * has been updated, or the inserted line count in
+ * line.c:file_gline() will be wrong.)
+ */
+ tp = ntp;
+ CIRCLEQ_INSERT_TAIL(tiqh, tp, q);
+
+ /* Reset the cursor. */
+ sp->lno = tp->lno;
+
+ /* Update the new line. */
+ if (sp->s_change(sp, ep, tp->lno, LINE_INSERT))
+ goto err;
+
+ /* Set the renumber bit. */
+ F_SET(sp, S_RENUMBER);
+
+ /* Refresh if nothing waiting. */
+ if ((margin || !KEYS_WAITING(sp)) &&
+ sp->s_refresh(sp, ep))
+ goto err;
+ goto next_ch;
+ case K_ESCAPE: /* Escape. */
+ if (!LF_ISSET(TXT_ESCAPE))
+ goto ins_ch;
+
+ LINE_RESOLVE;
+
+ /*
+ * If there aren't any trailing characters in the line
+ * and the user hasn't entered any characters, delete
+ * the autoindent characters.
+ */
+ if (!tp->insert && sp->cno <= tp->ai) {
+ tp->len = tp->owrite = 0;
+ sp->cno = 0;
+ } else if (LF_ISSET(TXT_AUTOINDENT))
+ txt_ai_resolve(sp, tp);
+
+ /* If there are insert characters, copy them down. */
+k_escape: if (tp->insert && tp->owrite)
+ memmove(tp->lb + sp->cno,
+ tp->lb + sp->cno + tp->owrite, tp->insert);
+ tp->len -= tp->owrite;
+
+ /*
+ * Delete any lines that were inserted into the text
+ * structure and then erased.
+ */
+ while (tp->q.cqe_next != (void *)tiqh) {
+ ntp = tp->q.cqe_next;
+ CIRCLEQ_REMOVE(tiqh, ntp, q);
+ text_free(ntp);
+ }
+
+ /*
+ * If not resolving the lines into the file, end
+ * it with a nul.
+ *
+ * XXX
+ * This is wrong, should pass back a length.
+ */
+ if (LF_ISSET(TXT_RESOLVE)) {
+ if (txt_resolve(sp, ep, tiqh))
+ goto err;
+ /*
+ * Clear input flag -- input buffer no longer
+ * valid.
+ */
+ F_CLR(sp, S_INPUT);
+ } else {
+ TBINC(sp, tp->lb, tp->lb_len, tp->len + 1);
+ tp->lb[tp->len] = '\0';
+ }
+
+ /*
+ * Set the return cursor position to rest on the last
+ * inserted character.
+ */
+ if (rp != NULL) {
+ rp->lno = tp->lno;
+ rp->cno = sp->cno ? sp->cno - 1 : 0;
+ if (sp->s_change(sp, ep, rp->lno, LINE_RESET))
+ goto err;
+ }
+ goto ret;
+ case K_CARAT: /* Delete autoindent chars. */
+ if (LF_ISSET(TXT_AUTOINDENT) && sp->cno <= tp->ai)
+ carat_st = C_CARATSET;
+ goto ins_ch;
+ case K_ZERO: /* Delete autoindent chars. */
+ if (LF_ISSET(TXT_AUTOINDENT) && sp->cno <= tp->ai)
+ carat_st = C_ZEROSET;
+ goto ins_ch;
+ case K_VEOF: /* Delete autoindent char. */
+ /*
+ * If in the first column or no characters to erase,
+ * ignore the ^D (this matches historic practice). If
+ * not doing autoindent or already inserted non-ai
+ * characters, it's a literal. The latter test is done
+ * in the switch, as the CARAT forms are N + 1, not N.
+ */
+ if (!LF_ISSET(TXT_AUTOINDENT))
+ goto ins_ch;
+ if (sp->cno == 0 || tp->ai == 0)
+ break;
+ switch (carat_st) {
+ case C_CARATSET: /* ^^D */
+ if (sp->cno > tp->ai + tp->offset + 1)
+ goto ins_ch;
+
+ /* Save the ai string for later. */
+ ait.lb = NULL;
+ ait.lb_len = 0;
+ TBINC(sp, ait.lb, ait.lb_len, tp->ai);
+ memmove(ait.lb, tp->lb, tp->ai);
+ ait.ai = ait.len = tp->ai;
+
+ carat_st = C_NOCHANGE;
+ goto leftmargin;
+ case C_ZEROSET: /* 0^D */
+ if (sp->cno > tp->ai + tp->offset + 1)
+ goto ins_ch;
+ carat_st = C_NOTSET;
+leftmargin: tp->lb[sp->cno - 1] = ' ';
+ tp->owrite += sp->cno - tp->offset;
+ tp->ai = 0;
+ sp->cno = tp->offset;
+ break;
+ case C_NOTSET: /* ^D */
+ if (sp->cno > tp->ai + tp->offset)
+ goto ins_ch;
+ (void)txt_outdent(sp, tp);
+ break;
+ default:
+ abort();
+ }
+ break;
+ case K_VERASE: /* Erase the last character. */
+ /*
+ * If can erase over the prompt, return. Len is 0
+ * if backspaced over the prompt, 1 if only CR entered.
+ */
+ if (LF_ISSET(TXT_BS) && sp->cno <= tp->offset) {
+ tp->len = 0;
+ goto ret;
+ }
+
+ /*
+ * If at the beginning of the line, try and drop back
+ * to a previously inserted line.
+ */
+ if (sp->cno == 0) {
+ if ((ntp = txt_backup(sp,
+ ep, tiqh, tp, flags)) == NULL)
+ goto err;
+ tp = ntp;
+ break;
+ }
+
+ /* If nothing to erase, bell the user. */
+ if (sp->cno <= tp->offset) {
+ msgq(sp, M_BERR,
+ "No more characters to erase.");
+ break;
+ }
+
+ /* Drop back one character. */
+ --sp->cno;
+
+ /*
+ * Increment overwrite, decrement ai if deleted.
+ *
+ * !!!
+ * Historic vi did not permit users to use erase
+ * characters to delete autoindent characters.
+ */
+ ++tp->owrite;
+ if (sp->cno < tp->ai)
+ --tp->ai;
+ break;
+ case K_VINTR:
+ /*
+ * !!!
+ * Historically, <interrupt> exited the user from
+ * editing the infoline, and returned to the main
+ * screen. It also beeped the terminal, but that
+ * seems excessive.
+ */
+ if (LF_ISSET(TXT_INFOLINE)) {
+ tp->lb[tp->len = 0] = '\0';
+ goto ret;
+ }
+ goto ins_ch;
+ case K_VWERASE: /* Skip back one word. */
+ /*
+ * If at the beginning of the line, try and drop back
+ * to a previously inserted line.
+ */
+ if (sp->cno == 0) {
+ if ((ntp = txt_backup(sp,
+ ep, tiqh, tp, flags)) == NULL)
+ goto err;
+ tp = ntp;
+ }
+
+ /*
+ * If at offset, nothing to erase so bell the user.
+ */
+ if (sp->cno <= tp->offset) {
+ msgq(sp, M_BERR,
+ "No more characters to erase.");
+ break;
+ }
+
+ /*
+ * First werase goes back to any autoindent
+ * and second werase goes back to the offset.
+ *
+ * !!!
+ * Historic vi did not permit users to use erase
+ * characters to delete autoindent characters.
+ */
+ if (tp->ai && sp->cno > tp->ai)
+ max = tp->ai;
+ else {
+ tp->ai = 0;
+ max = tp->offset;
+ }
+
+ /* Skip over trailing space characters. */
+ while (sp->cno > max && isblank(tp->lb[sp->cno - 1])) {
+ --sp->cno;
+ ++tp->owrite;
+ }
+ if (sp->cno == max)
+ break;
+ /*
+ * There are three types of word erase found on UNIX
+ * systems. They can be identified by how the string
+ * /a/b/c is treated -- as 1, 3, or 6 words. Historic
+ * vi had two classes of characters, and strings were
+ * delimited by them and <blank>'s, so, 6 words. The
+ * historic tty interface used <blank>'s to delimit
+ * strings, so, 1 word. The algorithm offered in the
+ * 4.4BSD tty interface (as stty altwerase) treats it
+ * as 3 words -- there are two classes of characters,
+ * and strings are delimited by them and <blank>'s.
+ * The difference is that the type of the first erased
+ * character erased is ignored, which is exactly right
+ * when erasing pathname components. Here, the options
+ * TXT_ALTWERASE and TXT_TTYWERASE specify the 4.4BSD
+ * tty interface and the historic tty driver behavior,
+ * respectively, and the default is the same as the
+ * historic vi behavior.
+ */
+ if (LF_ISSET(TXT_TTYWERASE))
+ while (sp->cno > max) {
+ --sp->cno;
+ ++tp->owrite;
+ if (isblank(tp->lb[sp->cno - 1]))
+ break;
+ }
+ else {
+ if (LF_ISSET(TXT_ALTWERASE)) {
+ --sp->cno;
+ ++tp->owrite;
+ if (isblank(tp->lb[sp->cno - 1]))
+ break;
+ }
+ if (sp->cno > max)
+ tmp = inword(tp->lb[sp->cno - 1]);
+ while (sp->cno > max) {
+ --sp->cno;
+ ++tp->owrite;
+ if (tmp != inword(tp->lb[sp->cno - 1])
+ || isblank(tp->lb[sp->cno - 1]))
+ break;
+ }
+ }
+ break;
+ case K_VKILL: /* Restart this line. */
+ /*
+ * If at the beginning of the line, try and drop back
+ * to a previously inserted line.
+ */
+ if (sp->cno == 0) {
+ if ((ntp = txt_backup(sp,
+ ep, tiqh, tp, flags)) == NULL)
+ goto err;
+ tp = ntp;
+ }
+
+ /* If at offset, nothing to erase so bell the user. */
+ if (sp->cno <= tp->offset) {
+ msgq(sp, M_BERR,
+ "No more characters to erase.");
+ break;
+ }
+
+ /*
+ * First kill goes back to any autoindent
+ * and second kill goes back to the offset.
+ *
+ * !!!
+ * Historic vi did not permit users to use erase
+ * characters to delete autoindent characters.
+ */
+ if (tp->ai && sp->cno > tp->ai)
+ max = tp->ai;
+ else {
+ tp->ai = 0;
+ max = tp->offset;
+ }
+ tp->owrite += sp->cno - max;
+ sp->cno = max;
+ break;
+ case K_CNTRLT: /* Add autoindent char. */
+ if (!LF_ISSET(TXT_CNTRLT))
+ goto ins_ch;
+ if (txt_indent(sp, tp))
+ goto err;
+ goto ebuf_chk;
+ case K_CNTRLZ:
+ (void)sp->s_suspend(sp);
+ break;
+#ifdef HISTORIC_PRACTICE_IS_TO_INSERT_NOT_REPAINT
+ case K_FORMFEED:
+ F_SET(sp, S_REFRESH);
+ break;
+#endif
+ case K_RIGHTBRACE:
+ case K_RIGHTPAREN:
+ showmatch = LF_ISSET(TXT_SHOWMATCH);
+ goto ins_ch;
+ case K_VLNEXT: /* Quote the next character. */
+ /* If in hex mode, see if we've entered a hex value. */
+ if (hex == H_INHEX) {
+ if (txt_hex(sp, tp, &tmp, ch))
+ goto err;
+ if (tmp) {
+ hex = H_NOTSET;
+ goto next_ch;
+ }
+ }
+ ch = '^';
+ quoted = Q_NEXTCHAR;
+ /* FALLTHROUGH */
+ default: /* Insert the character. */
+ins_ch: /*
+ * If entering a space character after a word, check
+ * for abbreviations. If there was one, discard the
+ * replay characters.
+ */
+ if (isblank(ch) && abb == A_NOTSPACE && !replay) {
+ if (txt_abbrev(sp, tp, ch,
+ LF_ISSET(TXT_INFOLINE), &tmp, &ab_turnoff))
+ goto err;
+ if (tmp) {
+ if (LF_ISSET(TXT_RECORD))
+ rcol -= tmp;
+ goto next_ch;
+ }
+ }
+ /* If in hex mode, see if we've entered a hex value. */
+ if (hex == H_INHEX && !isxdigit(ch)) {
+ if (txt_hex(sp, tp, &tmp, ch))
+ goto err;
+ if (tmp) {
+ hex = H_NOTSET;
+ goto next_ch;
+ }
+ }
+ /* Check to see if we've crossed the margin. */
+ if (margin) {
+ if (sp->s_column(sp, ep, &col))
+ goto err;
+ if (col >= margin) {
+ if (txt_margin(sp, tp, &tmp, ch))
+ goto err;
+ if (tmp)
+ goto next_ch;
+ }
+ }
+ if (abb != A_NOTSET)
+ abb = isblank(ch) ? A_SPACE : A_NOTSPACE;
+
+ if (tp->owrite) /* Overwrite a character. */
+ --tp->owrite;
+ else if (tp->insert) { /* Insert a character. */
+ ++tp->len;
+ if (tp->insert == 1)
+ tp->lb[sp->cno + 1] = tp->lb[sp->cno];
+ else
+ memmove(tp->lb + sp->cno + 1,
+ tp->lb + sp->cno, tp->insert);
+ }
+
+ tp->lb[sp->cno++] = ch;
+
+ /*
+ * If we've reached the end of the buffer, then we
+ * need to switch into insert mode. This happens
+ * when there's a change to a mark and the user puts
+ * in more characters than the length of the motion.
+ */
+ebuf_chk: if (sp->cno >= tp->len) {
+ TBINC(sp, tp->lb, tp->lb_len, tp->len + 1);
+ LF_SET(TXT_APPENDEOL);
+ tp->lb[sp->cno] = CURSOR_CH;
+ ++tp->insert;
+ ++tp->len;
+ }
+
+ if (hex == H_NEXTCHAR)
+ hex = H_INHEX;
+ if (quoted == Q_NEXTCHAR)
+ quoted = Q_THISCHAR;
+ break;
+ }
+#if defined(DEBUG) && 1
+ if (sp->cno + tp->insert + tp->owrite != tp->len)
+ msgq(sp, M_ERR,
+ "len %u != cno: %u ai: %u insert %u overwrite %u",
+ tp->len, sp->cno, tp->ai, tp->insert, tp->owrite);
+ tp->len = sp->cno + tp->insert + tp->owrite;
+#endif
+ }
+
+ /* Clear input flag. */
+ret: F_CLR(sp, S_INPUT);
+
+ if (LF_ISSET(TXT_RECORD))
+ VIP(sp)->rep_cnt = rcol;
+ return (eval);
+
+ /* Error jump. */
+err: eval = 1;
+ txt_err(sp, ep, tiqh);
+ goto ret;
+}
+
+/*
+ * txt_abbrev --
+ * Handle abbreviations.
+ */
+static int
+txt_abbrev(sp, tp, pushc, isinfoline, didsubp, turnoffp)
+ SCR *sp;
+ TEXT *tp;
+ ARG_CHAR_T pushc;
+ int isinfoline, *didsubp, *turnoffp;
+{
+ CHAR_T ch;
+ SEQ *qp;
+ size_t len, off;
+ char *p;
+
+ /* Find the beginning of this "word". */
+ for (off = sp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) {
+ if (isblank(*p)) {
+ ++p;
+ break;
+ }
+ ++len;
+ if (off == tp->ai || off == tp->offset)
+ break;
+ }
+
+ /*
+ * !!!
+ * Historic vi exploded abbreviations on the command line. This has
+ * obvious problems in that unabbreviating the string can be extremely
+ * tricky, particularly if the string has, say, an embedded escape
+ * character. Personally, I think it's a stunningly bad idea. Other
+ * examples of problems this caused in historic vi are:
+ * :ab foo bar
+ * :ab foo baz
+ * results in "bar" being abbreviated to "baz", which wasn't what the
+ * user had in mind at all. Also, the commands:
+ * :ab foo bar
+ * :unab foo<space>
+ * resulted in an error message that "bar" wasn't mapped. Finally,
+ * since the string was already exploded by the time the unabbreviate
+ * command got it, all it knew was that an abbreviation had occurred.
+ * Cleverly, it checked the replacement string for its unabbreviation
+ * match, which meant that the commands:
+ * :ab foo1 bar
+ * :ab foo2 bar
+ * :unab foo2
+ * unabbreviates "foo1", and the commands:
+ * :ab foo bar
+ * :ab bar baz
+ * unabbreviates "foo"!
+ *
+ * Anyway, people neglected to first ask my opinion before they wrote
+ * macros that depend on this stuff, so, we make this work as follows.
+ * When checking for an abbreviation on the command line, if we get a
+ * string which is <blank> terminated and which starts at the beginning
+ * of the line, we check to see it is the abbreviate or unabbreviate
+ * commands. If it is, turn abbreviations off and return as if no
+ * abbreviation was found. Not also, minor trickiness, so that if the
+ * user erases the line and starts another command, we go ahead an turn
+ * abbreviations back on.
+ *
+ * This makes the layering look like a Nachos Supreme.
+ */
+ *didsubp = 0;
+ if (isinfoline)
+ if (off == tp->ai || off == tp->offset)
+ if (ex_is_abbrev(p, len)) {
+ *turnoffp = 1;
+ return (0);
+ } else
+ *turnoffp = 0;
+ else
+ if (*turnoffp)
+ return (0);
+
+ /* Check for any abbreviations. */
+ if ((qp = seq_find(sp, NULL, p, len, SEQ_ABBREV, NULL)) == NULL)
+ return (0);
+
+ /*
+ * Push the abbreviation onto the tty stack. Historically, characters
+ * resulting from an abbreviation expansion were themselves subject to
+ * map expansions, O_SHOWMATCH matching etc. This means the expanded
+ * characters will be re-tested for abbreviations. It's difficult to
+ * know what historic practice in this case was, since abbreviations
+ * were applied to :colon command lines, so entering abbreviations that
+ * looped was tricky, although possible. In addition, obvious loops
+ * didn't work as expected. (The command ':ab a b|ab b c|ab c a' will
+ * silently only implement and/or display the last abbreviation.)
+ *
+ * This implementation doesn't recover well from such abbreviations.
+ * The main input loop counts abbreviated characters, and, when it
+ * reaches a limit, discards any abbreviated characters on the queue.
+ * It's difficult to back up to the original position, as the replay
+ * queue would have to be adjusted, and the line state when an initial
+ * abbreviated character was received would have to be saved.
+ */
+ ch = pushc;
+ if (term_push(sp, &ch, 1, 0, CH_ABBREVIATED))
+ return (1);
+ if (term_push(sp, qp->output, qp->olen, 0, CH_ABBREVIATED))
+ return (1);
+
+ /*
+ * Move the cursor to the start of the abbreviation,
+ * adjust the length.
+ */
+ sp->cno -= len;
+ tp->len -= len;
+
+ /* Copy any insert characters back. */
+ if (tp->insert)
+ memmove(tp->lb + sp->cno + tp->owrite,
+ tp->lb + sp->cno + tp->owrite + len, tp->insert);
+
+ /*
+ * We return the length of the abbreviated characters. This is so
+ * the calling routine can replace the replay characters with the
+ * abbreviation. This means that subsequent '.' commands will produce
+ * the same text, regardless of intervening :[un]abbreviate commands.
+ * This is historic practice.
+ */
+ *didsubp = len;
+ return (0);
+}
+
+/* Offset to next column of stop size. */
+#define STOP_OFF(c, stop) (stop - (c) % stop)
+
+/*
+ * txt_ai_resolve --
+ * When a line is resolved by <esc> or <cr>, review autoindent
+ * characters.
+ */
+static void
+txt_ai_resolve(sp, tp)
+ SCR *sp;
+ TEXT *tp;
+{
+ u_long ts;
+ int del;
+ size_t cno, len, new, old, scno, spaces, tab_after_sp, tabs;
+ char *p;
+
+ /*
+ * If the line is empty, has an offset, or no autoindent
+ * characters, we're done.
+ */
+ if (!tp->len || tp->offset || !tp->ai)
+ return;
+
+ /*
+ * The autoindent characters plus any leading <blank> characters
+ * in the line are resolved into the minimum number of characters.
+ * Historic practice.
+ */
+ ts = O_VAL(sp, O_TABSTOP);
+
+ /* Figure out the last <blank> screen column. */
+ for (p = tp->lb, scno = 0, len = tp->len,
+ spaces = tab_after_sp = 0; len-- && isblank(*p); ++p)
+ if (*p == '\t') {
+ if (spaces)
+ tab_after_sp = 1;
+ scno += STOP_OFF(scno, ts);
+ } else {
+ ++spaces;
+ ++scno;
+ }
+
+ /*
+ * If there are no spaces, or no tabs after spaces and less than
+ * ts spaces, it's already minimal.
+ */
+ if (!spaces || !tab_after_sp && spaces < ts)
+ return;
+
+ /* Count up spaces/tabs needed to get to the target. */
+ for (cno = 0, tabs = 0; cno + STOP_OFF(cno, ts) <= scno; ++tabs)
+ cno += STOP_OFF(cno, ts);
+ spaces = scno - cno;
+
+ /*
+ * Figure out how many characters we're dropping -- if we're not
+ * dropping any, it's already minimal, we're done.
+ */
+ old = p - tp->lb;
+ new = spaces + tabs;
+ if (old == new)
+ return;
+
+ /* Shift the rest of the characters down, adjust the counts. */
+ del = old - new;
+ memmove(p - del, p, tp->len - old);
+ sp->cno -= del;
+ tp->len -= del;
+
+ /* Fill in space/tab characters. */
+ for (p = tp->lb; tabs--;)
+ *p++ = '\t';
+ while (spaces--)
+ *p++ = ' ';
+}
+
+/*
+ * txt_auto --
+ * Handle autoindent. If aitp isn't NULL, use it, otherwise,
+ * retrieve the line.
+ */
+int
+txt_auto(sp, ep, lno, aitp, len, tp)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ size_t len;
+ TEXT *aitp, *tp;
+{
+ size_t nlen;
+ char *p, *t;
+
+ if (aitp == NULL) {
+ if ((p = t = file_gline(sp, ep, lno, &len)) == NULL)
+ return (0);
+ } else
+ p = t = aitp->lb;
+ for (nlen = 0; len; ++p) {
+ if (!isblank(*p))
+ break;
+ /* If last character is a space, it counts. */
+ if (--len == 0) {
+ ++p;
+ break;
+ }
+ }
+
+ /* No indentation. */
+ if (p == t)
+ return (0);
+
+ /* Set count. */
+ nlen = p - t;
+
+ /* Make sure the buffer's big enough. */
+ BINC_RET(sp, tp->lb, tp->lb_len, tp->len + nlen);
+
+ /* Copy the indentation into the new buffer. */
+ memmove(tp->lb + nlen, tp->lb, tp->len);
+ memmove(tp->lb, t, nlen);
+ tp->len += nlen;
+
+ /* Return the additional length. */
+ tp->ai = nlen;
+ return (0);
+}
+
+/*
+ * txt_backup --
+ * Back up to the previously edited line.
+ */
+static TEXT *
+txt_backup(sp, ep, tiqh, tp, flags)
+ SCR *sp;
+ EXF *ep;
+ TEXTH *tiqh;
+ TEXT *tp;
+ u_int flags;
+{
+ TEXT *ntp;
+ recno_t lno;
+ size_t total;
+
+ /* Get a handle on the previous TEXT structure. */
+ if ((ntp = tp->q.cqe_prev) == (void *)tiqh) {
+ msgq(sp, M_BERR, "Already at the beginning of the insert");
+ return (tp);
+ }
+
+ /* Make sure that we have enough space. */
+ total = ntp->len + tp->insert;
+ if (LF_ISSET(TXT_APPENDEOL))
+ ++total;
+ if (total > ntp->lb_len &&
+ binc(sp, &ntp->lb, &ntp->lb_len, total))
+ return (NULL);
+
+ /*
+ * Append a cursor or copy inserted bytes to the end of the old line.
+ * Test for appending a cursor first, because the TEXT insert field
+ * will be 1 if we're appending a cursor. I don't think there's a
+ * third case, so abort() if there is.
+ */
+ if (LF_ISSET(TXT_APPENDEOL)) {
+ ntp->lb[ntp->len] = CURSOR_CH;
+ ntp->insert = 1;
+ } else if (tp->insert) {
+ memmove(ntp->lb + ntp->len, tp->lb + tp->owrite, tp->insert);
+ ntp->insert = tp->insert;
+ } else
+ abort();
+
+ /* Set bookkeeping information. */
+ sp->lno = ntp->lno;
+ sp->cno = ntp->len;
+ ntp->len += ntp->insert;
+
+ /* Release the current TEXT. */
+ lno = tp->lno;
+ CIRCLEQ_REMOVE(tiqh, tp, q);
+ text_free(tp);
+
+ /* Update the old line on the screen. */
+ if (sp->s_change(sp, ep, lno, LINE_DELETE))
+ return (NULL);
+
+ /* Return the old line. */
+ return (ntp);
+}
+
+/*
+ * txt_err --
+ * Handle an error during input processing.
+ */
+static void
+txt_err(sp, ep, tiqh)
+ SCR *sp;
+ EXF *ep;
+ TEXTH *tiqh;
+{
+ recno_t lno;
+ size_t len;
+
+ /*
+ * The problem with input processing is that the cursor is at an
+ * indeterminate position since some input may have been lost due
+ * to a malloc error. So, try to go back to the place from which
+ * the cursor started, knowing that it may no longer be available.
+ *
+ * We depend on at least one line number being set in the text
+ * chain.
+ */
+ for (lno = tiqh->cqh_first->lno;
+ file_gline(sp, ep, lno, &len) == NULL && lno > 0; --lno);
+
+ sp->lno = lno == 0 ? 1 : lno;
+ sp->cno = 0;
+
+ /* Redraw the screen, just in case. */
+ F_SET(sp, S_REDRAW);
+}
+
+/*
+ * txt_hex --
+ * Let the user insert any character value they want.
+ *
+ * !!!
+ * This is an extension. The pattern "^Vx[0-9a-fA-F]*" is a way
+ * for the user to specify a character value which their keyboard
+ * may not be able to enter.
+ */
+static int
+txt_hex(sp, tp, was_hex, pushc)
+ SCR *sp;
+ TEXT *tp;
+ int *was_hex;
+ ARG_CHAR_T pushc;
+{
+ CHAR_T ch, savec;
+ size_t len, off;
+ u_long value;
+ char *p, *wp;
+
+ /*
+ * Null-terminate the string. Since nul isn't a legal hex value,
+ * this should be okay, and lets us use a local routine, which
+ * presumably understands the character set, to convert the value.
+ */
+ savec = tp->lb[sp->cno];
+ tp->lb[sp->cno] = 0;
+
+ /* Find the previous HEX_CH. */
+ for (off = sp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) {
+ if (*p == HEX_CH) {
+ wp = p + 1;
+ break;
+ }
+ ++len;
+ /* If not on this line, there's nothing to do. */
+ if (off == tp->ai || off == tp->offset)
+ goto nothex;
+ }
+
+ /* If no length, then it wasn't a hex value. */
+ if (len == 0)
+ goto nothex;
+
+ /* Get the value. */
+ value = strtol(wp, NULL, 16);
+ if (value == LONG_MIN || value == LONG_MAX || value > MAX_CHAR_T) {
+nothex: tp->lb[sp->cno] = savec;
+ *was_hex = 0;
+ return (0);
+ }
+
+ ch = pushc;
+ if (term_push(sp, &ch, 1, 0, CH_NOMAP | CH_QUOTED))
+ return (1);
+ ch = value;
+ if (term_push(sp, &ch, 1, 0, CH_NOMAP | CH_QUOTED))
+ return (1);
+
+ tp->lb[sp->cno] = savec;
+
+ /* Move the cursor to the start of the hex value, adjust the length. */
+ sp->cno -= len + 1;
+ tp->len -= len + 1;
+
+ /* Copy any insert characters back. */
+ if (tp->insert)
+ memmove(tp->lb + sp->cno + tp->owrite,
+ tp->lb + sp->cno + tp->owrite + len + 1, tp->insert);
+
+ *was_hex = 1;
+ return (0);
+}
+
+/*
+ * Txt_indent and txt_outdent are truly strange. ^T and ^D do movements
+ * to the next or previous shiftwidth value, i.e. for a 1-based numbering,
+ * with shiftwidth=3, ^T moves a cursor on the 7th, 8th or 9th column to
+ * the 10th column, and ^D moves it back.
+ *
+ * !!!
+ * The ^T and ^D characters in historical vi only had special meaning when
+ * they were the first characters typed after entering text input mode.
+ * Since normal erase characters couldn't erase autoindent (in this case
+ * ^T) characters, this meant that inserting text into previously existing
+ * text was quite strange, ^T only worked if it was the first keystroke,
+ * and then it could only be erased by using ^D. This implementation treats
+ * ^T specially anywhere it occurs in the input, and permits the standard
+ * erase characters to erase characters inserted using it.
+ *
+ * XXX
+ * Technically, txt_indent, txt_outdent should part of the screen interface,
+ * as they require knowledge of the size of a space character on the screen.
+ * (Not the size of tabs, because tabs are logically composed of spaces.)
+ * They're left in the text code because they're complicated, not to mention
+ * the gruesome awareness that if spaces aren't a single column on the screen
+ * for any language, we're into some serious, ah, for lack of a better word,
+ * "issues".
+ */
+
+/*
+ * txt_indent --
+ * Handle ^T indents.
+ */
+static int
+txt_indent(sp, tp)
+ SCR *sp;
+ TEXT *tp;
+{
+ u_long sw, ts;
+ size_t cno, off, scno, spaces, tabs;
+
+ ts = O_VAL(sp, O_TABSTOP);
+ sw = O_VAL(sp, O_SHIFTWIDTH);
+
+ /* Get the current screen column. */
+ for (off = scno = 0; off < sp->cno; ++off)
+ if (tp->lb[off] == '\t')
+ scno += STOP_OFF(scno, ts);
+ else
+ ++scno;
+
+ /* Count up spaces/tabs needed to get to the target. */
+ for (cno = scno, scno += STOP_OFF(scno, sw), tabs = 0;
+ cno + STOP_OFF(cno, ts) <= scno; ++tabs)
+ cno += STOP_OFF(cno, ts);
+ spaces = scno - cno;
+
+ /* Put space/tab characters in place of any overwrite characters. */
+ for (; tp->owrite && tabs; --tp->owrite, --tabs, ++tp->ai)
+ tp->lb[sp->cno++] = '\t';
+ for (; tp->owrite && spaces; --tp->owrite, --spaces, ++tp->ai)
+ tp->lb[sp->cno++] = ' ';
+
+ if (!tabs && !spaces)
+ return (0);
+
+ /* Make sure there's enough room. */
+ BINC_RET(sp, tp->lb, tp->lb_len, tp->len + spaces + tabs);
+
+ /* Move the insert characters out of the way. */
+ if (tp->insert)
+ memmove(tp->lb + sp->cno + spaces + tabs,
+ tp->lb + sp->cno, tp->insert);
+
+ /* Add new space/tab characters. */
+ for (; tabs--; ++tp->len, ++tp->ai)
+ tp->lb[sp->cno++] = '\t';
+ for (; spaces--; ++tp->len, ++tp->ai)
+ tp->lb[sp->cno++] = ' ';
+ return (0);
+}
+
+/*
+ * txt_outdent --
+ * Handle ^D outdents.
+ *
+ */
+static int
+txt_outdent(sp, tp)
+ SCR *sp;
+ TEXT *tp;
+{
+ u_long sw, ts;
+ size_t cno, off, scno, spaces;
+
+ ts = O_VAL(sp, O_TABSTOP);
+ sw = O_VAL(sp, O_SHIFTWIDTH);
+
+ /* Get the current screen column. */
+ for (off = scno = 0; off < sp->cno; ++off)
+ if (tp->lb[off] == '\t')
+ scno += STOP_OFF(scno, ts);
+ else
+ ++scno;
+
+ /* Get the previous shiftwidth column. */
+ for (cno = scno; --scno % sw != 0;);
+
+ /* Decrement characters until less than or equal to that slot. */
+ for (; cno > scno; --sp->cno, --tp->ai, ++tp->owrite)
+ if (tp->lb[--off] == '\t')
+ cno -= STOP_OFF(cno, ts);
+ else
+ --cno;
+
+ /* Spaces needed to get to the target. */
+ spaces = scno - cno;
+
+ /* Maybe just a delete. */
+ if (spaces == 0)
+ return (0);
+
+ /* Make sure there's enough room. */
+ BINC_RET(sp, tp->lb, tp->lb_len, tp->len + spaces);
+
+ /* Use up any overwrite characters. */
+ for (; tp->owrite && spaces; --spaces, ++tp->ai, --tp->owrite)
+ tp->lb[sp->cno++] = ' ';
+
+ /* Maybe that was enough. */
+ if (spaces == 0)
+ return (0);
+
+ /* Move the insert characters out of the way. */
+ if (tp->insert)
+ memmove(tp->lb + sp->cno + spaces,
+ tp->lb + sp->cno, tp->insert);
+
+ /* Add new space characters. */
+ for (; spaces--; ++tp->len, ++tp->ai)
+ tp->lb[sp->cno++] = ' ';
+ return (0);
+}
+
+/*
+ * txt_resolve --
+ * Resolve the input text chain into the file.
+ */
+static int
+txt_resolve(sp, ep, tiqh)
+ SCR *sp;
+ EXF *ep;
+ TEXTH *tiqh;
+{
+ TEXT *tp;
+ recno_t lno;
+
+ /* The first line replaces a current line. */
+ tp = tiqh->cqh_first;
+ if (file_sline(sp, ep, tp->lno, tp->lb, tp->len))
+ return (1);
+
+ /* All subsequent lines are appended into the file. */
+ for (lno = tp->lno; (tp = tp->q.cqe_next) != (void *)&sp->tiq; ++lno)
+ if (file_aline(sp, ep, 0, lno, tp->lb, tp->len))
+ return (1);
+ return (0);
+}
+
+/*
+ * txt_showmatch --
+ * Show a character match.
+ *
+ * !!!
+ * Historic vi tried to display matches even in the :colon command line.
+ * I think not.
+ */
+static void
+txt_showmatch(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ struct timeval second;
+ VCS cs;
+ MARK m;
+ fd_set zero;
+ int cnt, endc, startc;
+
+ /*
+ * Do a refresh first, in case the v_ntext() code hasn't done
+ * one in awhile, so the user can see what we're complaining
+ * about.
+ */
+ if (sp->s_refresh(sp, ep))
+ return;
+ /*
+ * We don't display the match if it's not on the screen. Find
+ * out what the first character on the screen is.
+ */
+ if (sp->s_position(sp, ep, &m, 0, P_TOP))
+ return;
+
+ /* Initialize the getc() interface. */
+ cs.cs_lno = sp->lno;
+ cs.cs_cno = sp->cno - 1;
+ if (cs_init(sp, ep, &cs))
+ return;
+ startc = (endc = cs.cs_ch) == ')' ? '(' : '{';
+
+ /* Search for the match. */
+ for (cnt = 1;;) {
+ if (cs_prev(sp, ep, &cs))
+ return;
+ if (cs.cs_lno < m.lno ||
+ cs.cs_lno == m.lno && cs.cs_cno < m.cno)
+ return;
+ if (cs.cs_flags != 0) {
+ if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF) {
+ (void)sp->s_bell(sp);
+ return;
+ }
+ continue;
+ }
+ if (cs.cs_ch == endc)
+ ++cnt;
+ else if (cs.cs_ch == startc && --cnt == 0)
+ break;
+ }
+
+ /* Move to the match. */
+ m.lno = sp->lno;
+ m.cno = sp->cno;
+ sp->lno = cs.cs_lno;
+ sp->cno = cs.cs_cno;
+ (void)sp->s_refresh(sp, ep);
+
+ /*
+ * Sleep(3) is eight system calls. Do it fast -- besides,
+ * I don't want to wait an entire second.
+ */
+ FD_ZERO(&zero);
+ second.tv_sec = O_VAL(sp, O_MATCHTIME) / 10;
+ second.tv_usec = (O_VAL(sp, O_MATCHTIME) % 10) * 100000L;
+ (void)select(0, &zero, &zero, &zero, &second);
+
+ /* Return to the current location. */
+ sp->lno = m.lno;
+ sp->cno = m.cno;
+ (void)sp->s_refresh(sp, ep);
+}
+
+/*
+ * txt_margin --
+ * Handle margin wrap.
+ *
+ * !!!
+ * Historic vi belled the user each time a character was entered after
+ * crossing the margin until a space was entered which could be used to
+ * break the line. I don't, it tends to wake the cats.
+ */
+static int
+txt_margin(sp, tp, didbreak, pushc)
+ SCR *sp;
+ TEXT *tp;
+ int *didbreak;
+ ARG_CHAR_T pushc;
+{
+ CHAR_T ch;
+ size_t len, off, tlen;
+ char *p, *wp;
+
+ /* Find the closest previous blank. */
+ for (off = sp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) {
+ if (isblank(*p)) {
+ wp = p + 1;
+ break;
+ }
+ ++len;
+ /* If it's the beginning of the line, there's nothing to do. */
+ if (off == tp->ai || off == tp->offset) {
+ *didbreak = 0;
+ return (0);
+ }
+ }
+
+ /*
+ * Historic practice is to delete any trailing whitespace
+ * from the previous line.
+ */
+ for (tlen = len;; --p, --off) {
+ if (!isblank(*p))
+ break;
+ ++tlen;
+ if (off == tp->ai || off == tp->offset)
+ break;
+ }
+
+ ch = pushc;
+ if (term_push(sp, &ch, 1, 0, CH_NOMAP))
+ return (1);
+ if (len && term_push(sp, wp, len, 0, CH_NOMAP | CH_QUOTED))
+ return (1);
+ ch = '\n';
+ if (term_push(sp, &ch, 1, 0, CH_NOMAP))
+ return (1);
+
+ sp->cno -= tlen;
+ tp->owrite += tlen;
+ *didbreak = 1;
+ return (0);
+}
diff --git a/usr.bin/vi/nvi/v_paragraph.c b/usr.bin/vi/nvi/v_paragraph.c
new file mode 100644
index 000000000000..00591cbe899e
--- /dev/null
+++ b/usr.bin/vi/nvi/v_paragraph.c
@@ -0,0 +1,273 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_paragraph.c 8.4 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * Paragraphs are empty lines after text or values from the paragraph or
+ * section options.
+ */
+
+/*
+ * v_paragraphf -- [count]}
+ * Move forward count paragraphs.
+ */
+int
+v_paragraphf(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ enum { P_INTEXT, P_INBLANK } pstate;
+ size_t lastlen, len;
+ recno_t cnt, lastlno, lno;
+ char *p, *lp;
+
+ /* Figure out what state we're currently in. */
+ lno = fm->lno;
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL)
+ goto eof;
+
+ /*
+ * If we start in text, we want to switch states 2 * N - 1
+ * times, in non-text, 2 * N times.
+ */
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ cnt *= 2;
+ if (len == 0 || v_isempty(p, len))
+ pstate = P_INBLANK;
+ else {
+ --cnt;
+ pstate = P_INTEXT;
+ }
+
+ for (;;) {
+ lastlno = lno;
+ lastlen = len;
+ if ((p = file_gline(sp, ep, ++lno, &len)) == NULL)
+ goto eof;
+ switch (pstate) {
+ case P_INTEXT:
+ if (p[0] == '.' && len >= 2)
+ for (lp = VIP(sp)->paragraph; *lp; lp += 2)
+ if (lp[0] == p[1] &&
+ (lp[1] == ' ' || lp[1] == p[2]) &&
+ !--cnt)
+ goto found;
+ if (len == 0 || v_isempty(p, len)) {
+ if (!--cnt)
+ goto found;
+ pstate = P_INBLANK;
+ }
+ break;
+ case P_INBLANK:
+ if (len == 0 || v_isempty(p, len))
+ break;
+ if (--cnt) {
+ pstate = P_INTEXT;
+ break;
+ }
+ /*
+ * Historically, a motion command was up to the end
+ * of the previous line, whereas the movement command
+ * was to the start of the new "paragraph".
+ */
+found: if (F_ISSET(vp, VC_C | VC_D | VC_Y)) {
+ rp->lno = lastlno;
+ rp->cno = lastlen ? lastlen + 1 : 0;
+ } else {
+ rp->lno = lno;
+ rp->cno = 0;
+ }
+ return (0);
+ default:
+ abort();
+ }
+ }
+
+ /*
+ * EOF is a movement sink, however, the } command historically
+ * moved to the end of the last line if repeatedly invoked.
+ */
+eof: if (fm->lno != lno - 1) {
+ rp->lno = lno - 1;
+ rp->cno = len ? len - 1 : 0;
+ return (0);
+ }
+ if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL)
+ GETLINE_ERR(sp, fm->lno);
+ if (fm->cno != (len ? len - 1 : 0)) {
+ rp->lno = lno - 1;
+ rp->cno = len ? len - 1 : 0;
+ return (0);
+ }
+ v_eof(sp, ep, NULL);
+ return (1);
+}
+
+/*
+ * v_paragraphb -- [count]{
+ * Move forward count paragraph.
+ */
+int
+v_paragraphb(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ enum { P_INTEXT, P_INBLANK } pstate;
+ size_t len;
+ recno_t cnt, lno;
+ char *p, *lp;
+
+ /*
+ * The { command historically moved to the beginning of the first
+ * line if invoked on the first line.
+ *
+ * Check for SOF.
+ */
+ if (fm->lno <= 1) {
+ if (fm->cno == 0) {
+ v_sof(sp, NULL);
+ return (1);
+ }
+ rp->lno = 1;
+ rp->cno = 0;
+ return (0);
+ }
+
+ /* Figure out what state we're currently in. */
+ lno = fm->lno;
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL)
+ goto sof;
+
+ /*
+ * If we start in text, we want to switch states 2 * N - 1
+ * times, in non-text, 2 * N times.
+ */
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ cnt *= 2;
+ if (len == 0 || v_isempty(p, len))
+ pstate = P_INBLANK;
+ else {
+ --cnt;
+ pstate = P_INTEXT;
+ }
+
+ for (;;) {
+ if ((p = file_gline(sp, ep, --lno, &len)) == NULL)
+ goto sof;
+ switch (pstate) {
+ case P_INTEXT:
+ if (p[0] == '.' && len >= 2)
+ for (lp = VIP(sp)->paragraph; *lp; lp += 2)
+ if (lp[0] == p[1] &&
+ (lp[1] == ' ' || lp[1] == p[2]) &&
+ !--cnt)
+ goto found;
+ if (len == 0 || v_isempty(p, len)) {
+ if (!--cnt)
+ goto found;
+ pstate = P_INBLANK;
+ }
+ break;
+ case P_INBLANK:
+ if (len != 0 && !v_isempty(p, len)) {
+ if (!--cnt) {
+found: rp->lno = lno;
+ rp->cno = 0;
+ return (0);
+ }
+ pstate = P_INTEXT;
+ }
+ break;
+ default:
+ abort();
+ }
+ }
+
+ /* SOF is a movement sink. */
+sof: rp->lno = 1;
+ rp->cno = 0;
+ return (0);
+}
+
+/*
+ * v_buildparagraph --
+ * Build the paragraph command search pattern.
+ */
+int
+v_buildparagraph(sp)
+ SCR *sp;
+{
+ VI_PRIVATE *vip;
+ size_t p_len, s_len;
+ char *p, *p_p, *s_p;
+
+ /*
+ * The vi paragraph command searches for either a paragraph or
+ * section option macro.
+ */
+ p_len = (p_p = O_STR(sp, O_PARAGRAPHS)) == NULL ? 0 : strlen(p_p);
+ s_len = (s_p = O_STR(sp, O_SECTIONS)) == NULL ? 0 : strlen(s_p);
+
+ if (p_len == 0 && s_len == 0)
+ return (0);
+
+ MALLOC_RET(sp, p, char *, p_len + s_len + 1);
+
+ vip = VIP(sp);
+ if (vip->paragraph != NULL)
+ FREE(vip->paragraph, vip->paragraph_len);
+
+ if (p_p != NULL)
+ memmove(p, p_p, p_len + 1);
+ if (s_p != NULL)
+ memmove(p + p_len, s_p, s_len + 1);
+ vip->paragraph = p;
+ vip->paragraph_len = p_len + s_len + 1;
+ return (0);
+}
diff --git a/usr.bin/vi/nvi/v_put.c b/usr.bin/vi/nvi/v_put.c
new file mode 100644
index 000000000000..60227ea4ccfd
--- /dev/null
+++ b/usr.bin/vi/nvi/v_put.c
@@ -0,0 +1,130 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_put.c 8.6 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+static void inc_buf __P((SCR *, VICMDARG *));
+
+/*
+ * v_Put -- [buffer]P
+ * Insert the contents of the buffer before the cursor.
+ */
+int
+v_Put(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ if (F_ISSET(vp, VC_ISDOT))
+ inc_buf(sp, vp);
+ return (put(sp, ep,
+ NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, rp, 0));
+}
+
+/*
+ * v_put -- [buffer]p
+ * Insert the contents of the buffer after the cursor.
+ */
+int
+v_put(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ if (F_ISSET(vp, VC_ISDOT))
+ inc_buf(sp, vp);
+
+ return (put(sp, ep,
+ NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, rp, 1));
+}
+
+/*
+ * Historical whackadoo. The dot command `puts' the numbered buffer
+ * after the last one put. For example, `"4p.' would put buffer #4
+ * and buffer #5. If the user continued to enter '.', the #9 buffer
+ * would be repeatedly output. This was not documented, and is a bit
+ * tricky to reconstruct. Historical versions of vi also dropped the
+ * contents of the default buffer after each put, so after `"4p' the
+ * default buffer would be empty. This makes no sense to me, so we
+ * don't bother. Don't assume sequential order of numeric characters.
+ *
+ * And, if that weren't exciting enough, failed commands don't normally
+ * set the dot command. Well, boys and girls, an exception is that
+ * the buffer increment gets done regardless of the success of the put.
+ */
+static void
+inc_buf(sp, vp)
+ SCR *sp;
+ VICMDARG *vp;
+{
+ CHAR_T v;
+
+ switch (vp->buffer) {
+ case '1':
+ v = '2';
+ break;
+ case '2':
+ v = '3';
+ break;
+ case '3':
+ v = '4';
+ break;
+ case '4':
+ v = '5';
+ break;
+ case '5':
+ v = '6';
+ break;
+ case '6':
+ v = '7';
+ break;
+ case '7':
+ v = '8';
+ break;
+ case '8':
+ v = '9';
+ break;
+ default:
+ return;
+ }
+ VIP(sp)->sdot.buffer = vp->buffer = v;
+}
diff --git a/usr.bin/vi/nvi/v_redraw.c b/usr.bin/vi/nvi/v_redraw.c
new file mode 100644
index 000000000000..fca5ba33b713
--- /dev/null
+++ b/usr.bin/vi/nvi/v_redraw.c
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_redraw.c 8.2 (Berkeley) 8/25/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_redraw --
+ * Redraw the screen.
+ */
+int
+v_redraw(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ F_SET(sp, S_REFRESH);
+ return (0);
+}
diff --git a/usr.bin/vi/nvi/v_replace.c b/usr.bin/vi/nvi/v_replace.c
new file mode 100644
index 000000000000..f9378e7ac4ed
--- /dev/null
+++ b/usr.bin/vi/nvi/v_replace.c
@@ -0,0 +1,176 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_replace.c 8.12 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_replace -- [count]rc
+ * The r command in historic vi was almost beautiful in its badness.
+ * For example, "r<erase>" and "r<word erase>" beeped the terminal
+ * and deleted a single character. "Nr<carriage return>", where N
+ * was greater than 1, inserted a single carriage return. This may
+ * not be right, but at least it's not insane.
+ */
+int
+v_replace(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ CH ikey;
+ TEXT *tp;
+ recno_t lno;
+ size_t blen, len;
+ u_long cnt;
+ int rval;
+ char *bp, *p;
+
+ /*
+ * If the line doesn't exist, or it's empty, replacement isn't
+ * allowed. It's not hard to implement, but:
+ *
+ * 1: It's historic practice.
+ * 2: For consistency, this change would require that the more
+ * general case, "Nr", when the user is < N characters from
+ * the end of the line, also work.
+ * 3: Replacing a newline has somewhat odd semantics.
+ */
+ if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, fm->lno);
+ return (1);
+ }
+ goto nochar;
+ }
+ if (len == 0) {
+nochar: msgq(sp, M_BERR, "No characters to replace");
+ return (1);
+ }
+
+ /*
+ * Figure out how many characters to be replace; for no particular
+ * reason other than that the semantics of replacing the newline
+ * are confusing, only permit the replacement of the characters in
+ * the current line. I suppose we could simply append the replacement
+ * characters to the line, but I see no compelling reason to do so.
+ */
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ rp->cno = fm->cno + cnt - 1;
+ if (rp->cno > len - 1) {
+ v_eol(sp, ep, fm);
+ return (1);
+ }
+
+ /* Get the character, literal escapes, escape terminates. */
+ if (F_ISSET(vp, VC_ISDOT)) {
+ ikey.ch = VIP(sp)->rlast;
+ ikey.value = term_key_val(sp, ikey.ch);
+ } else {
+ if (term_key(sp, &ikey, 0) != INP_OK)
+ return (1);
+ switch (ikey.value) {
+ case K_ESCAPE:
+ *rp = *fm;
+ return (0);
+ case K_VLNEXT:
+ if (term_key(sp, &ikey, 0) != INP_OK)
+ return (1);
+ break;
+ }
+ VIP(sp)->rlast = ikey.ch;
+ }
+
+ /* Copy the line. */
+ GET_SPACE_RET(sp, bp, blen, len);
+ memmove(bp, p, len);
+ p = bp;
+
+ if (ikey.value == K_CR || ikey.value == K_NL) {
+ /* Set return line. */
+ rp->lno = fm->lno + cnt;
+
+ /* The first part of the current line. */
+ if (file_sline(sp, ep, fm->lno, p, fm->cno))
+ goto err_ret;
+
+ /*
+ * The rest of the current line. And, of course, now it gets
+ * tricky. Any white space after the replaced character is
+ * stripped, and autoindent is applied. Put the cursor on the
+ * last indent character as did historic vi.
+ */
+ for (p += fm->cno + cnt, len -= fm->cno + cnt;
+ len && isblank(*p); --len, ++p);
+
+ if ((tp = text_init(sp, p, len, len)) == NULL)
+ goto err_ret;
+ if (txt_auto(sp, ep, fm->lno, NULL, 0, tp))
+ goto err_ret;
+ rp->cno = tp->ai ? tp->ai - 1 : 0;
+ if (file_aline(sp, ep, 1, fm->lno, tp->lb, tp->len))
+ goto err_ret;
+ text_free(tp);
+
+ rval = 0;
+
+ /* All of the middle lines. */
+ while (--cnt)
+ if (file_aline(sp, ep, 1, fm->lno, "", 0)) {
+err_ret: rval = 1;
+ break;
+ }
+ } else {
+ memset(bp + fm->cno, ikey.ch, cnt);
+ rval = file_sline(sp, ep, fm->lno, bp, len);
+
+ rp->lno = fm->lno;
+ rp->cno = fm->cno + cnt - 1;
+ }
+ FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
diff --git a/usr.bin/vi/nvi/v_right.c b/usr.bin/vi/nvi/v_right.c
new file mode 100644
index 000000000000..54b7be92d005
--- /dev/null
+++ b/usr.bin/vi/nvi/v_right.c
@@ -0,0 +1,144 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_right.c 8.3 (Berkeley) 12/16/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_right -- [count]' ', [count]l
+ * Move right by columns.
+ *
+ * Special case: the 'c' and 'd' commands can move past the end of line.
+ */
+int
+v_right(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t lno;
+ u_long cnt;
+ size_t len;
+
+ if (file_gline(sp, ep, fm->lno, &len) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ v_eol(sp, ep, NULL);
+ else
+ GETLINE_ERR(sp, fm->lno);
+ return (1);
+ }
+
+ rp->lno = fm->lno;
+ if (len == 0 || fm->cno == len - 1) {
+ if (F_ISSET(vp, VC_C | VC_D | VC_Y)) {
+ rp->cno = len;
+ return (0);
+ }
+ v_eol(sp, ep, NULL);
+ return (1);
+ }
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ rp->cno = fm->cno + cnt;
+ if (rp->cno > len - 1)
+ if (F_ISSET(vp, VC_C | VC_D | VC_Y))
+ rp->cno = len;
+ else
+ rp->cno = len - 1;
+ return (0);
+}
+
+/*
+ * v_dollar -- [count]$
+ * Move to the last column.
+ */
+int
+v_dollar(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t lno;
+ size_t len;
+ u_long cnt;
+
+ /*
+ * A count moves down count - 1 rows, so, "3$" is the same as "2j$".
+ *
+ * !!!
+ * Historically, if the $ is a motion, and deleting from at or before
+ * the first non-blank of the line, it's a line motion, and the line
+ * motion flag is set.
+ */
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ if (cnt != 1) {
+ --vp->count;
+ if (v_down(sp, ep, vp, fm, tm, rp))
+ return (1);
+ rp->cno = 0;
+ if (nonblank(sp, ep, rp->lno, &rp->cno))
+ return (1);
+ if (fm->cno <= rp->cno && F_ISSET(vp, VC_C | VC_D | VC_Y))
+ F_SET(vp, VC_LMODE);
+ } else
+ rp->lno = fm->lno;
+
+ if (file_gline(sp, ep, rp->lno, &len) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ v_eol(sp, ep, NULL);
+ else
+ GETLINE_ERR(sp, rp->lno);
+ return (1);
+ }
+
+ if (len == 0) {
+ v_eol(sp, ep, NULL);
+ return (1);
+ }
+
+ /* If it's a motion component, move one past the end of the line. */
+ rp->cno = F_ISSET(vp, VC_C | VC_D | VC_Y) ? len : len - 1;
+ return (0);
+}
diff --git a/usr.bin/vi/nvi/v_screen.c b/usr.bin/vi/nvi/v_screen.c
new file mode 100644
index 000000000000..986bfa4f29a2
--- /dev/null
+++ b/usr.bin/vi/nvi/v_screen.c
@@ -0,0 +1,79 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)v_screen.c 8.8 (Berkeley) 11/28/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_screen --
+ * Switch screens.
+ */
+int
+v_screen(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ /*
+ * Try for the next lower screen, or, go back to the first
+ * screen on the stack.
+ */
+ if (sp->q.cqe_next != (void *)&sp->gp->dq)
+ sp->nextdisp = sp->q.cqe_next;
+ else if (sp->gp->dq.cqh_first == sp) {
+ msgq(sp, M_ERR, "No other screen to switch to.");
+ return (1);
+ } else
+ sp->nextdisp = sp->gp->dq.cqh_first;
+
+ /*
+ * Display the old screen's status line so the user can
+ * find the screen they want.
+ */
+ (void)status(sp, ep, fm->lno, 0);
+
+ /* Save the old screen's cursor information. */
+ sp->frp->lno = sp->lno;
+ sp->frp->cno = sp->cno;
+ F_SET(sp->frp, FR_CURSORSET);
+
+ F_SET(sp, S_SSWITCH);
+ return (0);
+}
diff --git a/usr.bin/vi/nvi/v_scroll.c b/usr.bin/vi/nvi/v_scroll.c
new file mode 100644
index 000000000000..f69ea0fb4e87
--- /dev/null
+++ b/usr.bin/vi/nvi/v_scroll.c
@@ -0,0 +1,354 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_scroll.c 8.8 (Berkeley) 12/16/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+/*
+ * The historic vi had a problem in that all movements were by physical
+ * lines, not by logical, or screen lines. Arguments can be made that this
+ * is the right thing to do. For example, single line movements, such as
+ * 'j' or 'k', should probably work on physical lines. Commands like "dj",
+ * or "j.", where '.' is a change command, make more sense for physical lines
+ * than they do for logical lines.
+ *
+ * These arguments, however, don't apply to scrolling commands like ^D and
+ * ^F -- if the window is fairly small, using physical lines can result in
+ * a half-page scroll repainting the entire screen, which is not what the
+ * user wanted. Second, if the line is larger than the screen, using physical
+ * lines can make it impossible to display parts of the line -- there aren't
+ * any commands that don't display the beginning of the line in historic vi,
+ * and if both the beginning and end of the line can't be on the screen at
+ * the same time, you lose. This is even worse in the case of the H, L, and
+ * M commands -- for large lines, they may all refer to the same line and
+ * will result in no movement at all.
+ *
+ * This implementation does the scrolling (^B, ^D, ^F, ^U, ^Y, ^E), and the
+ * cursor positioning commands (H, L, M) commands using logical lines, not
+ * physical.
+ *
+ * Another issue is that page and half-page scrolling commands historically
+ * moved to the first non-blank character in the new line. If the line is
+ * approximately the same size as the screen, this loses because the cursor
+ * before and after a ^D, may refer to the same location on the screen. In
+ * this implementation, scrolling commands set the cursor to the first non-
+ * blank character if the line changes because of the scroll. Otherwise,
+ * the cursor is left alone.
+ */
+
+/*
+ * v_lgoto -- [count]G
+ * Go to first non-blank character of the line count, the last line
+ * of the file by default.
+ */
+int
+v_lgoto(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t last;
+
+ if (file_lline(sp, ep, &last))
+ return (1);
+ if (F_ISSET(vp, VC_C1SET)) {
+ if (last < vp->count) {
+ v_eof(sp, ep, fm);
+ return (1);
+ }
+ rp->lno = vp->count;
+ } else
+ rp->lno = last ? last : 1;
+ return (0);
+}
+
+/*
+ * v_home -- [count]H
+ * Move to the first non-blank character of the logical line
+ * count from the top of the screen, 1 by default.
+ */
+int
+v_home(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ return (sp->s_position(sp, ep, rp,
+ F_ISSET(vp, VC_C1SET) ? vp->count : 0, P_TOP));
+}
+
+/*
+ * v_middle -- M
+ * Move to the first non-blank character of the logical line
+ * in the middle of the screen.
+ */
+int
+v_middle(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ /*
+ * Yielding to none in our quest for compatibility with every
+ * historical blemish of vi, no matter how strange it might be,
+ * we permit the user to enter a count and then ignore it.
+ */
+ return (sp->s_position(sp, ep, rp, 0, P_MIDDLE));
+}
+
+/*
+ * v_bottom -- [count]L
+ * Move to the first non-blank character of the logical line
+ * count from the bottom of the screen, 1 by default.
+ */
+int
+v_bottom(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ return (sp->s_position(sp, ep,
+ rp, F_ISSET(vp, VC_C1SET) ? vp->count : 0, P_BOTTOM));
+}
+
+/*
+ * v_up -- [count]^P, [count]k, [count]-
+ * Move up by lines.
+ */
+int
+v_up(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t lno;
+
+ lno = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+
+ if (fm->lno <= lno) {
+ v_sof(sp, fm);
+ return (1);
+ }
+ rp->lno = fm->lno - lno;
+ return (0);
+}
+
+/*
+ * v_cr -- [count]^M
+ * In a script window, send the line to the shell.
+ * In a regular window, move down by lines.
+ */
+int
+v_cr(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ /*
+ * If it's a script window, exec the line,
+ * otherwise it's the same as v_down().
+ */
+ return (F_ISSET(sp, S_SCRIPT) ?
+ sscr_exec(sp, ep, fm->lno) : v_down(sp, ep, vp, fm, tm, rp));
+}
+
+/*
+ * v_down -- [count]^J, [count]^N, [count]j, [count]^M, [count]+
+ * Move down by lines.
+ */
+int
+v_down(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t lno;
+
+ lno = fm->lno + (F_ISSET(vp, VC_C1SET) ? vp->count : 1);
+
+ if (file_gline(sp, ep, lno, NULL) == NULL) {
+ v_eof(sp, ep, fm);
+ return (1);
+ }
+ rp->lno = lno;
+ return (0);
+}
+
+/*
+ * v_hpageup -- [count]^U
+ * Page up half screens.
+ */
+int
+v_hpageup(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ /*
+ * Half screens always succeed unless already at SOF. Half screens
+ * set the scroll value, even if the command ultimately failed, in
+ * historic vi. It's probably a don't care.
+ */
+ if (F_ISSET(vp, VC_C1SET))
+ O_VAL(sp, O_SCROLL) = vp->count;
+ else
+ vp->count = O_VAL(sp, O_SCROLL);
+
+ return (sp->s_down(sp, ep, rp, (recno_t)O_VAL(sp, O_SCROLL), 1));
+}
+
+/*
+ * v_hpagedown -- [count]^D
+ * Page down half screens.
+ */
+int
+v_hpagedown(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ /*
+ * Half screens always succeed unless already at EOF. Half screens
+ * set the scroll value, even if the command ultimately failed, in
+ * historic vi. It's probably a don't care.
+ */
+ if (F_ISSET(vp, VC_C1SET))
+ O_VAL(sp, O_SCROLL) = vp->count;
+ else
+ vp->count = O_VAL(sp, O_SCROLL);
+
+ return (sp->s_up(sp, ep, rp, (recno_t)O_VAL(sp, O_SCROLL), 1));
+}
+
+/*
+ * v_pageup -- [count]^B
+ * Page up full screens.
+ *
+ * !!!
+ * Historic vi did not move to the SOF if the screen couldn't move, i.e.
+ * if SOF was already displayed on the screen. This implementation does
+ * move to SOF in that case, making ^B more like the the historic ^U.
+ */
+int
+v_pageup(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t count;
+
+ /* Calculation from POSIX 1003.2/D8. */
+ count = (F_ISSET(vp, VC_C1SET) ? vp->count : 1) * (sp->t_rows - 1);
+
+ return (sp->s_down(sp, ep, rp, count, 1));
+}
+
+/*
+ * v_pagedown -- [count]^F
+ * Page down full screens.
+ * !!!
+ * Historic vi did not move to the EOF if the screen couldn't move, i.e.
+ * if EOF was already displayed on the screen. This implementation does
+ * move to EOF in that case, making ^F more like the the historic ^D.
+ */
+int
+v_pagedown(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t count;
+
+ /* Calculation from POSIX 1003.2/D8. */
+ count = (F_ISSET(vp, VC_C1SET) ? vp->count : 1) * (sp->t_rows - 1);
+
+ return (sp->s_up(sp, ep, rp, count, 1));
+}
+
+/*
+ * v_lineup -- [count]^Y
+ * Page up by lines.
+ */
+int
+v_lineup(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ /*
+ * The cursor moves down, staying with its original line, unless it
+ * reaches the bottom of the screen.
+ */
+ return (sp->s_down(sp, ep,
+ rp, F_ISSET(vp, VC_C1SET) ? vp->count : 1, 0));
+}
+
+/*
+ * v_linedown -- [count]^E
+ * Page down by lines.
+ */
+int
+v_linedown(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ /*
+ * The cursor moves up, staying with its original line, unless it
+ * reaches the top of the screen.
+ */
+ return (sp->s_up(sp, ep,
+ rp, F_ISSET(vp, VC_C1SET) ? vp->count : 1, 0));
+}
diff --git a/usr.bin/vi/nvi/v_search.c b/usr.bin/vi/nvi/v_search.c
new file mode 100644
index 000000000000..25dcaf5e2e10
--- /dev/null
+++ b/usr.bin/vi/nvi/v_search.c
@@ -0,0 +1,364 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_search.c 8.16 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+static int bcorrect __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, u_int));
+static int fcorrect __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, u_int));
+static int getptrn __P((SCR *, EXF *, int, char **));
+
+/*
+ * v_searchn -- n
+ * Repeat last search.
+ */
+int
+v_searchn(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ int flags;
+
+ flags = SEARCH_MSG;
+ if (F_ISSET(vp, VC_C | VC_D | VC_Y))
+ flags |= SEARCH_EOL;
+ switch (sp->searchdir) {
+ case BACKWARD:
+ if (b_search(sp, ep, fm, rp, NULL, NULL, &flags))
+ return (1);
+ if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) &&
+ bcorrect(sp, ep, vp, fm, rp, flags))
+ return (1);
+ break;
+ case FORWARD:
+ if (f_search(sp, ep, fm, rp, NULL, NULL, &flags))
+ return (1);
+ if (F_ISSET(vp, VC_C | VC_D | VC_Y| VC_SH) &&
+ fcorrect(sp, ep, vp, fm, rp, flags))
+ return (1);
+ break;
+ case NOTSET:
+ msgq(sp, M_ERR, "No previous search pattern.");
+ return (1);
+ default:
+ abort();
+ }
+ return (0);
+}
+
+/*
+ * v_searchN -- N
+ * Reverse last search.
+ */
+int
+v_searchN(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ int flags;
+
+ flags = SEARCH_MSG;
+ if (F_ISSET(vp, VC_C | VC_D | VC_Y))
+ flags |= SEARCH_EOL;
+ switch (sp->searchdir) {
+ case BACKWARD:
+ if (f_search(sp, ep, fm, rp, NULL, NULL, &flags))
+ return (1);
+ if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) &&
+ fcorrect(sp, ep, vp, fm, rp, flags))
+ return (1);
+ break;
+ case FORWARD:
+ if (b_search(sp, ep, fm, rp, NULL, NULL, &flags))
+ return (1);
+ if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) &&
+ bcorrect(sp, ep, vp, fm, rp, flags))
+ return (1);
+ break;
+ case NOTSET:
+ msgq(sp, M_ERR, "No previous search pattern.");
+ return (1);
+ default:
+ abort();
+ }
+ return (0);
+}
+
+/*
+ * v_searchw -- [count]^A
+ * Search for the word under the cursor.
+ */
+int
+v_searchw(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ size_t blen, len;
+ u_int flags;
+ int rval;
+ char *bp;
+
+ len = vp->kbuflen + sizeof(RE_WSTART) + sizeof(RE_WSTOP);
+ GET_SPACE_RET(sp, bp, blen, len);
+ (void)snprintf(bp, blen, "%s%s%s", RE_WSTART, vp->keyword, RE_WSTOP);
+
+ flags = SEARCH_MSG;
+ rval = f_search(sp, ep, fm, rp, bp, NULL, &flags);
+
+ FREE_SPACE(sp, bp, blen);
+ if (rval)
+ return (1);
+ if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) &&
+ fcorrect(sp, ep, vp, fm, rp, flags))
+ return (1);
+ return (0);
+}
+
+/*
+ * v_searchb -- [count]?RE[? offset]
+ * Search backward.
+ */
+int
+v_searchb(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ int flags;
+ char *ptrn;
+
+ if (F_ISSET(vp, VC_ISDOT))
+ ptrn = NULL;
+ else {
+ if (getptrn(sp, ep, '?', &ptrn))
+ return (1);
+ if (ptrn == NULL)
+ return (0);
+ }
+
+ flags = SEARCH_MSG | SEARCH_PARSE | SEARCH_SET | SEARCH_TERM;
+ if (F_ISSET(vp, VC_C | VC_D | VC_Y))
+ flags |= SEARCH_EOL;
+ if (b_search(sp, ep, fm, rp, ptrn, NULL, &flags))
+ return (1);
+ if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) &&
+ bcorrect(sp, ep, vp, fm, rp, flags))
+ return (1);
+ return (0);
+}
+
+/*
+ * v_searchf -- [count]/RE[/ offset]
+ * Search forward.
+ */
+int
+v_searchf(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ int flags;
+ char *ptrn;
+
+ if (F_ISSET(vp, VC_ISDOT))
+ ptrn = NULL;
+ else {
+ if (getptrn(sp, ep, '/', &ptrn))
+ return (1);
+ if (ptrn == NULL)
+ return (0);
+ }
+
+ flags = SEARCH_MSG | SEARCH_PARSE | SEARCH_SET | SEARCH_TERM;
+ if (F_ISSET(vp, VC_C | VC_D | VC_Y))
+ flags |= SEARCH_EOL;
+ if (f_search(sp, ep, fm, rp, ptrn, NULL, &flags))
+ return (1);
+ if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) &&
+ fcorrect(sp, ep, vp, fm, rp, flags))
+ return (1);
+ return (0);
+}
+
+/*
+ * getptrn --
+ * Get the search pattern.
+ */
+static int
+getptrn(sp, ep, prompt, storep)
+ SCR *sp;
+ EXF *ep;
+ int prompt;
+ char **storep;
+{
+ TEXT *tp;
+
+ if (sp->s_get(sp, ep, &sp->tiq, prompt,
+ TXT_BS | TXT_CR | TXT_ESCAPE | TXT_PROMPT) != INP_OK)
+ return (1);
+
+ /* Len is 0 if backspaced over the prompt, 1 if only CR entered. */
+ tp = sp->tiq.cqh_first;
+ if (tp->len == 0)
+ *storep = NULL;
+ else
+ *storep = tp->lb;
+ return (0);
+}
+
+/*
+ * !!!
+ * Historically, commands didn't affect the line searched to if the motion
+ * command was a search and the pattern match was the start or end of the
+ * line. There were some special cases, however, concerning search to the
+ * start of end of a line.
+ *
+ * Vi was not, however, consistent, and it was fairly easy to confuse it.
+ * For example, given the two lines:
+ *
+ * abcdefghi
+ * ABCDEFGHI
+ *
+ * placing the cursor on the 'A' and doing y?$ would so confuse it that 'h'
+ * 'k' and put would no longer work correctly. In any case, we try to do
+ * the right thing, but it's not likely exactly match historic practice.
+ */
+
+/*
+ * bcorrect --
+ * Handle command with a backward search as the motion.
+ */
+static int
+bcorrect(sp, ep, vp, fm, rp, flags)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *rp;
+ u_int flags;
+{
+ size_t len;
+ char *p;
+
+ /*
+ * !!!
+ * Correct backward searches which start at column 0 to be one
+ * past the last column of the previous line.
+ *
+ * Backward searches become line mode operations if they start
+ * at column 0 and end at column 0 of another line.
+ */
+ if (fm->lno > rp->lno && fm->cno == 0) {
+ if ((p = file_gline(sp, ep, --fm->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, rp->lno);
+ return (1);
+ }
+ if (rp->cno == 0)
+ F_SET(vp, VC_LMODE);
+ fm->cno = len;
+ }
+
+ /*
+ * !!!
+ * Commands would become line mode operations if there was a delta
+ * specified to the search pattern.
+ */
+ if (LF_ISSET(SEARCH_DELTA)) {
+ F_SET(vp, VC_LMODE);
+ return (0);
+ }
+ return (0);
+}
+
+/*
+ * fcorrect --
+ * Handle command with a forward search as the motion.
+ */
+static int
+fcorrect(sp, ep, vp, fm, rp, flags)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *rp;
+ u_int flags;
+{
+ size_t len;
+ char *p;
+
+ /*
+ * !!!
+ * Correct forward searches which end at column 0 to be one
+ * past the last column of the previous line.
+ *
+ * Forward searches become line mode operations if they start
+ * at column 0 and end at column 0 of another line.
+ */
+ if (fm->lno < rp->lno && rp->cno == 0) {
+ if ((p = file_gline(sp, ep, --rp->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, rp->lno);
+ return (1);
+ }
+ if (fm->cno == 0)
+ F_SET(vp, VC_LMODE);
+ rp->cno = len;
+ }
+
+ /*
+ * !!!
+ * Commands would become line mode operations if there was a delta
+ * specified to the search pattern.
+ */
+ if (LF_ISSET(SEARCH_DELTA)) {
+ F_SET(vp, VC_LMODE);
+ return (0);
+ }
+
+ return (0);
+}
diff --git a/usr.bin/vi/nvi/v_section.c b/usr.bin/vi/nvi/v_section.c
new file mode 100644
index 000000000000..8d97e2f10b4e
--- /dev/null
+++ b/usr.bin/vi/nvi/v_section.c
@@ -0,0 +1,146 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_section.c 8.4 (Berkeley) 1/22/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * In historic vi, the section commands ignored empty lines, unlike the
+ * paragraph commands, which was probably okay. However, they also moved
+ * to the start of the last line when there where no more sections instead
+ * of the end of the last line like the paragraph commands. I've changed
+ * the latter behaviore to match the paragraphs command.
+ *
+ * In historic vi, a "function" was defined as the first character of the
+ * line being an open brace, which could be followed by anything. This
+ * implementation follows that historic practice.
+ */
+
+/* Macro to do a check on each line. */
+#define CHECK { \
+ if (len == 0) \
+ continue; \
+ if (p[0] == '{') { \
+ if (!--cnt) { \
+ rp->cno = 0; \
+ rp->lno = lno; \
+ return (0); \
+ } \
+ continue; \
+ } \
+ if (p[0] != '.' || len < 3) \
+ continue; \
+ for (lp = list; *lp; lp += 2) \
+ if (lp[0] == p[1] && \
+ (lp[1] == ' ' || lp[1] == p[2]) && !--cnt) { \
+ rp->cno = 0; \
+ rp->lno = lno; \
+ return (0); \
+ } \
+}
+
+/*
+ * v_sectionf -- [count]]]
+ * Move forward count sections/functions.
+ */
+int
+v_sectionf(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ size_t len;
+ recno_t cnt, lno;
+ char *p, *list, *lp;
+
+ /* Get macro list. */
+ if ((list = O_STR(sp, O_SECTIONS)) == NULL)
+ return (1);
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ for (lno = fm->lno; (p = file_gline(sp, ep, ++lno, &len)) != NULL;)
+ CHECK;
+
+ /* EOF is a movement sink. */
+ if (fm->lno != lno - 1) {
+ rp->lno = lno - 1;
+ rp->cno = len ? len - 1 : 0;
+ return (0);
+ }
+ v_eof(sp, ep, NULL);
+ return (1);
+}
+
+/*
+ * v_sectionb -- [count][[
+ * Move backward count sections/functions.
+ */
+int
+v_sectionb(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ size_t len;
+ recno_t cnt, lno;
+ char *p, *list, *lp;
+
+ /* Check for SOF. */
+ if (fm->lno <= 1) {
+ v_sof(sp, NULL);
+ return (1);
+ }
+
+ /* Get macro list. */
+ if ((list = O_STR(sp, O_SECTIONS)) == NULL)
+ return (1);
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ for (lno = fm->lno; (p = file_gline(sp, ep, --lno, &len)) != NULL;)
+ CHECK;
+
+ /* SOF is a movement sink. */
+ rp->lno = 1;
+ rp->cno = 0;
+ return (0);
+}
diff --git a/usr.bin/vi/nvi/v_sentence.c b/usr.bin/vi/nvi/v_sentence.c
new file mode 100644
index 000000000000..684952124f14
--- /dev/null
+++ b/usr.bin/vi/nvi/v_sentence.c
@@ -0,0 +1,328 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_sentence.c 8.7 (Berkeley) 8/26/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * In historic vi, a sentence was delimited by a '.', '?' or '!' character
+ * followed by TWO spaces or a newline. One or more empty lines was also
+ * treated as a separate sentence. The Berkeley documentation for historical
+ * vi states that any number of ')', ']', '"' and '\'' characters can be
+ * between the delimiter character and the spaces or end of line, however,
+ * the historical implementation did not handle additional '"' characters.
+ * We follow the documentation here, not the implementation.
+ *
+ * Once again, historical vi didn't do sentence movements associated with
+ * counts consistently, mostly in the presence of lines containing only
+ * white-space characters.
+ *
+ * This implementation also permits a single tab to delimit sentences, and
+ * treats lines containing only white-space characters as empty lines.
+ * And, tabs are eaten (along with spaces) when skipping to the start of the
+ * text follow a "sentence".
+ */
+
+/*
+ * v_sentencef -- [count])
+ * Move forward count sentences.
+ */
+int
+v_sentencef(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ enum { BLANK, NONE, PERIOD } state;
+ VCS cs;
+ u_long cnt;
+
+ cs.cs_lno = fm->lno;
+ cs.cs_cno = fm->cno;
+ if (cs_init(sp, ep, &cs))
+ return (1);
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+
+ /*
+ * If in white-space, the next start of sentence counts as one.
+ * This may not handle " . " correctly, but it's real unclear
+ * what correctly means in that case.
+ */
+ if (cs.cs_flags == CS_EMP || cs.cs_flags == 0 && isblank(cs.cs_ch)) {
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+ if (--cnt == 0) {
+ if (fm->lno != cs.cs_lno || fm->cno != cs.cs_cno)
+ goto okret;
+ return (1);
+ }
+ }
+ for (state = NONE;;) {
+ if (cs_next(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ break;
+ if (cs.cs_flags == CS_EOL) {
+ if ((state == PERIOD || state == BLANK) && --cnt == 0) {
+ if (cs_next(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == 0 &&
+ isblank(cs.cs_ch) && cs_fblank(sp, ep, &cs))
+ return (1);
+ goto okret;
+ }
+ state = NONE;
+ continue;
+ }
+ if (cs.cs_flags == CS_EMP) { /* An EMP is two sentences. */
+ if (--cnt == 0)
+ goto okret;
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+ if (--cnt == 0)
+ goto okret;
+ state = NONE;
+ continue;
+ }
+ switch (cs.cs_ch) {
+ case '.':
+ case '?':
+ case '!':
+ state = PERIOD;
+ break;
+ case ')':
+ case ']':
+ case '"':
+ case '\'':
+ if (state != PERIOD)
+ state = NONE;
+ break;
+ case '\t':
+ if (state == PERIOD)
+ state = BLANK;
+ /* FALLTHROUGH */
+ case ' ':
+ if (state == PERIOD) {
+ state = BLANK;
+ break;
+ }
+ if (state == BLANK && --cnt == 0) {
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+ goto okret;
+ }
+ /* FALLTHROUGH */
+ default:
+ state = NONE;
+ break;
+ }
+ }
+
+ /* EOF is a movement sink. */
+ if (fm->lno != cs.cs_lno || fm->cno != cs.cs_cno)
+ goto okret;
+ v_eof(sp, ep, NULL);
+ return (1);
+
+okret: rp->lno = cs.cs_lno;
+ rp->cno = cs.cs_cno;
+
+ /*
+ * Historic, uh, features, yeah, that's right, call 'em features.
+ * If the sentence movement is cutting an entire line, the buffer
+ * is in line mode. Reach up into the caller's VICMDARG structure,
+ * and whack the flags.
+ */
+ if (F_ISSET(vp, VC_C | VC_D | VC_Y) &&
+ fm->cno == 0 && (rp->cno == 0 || cs.cs_flags != 0)) {
+ if (rp->cno == 0)
+ --rp->lno;
+ F_SET(vp, VC_LMODE);
+ }
+ return (0);
+}
+
+/*
+ * v_sentenceb -- [count](
+ * Move backward count sentences.
+ */
+int
+v_sentenceb(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ VCS cs;
+ recno_t slno;
+ size_t len, scno;
+ u_long cnt;
+ int last1, last2;
+
+ if (fm->lno == 1 && fm->cno == 0) {
+ v_sof(sp, NULL);
+ return (1);
+ }
+
+ cs.cs_lno = fm->lno;
+ cs.cs_cno = fm->cno;
+ if (cs_init(sp, ep, &cs))
+ return (1);
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+
+ /*
+ * If on an empty line, skip to the next previous
+ * non-white-space character.
+ */
+ if (cs.cs_flags == CS_EMP) {
+ if (cs_bblank(sp, ep, &cs))
+ return (1);
+ for (;;) {
+ if (cs_next(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags != CS_EOL)
+ break;
+ }
+ }
+
+ for (last1 = last2 = 0;;) {
+ if (cs_prev(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_SOF) /* SOF is a movement sink. */
+ break;
+ if (cs.cs_flags == CS_EOL) {
+ last2 = last1;
+ last1 = 1;
+ continue;
+ }
+ if (cs.cs_flags == CS_EMP) {
+ if (--cnt == 0)
+ goto ret;
+ if (cs_bblank(sp, ep, &cs))
+ return (1);
+ last1 = last2 = 0;
+ continue;
+ }
+ switch (cs.cs_ch) {
+ case '.':
+ case '?':
+ case '!':
+ if (!last1 || --cnt != 0) {
+ last2 = last1 = 0;
+ continue;
+ }
+
+ret: slno = cs.cs_lno;
+ scno = cs.cs_cno;
+
+ /*
+ * Move to the start of the sentence, skipping blanks
+ * and special characters.
+ */
+ do {
+ if (cs_next(sp, ep, &cs))
+ return (1);
+ } while (!cs.cs_flags &&
+ (cs.cs_ch == ')' || cs.cs_ch == ']' ||
+ cs.cs_ch == '"' || cs.cs_ch == '\''));
+ if ((cs.cs_flags || isblank(cs.cs_ch)) &&
+ cs_fblank(sp, ep, &cs))
+ return (1);
+
+ /*
+ * If it was ". xyz", with the cursor on the 'x', or
+ * "end. ", with the cursor in the spaces, or the
+ * beginning of a sentence preceded by an empty line,
+ * we can end up where we started. Fix it.
+ */
+ if (fm->lno != cs.cs_lno || fm->cno != cs.cs_cno)
+ goto okret;
+
+ /*
+ * Well, if an empty line preceded possible blanks
+ * and the sentence, it could be a real sentence.
+ */
+ for (;;) {
+ if (cs_prev(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOL)
+ continue;
+ if (cs.cs_flags == 0 && isblank(cs.cs_ch))
+ continue;
+ break;
+ }
+ if (cs.cs_flags == CS_EMP)
+ goto okret;
+
+ /* But it wasn't; try again. */
+ ++cnt;
+ cs.cs_lno = slno;
+ cs.cs_cno = scno;
+ last2 = last1 = 0;
+ break;
+ case '\t':
+ last1 = last2 = 1;
+ break;
+ default:
+ last2 = last1;
+ last1 =
+ cs.cs_flags == CS_EOL || isblank(cs.cs_ch) ||
+ cs.cs_ch == ')' || cs.cs_ch == ']' ||
+ cs.cs_ch == '"' || cs.cs_ch == '\'' ? 1 : 0;
+ }
+ }
+
+okret: rp->lno = cs.cs_lno;
+ rp->cno = cs.cs_cno;
+
+ /*
+ * See comment in v_sentencef(). Ignore errors, they should
+ * never occur, and they'll get caught later.
+ */
+ if (F_ISSET(vp, VC_C | VC_D | VC_Y) && rp->cno == 0 &&
+ file_gline(sp, ep, fm->lno, &len) != NULL && (len == 0 ||
+ fm->cno == len - 1))
+ F_SET(vp, VC_LMODE);
+ return (0);
+}
diff --git a/usr.bin/vi/nvi/v_shift.c b/usr.bin/vi/nvi/v_shift.c
new file mode 100644
index 000000000000..2e984147a1c1
--- /dev/null
+++ b/usr.bin/vi/nvi/v_shift.c
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_shift.c 8.3 (Berkeley) 11/13/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+/*
+ * v_shiftl -- [count]<motion
+ * Shift lines left.
+ */
+int
+v_shiftl(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ EXCMDARG cmd;
+
+ SETCMDARG(cmd, C_SHIFTL, 2, fm->lno, tm->lno, 0, "<");
+ return (sp->s_ex_cmd(sp, ep, &cmd, rp));
+}
+
+/*
+ * v_shiftr -- [count]>motion
+ * Shift lines right.
+ */
+int
+v_shiftr(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ EXCMDARG cmd;
+
+ SETCMDARG(cmd, C_SHIFTR, 2, fm->lno, tm->lno, 0, ">");
+ return (sp->s_ex_cmd(sp, ep, &cmd, rp));
+}
diff --git a/usr.bin/vi/nvi/v_status.c b/usr.bin/vi/nvi/v_status.c
new file mode 100644
index 000000000000..9109c0196a80
--- /dev/null
+++ b/usr.bin/vi/nvi/v_status.c
@@ -0,0 +1,129 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_status.c 8.10 (Berkeley) 11/20/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_status -- ^G
+ * Show the file status.
+ */
+int
+v_status(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+
+ /*
+ * ^G in historic vi reset the cursor column to the first
+ * non-blank character in the line. This doesn't seem of
+ * any usefulness whatsoever, so I don't bother.
+ */
+ return (status(sp, ep, fm->lno, 1));
+}
+
+int
+status(sp, ep, lno, showlast)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ int showlast;
+{
+ recno_t last;
+ char *mo, *nc, *nf, *ro, *pid;
+#ifdef DEBUG
+ char pbuf[50];
+
+ (void)snprintf(pbuf, sizeof(pbuf), " (pid %u)", getpid());
+ pid = pbuf;
+#else
+ pid = "";
+#endif
+ /*
+ * See nvi/exf.c:file_init() for a description of how and
+ * when the read-only bit is set. Possible displays are:
+ *
+ * new file
+ * new file, readonly
+ * [un]modified
+ * [un]modified, readonly
+ * name changed, [un]modified
+ * name changed, [un]modified, readonly
+ *
+ * !!!
+ * The historic display for "name changed" was "[Not edited]".
+ */
+ if (F_ISSET(sp->frp, FR_NEWFILE)) {
+ F_CLR(sp->frp, FR_NEWFILE);
+ nf = "new file";
+ mo = nc = "";
+ } else {
+ nf = "";
+ if (sp->frp->cname != NULL) {
+ nc = "name changed";
+ mo = F_ISSET(ep, F_MODIFIED) ?
+ ", modified" : ", unmodified";
+ } else {
+ nc = "";
+ mo = F_ISSET(ep, F_MODIFIED) ?
+ "modified" : "unmodified";
+ }
+ }
+ ro = F_ISSET(sp->frp, FR_RDONLY) ? ", readonly" : "";
+ if (showlast) {
+ if (file_lline(sp, ep, &last))
+ return (1);
+ if (last >= 1)
+ msgq(sp, M_INFO,
+ "%s: %s%s%s%s: line %lu of %lu [%ld%%]%s",
+ FILENAME(sp->frp), nf, nc, mo, ro, lno,
+ last, (lno * 100) / last, pid);
+ else
+ msgq(sp, M_INFO, "%s: %s%s%s%s: empty file%s",
+ FILENAME(sp->frp), nf, nc, mo, ro, pid);
+ } else
+ msgq(sp, M_INFO, "%s: %s%s%s%s: line %lu%s",
+ FILENAME(sp->frp), nf, nc, mo, ro, lno, pid);
+ return (0);
+}
diff --git a/usr.bin/vi/nvi/v_stop.c b/usr.bin/vi/nvi/v_stop.c
new file mode 100644
index 000000000000..2f64bfedf195
--- /dev/null
+++ b/usr.bin/vi/nvi/v_stop.c
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_stop.c 8.5 (Berkeley) 10/28/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <string.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+int
+v_stop(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ /* If autowrite is set, write out the file. */
+ if (F_ISSET(ep, F_MODIFIED) && O_ISSET(sp, O_AUTOWRITE)) {
+ if (file_write(sp, ep, NULL, NULL, NULL, FS_ALL))
+ return (1);
+ if (sp->s_refresh(sp, ep))
+ return (1);
+ }
+ return (sp->s_suspend(sp));
+}
diff --git a/usr.bin/vi/nvi/v_switch.c b/usr.bin/vi/nvi/v_switch.c
new file mode 100644
index 000000000000..d0d000facb2b
--- /dev/null
+++ b/usr.bin/vi/nvi/v_switch.c
@@ -0,0 +1,86 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_switch.c 8.5 (Berkeley) 11/23/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "vi.h"
+#include "vcmd.h"
+#include "excmd.h"
+
+/*
+ * v_switch -- ^^
+ * Switch to the previous file.
+ */
+int
+v_switch(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ EXCMDARG cmd;
+ char *name;
+
+ /*
+ * Try the alternate file name, then the previous file
+ * name. Use the real name, not the user's current name.
+ */
+ if (sp->alt_name != NULL)
+ name = sp->alt_name;
+ else if (sp->p_frp != NULL)
+ name = sp->p_frp->name;
+ else {
+ msgq(sp, M_ERR, "No previous file to edit.");
+ return (1);
+ }
+
+ /* If autowrite is set, write out the file. */
+ if (F_ISSET(ep, F_MODIFIED))
+ if (O_ISSET(sp, O_AUTOWRITE)) {
+ if (file_write(sp, ep, NULL, NULL, NULL, FS_ALL))
+ return (1);
+ } else {
+ msgq(sp, M_ERR,
+ "Modified since last write; write or use :edit! to override.");
+ return (1);
+ }
+
+ SETCMDARG(cmd, C_EDIT, 0, OOBLNO, OOBLNO, 0, name);
+ return (sp->s_ex_cmd(sp, ep, &cmd, rp));
+}
diff --git a/usr.bin/vi/nvi/v_tag.c b/usr.bin/vi/nvi/v_tag.c
new file mode 100644
index 000000000000..530e35279420
--- /dev/null
+++ b/usr.bin/vi/nvi/v_tag.c
@@ -0,0 +1,79 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_tag.c 8.2 (Berkeley) 12/3/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+/*
+ * v_tagpush -- ^[
+ * Do a tag search on a the cursor keyword.
+ */
+int
+v_tagpush(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ EXCMDARG cmd;
+
+ SETCMDARG(cmd, C_TAG, 0, OOBLNO, 0, 0, vp->keyword);
+ return (sp->s_ex_cmd(sp, ep, &cmd, rp));
+}
+
+/*
+ * v_tagpop -- ^T
+ * Pop the tags stack.
+ */
+/* ARGSUSED */
+int
+v_tagpop(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ EXCMDARG cmd;
+
+ SETCMDARG(cmd, C_TAGPOP, 0, OOBLNO, 0, 0, NULL);
+ return (sp->s_ex_cmd(sp, ep, &cmd, rp));
+}
diff --git a/usr.bin/vi/nvi/v_text.c b/usr.bin/vi/nvi/v_text.c
new file mode 100644
index 000000000000..083b5b248e22
--- /dev/null
+++ b/usr.bin/vi/nvi/v_text.c
@@ -0,0 +1,827 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_text.c 8.23 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * !!!
+ * Repeated input in the historic vi is mostly wrong and this isn't very
+ * backward compatible. For example, if the user entered "3Aab\ncd" in
+ * the historic vi, the "ab" was repeated 3 times, and the "\ncd" was then
+ * appended to the result. There was also a hack which I don't remember
+ * right now, where "3o" would open 3 lines and then let the user fill them
+ * in, to make screen movements on 300 baud modems more tolerable. I don't
+ * think it's going to be missed.
+ */
+
+#define SET_TXT_STD(sp, f) { \
+ LF_INIT((f) | TXT_BEAUTIFY | TXT_CNTRLT | TXT_ESCAPE | \
+ TXT_MAPINPUT | TXT_RECORD | TXT_RESOLVE); \
+ if (O_ISSET(sp, O_ALTWERASE)) \
+ LF_SET(TXT_ALTWERASE); \
+ if (O_ISSET(sp, O_AUTOINDENT)) \
+ LF_SET(TXT_AUTOINDENT); \
+ if (O_ISSET(sp, O_SHOWMATCH)) \
+ LF_SET(TXT_SHOWMATCH); \
+ if (O_ISSET(sp, O_WRAPMARGIN)) \
+ LF_SET(TXT_WRAPMARGIN); \
+ if (F_ISSET(sp, S_SCRIPT)) \
+ LF_SET(TXT_CR); \
+ if (O_ISSET(sp, O_TTYWERASE)) \
+ LF_SET(TXT_TTYWERASE); \
+}
+
+/*
+ * !!!
+ * There's a problem with the way that we do logging for change commands with
+ * implied motions (e.g. A, I, O, cc, etc.). Since the main vi loop logs the
+ * starting cursor position before the change command "moves" the cursor, the
+ * cursor position to which we return on undo will be where the user entered
+ * the change command, not the start of the change. Several of the following
+ * routines re-log the cursor to make this work correctly. Historic vi tried
+ * to do the same thing, and mostly got it right. (The only spectacular way
+ * it fails is if the user entered 'o' from anywhere but the last character of
+ * the line, the undo returned the cursor to the start of the line. If the
+ * user was on the last character of the line, the cursor returned to that
+ * position.)
+ */
+
+static int v_CS __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, MARK *, u_int));
+
+/*
+ * v_iA -- [count]A
+ * Append text to the end of the line.
+ */
+int
+v_iA(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t lno;
+ u_long cnt;
+ size_t len;
+ u_int flags;
+ int first;
+ char *p;
+
+ SET_TXT_STD(sp, TXT_APPENDEOL);
+ if (F_ISSET(vp, VC_ISDOT))
+ LF_SET(TXT_REPLAY);
+ for (first = 1, lno = fm->lno,
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ /* Move the cursor to the end of the line + 1. */
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+ lno = 1;
+ len = 0;
+ } else {
+ /* Correct logging for implied cursor motion. */
+ sp->cno = len == 0 ? 0 : len - 1;
+ if (first == 1) {
+ log_cursor(sp, ep);
+ first = 0;
+ }
+ /* Start the change after the line. */
+ sp->cno = len;
+ }
+
+ if (v_ntext(sp, ep,
+ &sp->tiq, NULL, p, len, rp, 0, OOBLNO, flags))
+ return (1);
+
+ SET_TXT_STD(sp, TXT_APPENDEOL | TXT_REPLAY);
+ sp->lno = lno = rp->lno;
+ sp->cno = rp->cno;
+ }
+ return (0);
+}
+
+/*
+ * v_ia -- [count]a
+ * Append text to the cursor position.
+ */
+int
+v_ia(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t lno;
+ u_long cnt;
+ u_int flags;
+ size_t len;
+ char *p;
+
+ SET_TXT_STD(sp, 0);
+ if (F_ISSET(vp, VC_ISDOT))
+ LF_SET(TXT_REPLAY);
+ for (lno = fm->lno,
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ /*
+ * Move the cursor one column to the right and
+ * repaint the screen.
+ */
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+ lno = 1;
+ len = 0;
+ LF_SET(TXT_APPENDEOL);
+ } else if (len) {
+ if (len == sp->cno + 1) {
+ sp->cno = len;
+ LF_SET(TXT_APPENDEOL);
+ } else
+ ++sp->cno;
+ } else
+ LF_SET(TXT_APPENDEOL);
+
+ if (v_ntext(sp, ep,
+ &sp->tiq, NULL, p, len, rp, 0, OOBLNO, flags))
+ return (1);
+
+ SET_TXT_STD(sp, TXT_REPLAY);
+ sp->lno = lno = rp->lno;
+ sp->cno = rp->cno;
+ }
+ return (0);
+}
+
+/*
+ * v_iI -- [count]I
+ * Insert text at the first non-blank character in the line.
+ */
+int
+v_iI(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t lno;
+ u_long cnt;
+ size_t len;
+ u_int flags;
+ int first;
+ char *p;
+
+ SET_TXT_STD(sp, 0);
+ if (F_ISSET(vp, VC_ISDOT))
+ LF_SET(TXT_REPLAY);
+ for (first = 1, lno = fm->lno,
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ /*
+ * Move the cursor to the start of the line and repaint
+ * the screen.
+ */
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+ lno = 1;
+ len = 0;
+ } else {
+ sp->cno = 0;
+ if (nonblank(sp, ep, lno, &sp->cno))
+ return (1);
+ /* Correct logging for implied cursor motion. */
+ if (first == 1) {
+ log_cursor(sp, ep);
+ first = 0;
+ }
+ }
+ if (len == 0)
+ LF_SET(TXT_APPENDEOL);
+
+ if (v_ntext(sp, ep,
+ &sp->tiq, NULL, p, len, rp, 0, OOBLNO, flags))
+ return (1);
+
+ SET_TXT_STD(sp, TXT_REPLAY);
+ sp->lno = lno = rp->lno;
+ sp->cno = rp->cno;
+ }
+ return (0);
+}
+
+/*
+ * v_ii -- [count]i
+ * Insert text at the cursor position.
+ */
+int
+v_ii(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t lno;
+ u_long cnt;
+ size_t len;
+ u_int flags;
+ char *p;
+
+ SET_TXT_STD(sp, 0);
+ if (F_ISSET(vp, VC_ISDOT))
+ LF_SET(TXT_REPLAY);
+ for (lno = fm->lno,
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, fm->lno);
+ return (1);
+ }
+ lno = 1;
+ len = 0;
+ }
+ /* If len == sp->cno, it's a replay caused by a count. */
+ if (len == 0 || len == sp->cno)
+ LF_SET(TXT_APPENDEOL);
+
+ if (v_ntext(sp, ep,
+ &sp->tiq, NULL, p, len, rp, 0, OOBLNO, flags))
+ return (1);
+
+ /*
+ * On replay, if the line isn't empty, advance the insert
+ * by one (make it an append).
+ */
+ SET_TXT_STD(sp, TXT_REPLAY);
+ sp->lno = lno = rp->lno;
+ if ((sp->cno = rp->cno) != 0)
+ ++sp->cno;
+ }
+ return (0);
+}
+
+/*
+ * v_iO -- [count]O
+ * Insert text above this line.
+ */
+int
+v_iO(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t ai_line, lno;
+ size_t len;
+ u_long cnt;
+ u_int flags;
+ int first;
+ char *p;
+
+ SET_TXT_STD(sp, TXT_APPENDEOL);
+ if (F_ISSET(vp, VC_ISDOT))
+ LF_SET(TXT_REPLAY);
+ for (first = 1, cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ if (sp->lno == 1) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0)
+ goto insert;
+ p = NULL;
+ len = 0;
+ ai_line = OOBLNO;
+ } else {
+insert: p = "";
+ sp->cno = 0;
+ /* Correct logging for implied cursor motion. */
+ if (first == 1) {
+ log_cursor(sp, ep);
+ first = 0;
+ }
+ if (file_iline(sp, ep, sp->lno, p, 0))
+ return (1);
+ if ((p = file_gline(sp, ep, sp->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, sp->lno);
+ return (1);
+ }
+ ai_line = sp->lno + 1;
+ }
+
+ if (v_ntext(sp, ep,
+ &sp->tiq, NULL, p, len, rp, 0, ai_line, flags))
+ return (1);
+
+ SET_TXT_STD(sp, TXT_APPENDEOL | TXT_REPLAY);
+ sp->lno = lno = rp->lno;
+ sp->cno = rp->cno;
+ }
+ return (0);
+}
+
+/*
+ * v_io -- [count]o
+ * Insert text after this line.
+ */
+int
+v_io(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t ai_line, lno;
+ size_t len;
+ u_long cnt;
+ u_int flags;
+ int first;
+ char *p;
+
+ SET_TXT_STD(sp, TXT_APPENDEOL);
+ if (F_ISSET(vp, VC_ISDOT))
+ LF_SET(TXT_REPLAY);
+ for (first = 1,
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ if (sp->lno == 1) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0)
+ goto insert;
+ p = NULL;
+ len = 0;
+ ai_line = OOBLNO;
+ } else {
+insert: p = "";
+ sp->cno = 0;
+ /* Correct logging for implied cursor motion. */
+ if (first == 1) {
+ log_cursor(sp, ep);
+ first = 0;
+ }
+ len = 0;
+ if (file_aline(sp, ep, 1, sp->lno, p, len))
+ return (1);
+ if ((p = file_gline(sp, ep, ++sp->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, sp->lno);
+ return (1);
+ }
+ ai_line = sp->lno - 1;
+ }
+
+ if (v_ntext(sp, ep,
+ &sp->tiq, NULL, p, len, rp, 0, ai_line, flags))
+ return (1);
+
+ SET_TXT_STD(sp, TXT_APPENDEOL | TXT_REPLAY);
+ sp->lno = lno = rp->lno;
+ sp->cno = rp->cno;
+ }
+ return (0);
+}
+
+/*
+ * v_Change -- [buffer][count]C
+ * Change line command.
+ */
+int
+v_Change(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ return (v_CS(sp, ep, vp, fm, tm, rp, 0));
+}
+
+/*
+ * v_Subst -- [buffer][count]S
+ * Line substitute command.
+ */
+int
+v_Subst(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ u_int flags;
+
+ /*
+ * The S command is the same as a 'C' command from the beginning
+ * of the line. This is hard to do in the parser, so do it here.
+ *
+ * If autoindent is on, the change is from the first *non-blank*
+ * character of the line, not the first character. And, to make
+ * it just a bit more exciting, the initial space is handled as
+ * auto-indent characters.
+ */
+ LF_INIT(0);
+ if (O_ISSET(sp, O_AUTOINDENT)) {
+ fm->cno = 0;
+ if (nonblank(sp, ep, fm->lno, &fm->cno))
+ return (1);
+ LF_SET(TXT_AICHARS);
+ } else
+ fm->cno = 0;
+ sp->cno = fm->cno;
+ return (v_CS(sp, ep, vp, fm, tm, rp, flags));
+}
+
+/*
+ * v_CS --
+ * C and S commands.
+ */
+static int
+v_CS(sp, ep, vp, fm, tm, rp, iflags)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+ u_int iflags;
+{
+ recno_t lno;
+ size_t len;
+ char *p;
+ u_int flags;
+
+ SET_TXT_STD(sp, iflags);
+ if (F_ISSET(vp, VC_ISDOT))
+ LF_SET(TXT_REPLAY);
+
+ /*
+ * There are two cases -- if a count is supplied, we do a line
+ * mode change where we delete the lines and then insert text
+ * into a new line. Otherwise, we replace the current line.
+ */
+ tm->lno = fm->lno + (F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0);
+ if (fm->lno != tm->lno) {
+ /* Make sure that the to line is real. */
+ if (file_gline(sp, ep, tm->lno, NULL) == NULL) {
+ v_eof(sp, ep, fm);
+ return (1);
+ }
+
+ /* Cut the lines. */
+ if (cut(sp, ep,
+ NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ fm, tm, CUT_LINEMODE))
+ return (1);
+
+ /* Insert a line while we still can... */
+ if (file_iline(sp, ep, fm->lno, "", 0))
+ return (1);
+ ++fm->lno;
+ ++tm->lno;
+
+ /* Delete the lines. */
+ if (delete(sp, ep, fm, tm, 1))
+ return (1);
+
+ /* Get the inserted line. */
+ if ((p = file_gline(sp, ep, --fm->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, fm->lno);
+ return (1);
+ }
+ tm = NULL;
+ sp->lno = fm->lno;
+ sp->cno = 0;
+ LF_SET(TXT_APPENDEOL);
+ } else {
+ /* The line may be empty, but that's okay. */
+ if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, tm->lno);
+ return (1);
+ }
+ len = 0;
+ LF_SET(TXT_APPENDEOL);
+ } else {
+ if (cut(sp, ep,
+ NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ fm, tm, CUT_LINEMODE))
+ return (1);
+ tm->cno = len;
+ if (len == 0)
+ LF_SET(TXT_APPENDEOL);
+ LF_SET(TXT_EMARK | TXT_OVERWRITE);
+ }
+ }
+ /* Correct logging for implied cursor motion. */
+ log_cursor(sp, ep);
+ return (v_ntext(sp, ep,
+ &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags));
+}
+
+/*
+ * v_change -- [buffer][count]c[count]motion
+ * Change command.
+ */
+int
+v_change(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t lno;
+ size_t blen, len;
+ u_int flags;
+ int lmode, rval;
+ char *bp, *p;
+
+ SET_TXT_STD(sp, 0);
+ if (F_ISSET(vp, VC_ISDOT))
+ LF_SET(TXT_REPLAY);
+
+ /*
+ * Move the cursor to the start of the change. Note, if autoindent
+ * is turned on, the cc command in line mode changes from the first
+ * *non-blank* character of the line, not the first character. And,
+ * to make it just a bit more exciting, the initial space is handled
+ * as auto-indent characters.
+ */
+ lmode = F_ISSET(vp, VC_LMODE) ? CUT_LINEMODE : 0;
+ if (lmode) {
+ fm->cno = 0;
+ if (O_ISSET(sp, O_AUTOINDENT)) {
+ if (nonblank(sp, ep, fm->lno, &fm->cno))
+ return (1);
+ LF_SET(TXT_AICHARS);
+ }
+ }
+ sp->lno = fm->lno;
+ sp->cno = fm->cno;
+
+ /* Correct logging for implied cursor motion. */
+ log_cursor(sp, ep);
+
+ /*
+ * If changing within a single line, the line either currently has
+ * text or it doesn't. If it doesn't, just insert text. Otherwise,
+ * copy it and overwrite it.
+ */
+ if (fm->lno == tm->lno) {
+ if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+ if (p == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, fm->lno);
+ return (1);
+ }
+ }
+ len = 0;
+ LF_SET(TXT_APPENDEOL);
+ } else {
+ if (cut(sp, ep,
+ NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ fm, tm, lmode))
+ return (1);
+ if (len == 0)
+ LF_SET(TXT_APPENDEOL);
+ LF_SET(TXT_EMARK | TXT_OVERWRITE);
+ }
+ return (v_ntext(sp, ep,
+ &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags));
+ }
+
+ /*
+ * It's trickier if changing over multiple lines. If we're in
+ * line mode we delete all of the lines and insert a replacement
+ * line which the user edits. If there was leading whitespace
+ * in the first line being changed, we copy it and use it as the
+ * replacement. If we're not in line mode, we just delete the
+ * text and start inserting.
+ *
+ * Copy the text.
+ */
+ if (cut(sp, ep,
+ NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, lmode))
+ return (1);
+
+ /* If replacing entire lines and there's leading text. */
+ if (lmode && fm->cno) {
+ /* Get a copy of the first line changed. */
+ if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, fm->lno);
+ return (1);
+ }
+ /* Copy the leading text elsewhere. */
+ GET_SPACE_RET(sp, bp, blen, fm->cno);
+ memmove(bp, p, fm->cno);
+ } else
+ bp = NULL;
+
+ /* Delete the text. */
+ if (delete(sp, ep, fm, tm, lmode))
+ return (1);
+
+ /* If replacing entire lines, insert a replacement line. */
+ if (lmode) {
+ if (file_iline(sp, ep, fm->lno, bp, fm->cno))
+ return (1);
+ sp->lno = fm->lno;
+ len = sp->cno = fm->cno;
+ }
+
+ /* Get the line we're editing. */
+ if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, fm->lno);
+ return (1);
+ }
+ len = 0;
+ }
+
+ /* Check to see if we're appending to the line. */
+ if (fm->cno >= len)
+ LF_SET(TXT_APPENDEOL);
+
+ /* No to mark. */
+ tm = NULL;
+
+ rval = v_ntext(sp, ep, &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags);
+
+ if (bp != NULL)
+ FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
+
+/*
+ * v_Replace -- [count]R
+ * Overwrite multiple characters.
+ */
+int
+v_Replace(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t lno;
+ u_long cnt;
+ size_t len;
+ u_int flags;
+ char *p;
+
+ SET_TXT_STD(sp, 0);
+ if (F_ISSET(vp, VC_ISDOT))
+ LF_SET(TXT_REPLAY);
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ if ((p = file_gline(sp, ep, rp->lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, rp->lno);
+ return (1);
+ }
+ len = 0;
+ LF_SET(TXT_APPENDEOL);
+ } else {
+ if (len == 0)
+ LF_SET(TXT_APPENDEOL);
+ LF_SET(TXT_OVERWRITE | TXT_REPLACE);
+ }
+ tm->lno = rp->lno;
+ tm->cno = len ? len : 0;
+ if (v_ntext(sp, ep, &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags))
+ return (1);
+
+ /*
+ * Special case. The historic vi handled [count]R badly, in that R
+ * would replace some number of characters, and then the count would
+ * append count-1 copies of the replacing chars to the replaced space.
+ * This seems wrong, so this version counts R commands. There is some
+ * trickiness in moving back to where the user stopped replacing after
+ * each R command. Basically, if the user ended with a newline, we
+ * want to use rp->cno (which will be 0). Otherwise, use the column
+ * after the returned cursor, unless it would be past the end of the
+ * line, in which case we append to the line.
+ */
+ while (--cnt) {
+ if ((p = file_gline(sp, ep, rp->lno, &len)) == NULL)
+ GETLINE_ERR(sp, rp->lno);
+ SET_TXT_STD(sp, TXT_REPLAY);
+
+ sp->lno = rp->lno;
+
+ if (len == 0 || rp->cno == len - 1) {
+ sp->cno = len;
+ LF_SET(TXT_APPENDEOL);
+ } else {
+ sp->cno = rp->cno;
+ if (rp->cno != 0)
+ ++sp->cno;
+ LF_SET(TXT_OVERWRITE | TXT_REPLACE);
+ }
+
+ if (v_ntext(sp, ep,
+ &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * v_subst -- [buffer][count]s
+ * Substitute characters.
+ */
+int
+v_subst(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t lno;
+ size_t len;
+ u_int flags;
+ char *p;
+
+ SET_TXT_STD(sp, 0);
+ if (F_ISSET(vp, VC_ISDOT))
+ LF_SET(TXT_REPLAY);
+ if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, fm->lno);
+ return (1);
+ }
+ len = 0;
+ LF_SET(TXT_APPENDEOL);
+ } else {
+ if (len == 0)
+ LF_SET(TXT_APPENDEOL);
+ LF_SET(TXT_EMARK | TXT_OVERWRITE);
+ }
+
+ tm->lno = fm->lno;
+ tm->cno = fm->cno + (F_ISSET(vp, VC_C1SET) ? vp->count : 1);
+ if (tm->cno > len)
+ tm->cno = len;
+
+ if (p != NULL && cut(sp, ep,
+ NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, 0))
+ return (1);
+
+ return (v_ntext(sp, ep,
+ &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags));
+}
diff --git a/usr.bin/vi/nvi/v_ulcase.c b/usr.bin/vi/nvi/v_ulcase.c
new file mode 100644
index 000000000000..12fd1c6626b9
--- /dev/null
+++ b/usr.bin/vi/nvi/v_ulcase.c
@@ -0,0 +1,159 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_ulcase.c 8.3 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_ulcase -- [count]~
+ * Toggle upper & lower case letters.
+ *
+ * !!!
+ * In historic vi, the count was ignored. It would have been better
+ * if there had been an associated motion, but it's too late to change
+ * it now.
+ */
+int
+v_ulcase(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t lno;
+ size_t blen, lcnt, len;
+ u_long cnt;
+ int ch, change, rval;
+ char *bp, *p;
+
+ /* Figure out what memory to use. */
+ GET_SPACE_RET(sp, bp, blen, 256);
+
+ /*
+ * !!!
+ * Historic vi didn't permit ~ to cross newline boundaries.
+ * I can think of no reason why it shouldn't, which at least
+ * lets you auto-repeat through a paragraph.
+ */
+ rval = 0;
+ for (change = -1, cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt;) {
+ /* Get the line; EOF is an infinite sink. */
+ if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno >= fm->lno) {
+ GETLINE_ERR(sp, fm->lno);
+ rval = 1;
+ break;
+ }
+ if (change == -1) {
+ v_eof(sp, ep, NULL);
+ return (1);
+ }
+ break;
+ }
+
+ /* Set current line number. */
+ lno = fm->lno;
+
+ /* Empty lines just decrement the count. */
+ if (len == 0) {
+ --cnt;
+ ++fm->lno;
+ fm->cno = 0;
+ change = 0;
+ continue;
+ }
+
+ /* Get a copy of the line. */
+ ADD_SPACE_RET(sp, bp, blen, len);
+ memmove(bp, p, len);
+
+ /* Set starting pointer. */
+ if (change == -1)
+ p = bp + fm->cno;
+ else
+ p = bp;
+
+ /*
+ * Figure out how many characters get changed in this
+ * line. Set the final cursor column.
+ */
+ if (fm->cno + cnt >= len) {
+ lcnt = len - fm->cno;
+ ++fm->lno;
+ fm->cno = 0;
+ } else
+ fm->cno += lcnt = cnt;
+ cnt -= lcnt;
+
+ /* Change the line. */
+ for (change = 0; lcnt--; ++p) {
+ ch = *(u_char *)p;
+ if (islower(ch)) {
+ *p = toupper(ch);
+ change = 1;
+ } else if (isupper(ch)) {
+ *p = tolower(ch);
+ change = 1;
+ }
+ }
+
+ /* Update the line if necessary. */
+ if (change && file_sline(sp, ep, lno, bp, len)) {
+ rval = 1;
+ break;
+ }
+ }
+
+ /* If changed lines, could be on an illegal line. */
+ if (fm->lno != lno && file_gline(sp, ep, fm->lno, &len) == NULL) {
+ --fm->lno;
+ fm->cno = len ? len - 1 : 0;
+ }
+ *rp = *fm;
+
+ FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
diff --git a/usr.bin/vi/nvi/v_undo.c b/usr.bin/vi/nvi/v_undo.c
new file mode 100644
index 000000000000..87c749b38179
--- /dev/null
+++ b/usr.bin/vi/nvi/v_undo.c
@@ -0,0 +1,136 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_undo.c 8.6 (Berkeley) 1/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_Undo -- U
+ * Undo changes to this line.
+ */
+int
+v_Undo(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ /*
+ * Historically, U reset the cursor to the first column in the line
+ * (not the first non-blank). This seems a bit non-intuitive, but,
+ * considering that we may have undone multiple changes, anything
+ * else (including the cursor position stored in the logging records)
+ * is going to appear random.
+ */
+ rp->lno = fm->lno;
+ rp->cno = 0;
+
+ /*
+ * !!!
+ * Set up the flags so that an immediately subsequent 'u' will roll
+ * forward, instead of backward. In historic vi, a 'u' following a
+ * 'U' redid all of the changes to the line. Given that the user has
+ * explicitly discarded those changes by entering 'U', it seems likely
+ * that the user wants something between the original and end forms of
+ * the line, so starting to replay the changes seems the best way to
+ * get to there.
+ */
+ F_SET(ep, F_UNDO);
+ ep->lundo = BACKWARD;
+
+ return (log_setline(sp, ep));
+}
+
+/*
+ * v_undo -- u
+ * Undo the last change.
+ */
+int
+v_undo(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ /* Set the command count. */
+ VIP(sp)->u_ccnt = sp->ccnt;
+
+ /*
+ * !!!
+ * In historic vi, 'u' toggled between "undo" and "redo", i.e. 'u'
+ * undid the last undo. However, if there has been a change since
+ * the last undo/redo, we always do an undo. To make this work when
+ * the user can undo multiple operations, we leave the old semantic
+ * unchanged, but make '.' after a 'u' do another undo/redo operation.
+ * This has two problems.
+ *
+ * The first is that 'u' didn't set '.' in historic vi. So, if a
+ * user made a change, realized it was in the wrong place, does a
+ * 'u' to undo it, moves to the right place and then does '.', the
+ * change was reapplied. To make this work, we only apply the '.'
+ * to the undo command if it's the command immediately following an
+ * undo command. See vi/vi.c:getcmd() for the details.
+ *
+ * The second is that the traditional way to view the numbered cut
+ * buffers in vi was to enter the commands "1pu.u.u.u. which will
+ * no longer work because the '.' immediately follows the 'u' command.
+ * Since we provide a much better method of viewing buffers, and
+ * nobody can think of a better way of adding in multiple undo, this
+ * remains broken.
+ */
+ if (!F_ISSET(ep, F_UNDO)) {
+ F_SET(ep, F_UNDO);
+ ep->lundo = BACKWARD;
+ } else if (!F_ISSET(vp, VC_ISDOT))
+ ep->lundo = ep->lundo == BACKWARD ? FORWARD : BACKWARD;
+
+ switch (ep->lundo) {
+ case BACKWARD:
+ return (log_backward(sp, ep, rp));
+ case FORWARD:
+ return (log_forward(sp, ep, rp));
+ default:
+ abort();
+ }
+ /* NOTREACHED */
+}
diff --git a/usr.bin/vi/nvi/v_util.c b/usr.bin/vi/nvi/v_util.c
new file mode 100644
index 000000000000..83c4fb4ebbf6
--- /dev/null
+++ b/usr.bin/vi/nvi/v_util.c
@@ -0,0 +1,127 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_util.c 8.5 (Berkeley) 11/15/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_eof --
+ * Vi end-of-file error.
+ */
+void
+v_eof(sp, ep, mp)
+ SCR *sp;
+ EXF *ep;
+ MARK *mp;
+{
+ u_long lno;
+
+ if (mp == NULL)
+ msgq(sp, M_BERR, "Already at end-of-file.");
+ else {
+ if (file_lline(sp, ep, &lno))
+ return;
+ if (mp->lno >= lno)
+ msgq(sp, M_BERR, "Already at end-of-file.");
+ else
+ msgq(sp, M_BERR,
+ "Movement past the end-of-file.");
+ }
+}
+
+/*
+ * v_eol --
+ * Vi end-of-line error.
+ */
+void
+v_eol(sp, ep, mp)
+ SCR *sp;
+ EXF *ep;
+ MARK *mp;
+{
+ size_t len;
+
+ if (mp == NULL)
+ msgq(sp, M_BERR, "Already at end-of-line.");
+ else {
+ if (file_gline(sp, ep, mp->lno, &len) == NULL) {
+ GETLINE_ERR(sp, mp->lno);
+ return;
+ }
+ if (mp->cno == len - 1)
+ msgq(sp, M_BERR, "Already at end-of-line.");
+ else
+ msgq(sp, M_BERR, "Movement past the end-of-line.");
+ }
+}
+
+/*
+ * v_sof --
+ * Vi start-of-file error.
+ */
+void
+v_sof(sp, mp)
+ SCR *sp;
+ MARK *mp;
+{
+ if (mp == NULL || mp->lno == 1)
+ msgq(sp, M_BERR, "Already at the beginning of the file.");
+ else
+ msgq(sp, M_BERR, "Movement past the beginning of the file.");
+}
+
+/*
+ * v_isempty --
+ * Return if the line contains nothing but white-space characters.
+ */
+int
+v_isempty(p, len)
+ char *p;
+ size_t len;
+{
+ for (; len--; ++p)
+ if (!isblank(*p))
+ return (0);
+ return (1);
+}
diff --git a/usr.bin/vi/nvi/v_word.c b/usr.bin/vi/nvi/v_word.c
new file mode 100644
index 000000000000..8d917d68e088
--- /dev/null
+++ b/usr.bin/vi/nvi/v_word.c
@@ -0,0 +1,560 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_word.c 8.10 (Berkeley) 10/26/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * There are two types of "words". Bigwords are easy -- groups of anything
+ * delimited by whitespace. Normal words are trickier. They are either a
+ * group of characters, numbers and underscores, or a group of anything but,
+ * delimited by whitespace. When for a word, if you're in whitespace, it's
+ * easy, just remove the whitespace and go to the beginning or end of the
+ * word. Otherwise, figure out if the next character is in a different group.
+ * If it is, go to the beginning or end of that group, otherwise, go to the
+ * beginning or end of the current group. The historic version of vi didn't
+ * get this right, so, for example, there were cases where "4e" was not the
+ * same as "eeee". To get it right you have to resolve the cursor after each
+ * search so that the look-ahead to figure out what type of "word" the cursor
+ * is in will be correct.
+ *
+ * Empty lines, and lines that consist of only white-space characters count
+ * as a single word, and the beginning and end of the file counts as an
+ * infinite number of words.
+ *
+ * Movements associated with commands are different than movement commands.
+ * For example, in "abc def", with the cursor on the 'a', "cw" is from
+ * 'a' to 'c', while "w" is from 'a' to 'd'. In general, trailing white
+ * space is discarded from the change movement. Another example is that,
+ * in the same string, a "cw" on any white space character replaces that
+ * single character, and nothing else. Ain't nothin' in here that's easy.
+ *
+ * One historic note -- in the original vi, the 'w', 'W' and 'B' commands
+ * would treat groups of empty lines as individual words, i.e. the command
+ * would move the cursor to each new empty line. The 'e' and 'E' commands
+ * would treat groups of empty lines as a single word, i.e. the first use
+ * would move past the group of lines. The 'b' command would just beep at
+ * you. If the lines contained only white-space characters, the 'w' and 'W'
+ * commands will just beep at you, and the 'B', 'b', 'E' and 'e' commands
+ * will treat the group as a single word, and the 'B' and 'b' commands will
+ * treat the lines as individual words. This implementation treats both
+ * cases as a single white-space word.
+ */
+
+#define FW(test) for (; len && (test); --len, ++p)
+#define BW(test) for (; len && (test); --len, --p)
+
+enum which {BIGWORD, LITTLEWORD};
+
+static int bword __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, int));
+static int eword __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, int));
+static int fword __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, enum which));
+
+/*
+ * v_wordw -- [count]w
+ * Move forward a word at a time.
+ */
+int
+v_wordw(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ return (fword(sp, ep, vp, fm, rp, LITTLEWORD));
+}
+
+/*
+ * v_wordW -- [count]W
+ * Move forward a bigword at a time.
+ */
+int
+v_wordW(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ return (fword(sp, ep, vp, fm, rp, BIGWORD));
+}
+
+/*
+ * fword --
+ * Move forward by words.
+ */
+static int
+fword(sp, ep, vp, fm, rp, type)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *rp;
+ enum which type;
+{
+ enum { INWORD, NOTWORD } state;
+ VCS cs;
+ u_long cnt;
+
+ cs.cs_lno = fm->lno;
+ cs.cs_cno = fm->cno;
+ if (cs_init(sp, ep, &cs))
+ return (1);
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+
+ /*
+ * If in white-space:
+ * If the count is 1, and it's a change command, we're done.
+ * Else, move to the first non-white-space character, which
+ * counts as a single word move. If it's a motion command,
+ * don't move off the end of the line.
+ */
+ if (cs.cs_flags == CS_EMP || cs.cs_flags == 0 && isblank(cs.cs_ch)) {
+ if (cs.cs_flags != CS_EMP && cnt == 1) {
+ if (F_ISSET(vp, VC_C)) {
+ ++cs.cs_cno;
+ goto ret3;
+ }
+ if (F_ISSET(vp, VC_D | VC_Y)) {
+ if (cs_fspace(sp, ep, &cs))
+ return (1);
+ goto ret1;
+ }
+ }
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+ --cnt;
+ }
+
+ /*
+ * Cyclically move to the next word -- this involves skipping
+ * over word characters and then any trailing non-word characters.
+ * Note, for the 'w' command, the definition of a word keeps
+ * switching.
+ */
+ if (type == BIGWORD)
+ while (cnt--) {
+ for (;;) {
+ if (cs_next(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret2;
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ break;
+ }
+ /*
+ * If a motion command and we're at the end of the
+ * last word, we're done. Delete and yank eat any
+ * trailing blanks, but we don't move off the end
+ * of the line regardless.
+ */
+ if (cnt == 0 && F_ISSET(vp, VC_C | VC_D | VC_Y)) {
+ if (F_ISSET(vp, VC_D | VC_Y) &&
+ cs_fspace(sp, ep, &cs))
+ return (1);
+ break;
+ }
+
+ /* Eat whitespace characters. */
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret2;
+ }
+ else
+ while (cnt--) {
+ state = cs.cs_flags == 0 &&
+ inword(cs.cs_ch) ? INWORD : NOTWORD;
+ for (;;) {
+ if (cs_next(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret2;
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ break;
+ if (state == INWORD) {
+ if (!inword(cs.cs_ch))
+ break;
+ } else
+ if (inword(cs.cs_ch))
+ break;
+ }
+ /* See comment above. */
+ if (cnt == 0 && F_ISSET(vp, VC_C | VC_D | VC_Y)) {
+ if (F_ISSET(vp, VC_D | VC_Y) &&
+ cs_fspace(sp, ep, &cs))
+ return (1);
+ break;
+ }
+
+ /* Eat whitespace characters. */
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret2;
+ }
+
+ /*
+ * If a motion command, and eating the trailing non-word would
+ * move us off this line, don't do it. Move the return cursor
+ * to one past the EOL instead.
+ */
+ret1: if (F_ISSET(vp, VC_C | VC_D | VC_Y) && cs.cs_flags == CS_EOL)
+ ++cs.cs_cno;
+
+ /* If we didn't move, we must be at EOF. */
+ret2: if (cs.cs_lno == fm->lno && cs.cs_cno == fm->cno) {
+ v_eof(sp, ep, fm);
+ return (1);
+ }
+ /*
+ * If at EOF, and it's a motion command, move the return cursor
+ * one past the EOF.
+ */
+ if (F_ISSET(vp, VC_C | VC_D | VC_Y) && cs.cs_flags == CS_EOF)
+ ++cs.cs_cno;
+ret3: rp->lno = cs.cs_lno;
+ rp->cno = cs.cs_cno;
+ return (0);
+}
+
+/*
+ * v_wordb -- [count]b
+ * Move backward a word at a time.
+ */
+int
+v_wordb(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ return (bword(sp, ep, vp, fm, rp, 0));
+}
+
+/*
+ * v_WordB -- [count]B
+ * Move backward a bigword at a time.
+ */
+int
+v_wordB(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ return (bword(sp, ep, vp, fm, rp, 1));
+}
+
+/*
+ * bword --
+ * Move backward by words.
+ */
+static int
+bword(sp, ep, vp, fm, rp, spaceonly)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *rp;
+ int spaceonly;
+{
+ register char *p;
+ recno_t lno;
+ size_t len;
+ u_long cno, cnt;
+ char *startp;
+
+ lno = fm->lno;
+ cno = fm->cno;
+
+ /* Check for start of file. */
+ if (lno == 1 && cno == 0) {
+ v_sof(sp, NULL);
+ return (1);
+ }
+
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ v_sof(sp, NULL);
+ else
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+
+ /*
+ * Reset the length to the number of characters in the line; the
+ * first character is the current cursor position.
+ */
+ len = cno ? cno + 1 : 0;
+ if (len == 0)
+ goto line;
+ for (startp = p, p += cno; cnt--;) {
+ if (spaceonly) {
+ if (!isblank(*p)) {
+ if (len < 2)
+ goto line;
+ --p;
+ --len;
+ }
+ BW(isblank(*p));
+ if (len)
+ BW(!isblank(*p));
+ else
+ goto line;
+ } else {
+ if (!isblank(*p)) {
+ if (len < 2)
+ goto line;
+ --p;
+ --len;
+ }
+ BW(isblank(*p));
+ if (len)
+ if (inword(*p))
+ BW(inword(*p));
+ else
+ BW(!isblank(*p) && !inword(*p));
+ else
+ goto line;
+ }
+
+ if (cnt && len == 0) {
+ /* If we hit SOF, stay there (historic practice). */
+line: if (lno == 1) {
+ rp->lno = 1;
+ rp->cno = 0;
+ return (0);
+ }
+
+ /*
+ * Get the line. If the line is empty, decrement
+ * count and get another one.
+ */
+ if ((p = file_gline(sp, ep, --lno, &len)) == NULL) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+ if (len == 0) {
+ if (cnt == 0 || --cnt == 0) {
+ rp->lno = lno;
+ rp->cno = 0;
+ return (0);
+ }
+ goto line;
+ }
+
+ /*
+ * Set the cursor to the end of the line. If the word
+ * at the end of this line has only a single character,
+ * we've already skipped over it.
+ */
+ startp = p;
+ if (len) {
+ p += len - 1;
+ if (cnt && len > 1 && !isblank(p[0]))
+ if (inword(p[0])) {
+ if (!inword(p[-1]))
+ --cnt;
+ } else if (!isblank(p[-1]) &&
+ !inword(p[-1]))
+ --cnt;
+ }
+ } else {
+ ++p;
+ ++len;
+ }
+ }
+ rp->lno = lno;
+ rp->cno = p - startp;
+ return (0);
+}
+
+/*
+ * v_worde -- [count]e
+ * Move forward to the end of the word.
+ */
+int
+v_worde(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ return (eword(sp, ep, vp, fm, rp, 0));
+}
+
+/*
+ * v_wordE -- [count]E
+ * Move forward to the end of the bigword.
+ */
+int
+v_wordE(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ return (eword(sp, ep, vp, fm, rp, 1));
+}
+
+/*
+ * eword --
+ * Move forward to the end of the word.
+ */
+static int
+eword(sp, ep, vp, fm, rp, spaceonly)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *rp;
+ int spaceonly;
+{
+ register char *p;
+ recno_t lno;
+ size_t len, llen;
+ u_long cno, cnt;
+ int empty;
+ char *startp;
+
+ lno = fm->lno;
+ cno = fm->cno;
+
+ if ((p = file_gline(sp, ep, lno, &llen)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ v_eof(sp, ep, NULL);
+ else
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+
+ /*
+ * Reset the length; the first character is the current cursor
+ * position. If no more characters in this line, may already
+ * be at EOF.
+ */
+ len = llen - cno;
+ if (empty = llen == 0 || llen == cno + 1)
+ goto line;
+
+ for (startp = p += cno; cnt--; empty = 0) {
+ if (spaceonly) {
+ if (!isblank(*p)) {
+ if (len < 2)
+ goto line;
+ ++p;
+ --len;
+ }
+ FW(isblank(*p));
+ if (len)
+ FW(!isblank(*p));
+ else
+ ++cnt;
+ } else {
+ if (!isblank(*p)) {
+ if (len < 2)
+ goto line;
+ ++p;
+ --len;
+ }
+ FW(isblank(*p));
+ if (len)
+ if (inword(*p))
+ FW(inword(*p));
+ else
+ FW(!isblank(*p) && !inword(*p));
+ else
+ ++cnt;
+ }
+
+ if (cnt && len == 0) {
+ /* If we hit EOF, stay there (historic practice). */
+line: if ((p = file_gline(sp, ep, ++lno, &llen)) == NULL) {
+ /*
+ * If already at eof, complain, unless it's
+ * a change command or a delete command and
+ * there's something to delete.
+ */
+ if (empty) {
+ if (F_ISSET(vp, VC_C) ||
+ F_ISSET(vp, VC_D) && llen != 0) {
+ rp->lno = lno - 1;
+ rp->cno = llen ? llen : 1;
+ return (0);
+ }
+ v_eof(sp, ep, NULL);
+ return (1);
+ }
+ if ((p =
+ file_gline(sp, ep, --lno, &llen)) == NULL) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+ rp->lno = lno;
+ rp->cno = llen ? llen - 1 : 0;
+ /* The 'c', 'd' and 'y' need one more space. */
+ if (F_ISSET(vp, VC_C | VC_D | VC_Y))
+ ++rp->cno;
+ return (0);
+ }
+ len = llen;
+ cno = 0;
+ startp = p;
+ } else {
+ --p;
+ ++len;
+ }
+ }
+ rp->lno = lno;
+ rp->cno = cno + (p - startp);
+
+ /* The 'c', 'd' and 'y' need one more space. */
+ if (F_ISSET(vp, VC_C | VC_D | VC_Y))
+ ++rp->cno;
+ return (0);
+}
diff --git a/usr.bin/vi/nvi/v_xchar.c b/usr.bin/vi/nvi/v_xchar.c
new file mode 100644
index 000000000000..019862240f26
--- /dev/null
+++ b/usr.bin/vi/nvi/v_xchar.c
@@ -0,0 +1,135 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_xchar.c 8.4 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+#define NODEL(sp) { \
+ msgq(sp, M_BERR, "No characters to delete."); \
+ return (1); \
+}
+
+/*
+ * v_xchar --
+ * Deletes the character(s) on which the cursor sits.
+ */
+int
+v_xchar(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ MARK m;
+ recno_t lno;
+ u_long cnt;
+ size_t len;
+
+ if (file_gline(sp, ep, fm->lno, &len) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ NODEL(sp);
+ GETLINE_ERR(sp, fm->lno);
+ return (1);
+ }
+
+ if (len == 0)
+ NODEL(sp);
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+
+ /*
+ * Deleting from the cursor toward the end of line, w/o moving the
+ * cursor. Note, "2x" at EOL isn't the same as "xx" because the
+ * left movement of the cursor as part of the 'x' command isn't
+ * taken into account. Historically correct.
+ */
+ tm->lno = fm->lno;
+ if (cnt < len - fm->cno) {
+ tm->cno = fm->cno + cnt;
+ m = *fm;
+ } else {
+ tm->cno = len;
+ m.lno = fm->lno;
+ m.cno = fm->cno ? fm->cno - 1 : 0;
+ }
+
+ if (cut(sp, ep,
+ NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, 0))
+ return (1);
+ if (delete(sp, ep, fm, tm, 0))
+ return (1);
+
+ *rp = m;
+ return (0);
+}
+
+/*
+ * v_Xchar --
+ * Deletes the character(s) immediately before the current cursor
+ * position.
+ */
+int
+v_Xchar(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ u_long cnt;
+
+ if (fm->cno == 0) {
+ msgq(sp, M_BERR, "Already at the left-hand margin.");
+ return (1);
+ }
+
+ *tm = *fm;
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ fm->cno = cnt >= tm->cno ? 0 : tm->cno - cnt;
+
+ if (cut(sp, ep,
+ NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, 0))
+ return (1);
+ if (delete(sp, ep, fm, tm, 0))
+ return (1);
+
+ *rp = *fm;
+ return (0);
+}
diff --git a/usr.bin/vi/nvi/v_yank.c b/usr.bin/vi/nvi/v_yank.c
new file mode 100644
index 000000000000..7b2718e647a4
--- /dev/null
+++ b/usr.bin/vi/nvi/v_yank.c
@@ -0,0 +1,114 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_yank.c 8.11 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_Yank -- [buffer][count]Y
+ * Yank lines of text into a cut buffer.
+ */
+int
+v_Yank(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ if (file_gline(sp, ep, tm->lno, NULL) == NULL) {
+ v_eof(sp, ep, fm);
+ return (1);
+ }
+ if (cut(sp, ep, NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ fm, tm, CUT_LINEMODE))
+ return (1);
+
+ sp->rptlines[L_YANKED] += (tm->lno - fm->lno) + 1;
+ return (0);
+}
+
+/*
+ * v_yank -- [buffer][count]y[count][motion]
+ * Yank text (or lines of text) into a cut buffer.
+ */
+int
+v_yank(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ if (F_ISSET(vp, VC_LMODE)) {
+ if (file_gline(sp, ep, tm->lno, NULL) == NULL) {
+ v_eof(sp, ep, fm);
+ return (1);
+ }
+ if (cut(sp, ep,
+ NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ fm, tm, CUT_LINEMODE))
+ return (1);
+ } else if (cut(sp, ep,
+ NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, 0))
+ return (1);
+
+ /*
+ * !!!
+ * Historic vi moved the cursor to the from MARK if it was before the
+ * current cursor. This makes no sense. For example, "yj" moves the
+ * cursor but "yk" does not. Unfortunately, it's too late to change
+ * this now. Matching the historic semantics isn't easy. The line
+ * number was always changed and column movement was usually relative.
+ * However, "y'a" moved the cursor to the first non-blank of the line
+ * marked by a, while "y`a" moved the cursor to the line and column
+ * marked by a.
+ */
+ if (F_ISSET(vp, VC_REVMOVE)) {
+ rp->lno = fm->lno;
+ if (vp->mkp == &vikeys['\'']) {
+ rp->cno = 0;
+ (void)nonblank(sp, ep, rp->lno, &rp->cno);
+ } else if (vp->mkp == &vikeys['`'])
+ rp->cno = fm->cno;
+ else
+ rp->cno = sp->s_relative(sp, ep, rp->lno);
+ }
+
+ sp->rptlines[L_YANKED] += (tm->lno - fm->lno) + 1;
+ return (0);
+}
diff --git a/usr.bin/vi/nvi/v_z.c b/usr.bin/vi/nvi/v_z.c
new file mode 100644
index 000000000000..31937ffc951c
--- /dev/null
+++ b/usr.bin/vi/nvi/v_z.c
@@ -0,0 +1,133 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)v_z.c 8.8 (Berkeley) 12/2/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_z -- [count]z[count][-.+^<CR>]
+ * Move the screen.
+ */
+int
+v_z(sp, ep, vp, fm, tm, rp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ MARK *fm, *tm, *rp;
+{
+ recno_t last, lno;
+ u_int value;
+
+ /*
+ * The first count is the line to use. If the value doesn't
+ * exist, use the last line.
+ */
+ if (F_ISSET(vp, VC_C1SET)) {
+ lno = vp->count;
+ if (file_lline(sp, ep, &last))
+ return (1);
+ if (lno > last)
+ lno = last;
+ } else
+ lno = fm->lno;
+
+ /* Set return cursor values. */
+ rp->lno = lno;
+ rp->cno = fm->cno;
+
+ /*
+ * The second count is the displayed window size, i.e. the 'z'
+ * command is another way to get artificially small windows.
+ *
+ * !!!
+ * A window size of 0 was historically allowed, and simply ignored.
+ * Also, this could be much more simply done by modifying the value
+ * of the O_WINDOW option, but that's not how it worked historically.
+ */
+ if (F_ISSET(vp, VC_C2SET) &&
+ vp->count2 != 0 && sp->s_rrel(sp, vp->count2))
+ return (1);
+
+ switch (vp->character) {
+ case '-': /* Put the line at the bottom. */
+ if (sp->s_fill(sp, ep, lno, P_BOTTOM))
+ return (1);
+ break;
+ case '.': /* Put the line in the middle. */
+ if (sp->s_fill(sp, ep, lno, P_MIDDLE))
+ return (1);
+ break;
+ default: /* Put the line at the top for <cr>. */
+ value = term_key_val(sp, vp->character);
+ if (value != K_CR && value != K_NL) {
+ msgq(sp, M_ERR, "usage: %s.", vp->kp->usage);
+ return (1);
+ }
+ /* FALLTHROUGH */
+ case '+': /* Put the line at the top. */
+ if (sp->s_fill(sp, ep, lno, P_TOP))
+ return (1);
+ break;
+ case '^': /* Print the screen before the z- screen. */
+ /*
+ * !!!
+ * Historic practice isn't real clear on this one. It seems
+ * that the command "70z^" is the same as ":70<cr>z-z^" with
+ * an off-by-one difference. So, until I find documentation
+ * to the contrary, the z^ command in this implementation
+ * displays the screen immediately before the current one.
+ * Fill the screen with the selected line at the bottom, then,
+ * scroll the screen down a page, and move to the middle line
+ * of the screen. Historic vi moved the cursor to some random
+ * place in the screen, as far as I can tell.
+ */
+ if (sp->s_fill(sp, ep, lno, P_BOTTOM))
+ return (1);
+ if (sp->s_down(sp, ep, rp, sp->t_maxrows - 1, 1))
+ return (1);
+ if (sp->s_position(sp, ep, rp, 0, P_MIDDLE))
+ return (1);
+ break;
+ }
+
+ /* If the map changes, have to redraw the entire screen. */
+ F_SET(sp, S_REDRAW);
+
+ return (0);
+}
diff --git a/usr.bin/vi/nvi/vcmd.c b/usr.bin/vi/nvi/vcmd.c
new file mode 100644
index 000000000000..d7f1caf81d0c
--- /dev/null
+++ b/usr.bin/vi/nvi/vcmd.c
@@ -0,0 +1,522 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)vcmd.c 8.22 (Berkeley) 1/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * This array maps keystrokes to vi command functions. It is known
+ * in ex/ex_usage.c that it takes four columns to name a vi character.
+ */
+VIKEYS const vikeys [MAXVIKEY + 1] = {
+/* 000 NUL -- The code in vi.c expects key 0 to be undefined. */
+ {NULL},
+/* 001 ^A */
+ {v_searchw, V_ABS|V_CNT|V_MOVE|V_KEYW|V_RCM_SET,
+ "[count]^A",
+ "^A search forward for cursor word"},
+/* 002 ^B */
+ {v_pageup, V_ABS|V_CNT|V_RCM_SETLFNB,
+ "[count]^B",
+ "^B page up by screens"},
+/* 003 ^C */
+ {NULL, 0,
+ "^C",
+ "^C interrupt a search or global command"},
+/* 004 ^D */
+ {v_hpagedown, V_ABS|V_CNT|V_RCM_SETLFNB,
+ "[count]^D",
+ "^D page down by half screens (setting count)"},
+/* 005 ^E */
+ {v_linedown, V_CNT,
+ "[count]^E",
+ "^E page down by lines"},
+/* 006 ^F */
+ {v_pagedown, V_ABS|V_CNT|V_RCM_SETLFNB,
+ "[count]^F",
+ "^F page down by screens"},
+/* 007 ^G */
+ {v_status, 0,
+ "^G",
+ "^G file status"},
+/* 010 ^H */
+ {v_left, V_CNT|V_MOVE|V_RCM_SET,
+ "[count]^H",
+ "^H move left by columns"},
+/* 011 ^I */
+ {NULL},
+/* 012 ^J */
+ {v_down, V_CNT|V_LMODE|V_MOVE|V_RCM,
+ "[count]^J",
+ "^J move down by lines"},
+/* 013 ^K */
+ {NULL},
+/* 014 ^L */
+ {v_redraw, 0,
+ "^L",
+ "^L redraw screen"},
+/* 015 ^M */
+ {v_cr, V_CNT|V_LMODE|V_MOVE|V_RCM_SETFNB,
+ "[count]^M",
+ "^M move down by lines (to first non-blank)"},
+/* 016 ^N */
+ {v_down, V_CNT|V_LMODE|V_MOVE|V_RCM,
+ "[count]^N",
+ "^N move down by lines"},
+/* 017 ^O */
+ {NULL},
+/* 020 ^P */
+ {v_up, V_CNT|V_LMODE|V_MOVE|V_RCM,
+ "[count]^P",
+ "^P move up by lines"},
+/* 021 ^Q -- not available, used for hardware flow control. */
+ {NULL},
+/* 022 ^R */
+ {v_redraw, 0,
+ "^R",
+ "^R redraw screen"},
+/* 023 ^S -- not available, used for hardware flow control. */
+ {NULL},
+/* 024 ^T */
+ {v_tagpop, V_RCM_SET,
+ "^T",
+ "^T tag pop"},
+/* 025 ^U */
+ {v_hpageup, V_ABS|V_CNT|V_RCM_SETLFNB,
+ "[count]^U",
+ "^U half page up (set count)"},
+/* 026 ^V */
+ {NULL, 0,
+ "^V",
+ "^V input a literal character"},
+/* 027 ^W */
+ {v_screen, 0,
+ "^W",
+ "^W move to next screen"},
+/* 030 ^X */
+ {NULL},
+/* 031 ^Y */
+ {v_lineup, V_CNT,
+ "[count]^Y",
+ "^Y page up by lines"},
+/* 032 ^Z */
+ {v_stop, 0,
+ "^Z",
+ "^Z suspend editor"},
+/* 033 ^[ */
+ {NULL, 0,
+ "^[ <escape>",
+ "^[ <escape> leave input mode, return to command mode"},
+/* 034 ^\ */
+ {NULL},
+/* 035 ^] */
+ {v_tagpush, V_KEYW|V_RCM_SET,
+ "^]",
+ "^] tag push cursor word"},
+/* 036 ^^ */
+ {v_switch, 0,
+ "^^",
+ "^^ switch to previous file"},
+/* 037 ^_ */
+ {NULL},
+/* 040 ' ' */
+ {v_right, V_CNT|V_MOVE|V_RCM_SET,
+ "[count]' '",
+ " <space> move right by columns"},
+/* 041 ! */
+ {v_filter, V_CNT|V_DOT|V_MOTION|V_RCM_SET,
+ "[count]![count]motion command(s)",
+ " ! filter through command(s) to motion"},
+/* 042 " */
+ {NULL},
+/* 043 # */
+ {v_increment, V_CHAR|V_CNT|V_DOT|V_KEYNUM|V_RCM_SET,
+ "[count]#[#+-]",
+ " # number increment/decrement"},
+/* 044 $ */
+ {v_dollar, V_CNT|V_MOVE|V_RCM_SETLAST,
+ " [count]$",
+ " $ move to last column"},
+/* 045 % */
+ {v_match, V_ABS|V_MOVE|V_RCM_SET,
+ "%",
+ " % move to match"},
+/* 046 & */
+ {v_again, 0,
+ "&",
+ " & repeat substitution"},
+/* 047 ' */
+ {v_gomark, V_ABS|V_CHAR|V_LMODE|V_MOVE|V_RCM_SETFNB,
+ "'['a-z]",
+ " ' move to mark (to first non-blank)"},
+/* 050 ( */
+ {v_sentenceb, V_CNT|V_MOVE|V_RCM_SET,
+ "[count](",
+ " ( move back sentence"},
+/* 051 ) */
+ {v_sentencef, V_ABS|V_CNT|V_MOVE|V_RCM_SET,
+ "[count])",
+ " ) move forward sentence"},
+/* 052 * */
+ {NULL},
+/* 053 + */
+ {v_down, V_CNT|V_LMODE|V_MOVE|V_RCM_SETFNB,
+ "[count]+",
+ " + move down by lines (to first non-blank)"},
+/* 054 , */
+ {v_chrrepeat, V_CNT|V_MOVE|V_RCM_SET,
+ "[count],",
+ " , reverse last F, f, T or t search"},
+/* 055 - */
+ {v_up, V_CNT|V_LMODE|V_MOVE|V_RCM_SETFNB,
+ "[count]-",
+ " - move up by lines (to first non-blank)"},
+/* 056 . */
+ {NULL, 0,
+ ".",
+ " . repeat the last command"},
+/* 057 / */
+ {v_searchf, V_ABS|V_MOVE|V_RCM_SET,
+ "/RE[/ offset]",
+ " / search forward"},
+/* 060 0 */
+ {v_zero, V_MOVE|V_RCM_SET,
+ "0",
+ " 0 move to first character"},
+/* 061 1 */
+ {NULL},
+/* 062 2 */
+ {NULL},
+/* 063 3 */
+ {NULL},
+/* 064 4 */
+ {NULL},
+/* 065 5 */
+ {NULL},
+/* 066 6 */
+ {NULL},
+/* 067 7 */
+ {NULL},
+/* 070 8 */
+ {NULL},
+/* 071 9 */
+ {NULL},
+/* 072 : */
+ {v_ex, 0,
+ ":command [| command] ...",
+ " : ex command"},
+/* 073 ; */
+ {v_chrepeat, V_CNT|V_MOVE|V_RCM_SET,
+ "[count];",
+ " ; repeat last F, f, T or t search"},
+/* 074 < */
+ {v_shiftl, V_CNT|V_DOT|V_MOTION|V_RCM_SET|VC_SH,
+ "[count]<[count]motion",
+ " < shift lines left to motion"},
+/* 075 = */
+ {NULL},
+/* 076 > */
+ {v_shiftr, V_CNT|V_DOT|V_MOTION|V_RCM_SET|VC_SH,
+ "[count]>[count]motion",
+ " > shift lines right to motion"},
+/* 077 ? */
+ {v_searchb, V_ABS|V_MOVE|V_RCM_SET,
+ "?RE[? offset]",
+ " ? search backward"},
+/* 100 @ */
+ {v_at, V_RBUF|V_RCM_SET,
+ "@buffer",
+ " @ execute buffer"},
+/* 101 A */
+ {v_iA, V_CNT|V_DOT|V_RCM_SET,
+ "[count]A",
+ " A append to the line"},
+/* 102 B */
+ {v_wordB, V_CNT|V_MOVE|V_RCM_SET,
+ "[count]B",
+ " B move back bigword"},
+/* 103 C */
+ {v_Change, V_CNT|V_DOT|V_OBUF|V_RCM_SET,
+ "[buffer][count]C",
+ " C change to end-of-line"},
+/* 104 D */
+ {v_Delete, V_CNT|V_DOT|V_OBUF|V_RCM_SET,
+ "[buffer][count]D",
+ " D delete to end-of-line"},
+/* 105 E */
+ {v_wordE, V_CNT|V_MOVE|V_RCM_SET,
+ "[count]E",
+ " E move to end of bigword"},
+/* 106 F */
+ {v_chF, V_CHAR|V_CNT|V_MOVE|V_RCM_SET,
+ "[count]F character",
+ " F character in line backward search"},
+/* 107 G */
+ {v_lgoto, V_ABS|V_CNT|V_LMODE|V_MOVE|V_RCM_SETFNB,
+ "[count]G",
+ " G move to line"},
+/* 110 H */
+ {v_home, V_CNT|V_LMODE|V_MOVE|V_RCM_SETNNB,
+ "[count]H",
+ " H move to count lines from screen top"},
+/* 111 I */
+ {v_iI, V_CNT|V_DOT|V_RCM_SET,
+ "[count]I",
+ " I insert at line beginning"},
+/* 112 J */
+ {v_join, V_CNT|V_DOT|V_RCM_SET,
+ "[count]J",
+ " J join lines"},
+/* 113 K */
+ {NULL},
+/* 114 L */
+ {v_bottom, V_CNT|V_LMODE|V_MOVE|V_RCM_SETNNB,
+ "[count]L",
+ " L move to screen bottom"},
+/* 115 M */
+ {v_middle, V_CNT|V_LMODE|V_MOVE|V_RCM_SETNNB,
+ "M",
+ " M move to screen middle"},
+/* 116 N */
+ {v_searchN, V_ABS|V_MOVE|V_RCM_SET,
+ "n",
+ " N reverse last search"},
+/* 117 O */
+ {v_iO, V_CNT|V_DOT|V_RCM_SET,
+ "[count]O",
+ " O insert above line"},
+/* 120 P */
+ {v_Put, V_CNT|V_DOT|V_OBUF|V_RCM_SET,
+ "[buffer]P",
+ " P insert before cursor from buffer"},
+/* 121 Q */
+ {v_exmode, 0,
+ "Q",
+ " Q switch to ex mode"},
+/* 122 R */
+ {v_Replace, V_CNT|V_DOT|V_RCM_SET,
+ "[count]R",
+ " R replace characters"},
+/* 123 S */
+ {v_Subst, V_CNT|V_DOT|V_LMODE|V_OBUF|V_RCM_SET,
+ "[buffer][count]S",
+ " S substitute for the line(s)"},
+/* 124 T */
+ {v_chT, V_CHAR|V_CNT|V_MOVE|V_RCM_SET,
+ "[count]T character",
+ " T before character in line backward search"},
+/* 125 U */
+ {v_Undo, V_RCM_SET,
+ "U",
+ " U Restore the current line"},
+/* 126 V */
+ {NULL},
+/* 127 W */
+ {v_wordW, V_CNT|V_MOVE|V_RCM_SET,
+ "[count]W",
+ " W move to next bigword"},
+/* 130 X */
+ {v_Xchar, V_CNT|V_DOT|V_OBUF|V_RCM_SET,
+ "[buffer][count]X",
+ " X delete character before cursor"},
+/* 131 Y */
+ {v_Yank, V_CNT|V_LMODE|V_OBUF,
+ "[buffer][count]Y",
+ " Y copy line"},
+/* 132 Z */
+ {v_exit, 0,
+ "ZZ",
+ "ZZ save file and exit"},
+/* 133 [ */
+ {v_sectionb, V_ABS|V_LMODE|V_MOVE|V_RCM_SET,
+ "[[",
+ "[[ move back section"},
+/* 134 \ */
+ {NULL},
+/* 135 ] */
+ {v_sectionf, V_ABS|V_LMODE|V_MOVE|V_RCM_SET,
+ "]]",
+ "]] move forward section"},
+/* 136 ^ */
+ /*
+ * DON'T set the V_RCM_SETFNB flag, the function has to do
+ * the work anyway, in case it's a motion component. DO set
+ * V_RCM_SET, so that any motion that's part of a command is
+ * preserved.
+ */
+ {v_first, V_CNT|V_MOVE|V_RCM_SET,
+ "^",
+ " ^ move to first non-blank"},
+/* 137 _ */
+ /*
+ * DON'T set the V_RCM_SETFNB flag, the function has to do
+ * the work anyway, in case it's a motion component. DO set
+ * V_RCM_SET, so that any motion that's part of a command is
+ * preserved.
+ */
+ {v_cfirst, V_CNT|V_MOVE|V_RCM_SET,
+ "_",
+ " _ move to first non-blank"},
+/* 140 ` */
+ {v_gomark, V_ABS|V_CHAR|V_MOVE|V_RCM_SET,
+ "`[`a-z]",
+ " ` move to mark"},
+/* 141 a */
+ {v_ia, V_CNT|V_DOT|V_RCM_SET,
+ "[count]a",
+ " a append after cursor"},
+/* 142 b */
+ {v_wordb, V_CNT|V_MOVE|V_RCM_SET,
+ "[count]b",
+ " b move back word"},
+/* 143 c */
+ {v_change, V_CNT|V_DOT|V_MOTION|V_OBUF|V_RCM_SET|VC_C,
+ "[buffer][count]c[count]motion",
+ " c change to motion"},
+/* 144 d */
+ {v_delete, V_CNT|V_DOT|V_MOTION|V_OBUF|V_RCM_SET|VC_D,
+ "[buffer][count]d[count]motion",
+ " d delete to motion"},
+/* 145 e */
+ {v_worde, V_CNT|V_MOVE|V_RCM_SET,
+ "[count]e",
+ " e move to end of word"},
+/* 146 f */
+ {v_chf, V_CHAR|V_CNT|V_MOVE|V_RCM_SET,
+ "[count]f character",
+ " f character in line forward search"},
+/* 147 g */
+ {NULL},
+/* 150 h */
+ {v_left, V_CNT|V_MOVE|V_RCM_SET,
+ "[count]h",
+ " h move left by columns"},
+/* 151 i */
+ {v_ii, V_CNT|V_DOT|V_RCM_SET,
+ "[count]i",
+ " i insert before cursor"},
+/* 152 j */
+ {v_down, V_CNT|V_LMODE|V_MOVE|V_RCM,
+ "[count]j",
+ " j move down by lines"},
+/* 153 k */
+ {v_up, V_CNT|V_LMODE|V_MOVE|V_RCM,
+ "[count]k",
+ " k move up by lines"},
+/* 154 l */
+ {v_right, V_CNT|V_MOVE|V_RCM_SET,
+ "[count]l",
+ " l move right by columns"},
+/* 155 m */
+ {v_mark, V_CHAR,
+ "m[a-z]",
+ " m set mark"},
+/* 156 n */
+ {v_searchn, V_ABS|V_MOVE|V_RCM_SET,
+ "n",
+ " n repeat last search"},
+/* 157 o */
+ {v_io, V_CNT|V_DOT|V_RCM_SET,
+ "[count]o",
+ " o append after line"},
+/* 160 p */
+ {v_put, V_CNT|V_DOT|V_OBUF|V_RCM_SET,
+ "[buffer]p",
+ " p insert after cursor from buffer"},
+/* 161 q */
+ {NULL},
+/* 162 r */
+ {v_replace, V_CNT|V_DOT|V_RCM_SET,
+ "[count]r character",
+ " r replace character"},
+/* 163 s */
+ {v_subst, V_CNT|V_DOT|V_OBUF|V_RCM_SET,
+ "[buffer][count]s",
+ " s substitute character"},
+/* 164 t */
+ {v_cht, V_CHAR|V_CNT|V_MOVE|V_RCM_SET,
+ "[count]t character",
+ " t before character in line forward search"},
+/* 165 u */
+ /*
+ * DON'T set the V_DOT flag, it' more complicated than that.
+ * See vi/vi.c for details.
+ */
+ {v_undo, V_RCM_SET,
+ "u",
+ " u undo last change"},
+/* 166 v */
+ {NULL},
+/* 167 w */
+ {v_wordw, V_CNT|V_MOVE|V_RCM_SET,
+ "[count]w",
+ " w move to next word"},
+/* 170 x */
+ {v_xchar, V_CNT|V_DOT|V_OBUF|V_RCM_SET,
+ "[buffer][count]x",
+ " x delete character"},
+/* 171 y */
+ {v_yank, V_CNT|V_MOTION|V_OBUF|V_RCM_SET|VC_Y,
+ "[buffer][count]y[count]motion",
+ " y copy text to motion into a cut buffer"},
+/* 172 z */
+ /*
+ * DON'T set the V_CHAR flag, the char isn't required,
+ * so it's handled specially in getcmd().
+ */
+ {v_z, V_CNT|V_RCM_SETFNB,
+ "[line]z[window_size][-|.|+|^|<CR>]",
+ " z redraw window"},
+/* 173 { */
+ {v_paragraphb, V_ABS|V_CNT|V_LMODE|V_MOVE|V_RCM_SET,
+ "[count]{",
+ " { move back paragraph"},
+/* 174 | */
+ {v_ncol, V_ABS|V_CNT|V_MOVE|V_RCM_SET,
+ "[count]|",
+ " | move to column"},
+/* 175 } */
+ {v_paragraphf, V_ABS|V_CNT|V_LMODE|V_MOVE|V_RCM_SET,
+ "[count]}",
+ " } move forward paragraph"},
+/* 176 ~ */
+ {v_ulcase, V_CNT|V_DOT|V_RCM_SET,
+ "[count]~",
+ " ~ reverse case"},
+};
diff --git a/usr.bin/vi/nvi/vcmd.h b/usr.bin/vi/nvi/vcmd.h
new file mode 100644
index 000000000000..d48fa26acf5e
--- /dev/null
+++ b/usr.bin/vi/nvi/vcmd.h
@@ -0,0 +1,274 @@
+/*-
+ * Copyright (c) 1992, 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.
+ *
+ * @(#)vcmd.h 8.23 (Berkeley) 1/8/94
+ */
+
+typedef struct _vikeys VIKEYS;
+
+/* Structure passed around to functions implementing vi commands. */
+typedef struct _vicmdarg {
+#define vp_startzero buffer /* START ZERO OUT. */
+ CHAR_T buffer; /* Buffer. */
+ CHAR_T character; /* Character. */
+ u_long count; /* Count. */
+ u_long count2; /* Second count (only used by z). */
+ int key; /* Command key. */
+ VIKEYS const *kp; /* VIKEYS key. */
+ VIKEYS const *mkp; /* VIKEYS motion key. */
+ size_t klen; /* Keyword length. */
+
+/*
+ * Historic vi allowed "dl" when the cursor was on the last column, deleting
+ * the last character, and similarly allowed "dw" when the cursor was on the
+ * last column of the file. It didn't allow "dh" when the cursor was on
+ * column 1, although these cases are not strictly analogous. The point is
+ * that some movements would succeed if they were associated with a motion
+ * command, and fail otherwise. This is part of the off-by-1 schizophrenia
+ * that plagued vi. Other examples are that "dfb" deleted everything up to
+ * and including the next 'b' character, but "d/b" only deleted everything
+ * up to the next 'b' character. While this implementation regularizes the
+ * interface to the extent possible, there are many special cases that can't
+ * be fixed. This is implemented by setting special flags per command so that
+ * the motion routines know what's really going on.
+ *
+ * Note, the VC_COMMASK flags are set in the vikeys array, and therefore
+ * must have values not used in the set of flags declared in the VIKEYS
+ * structure below.
+ */
+#define VC_C 0x0001 /* The 'c' command. */
+#define VC_D 0x0002 /* The 'd' command. */
+#define VC_SH 0x0004 /* The '>' command. */
+#define VC_Y 0x0008 /* The 'y' command. */
+#define VC_COMMASK 0x000f /* Mask for special flags. */
+
+#define VC_BUFFER 0x0010 /* Buffer set. */
+#define VC_C1SET 0x0020 /* Count 1 set. */
+#define VC_C1RESET 0x0040 /* Reset the C1SET flag for dot commands. */
+#define VC_C2SET 0x0080 /* Count 2 set. */
+#define VC_LMODE 0x0100 /* Motion is line oriented. */
+#define VC_ISDOT 0x0200 /* Command was the dot command. */
+#define VC_REVMOVE 0x0400 /* Movement was before the cursor. */
+
+ u_int flags;
+
+#define vp_endzero keyword /* END ZERO OUT. */
+ char *keyword; /* Keyword. */
+ size_t kbuflen; /* Keyword buffer length. */
+} VICMDARG;
+
+/* Vi command structure. */
+struct _vikeys { /* Underlying function. */
+ int (*func) __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, MARK *));
+
+#define V_DONTUSE1 0x000001 /* VC_C */
+#define V_DONTUSE2 0x000002 /* VC_D */
+#define V_DONTUSE3 0x000004 /* VC_SH */
+#define V_DONTUSE4 0x000008 /* VC_Y */
+#define V_ABS 0x000010 /* Absolute movement, set '' mark. */
+#define V_CHAR 0x000020 /* Character (required, trailing). */
+#define V_CNT 0x000040 /* Count (optional, leading). */
+#define V_DOT 0x000080 /* On success, sets dot command. */
+#define V_KEYNUM 0x000100 /* Cursor referenced number. */
+#define V_KEYW 0x000200 /* Cursor referenced word. */
+#define V_LMODE 0x000400 /* Motion is line oriented. */
+#define V_MOTION 0x000800 /* Motion (required, trailing). */
+#define V_MOVE 0x001000 /* Command defines movement. */
+#define V_OBUF 0x002000 /* Buffer (optional, leading). */
+#define V_RBUF 0x004000 /* Buffer (required, trailing). */
+#define V_RCM 0x008000 /* Use relative cursor movment (RCM). */
+#define V_RCM_SET 0x010000 /* RCM: set to current position. */
+#define V_RCM_SETFNB 0x020000 /* RCM: set to first non-blank (FNB). */
+#define V_RCM_SETLAST 0x040000 /* RCM: set to last character. */
+#define V_RCM_SETLFNB 0x080000 /* RCM: set to FNB if line moved. */
+#define V_RCM_SETNNB 0x100000 /* RCM: set to next non-blank. */
+ u_long flags;
+ char *usage; /* Usage line. */
+ char *help; /* Help line. */
+};
+#define MAXVIKEY 126 /* List of vi commands. */
+extern VIKEYS const vikeys[MAXVIKEY + 1];
+
+/* Definition of a "word". */
+#define inword(ch) (isalnum(ch) || (ch) == '_')
+
+/* Character stream structure, prototypes. */
+typedef struct _vcs {
+ recno_t cs_lno; /* Line. */
+ size_t cs_cno; /* Column. */
+ char *cs_bp; /* Buffer. */
+ size_t cs_len; /* Length. */
+ int cs_ch; /* Character. */
+#define CS_EMP 1 /* Empty line. */
+#define CS_EOF 2 /* End-of-file. */
+#define CS_EOL 3 /* End-of-line. */
+#define CS_SOF 4 /* Start-of-file. */
+ int cs_flags; /* Return flags. */
+} VCS;
+
+int cs_bblank __P((SCR *, EXF *, VCS *));
+int cs_fblank __P((SCR *, EXF *, VCS *));
+int cs_fspace __P((SCR *, EXF *, VCS *));
+int cs_init __P((SCR *, EXF *, VCS *));
+int cs_next __P((SCR *, EXF *, VCS *));
+int cs_prev __P((SCR *, EXF *, VCS *));
+
+/* Vi private, per-screen memory. */
+typedef struct _vi_private {
+ VICMDARG sdot; /* Saved dot, motion command. */
+ VICMDARG sdotmotion;
+
+ CHAR_T rlast; /* Last 'r' command character. */
+
+ char *rep; /* Input replay buffer. */
+ size_t rep_len; /* Input replay buffer length. */
+ size_t rep_cnt; /* Input replay buffer characters. */
+
+ CHAR_T inc_lastch; /* Last increment character. */
+ long inc_lastval; /* Last increment value. */
+
+ char *paragraph; /* Paragraph search list. */
+ size_t paragraph_len; /* Paragraph search list length. */
+
+ u_long u_ccnt; /* Undo command count. */
+} VI_PRIVATE;
+
+#define VIP(sp) ((VI_PRIVATE *)((sp)->vi_private))
+
+/* Vi function prototypes. */
+int txt_auto __P((SCR *, EXF *, recno_t, TEXT *, size_t, TEXT *));
+int v_buildparagraph __P((SCR *));
+int v_end __P((SCR *));
+void v_eof __P((SCR *, EXF *, MARK *));
+void v_eol __P((SCR *, EXF *, MARK *));
+int v_exwrite __P((void *, const char *, int));
+int v_init __P((SCR *, EXF *));
+int v_isempty __P((char *, size_t));
+int v_msgflush __P((SCR *));
+int v_ntext __P((SCR *, EXF *, TEXTH *, MARK *,
+ const char *, const size_t, MARK *, int, recno_t, u_int));
+int v_optchange __P((SCR *, int));
+int v_screen_copy __P((SCR *, SCR *));
+int v_screen_end __P((SCR *));
+void v_sof __P((SCR *, MARK *));
+int vi __P((SCR *, EXF *));
+
+#define VIPROTO(type, name) \
+ type name __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, MARK *))
+
+VIPROTO(int, v_again);
+VIPROTO(int, v_at);
+VIPROTO(int, v_bottom);
+VIPROTO(int, v_cfirst);
+VIPROTO(int, v_Change);
+VIPROTO(int, v_change);
+VIPROTO(int, v_chF);
+VIPROTO(int, v_chf);
+VIPROTO(int, v_chrepeat);
+VIPROTO(int, v_chrrepeat);
+VIPROTO(int, v_chT);
+VIPROTO(int, v_cht);
+VIPROTO(int, v_cr);
+VIPROTO(int, v_Delete);
+VIPROTO(int, v_delete);
+VIPROTO(int, v_dollar);
+VIPROTO(int, v_down);
+VIPROTO(int, v_ex);
+VIPROTO(int, v_exit);
+VIPROTO(int, v_exmode);
+VIPROTO(int, v_filter);
+VIPROTO(int, v_first);
+VIPROTO(int, v_gomark);
+VIPROTO(int, v_home);
+VIPROTO(int, v_hpagedown);
+VIPROTO(int, v_hpageup);
+VIPROTO(int, v_iA);
+VIPROTO(int, v_ia);
+VIPROTO(int, v_iI);
+VIPROTO(int, v_ii);
+VIPROTO(int, v_increment);
+VIPROTO(int, v_iO);
+VIPROTO(int, v_io);
+VIPROTO(int, v_join);
+VIPROTO(int, v_left);
+VIPROTO(int, v_lgoto);
+VIPROTO(int, v_linedown);
+VIPROTO(int, v_lineup);
+VIPROTO(int, v_mark);
+VIPROTO(int, v_match);
+VIPROTO(int, v_middle);
+VIPROTO(int, v_ncol);
+VIPROTO(int, v_pagedown);
+VIPROTO(int, v_pageup);
+VIPROTO(int, v_paragraphb);
+VIPROTO(int, v_paragraphf);
+VIPROTO(int, v_Put);
+VIPROTO(int, v_put);
+VIPROTO(int, v_redraw);
+VIPROTO(int, v_Replace);
+VIPROTO(int, v_replace);
+VIPROTO(int, v_right);
+VIPROTO(int, v_screen);
+VIPROTO(int, v_searchb);
+VIPROTO(int, v_searchf);
+VIPROTO(int, v_searchN);
+VIPROTO(int, v_searchn);
+VIPROTO(int, v_searchw);
+VIPROTO(int, v_sectionb);
+VIPROTO(int, v_sectionf);
+VIPROTO(int, v_sentenceb);
+VIPROTO(int, v_sentencef);
+VIPROTO(int, v_shiftl);
+VIPROTO(int, v_shiftr);
+VIPROTO(int, v_status);
+VIPROTO(int, v_stop);
+VIPROTO(int, v_Subst);
+VIPROTO(int, v_subst);
+VIPROTO(int, v_switch);
+VIPROTO(int, v_tagpop);
+VIPROTO(int, v_tagpush);
+VIPROTO(int, v_ulcase);
+VIPROTO(int, v_Undo);
+VIPROTO(int, v_undo);
+VIPROTO(int, v_up);
+VIPROTO(int, v_wordB);
+VIPROTO(int, v_wordb);
+VIPROTO(int, v_wordE);
+VIPROTO(int, v_worde);
+VIPROTO(int, v_wordW);
+VIPROTO(int, v_wordw);
+VIPROTO(int, v_Xchar);
+VIPROTO(int, v_xchar);
+VIPROTO(int, v_Yank);
+VIPROTO(int, v_yank);
+VIPROTO(int, v_z);
+VIPROTO(int, v_zero);
diff --git a/usr.bin/vi/nvi/vi.c b/usr.bin/vi/nvi/vi.c
new file mode 100644
index 000000000000..f969d78cfabf
--- /dev/null
+++ b/usr.bin/vi/nvi/vi.c
@@ -0,0 +1,780 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)vi.c 8.45 (Berkeley) 1/22/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+static int getcmd __P((SCR *, EXF *,
+ VICMDARG *, VICMDARG *, VICMDARG *, int *));
+static inline int
+ getcount __P((SCR *, ARG_CHAR_T, u_long *));
+static inline int
+ getkey __P((SCR *, CH *, u_int));
+static int getkeyword __P((SCR *, EXF *, VICMDARG *, u_int));
+static int getmotion __P((SCR *, EXF *,
+ VICMDARG *, VICMDARG *, MARK *, MARK *));
+
+/*
+ * Side-effect:
+ * The dot structure can be set by the underlying vi functions,
+ * see v_Put() and v_put().
+ */
+#define DOT (&VIP(sp)->sdot)
+#define DOTMOTION (&VIP(sp)->sdotmotion)
+
+/*
+ * vi --
+ * Main vi command loop.
+ */
+int
+vi(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ MARK abs, fm, tm, m;
+ VICMDARG cmd, *vp;
+ u_int flags, saved_mode;
+ int comcount, eval;
+
+ /* Start vi. */
+ if (v_init(sp, ep))
+ return (1);
+
+ /* Paint the screen. */
+ if (sp->s_refresh(sp, ep)) {
+ (void)v_end(sp);
+ return (1);
+ }
+
+ /* Command initialization. */
+ memset(&cmd, 0, sizeof(VICMDARG));
+
+ for (eval = 0, vp = &cmd;;) {
+ if (!MAPPED_KEYS_WAITING(sp) && log_cursor(sp, ep))
+ goto err;
+
+ /*
+ * We get a command, which may or may not have an associated
+ * motion. If it does, we get it too, calling its underlying
+ * function to get the resulting mark. We then call the
+ * command setting the cursor to the resulting mark.
+ */
+ if (getcmd(sp, ep, DOT, vp, NULL, &comcount))
+ goto err;
+
+ /*
+ * Historical practice: if a dot command gets a new count,
+ * any motion component goes away, i.e. "d3w2." deletes a
+ * total of 5 words.
+ */
+ if (F_ISSET(vp, VC_ISDOT) && comcount)
+ DOTMOTION->count = 1;
+
+ /* Get any associated keyword. */
+ flags = vp->kp->flags;
+ if (LF_ISSET(V_KEYNUM | V_KEYW) &&
+ getkeyword(sp, ep, vp, flags))
+ goto err;
+
+ /* If a non-relative movement, copy the future absolute mark. */
+ if (LF_ISSET(V_ABS)) {
+ abs.lno = sp->lno;
+ abs.cno = sp->cno;
+ }
+
+ /*
+ * Do any required motion; getmotion sets the from MARK
+ * and the line mode flag.
+ */
+ if (LF_ISSET(V_MOTION)) {
+ if (getmotion(sp, ep, DOTMOTION, vp, &fm, &tm))
+ goto err;
+ } else {
+ /*
+ * Set everything to the current cursor position.
+ * Line commands (ex: Y) default to the current line.
+ */
+ tm.lno = fm.lno = sp->lno;
+ tm.cno = fm.cno = sp->cno;
+
+ /*
+ * Set line mode flag, for example, "yy".
+ *
+ * If a count is set, we set the to MARK here relative
+ * to the cursor/from MARK. This is done for commands
+ * that take both counts and motions, i.e. "4yy" and
+ * "y%" -- there's no way the command can known which
+ * the user did, so we have to do it here. There are
+ * other commands that are line mode commands and take
+ * counts ("#G", "#H") and for which this calculation
+ * is either meaningless or wrong. Each command must
+ * do its own validity checking of the value.
+ */
+ if (F_ISSET(vp->kp, V_LMODE)) {
+ F_SET(vp, VC_LMODE);
+ if (F_ISSET(vp, VC_C1SET)) {
+ tm.lno = sp->lno + vp->count - 1;
+ tm.cno = sp->cno;
+ }
+ }
+ }
+
+ /* Increment the command count. */
+ ++sp->ccnt;
+
+ /*
+ * Call the function. Set the return cursor to the current
+ * cursor position first -- the underlying routines don't
+ * bother to do the work if it doesn't move.
+ */
+ m.lno = sp->lno;
+ m.cno = sp->cno;
+ saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE);
+ if ((vp->kp->func)(sp, ep, vp, &fm, &tm, &m))
+ goto err;
+#ifdef DEBUG
+ /* Make sure no function left the temporary space locked. */
+ if (F_ISSET(sp->gp, G_TMP_INUSE)) {
+ msgq(sp, M_ERR,
+ "Error: vi: temporary buffer not released.");
+ return (1);
+ }
+#endif
+ /*
+ * If that command took us out of vi or changed the screen,
+ * then exit the loop without further action.
+ */
+ if (saved_mode != F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE))
+ break;
+
+ /* Set the absolute mark. */
+ if (LF_ISSET(V_ABS) && mark_set(sp, ep, ABSMARK1, &abs, 1))
+ goto err;
+
+ /* Set the dot command structure. */
+ if (LF_ISSET(V_DOT)) {
+ *DOT = cmd;
+ F_SET(DOT, VC_ISDOT);
+ /*
+ * If a count was supplied for both the command and
+ * its motion, the count was used only for the motion.
+ * Turn the count back on for the dot structure.
+ */
+ if (F_ISSET(vp, VC_C1RESET))
+ F_SET(DOT, VC_C1SET);
+ }
+
+ /*
+ * Some vi row movements are "attracted" to the last position
+ * set, i.e. the V_RCM commands are moths to the V_RCM_SET
+ * commands' candle. It's broken into two parts. Here we deal
+ * with the command flags. In sp->relative(), we deal with the
+ * screen flags. If the movement is to the EOL the vi command
+ * handles it. If it's to the beginning, we handle it here.
+ *
+ * Note, some commands (e.g. _, ^) don't set the V_RCM_SETFNB
+ * flag, but do the work themselves. The reason is that they
+ * have to modify the column in case they're being used as a
+ * motion component. Other similar commands (e.g. +, -) don't
+ * have to modify the column because they are always line mode
+ * operations when used as motions, so the column number isn't
+ * of any interest.
+ *
+ * Does this totally violate the screen and editor layering?
+ * You betcha. As they say, if you think you understand it,
+ * you don't.
+ */
+ switch (LF_ISSET(V_RCM | V_RCM_SETFNB |
+ V_RCM_SETLAST | V_RCM_SETLFNB | V_RCM_SETNNB)) {
+ case 0:
+ break;
+ case V_RCM:
+ m.cno = sp->s_relative(sp, ep, m.lno);
+ break;
+ case V_RCM_SETLAST:
+ sp->rcmflags = RCM_LAST;
+ break;
+ case V_RCM_SETLFNB:
+ if (fm.lno != m.lno) {
+ if (nonblank(sp, ep, m.lno, &m.cno))
+ goto err;
+ sp->rcmflags = RCM_FNB;
+ }
+ break;
+ case V_RCM_SETFNB:
+ m.cno = 0;
+ /* FALLTHROUGH */
+ case V_RCM_SETNNB:
+ if (nonblank(sp, ep, m.lno, &m.cno))
+ goto err;
+ sp->rcmflags = RCM_FNB;
+ break;
+ default:
+ abort();
+ }
+
+ /* Update the cursor. */
+ sp->lno = m.lno;
+ sp->cno = m.cno;
+
+ if (!MAPPED_KEYS_WAITING(sp)) {
+ (void)msg_rpt(sp, 1);
+
+ if (0)
+err: term_map_flush(sp, "Vi error");
+ }
+
+ /* Refresh the screen. */
+ if (sp->s_refresh(sp, ep)) {
+ eval = 1;
+ break;
+ }
+
+ /* Set the new favorite position. */
+ if (LF_ISSET(V_RCM_SET)) {
+ sp->rcmflags = 0;
+ (void)sp->s_column(sp, ep, &sp->rcm);
+ }
+ }
+
+ return (v_end(sp) || eval);
+}
+
+#define KEY(key, map) { \
+ if (getkey(sp, &ikey, map)) \
+ return (1); \
+ key = ikey.ch; \
+}
+
+/*
+ * getcmd --
+ *
+ * The command structure for vi is less complex than ex (and don't think
+ * I'm not grateful!) The command syntax is:
+ *
+ * [count] [buffer] [count] key [[motion] | [buffer] [character]]
+ *
+ * and there are several special cases. The motion value is itself a vi
+ * command, with the syntax:
+ *
+ * [count] key [character]
+ */
+static int
+getcmd(sp, ep, dp, vp, ismotion, comcountp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *dp, *vp;
+ VICMDARG *ismotion; /* Previous key if getting motion component. */
+ int *comcountp;
+{
+ VIKEYS const *kp;
+ u_int flags;
+ CH ikey;
+ CHAR_T key;
+
+ /* Refresh the command structure. */
+ memset(&vp->vp_startzero, 0,
+ (char *)&vp->vp_endzero - (char *)&vp->vp_startzero);
+
+ /* An escape bells the user if in command mode. */
+ if (getkey(sp, &ikey, TXT_MAPCOMMAND)) {
+ if (ikey.value == K_ESCAPE && ismotion == NULL)
+ msgq(sp, M_BERR, "Already in command mode");
+ return (1);
+ }
+
+ key = ikey.ch;
+ if (key > MAXVIKEY) {
+ msgq(sp, M_BERR, "%s isn't a vi command", charname(sp, key));
+ return (1);
+ }
+
+ /* Pick up optional buffer. */
+ if (key == '"') {
+ KEY(vp->buffer, 0);
+ F_SET(vp, VC_BUFFER);
+ KEY(key, TXT_MAPCOMMAND);
+ }
+
+ /*
+ * Pick up optional count, where a leading 0 is not a count,
+ * it's a command.
+ */
+ if (isdigit(key) && key != '0') {
+ if (getcount(sp, key, &vp->count))
+ return (1);
+ F_SET(vp, VC_C1SET);
+ *comcountp = 1;
+ KEY(key, TXT_MAPCOMMAND);
+ } else
+ *comcountp = 0;
+
+ /* Pick up optional buffer. */
+ if (key == '"') {
+ if (F_ISSET(vp, VC_BUFFER)) {
+ msgq(sp, M_ERR, "Only one buffer can be specified.");
+ return (1);
+ }
+ KEY(vp->buffer, 0);
+ F_SET(vp, VC_BUFFER);
+ KEY(key, TXT_MAPCOMMAND);
+ }
+
+ /*
+ * Find the command. The only legal command with no underlying
+ * function is dot.
+ */
+ kp = vp->kp = &vikeys[vp->key = key];
+ if (kp->func == NULL) {
+ if (key != '.') {
+ msgq(sp, M_ERR,
+ "%s isn't a command", charname(sp, key));
+ return (1);
+ }
+
+ /* If called for a motion command, stop now. */
+ if (dp == NULL)
+ goto usage;
+
+ /* A repeatable command must have been executed. */
+ if (!F_ISSET(dp, VC_ISDOT)) {
+ msgq(sp, M_ERR, "No command to repeat.");
+ return (1);
+ }
+
+ /*
+ * !!!
+ * If a '.' is immediately entered after an undo command, we
+ * replay the log instead of redoing the last command. This
+ * is necessary because 'u' can't set the dot command -- see
+ * vi/v_undo.c:v_undo for details.
+ */
+ if (VIP(sp)->u_ccnt == sp->ccnt) {
+ vp->kp = &vikeys['u'];
+ F_SET(vp, VC_ISDOT);
+ return (0);
+ }
+
+ /* Set new count/buffer, if any, and return. */
+ if (F_ISSET(vp, VC_C1SET)) {
+ F_SET(dp, VC_C1SET);
+ dp->count = vp->count;
+ }
+ if (F_ISSET(vp, VC_BUFFER))
+ dp->buffer = vp->buffer;
+ *vp = *dp;
+ return (0);
+ }
+
+ flags = kp->flags;
+
+ /* Check for illegal count. */
+ if (F_ISSET(vp, VC_C1SET) && !LF_ISSET(V_CNT))
+ goto usage;
+
+ /* Illegal motion command. */
+ if (ismotion == NULL) {
+ /* Illegal buffer. */
+ if (!LF_ISSET(V_OBUF) && F_ISSET(vp, VC_BUFFER))
+ goto usage;
+
+ /* Required buffer. */
+ if (LF_ISSET(V_RBUF))
+ KEY(vp->buffer, 0);
+
+ /*
+ * Special case: '[', ']' and 'Z' commands. Doesn't the
+ * fact that the *single* characters don't mean anything
+ * but the *doubled* characters do just frost your shorts?
+ */
+ if (vp->key == '[' || vp->key == ']' || vp->key == 'Z') {
+ KEY(key, TXT_MAPCOMMAND);
+ if (vp->key != key)
+ goto usage;
+ }
+ /* Special case: 'z' command. */
+ if (vp->key == 'z') {
+ KEY(vp->character, 0);
+ if (isdigit(vp->character)) {
+ if (getcount(sp, vp->character, &vp->count2))
+ return (1);
+ F_SET(vp, VC_C2SET);
+ KEY(vp->character, 0);
+ }
+ }
+ }
+
+ /*
+ * Commands that have motion components can be doubled to
+ * imply the current line.
+ */
+ else if (ismotion->key != key && !LF_ISSET(V_MOVE)) {
+usage: msgq(sp, M_ERR, "Usage: %s", ismotion != NULL ?
+ vikeys[ismotion->key].usage : kp->usage);
+ return (1);
+ }
+
+ /* Required character. */
+ if (LF_ISSET(V_CHAR))
+ KEY(vp->character, 0);
+
+ return (0);
+}
+
+/*
+ * getmotion --
+ *
+ * Get resulting motion mark.
+ */
+static int
+getmotion(sp, ep, dm, vp, fm, tm)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *dm, *vp;
+ MARK *fm, *tm;
+{
+ MARK m;
+ VICMDARG motion;
+ u_long cnt;
+ int notused;
+
+ /* If '.' command, use the dot motion, else get the motion command. */
+ if (F_ISSET(vp, VC_ISDOT)) {
+ motion = *dm;
+ F_SET(&motion, VC_ISDOT);
+ } else if (getcmd(sp, ep, NULL, &motion, vp, &notused))
+ return (1);
+
+ /*
+ * A count may be provided both to the command and to the motion, in
+ * which case the count is multiplicative. For example, "3y4y" is the
+ * same as "12yy". This count is provided to the motion command and
+ * not to the regular function.
+ */
+ cnt = motion.count = F_ISSET(&motion, VC_C1SET) ? motion.count : 1;
+ if (F_ISSET(vp, VC_C1SET)) {
+ motion.count *= vp->count;
+ F_SET(&motion, VC_C1SET);
+
+ /*
+ * Set flags to restore the original values of the command
+ * structure so dot commands can change the count values,
+ * e.g. "2dw" "3." deletes a total of five words.
+ */
+ F_CLR(vp, VC_C1SET);
+ F_SET(vp, VC_C1RESET);
+ }
+
+ /*
+ * Some commands can be repeated to indicate the current line. In
+ * this case, or if the command is a "line command", set the flags
+ * appropriately. If not a doubled command, run the function to get
+ * the resulting mark.
+ */
+ if (vp->key == motion.key) {
+ F_SET(vp, VC_LMODE);
+
+ /*
+ * Set the end of the command; the column is after the line.
+ *
+ * If the current line is missing, i.e. the file is empty,
+ * historic vi permitted a "cc" or "!!" command to insert
+ * text.
+ */
+ tm->lno = sp->lno + motion.count - 1;
+ if (file_gline(sp, ep, tm->lno, &tm->cno) == NULL) {
+ if (tm->lno != 1 || vp->key != 'c' && vp->key != '!') {
+ m.lno = sp->lno;
+ m.cno = sp->cno;
+ v_eof(sp, ep, &m);
+ return (1);
+ }
+ tm->cno = 0;
+ }
+
+ /* Set the origin of the command. */
+ fm->lno = sp->lno;
+ fm->cno = 0;
+ } else {
+ /*
+ * Motion commands change the underlying movement (*snarl*).
+ * For example, "l" is illegal at the end of a line, but "dl"
+ * is not. Set flags so the function knows the situation.
+ */
+ F_SET(&motion, vp->kp->flags & VC_COMMASK);
+
+ /*
+ * Everything starts at the current position. This permits
+ * commands like 'j' and 'k', that are line oriented motions
+ * and have special cursor suck semantics when they are used
+ * as standalone commands, to ignore column positioning.
+ */
+ fm->lno = tm->lno = sp->lno;
+ fm->cno = tm->cno = sp->cno;
+ if ((motion.kp->func)(sp, ep, &motion, fm, NULL, tm))
+ return (1);
+
+ /*
+ * If the underlying motion was a line motion, set the flag
+ * in the command structure. Underlying commands can also
+ * flag the movement as a line motion (see v_sentence).
+ */
+ if (F_ISSET(motion.kp, V_LMODE) || F_ISSET(&motion, VC_LMODE))
+ F_SET(vp, VC_LMODE);
+
+ /*
+ * If the motion is in the reverse direction, switch the from
+ * and to MARK's so that it's always in a forward direction.
+ * Because the motion is always from the from MARK to, but not
+ * including, the to MARK, the function may have modified the
+ * from MARK, so that it gets the one-past-the-place semantics
+ * we use; see v_match() for an example. Also set a flag so
+ * that the underlying function knows that we did this; v_yank,
+ * for example, has to know so it gets the return cursor right.
+ */
+ if (tm->lno < fm->lno ||
+ tm->lno == fm->lno && tm->cno < fm->cno) {
+ m = *fm;
+ *fm = *tm;
+ *tm = m;
+ F_SET(vp, VC_REVMOVE);
+ }
+ }
+
+ /*
+ * If the command sets dot, save the motion structure. The
+ * motion count was changed above and needs to be reset, that's
+ * why this is done here, and not in the calling routine.
+ */
+ if (F_ISSET(vp->kp, V_DOT)) {
+ *dm = motion;
+ dm->count = cnt;
+ }
+
+ /* Let the underlying function know what motion command was used. */
+ vp->mkp = motion.kp;
+ return (0);
+}
+
+#define innum(c) (isdigit(c) || strchr("abcdefABCDEF", c))
+
+/*
+ * getkeyword --
+ * Get the "word" the cursor is on.
+ */
+static int
+getkeyword(sp, ep, kp, flags)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *kp;
+ u_int flags;
+{
+ recno_t lno;
+ size_t beg, end, len;
+ char *p;
+
+ if ((p = file_gline(sp, ep, sp->lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ v_eof(sp, ep, NULL);
+ else
+ GETLINE_ERR(sp, sp->lno);
+ return (1);
+ }
+ beg = sp->cno;
+
+ /* May not be a keyword at all. */
+ if (p == NULL || len == 0 ||
+ LF_ISSET(V_KEYW) && !inword(p[beg]) ||
+ LF_ISSET(V_KEYNUM) && !innum(p[beg]) &&
+ p[beg] != '-' && p[beg] != '+') {
+noword: msgq(sp, M_BERR, "Cursor not in a %s",
+ LF_ISSET(V_KEYW) ? "word" : "number");
+ return (1);
+ }
+
+ /*
+ * !!!
+ * Find the beginning/end of the keyword. Keywords (V_KEYW) are
+ * used for cursor-word searching and for tags. Historical vi
+ * only used the word in a tag search from the cursor to the end
+ * of the word, i.e. if the cursor was on the 'b' in " abc ", the
+ * tag was "bc". For no particular reason, we make cursor word
+ * searches follow the same rule.
+ */
+ if (beg != 0)
+ if (LF_ISSET(V_KEYW)) {
+#ifdef MOVE_TO_KEYWORD_BEGINNING
+ for (;;) {
+ --beg;
+ if (!inword(p[beg])) {
+ ++beg;
+ break;
+ }
+ if (beg == 0)
+ break;
+ }
+#endif
+ } else {
+ for (;;) {
+ --beg;
+ if (!innum(p[beg])) {
+ if (beg > 0 && p[beg - 1] == '0' &&
+ (p[beg] == 'X' || p[beg] == 'x'))
+ --beg;
+ else
+ ++beg;
+ break;
+ }
+ if (beg == 0)
+ break;
+ }
+
+ /* Skip possible leading sign. */
+ if (beg != 0 && p[beg] != '0' &&
+ (p[beg - 1] == '+' || p[beg - 1] == '-'))
+ --beg;
+ }
+
+ if (LF_ISSET(V_KEYW)) {
+ for (end = sp->cno; ++end < len && inword(p[end]););
+ --end;
+ } else {
+ for (end = sp->cno; ++end < len;) {
+ if (p[end] == 'X' || p[end] == 'x') {
+ if (end != beg + 1 || p[beg] != '0')
+ break;
+ continue;
+ }
+ if (!innum(p[end]))
+ break;
+ }
+
+ /* Just a sign isn't a number. */
+ if (end == beg && (p[beg] == '+' || p[beg] == '-'))
+ goto noword;
+ --end;
+ }
+
+ /*
+ * Getting a keyword implies moving the cursor to its beginning.
+ * Refresh now.
+ */
+ if (beg != sp->cno) {
+ sp->cno = beg;
+ sp->s_refresh(sp, ep);
+ }
+
+ /*
+ * XXX
+ * 8-bit clean problem. Numeric keywords are handled using strtol(3)
+ * and friends. This would have to be fixed in v_increment and here
+ * to not depend on a trailing NULL.
+ */
+ len = (end - beg) + 2; /* XXX */
+ kp->klen = (end - beg) + 1;
+ BINC_RET(sp, kp->keyword, kp->kbuflen, len);
+ memmove(kp->keyword, p + beg, kp->klen);
+ kp->keyword[kp->klen] = '\0'; /* XXX */
+ return (0);
+}
+
+/*
+ * getcount --
+ * Return the next count.
+ */
+static inline int
+getcount(sp, fkey, countp)
+ SCR *sp;
+ ARG_CHAR_T fkey;
+ u_long *countp;
+{
+ u_long count, tc;
+ CH ikey;
+
+ ikey.ch = fkey;
+ count = tc = 0;
+ do {
+ /* Assume that overflow results in a smaller number. */
+ tc = count * 10 + ikey.ch - '0';
+ if (count > tc) {
+ /* Toss to the next non-digit. */
+ do {
+ if (getkey(sp, &ikey,
+ TXT_MAPCOMMAND | TXT_MAPNODIGIT))
+ return (1);
+ } while (isdigit(ikey.ch));
+ msgq(sp, M_ERR, "Number larger than %lu", ULONG_MAX);
+ return (1);
+ }
+ count = tc;
+ if (getkey(sp, &ikey, TXT_MAPCOMMAND | TXT_MAPNODIGIT))
+ return (1);
+ } while (isdigit(ikey.ch));
+ *countp = count;
+ return (0);
+}
+
+/*
+ * getkey --
+ * Return the next key.
+ */
+static inline int
+getkey(sp, ikeyp, map)
+ SCR *sp;
+ CH *ikeyp;
+ u_int map;
+{
+ switch (term_key(sp, ikeyp, map)) {
+ case INP_OK:
+ break;
+ case INP_EOF:
+ F_SET(sp, S_EXIT_FORCE);
+ /* FALLTHROUGH */
+ case INP_ERR:
+ return (1);
+ }
+ return (ikeyp->value == K_ESCAPE);
+}
diff --git a/usr.bin/vi/options.c b/usr.bin/vi/options.c
new file mode 100644
index 000000000000..9c74160c5c01
--- /dev/null
+++ b/usr.bin/vi/options.c
@@ -0,0 +1,822 @@
+/*-
+ * Copyright (c) 1991, 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
+static char sccsid[] = "@(#)options.c 8.36 (Berkeley) 12/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <curses.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "pathnames.h"
+
+static int opts_abbcmp __P((const void *, const void *));
+static int opts_cmp __P((const void *, const void *));
+static OPTLIST const *opts_prefix __P((char *));
+static int opts_print __P((SCR *, OPTLIST const *, OPTION *));
+
+/*
+ * O'Reilly noted options and abbreviations are from "Learning the VI Editor",
+ * Fifth Edition, May 1992. There's no way of knowing what systems they are
+ * actually from.
+ *
+ * HPUX noted options and abbreviations are from "The Ultimate Guide to the
+ * VI and EX Text Editors", 1990.
+ */
+static OPTLIST const optlist[] = {
+/* O_ALTWERASE 4.4BSD */
+ {"altwerase", f_altwerase, OPT_0BOOL, 0},
+/* O_AUTOINDENT 4BSD */
+ {"autoindent", NULL, OPT_0BOOL, 0},
+/* O_AUTOPRINT 4BSD */
+ {"autoprint", NULL, OPT_1BOOL, 0},
+/* O_AUTOWRITE 4BSD */
+ {"autowrite", NULL, OPT_0BOOL, 0},
+/* O_BEAUTIFY 4BSD */
+ {"beautify", NULL, OPT_0BOOL, 0},
+/* O_COLUMNS 4.4BSD */
+ {"columns", f_columns, OPT_NUM, OPT_NOSAVE},
+/* O_COMMENT 4.4BSD */
+ {"comment", NULL, OPT_0BOOL, 0},
+/* O_DIGRAPH XXX: Elvis */
+ {"digraph", NULL, OPT_0BOOL, 0},
+/* O_DIRECTORY 4BSD */
+ {"directory", NULL, OPT_STR, 0},
+/* O_EDCOMPATIBLE 4BSD */
+ {"edcompatible",NULL, OPT_0BOOL, 0},
+/* O_ERRORBELLS 4BSD */
+ {"errorbells", NULL, OPT_0BOOL, 0},
+/* O_EXRC System V (undocumented) */
+ {"exrc", NULL, OPT_0BOOL, 0},
+/* O_EXTENDED 4.4BSD */
+ {"extended", NULL, OPT_0BOOL, 0},
+/* O_FLASH HPUX */
+ {"flash", NULL, OPT_1BOOL, 0},
+/* O_HARDTABS 4BSD */
+ {"hardtabs", NULL, OPT_NUM, 0},
+/* O_IGNORECASE 4BSD */
+ {"ignorecase", NULL, OPT_0BOOL, 0},
+/* O_KEYTIME 4.4BSD */
+ {"keytime", f_keytime, OPT_NUM, 0},
+/* O_LEFTRIGHT 4.4BSD */
+ {"leftright", f_leftright, OPT_0BOOL, 0},
+/* O_LINES 4.4BSD */
+ {"lines", f_lines, OPT_NUM, OPT_NOSAVE},
+/* O_LISP 4BSD */
+ {"lisp", f_lisp, OPT_0BOOL, 0},
+/* O_LIST 4BSD */
+ {"list", f_list, OPT_0BOOL, 0},
+/* O_MAGIC 4BSD */
+ {"magic", NULL, OPT_1BOOL, 0},
+/* O_MATCHTIME 4.4BSD */
+ {"matchtime", f_matchtime, OPT_NUM, 0},
+/* O_MESG 4BSD */
+ {"mesg", f_mesg, OPT_1BOOL, 0},
+/* O_MODELINE 4BSD */
+ {"modeline", f_modeline, OPT_0BOOL, 0},
+/* O_NUMBER 4BSD */
+ {"number", f_number, OPT_0BOOL, 0},
+/* O_OPEN 4BSD */
+ {"open", NULL, OPT_1BOOL, 0},
+/* O_OPTIMIZE 4BSD */
+ {"optimize", f_optimize, OPT_1BOOL, 0},
+/* O_PARAGRAPHS 4BSD */
+ {"paragraphs", f_paragraph, OPT_STR, 0},
+/* O_PROMPT 4BSD */
+ {"prompt", NULL, OPT_1BOOL, 0},
+/* O_READONLY 4BSD (undocumented) */
+ {"readonly", f_readonly, OPT_0BOOL, 0},
+/* O_RECDIR 4.4BSD */
+ {"recdir", NULL, OPT_STR, 0},
+/* O_REDRAW 4BSD */
+ {"redraw", NULL, OPT_0BOOL, 0},
+/* O_REMAP 4BSD */
+ {"remap", NULL, OPT_1BOOL, 0},
+/* O_REPORT 4BSD */
+ {"report", NULL, OPT_NUM, OPT_NOSTR},
+/* O_RULER 4.4BSD */
+ {"ruler", f_ruler, OPT_0BOOL, 0},
+/* O_SCROLL 4BSD */
+ {"scroll", NULL, OPT_NUM, 0},
+/* O_SECTIONS 4BSD */
+ {"sections", f_section, OPT_STR, 0},
+/* O_SHELL 4BSD */
+ {"shell", NULL, OPT_STR, 0},
+/* O_SHIFTWIDTH 4BSD */
+ {"shiftwidth", f_shiftwidth, OPT_NUM, 0},
+/* O_SHOWDIRTY 4.4BSD */
+ {"showdirty", NULL, OPT_0BOOL, 0},
+/* O_SHOWMATCH 4BSD */
+ {"showmatch", NULL, OPT_0BOOL, 0},
+/* O_SHOWMODE 4.4BSD */
+ {"showmode", NULL, OPT_0BOOL, 0},
+/* O_SIDESCROLL 4.4BSD */
+ {"sidescroll", f_sidescroll, OPT_NUM, 0},
+/* O_SLOWOPEN 4BSD */
+ {"slowopen", NULL, OPT_0BOOL, 0},
+/* O_SOURCEANY 4BSD (undocumented) */
+ {"sourceany", f_sourceany, OPT_0BOOL, 0},
+/* O_TABSTOP 4BSD */
+ {"tabstop", f_tabstop, OPT_NUM, 0},
+/* O_TAGLENGTH 4BSD */
+ {"taglength", NULL, OPT_NUM, OPT_NOSTR},
+/* O_TAGS 4BSD */
+ {"tags", f_tags, OPT_STR, 0},
+/* O_TERM 4BSD */
+ {"term", f_term, OPT_STR, OPT_NOSAVE},
+/* O_TERSE 4BSD */
+ {"terse", NULL, OPT_0BOOL, 0},
+/* O_TIMEOUT 4BSD (undocumented) */
+ {"timeout", NULL, OPT_1BOOL, 0},
+/* O_TTYWERASE 4.4BSD */
+ {"ttywerase", f_ttywerase, OPT_0BOOL, 0},
+/* O_VERBOSE 4.4BSD */
+ {"verbose", NULL, OPT_0BOOL, 0},
+/* O_W1200 4BSD */
+ {"w1200", f_w1200, OPT_NUM, OPT_NEVER},
+/* O_W300 4BSD */
+ {"w300", f_w300, OPT_NUM, OPT_NEVER},
+/* O_W9600 4BSD */
+ {"w9600", f_w9600, OPT_NUM, OPT_NEVER},
+/* O_WARN 4BSD */
+ {"warn", NULL, OPT_1BOOL, 0},
+/* O_WINDOW 4BSD */
+ {"window", f_window, OPT_NUM, 0},
+/* O_WRAPMARGIN 4BSD */
+ {"wrapmargin", f_wrapmargin, OPT_NUM, OPT_NOSTR},
+/* O_WRAPSCAN 4BSD */
+ {"wrapscan", NULL, OPT_1BOOL, 0},
+/* O_WRITEANY 4BSD */
+ {"writeany", NULL, OPT_0BOOL, 0},
+ {NULL},
+};
+
+typedef struct abbrev {
+ char *name;
+ int offset;
+} OABBREV;
+
+static OABBREV const abbrev[] = {
+ {"ai", O_AUTOINDENT}, /* 4BSD */
+ {"ap", O_AUTOPRINT}, /* 4BSD */
+ {"aw", O_AUTOWRITE}, /* 4BSD */
+ {"bf", O_BEAUTIFY}, /* 4BSD */
+ {"co", O_COLUMNS}, /* 4.4BSD */
+ {"dir", O_DIRECTORY}, /* 4BSD */
+ {"eb", O_ERRORBELLS}, /* 4BSD */
+ {"ed", O_EDCOMPATIBLE}, /* 4BSD (undocumented) */
+ {"ex", O_EXRC}, /* System V (undocumented) */
+ {"ht", O_HARDTABS}, /* 4BSD */
+ {"ic", O_IGNORECASE}, /* 4BSD */
+ {"li", O_LINES}, /* 4.4BSD */
+ {"modelines", O_MODELINE}, /* HPUX */
+ {"nu", O_NUMBER}, /* 4BSD */
+ {"opt", O_OPTIMIZE}, /* 4BSD */
+ {"para", O_PARAGRAPHS}, /* 4BSD */
+ {"re", O_REDRAW}, /* O'Reilly */
+ {"ro", O_READONLY}, /* 4BSD (undocumented) */
+ {"scr", O_SCROLL}, /* 4BSD (undocumented) */
+ {"sect", O_SECTIONS}, /* O'Reilly */
+ {"sh", O_SHELL}, /* 4BSD */
+ {"slow", O_SLOWOPEN}, /* 4BSD */
+ {"sm", O_SHOWMATCH}, /* 4BSD */
+ {"sw", O_SHIFTWIDTH}, /* 4BSD */
+ {"tag", O_TAGS}, /* 4BSD (undocumented) */
+ {"tl", O_TAGLENGTH}, /* 4BSD */
+ {"to", O_TIMEOUT}, /* 4BSD (undocumented) */
+ {"ts", O_TABSTOP}, /* 4BSD */
+ {"tty", O_TERM}, /* 4BSD (undocumented) */
+ {"ttytype", O_TERM}, /* 4BSD (undocumented) */
+ {"w", O_WINDOW}, /* O'Reilly */
+ {"wa", O_WRITEANY}, /* 4BSD */
+ {"wi", O_WINDOW}, /* 4BSD (undocumented) */
+ {"wm", O_WRAPMARGIN}, /* 4BSD */
+ {"ws", O_WRAPSCAN}, /* 4BSD */
+ {NULL},
+};
+
+/*
+ * opts_init --
+ * Initialize some of the options. Since the user isn't really
+ * "setting" these variables, don't set their OPT_SET bits.
+ */
+int
+opts_init(sp)
+ SCR *sp;
+{
+ ARGS *argv[2], a, b;
+ OPTLIST const *op;
+ u_long v;
+ int cnt;
+ char *s, b1[1024];
+
+ a.bp = b1;
+ a.len = 0;
+ b.bp = NULL;
+ b.len = 0;
+ argv[0] = &a;
+ argv[1] = &b;
+
+#define SET_DEF(opt, str) { \
+ if (str != b1) /* GCC puts strings in text-space. */ \
+ (void)strcpy(b1, str); \
+ a.len = strlen(b1); \
+ if (opts_set(sp, argv)) { \
+ msgq(sp, M_ERR, \
+ "Unable to set default %s option", optlist[opt]); \
+ return (1); \
+ } \
+ F_CLR(&sp->opts[opt], OPT_SET); \
+}
+ /* Set default values. */
+ for (op = optlist, cnt = 0; op->name != NULL; ++op, ++cnt)
+ if (op->type == OPT_0BOOL)
+ O_CLR(sp, cnt);
+ else if (op->type == OPT_1BOOL)
+ O_SET(sp, cnt);
+
+ /*
+ * !!!
+ * Vi historically stored temporary files in /var/tmp. We store them
+ * in /tmp by default, hoping it's a memory based file system. There
+ * are two ways to change this -- the user can set either the directory
+ * option or the TMPDIR environmental variable.
+ */
+ (void)snprintf(b1, sizeof(b1), "directory=%s",
+ (s = getenv("TMPDIR")) == NULL ? _PATH_TMP : s);
+ SET_DEF(O_DIRECTORY, b1);
+ SET_DEF(O_KEYTIME, "keytime=6");
+ SET_DEF(O_MATCHTIME, "matchtime=7");
+ SET_DEF(O_REPORT, "report=5");
+ SET_DEF(O_PARAGRAPHS, "paragraphs=IPLPPPQPP LIpplpipbp");
+ (void)snprintf(b1, sizeof(b1), "recdir=%s", _PATH_PRESERVE);
+ SET_DEF(O_RECDIR, b1);
+ (void)snprintf(b1, sizeof(b1), "scroll=%ld", O_VAL(sp, O_LINES) / 2);
+ SET_DEF(O_SCROLL, b1);
+ SET_DEF(O_SECTIONS, "sections=NHSHH HUnhsh");
+ (void)snprintf(b1, sizeof(b1),
+ "shell=%s", (s = getenv("SHELL")) == NULL ? _PATH_BSHELL : s);
+ SET_DEF(O_SHELL, b1);
+ SET_DEF(O_SHIFTWIDTH, "shiftwidth=8");
+ SET_DEF(O_SIDESCROLL, "sidescroll=16");
+ SET_DEF(O_TABSTOP, "tabstop=8");
+ (void)snprintf(b1, sizeof(b1), "tags=%s", _PATH_TAGS);
+ SET_DEF(O_TAGS, b1);
+ (void)snprintf(b1, sizeof(b1),
+ "term=%s", (s = getenv("TERM")) == NULL ? "unknown" : s);
+ SET_DEF(O_TERM, b1);
+
+ /*
+ * The default window option values are:
+ * 8 if baud rate <= 600
+ * 16 if baud rate <= 1200
+ * LINES - 1 if baud rate > 1200
+ */
+ v = baud_from_bval(sp);
+ if (v <= 600)
+ v = 8;
+ else if (v <= 1200)
+ v = 16;
+ else
+ v = O_VAL(sp, O_LINES) - 1;
+ (void)snprintf(b1, sizeof(b1), "window=%lu", v);
+ SET_DEF(O_WINDOW, b1);
+
+ SET_DEF(O_WRAPMARGIN, "wrapmargin=0");
+
+ /*
+ * By default, the historic vi always displayed information
+ * about two options, redraw and term. Term seems sufficient.
+ */
+ F_SET(&sp->opts[O_TERM], OPT_SET);
+ return (0);
+}
+
+/*
+ * opts_set --
+ * Change the values of one or more options.
+ */
+int
+opts_set(sp, argv)
+ SCR *sp;
+ ARGS *argv[];
+{
+ enum optdisp disp;
+ OABBREV atmp, *ap;
+ OPTLIST const *op;
+ OPTLIST otmp;
+ OPTION *spo;
+ u_long value, turnoff;
+ int ch, offset, rval;
+ char *endp, *equals, *name, *p;
+
+ disp = NO_DISPLAY;
+ for (rval = 0; (*argv)->len != 0; ++argv) {
+ /*
+ * The historic vi dumped the options for each occurrence of
+ * "all" in the set list. Puhleeze.
+ */
+ if (!strcmp(argv[0]->bp, "all")) {
+ disp = ALL_DISPLAY;
+ continue;
+ }
+
+ /* Find equals sign or end of set, skipping backquoted chars. */
+ for (p = name = argv[0]->bp, equals = NULL; ch = *p; ++p)
+ switch(ch) {
+ case '=':
+ equals = p;
+ break;
+ case '\\':
+ /* Historic vi just used the backslash. */
+ if (p[1] == '\0')
+ break;
+ ++p;
+ break;
+ }
+
+ turnoff = 0;
+ op = NULL;
+ if (equals)
+ *equals++ = '\0';
+
+ /* Check list of abbreviations. */
+ atmp.name = name;
+ if ((ap = bsearch(&atmp, abbrev,
+ sizeof(abbrev) / sizeof(OABBREV) - 1,
+ sizeof(OABBREV), opts_abbcmp)) != NULL) {
+ op = optlist + ap->offset;
+ goto found;
+ }
+
+ /* Check list of options. */
+ otmp.name = name;
+ if ((op = bsearch(&otmp, optlist,
+ sizeof(optlist) / sizeof(OPTLIST) - 1,
+ sizeof(OPTLIST), opts_cmp)) != NULL)
+ goto found;
+
+ /* Try the name without any leading "no". */
+ if (name[0] == 'n' && name[1] == 'o') {
+ turnoff = 1;
+ name += 2;
+ } else
+ goto prefix;
+
+ /* Check list of abbreviations. */
+ atmp.name = name;
+ if ((ap = bsearch(&atmp, abbrev,
+ sizeof(abbrev) / sizeof(OABBREV) - 1,
+ sizeof(OABBREV), opts_abbcmp)) != NULL) {
+ op = optlist + ap->offset;
+ goto found;
+ }
+
+ /* Check list of options. */
+ otmp.name = name;
+ if ((op = bsearch(&otmp, optlist,
+ sizeof(optlist) / sizeof(OPTLIST) - 1,
+ sizeof(OPTLIST), opts_cmp)) != NULL)
+ goto found;
+
+ /* Check for prefix match. */
+prefix: op = opts_prefix(name);
+
+found: if (op == NULL) {
+ msgq(sp, M_ERR,
+ "no %s option: 'set all' gives all option values",
+ name);
+ continue;
+ }
+
+ /* Find current option values. */
+ offset = op - optlist;
+ spo = sp->opts + offset;
+
+ /* Set name, value. */
+ switch (op->type) {
+ case OPT_0BOOL:
+ case OPT_1BOOL:
+ if (equals) {
+ msgq(sp, M_ERR,
+ "set: [no]%s option doesn't take a value",
+ name);
+ break;
+ }
+ if (op->func != NULL) {
+ if (op->func(sp, spo, NULL, turnoff)) {
+ rval = 1;
+ break;
+ }
+ } else if (turnoff)
+ O_CLR(sp, offset);
+ else
+ O_SET(sp, offset);
+ goto change;
+ case OPT_NUM:
+ /*
+ * !!!
+ * Extension to historic vi. If the OPT_NOSTR flag is
+ * set, a numeric option may be turned off by using a
+ * "no" prefix, e.g. "nowrapmargin". (We assume that
+ * setting the value to 0 turns a numeric option off.)
+ */
+ if (turnoff) {
+ if (F_ISSET(op, OPT_NOSTR)) {
+ value = 0;
+ goto nostr;
+ }
+ msgq(sp, M_ERR,
+ "set: %s option isn't a boolean", name);
+ break;
+ }
+ if (!equals) {
+ if (!disp)
+ disp = SELECT_DISPLAY;
+ F_SET(spo, OPT_SELECTED);
+ break;
+ }
+ value = strtol(equals, &endp, 10);
+ if (*endp && !isblank(*endp)) {
+ msgq(sp, M_ERR,
+ "set %s: illegal number %s", name, equals);
+ break;
+ }
+nostr: if (op->func != NULL) {
+ if (op->func(sp, spo, equals, value)) {
+ rval = 1;
+ break;
+ }
+ } else
+ O_VAL(sp, offset) = value;
+ goto change;
+ case OPT_STR:
+ if (turnoff) {
+ msgq(sp, M_ERR,
+ "set: %s option isn't a boolean", name);
+ break;
+ }
+ if (!equals) {
+ if (!disp)
+ disp = SELECT_DISPLAY;
+ F_SET(spo, OPT_SELECTED);
+ break;
+ }
+ if (op->func != NULL) {
+ if (op->func(sp, spo, equals, (u_long)0)) {
+ rval = 1;
+ break;
+ }
+ } else {
+ if (F_ISSET(&sp->opts[offset], OPT_ALLOCATED))
+ free(O_STR(sp, offset));
+ if ((O_STR(sp, offset) =
+ strdup(equals)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ rval = 1;
+ break;
+ } else
+ F_SET(&sp->opts[offset], OPT_ALLOCATED);
+ }
+change: if (sp->s_optchange != NULL)
+ (void)sp->s_optchange(sp, offset);
+ F_SET(&sp->opts[offset], OPT_SET);
+ break;
+ default:
+ abort();
+ }
+ }
+ if (disp)
+ opts_dump(sp, disp);
+ return (rval);
+}
+
+/*
+ * opts_dump --
+ * List the current values of selected options.
+ */
+void
+opts_dump(sp, type)
+ SCR *sp;
+ enum optdisp type;
+{
+ OPTLIST const *op;
+ int base, b_num, cnt, col, colwidth, curlen, s_num;
+ int numcols, numrows, row;
+ int b_op[O_OPTIONCOUNT], s_op[O_OPTIONCOUNT];
+ char nbuf[20];
+
+ /*
+ * Options are output in two groups -- those that fit in a column and
+ * those that don't. Output is done on 6 character "tab" boundaries
+ * for no particular reason. (Since we don't output tab characters,
+ * we can ignore the terminal's tab settings.) Ignore the user's tab
+ * setting because we have no idea how reasonable it is.
+ */
+#define BOUND 6
+
+ /* Find a column width we can live with. */
+ for (cnt = 6; cnt > 1; --cnt) {
+ colwidth = (sp->cols - 1) / cnt & ~(BOUND - 1);
+ if (colwidth >= 10) {
+ colwidth = (colwidth + BOUND) & ~(BOUND - 1);
+ break;
+ }
+ colwidth = 0;
+ }
+
+ /*
+ * Two passes. First, get the set of options to list, entering them
+ * into the column list or the overflow list. No error checking,
+ * since we know that at least one option (O_TERM) has the OPT_SET bit
+ * set.
+ */
+ for (b_num = s_num = 0, op = optlist; op->name; ++op) {
+ cnt = op - optlist;
+
+ /* If OPT_NEVER set, it's never displayed. */
+ if (F_ISSET(op, OPT_NEVER))
+ continue;
+
+ switch (type) {
+ case ALL_DISPLAY: /* Display all. */
+ break;
+ case CHANGED_DISPLAY: /* Display changed. */
+ if (!F_ISSET(&sp->opts[cnt], OPT_SET))
+ continue;
+ break;
+ case SELECT_DISPLAY: /* Display selected. */
+ if (!F_ISSET(&sp->opts[cnt], OPT_SELECTED))
+ continue;
+ break;
+ default:
+ case NO_DISPLAY:
+ abort();
+ /* NOTREACHED */
+ }
+ F_CLR(&sp->opts[cnt], OPT_SELECTED);
+
+ curlen = strlen(op->name);
+ switch (op->type) {
+ case OPT_0BOOL:
+ case OPT_1BOOL:
+ if (!O_ISSET(sp, cnt))
+ curlen += 2;
+ break;
+ case OPT_NUM:
+ (void)snprintf(nbuf,
+ sizeof(nbuf), "%ld", O_VAL(sp, cnt));
+ curlen += strlen(nbuf);
+ break;
+ case OPT_STR:
+ curlen += strlen(O_STR(sp, cnt)) + 3;
+ break;
+ }
+ /* Offset by two so there's a gap. */
+ if (curlen < colwidth - 2)
+ s_op[s_num++] = cnt;
+ else
+ b_op[b_num++] = cnt;
+ }
+
+ numcols = (sp->cols - 1) / colwidth;
+ if (s_num > numcols) {
+ numrows = s_num / numcols;
+ if (s_num % numcols)
+ ++numrows;
+ } else
+ numrows = 1;
+
+ for (row = 0; row < numrows;) {
+ for (base = row, col = 0; col < numcols; ++col) {
+ cnt = opts_print(sp,
+ &optlist[s_op[base]], &sp->opts[s_op[base]]);
+ if ((base += numrows) >= s_num)
+ break;
+ (void)ex_printf(EXCOOKIE,
+ "%*s", (int)(colwidth - cnt), "");
+ }
+ if (++row < numrows || b_num)
+ (void)ex_printf(EXCOOKIE, "\n");
+ }
+
+ for (row = 0; row < b_num;) {
+ (void)opts_print(sp, &optlist[b_op[row]], &sp->opts[b_op[row]]);
+ if (++row < b_num)
+ (void)ex_printf(EXCOOKIE, "\n");
+ }
+ (void)ex_printf(EXCOOKIE, "\n");
+}
+
+/*
+ * opts_print --
+ * Print out an option.
+ */
+static int
+opts_print(sp, op, spo)
+ SCR *sp;
+ OPTLIST const *op;
+ OPTION *spo;
+{
+ int curlen, offset;
+
+ curlen = 0;
+ offset = op - optlist;
+ switch (op->type) {
+ case OPT_0BOOL:
+ case OPT_1BOOL:
+ curlen += ex_printf(EXCOOKIE,
+ "%s%s", O_ISSET(sp, offset) ? "" : "no", op->name);
+ break;
+ case OPT_NUM:
+ curlen += ex_printf(EXCOOKIE,
+ "%s=%ld", op->name, O_VAL(sp, offset));
+ break;
+ case OPT_STR:
+ curlen += ex_printf(EXCOOKIE,
+ "%s=\"%s\"", op->name, O_STR(sp, offset));
+ break;
+ }
+ return (curlen);
+}
+
+/*
+ * opts_save --
+ * Write the current configuration to a file.
+ */
+int
+opts_save(sp, fp)
+ SCR *sp;
+ FILE *fp;
+{
+ OPTION *spo;
+ OPTLIST const *op;
+ int ch, cnt;
+ char *p;
+
+ for (spo = sp->opts, op = optlist; op->name; ++op) {
+ if (F_ISSET(op, OPT_NOSAVE))
+ continue;
+ cnt = op - optlist;
+ switch (op->type) {
+ case OPT_0BOOL:
+ case OPT_1BOOL:
+ if (O_ISSET(sp, cnt))
+ (void)fprintf(fp, "set %s\n", op->name);
+ else
+ (void)fprintf(fp, "set no%s\n", op->name);
+ break;
+ case OPT_NUM:
+ (void)fprintf(fp,
+ "set %s=%-3d\n", op->name, O_VAL(sp, cnt));
+ break;
+ case OPT_STR:
+ (void)fprintf(fp, "set ");
+ for (p = op->name; (ch = *p) != '\0'; ++p) {
+ if (isblank(ch))
+ (void)putc('\\', fp);
+ (void)putc(ch, fp);
+ }
+ (void)putc('=', fp);
+ for (p = O_STR(sp, cnt); (ch = *p) != '\0'; ++p) {
+ if (isblank(ch))
+ (void)putc('\\', fp);
+ (void)putc(ch, fp);
+ }
+ (void)putc('\n', fp);
+ break;
+ }
+ if (ferror(fp)) {
+ msgq(sp, M_ERR, "I/O error: %s", strerror(errno));
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * opts_prefix --
+ * Check to see if the name is the prefix of one (and only one)
+ * option. If so, return the option.
+ */
+static OPTLIST const *
+opts_prefix(name)
+ char *name;
+{
+ OPTLIST const *op, *save_op;
+ size_t len;
+
+ save_op = NULL;
+ len = strlen(name);
+ for (op = optlist; op->name != NULL; ++op) {
+ if (op->name[0] < name[0])
+ continue;
+ if (op->name[0] > name[0])
+ break;
+ if (!memcmp(op->name, name, len)) {
+ if (save_op != NULL)
+ return (NULL);
+ save_op = op;
+ }
+ }
+ return (save_op);
+}
+
+static int
+opts_abbcmp(a, b)
+ const void *a, *b;
+{
+ return(strcmp(((OABBREV *)a)->name, ((OABBREV *)b)->name));
+}
+
+static int
+opts_cmp(a, b)
+ const void *a, *b;
+{
+ return(strcmp(((OPTLIST *)a)->name, ((OPTLIST *)b)->name));
+}
+
+/*
+ * opts_free --
+ * Free all option strings
+ */
+void
+opts_free(sp)
+ SCR *sp;
+{
+ int cnt;
+ char *p;
+
+ for (cnt = 0; cnt < O_OPTIONCOUNT; ++cnt)
+ if (F_ISSET(&sp->opts[cnt], OPT_ALLOCATED)) {
+ p = O_STR(sp, cnt);
+ FREE(p, strlen(p) + 1);
+ }
+}
+
+/*
+ * opts_copy --
+ * Copy a screen's OPTION array.
+ */
+int
+opts_copy(orig, sp)
+ SCR *orig, *sp;
+{
+ OPTION *op;
+ int cnt;
+
+ /* Copy most everything without change. */
+ memmove(sp->opts, orig->opts, sizeof(orig->opts));
+
+ /*
+ * Allocate copies of the strings -- keep trying to reallocate
+ * after ENOMEM failure, otherwise end up with more than one
+ * screen referencing the original memory.
+ */
+ for (op = sp->opts, cnt = 0; cnt < O_OPTIONCOUNT; ++cnt, ++op)
+ if (F_ISSET(&sp->opts[cnt], OPT_ALLOCATED) &&
+ (O_STR(sp, cnt) = strdup(O_STR(sp, cnt))) == NULL) {
+ msgq(orig, M_SYSERR, NULL);
+ return (1);
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/options.h.stub b/usr.bin/vi/options.h.stub
new file mode 100644
index 000000000000..f5f52b52b3d3
--- /dev/null
+++ b/usr.bin/vi/options.h.stub
@@ -0,0 +1,110 @@
+/*-
+ * Copyright (c) 1991, 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.
+ *
+ * @(#)options.h.stub 8.14 (Berkeley) 12/20/93
+ */
+
+struct _option {
+ union {
+ u_long val; /* Value or boolean. */
+ char *str; /* String. */
+ } o_u;
+ size_t len; /* String length. */
+
+#define OPT_ALLOCATED 0x01 /* Allocated space. */
+#define OPT_SELECTED 0x02 /* Selected for display. */
+#define OPT_SET 0x04 /* Set (display for the user). */
+ u_char flags;
+};
+
+struct _optlist {
+ char *name; /* Name. */
+ /* Change function. */
+ int (*func) __P((SCR *, OPTION *, char *, u_long));
+ /* Type of object. */
+ enum { OPT_0BOOL, OPT_1BOOL, OPT_NUM, OPT_STR } type;
+
+#define OPT_NEVER 0x01 /* Never display the option. */
+#define OPT_NOSAVE 0x02 /* Mkexrc command doesn't save. */
+#define OPT_NOSTR 0x04 /* String that takes a "no". */
+ u_int flags;
+};
+
+/* Clear, set, test boolean options. */
+#define O_SET(sp, o) (sp)->opts[(o)].o_u.val = 1
+#define O_CLR(sp, o) (sp)->opts[(o)].o_u.val = 0
+#define O_ISSET(sp, o) ((sp)->opts[(o)].o_u.val)
+
+/* Get option values. */
+#define O_LEN(sp, o) (sp)->opts[(o)].len
+#define O_STR(sp, o) (sp)->opts[(o)].o_u.str
+#define O_VAL(sp, o) (sp)->opts[(o)].o_u.val
+
+/* Option routines. */
+enum optdisp { NO_DISPLAY, ALL_DISPLAY, CHANGED_DISPLAY, SELECT_DISPLAY };
+
+int opts_copy __P((SCR *, SCR *));
+void opts_dump __P((SCR *, enum optdisp));
+void opts_free __P((SCR *));
+int opts_init __P((SCR *));
+int opts_save __P((SCR *, FILE *));
+int opts_set __P((SCR *, ARGS *[]));
+
+/* Per-option change routines. */
+int f_altwerase __P((SCR *, OPTION *, char *, u_long));
+int f_columns __P((SCR *, OPTION *, char *, u_long));
+int f_keytime __P((SCR *, OPTION *, char *, u_long));
+int f_leftright __P((SCR *, OPTION *, char *, u_long));
+int f_lines __P((SCR *, OPTION *, char *, u_long));
+int f_lisp __P((SCR *, OPTION *, char *, u_long));
+int f_list __P((SCR *, OPTION *, char *, u_long));
+int f_matchtime __P((SCR *, OPTION *, char *, u_long));
+int f_mesg __P((SCR *, OPTION *, char *, u_long));
+int f_modeline __P((SCR *, OPTION *, char *, u_long));
+int f_number __P((SCR *, OPTION *, char *, u_long));
+int f_optimize __P((SCR *, OPTION *, char *, u_long));
+int f_paragraph __P((SCR *, OPTION *, char *, u_long));
+int f_readonly __P((SCR *, OPTION *, char *, u_long));
+int f_ruler __P((SCR *, OPTION *, char *, u_long));
+int f_section __P((SCR *, OPTION *, char *, u_long));
+int f_shiftwidth __P((SCR *, OPTION *, char *, u_long));
+int f_sidescroll __P((SCR *, OPTION *, char *, u_long));
+int f_sourceany __P((SCR *, OPTION *, char *, u_long));
+int f_tabstop __P((SCR *, OPTION *, char *, u_long));
+int f_tags __P((SCR *, OPTION *, char *, u_long));
+int f_term __P((SCR *, OPTION *, char *, u_long));
+int f_ttywerase __P((SCR *, OPTION *, char *, u_long));
+int f_w1200 __P((SCR *, OPTION *, char *, u_long));
+int f_w300 __P((SCR *, OPTION *, char *, u_long));
+int f_w9600 __P((SCR *, OPTION *, char *, u_long));
+int f_window __P((SCR *, OPTION *, char *, u_long));
+int f_wrapmargin __P((SCR *, OPTION *, char *, u_long));
diff --git a/usr.bin/vi/options_f.c b/usr.bin/vi/options_f.c
new file mode 100644
index 000000000000..efd596f9585a
--- /dev/null
+++ b/usr.bin/vi/options_f.c
@@ -0,0 +1,547 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)options_f.c 8.25 (Berkeley) 12/20/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "tag.h"
+
+static int opt_putenv __P((char *));
+
+#define DECL(f) \
+ int \
+ f(sp, op, str, val) \
+ SCR *sp; \
+ OPTION *op; \
+ char *str; \
+ u_long val;
+#define CALL(f) \
+ f(sp, op, str, val)
+
+#define turnoff val
+
+DECL(f_altwerase)
+{
+ if (turnoff)
+ O_CLR(sp, O_ALTWERASE);
+ else {
+ O_SET(sp, O_ALTWERASE);
+ O_CLR(sp, O_TTYWERASE);
+ }
+ return (0);
+}
+
+DECL(f_ttywerase)
+{
+ if (turnoff)
+ O_CLR(sp, O_TTYWERASE);
+ else {
+ O_SET(sp, O_TTYWERASE);
+ O_CLR(sp, O_ALTWERASE);
+ }
+ return (0);
+}
+
+DECL(f_columns)
+{
+ char buf[25];
+
+ /* Validate the number. */
+ if (val < MINIMUM_SCREEN_COLS) {
+ msgq(sp, M_ERR, "Screen columns too small, less than %d.",
+ MINIMUM_SCREEN_COLS);
+ return (1);
+ }
+ if (val < O_VAL(sp, O_SHIFTWIDTH)) {
+ msgq(sp, M_ERR,
+ "Screen columns too small, less than shiftwidth.");
+ return (1);
+ }
+ if (val < O_VAL(sp, O_SIDESCROLL)) {
+ msgq(sp, M_ERR,
+ "Screen columns too small, less than sidescroll.");
+ return (1);
+ }
+ if (val < O_VAL(sp, O_TABSTOP)) {
+ msgq(sp, M_ERR,
+ "Screen columns too small, less than tabstop.");
+ return (1);
+ }
+ if (val < O_VAL(sp, O_WRAPMARGIN)) {
+ msgq(sp, M_ERR,
+ "Screen columns too small, less than wrapmargin.");
+ return (1);
+ }
+#ifdef XXX_NOT_RIGHT
+ /*
+ * This has to be checked by reaching down into the screen code.
+ */
+ if (val < O_NUMBER_LENGTH) {
+ msgq(sp, M_ERR,
+ "Screen columns too small, less than number option.");
+ return (1);
+ }
+#endif
+ /* Set the columns value in the environment for curses. */
+ (void)snprintf(buf, sizeof(buf), "COLUMNS=%lu", val);
+ if (opt_putenv(buf))
+ return (1);
+
+ /* This is expensive, don't do it unless it's necessary. */
+ if (O_VAL(sp, O_COLUMNS) == val)
+ return (0);
+
+ /* Set the value. */
+ O_VAL(sp, O_COLUMNS) = val;
+
+ F_SET(sp, S_RESIZE);
+ return (0);
+}
+
+DECL(f_keytime)
+{
+ O_VAL(sp, O_KEYTIME) = val;
+ return (0);
+}
+
+DECL(f_leftright)
+{
+ if (turnoff)
+ O_CLR(sp, O_LEFTRIGHT);
+ else
+ O_SET(sp, O_LEFTRIGHT);
+ F_SET(sp, S_REFORMAT | S_REDRAW);
+ return (0);
+}
+
+DECL(f_lines)
+{
+ char buf[25];
+
+ /* Validate the number. */
+ if (val < MINIMUM_SCREEN_ROWS) {
+ msgq(sp, M_ERR, "Screen lines too small, less than %d.",
+ MINIMUM_SCREEN_ROWS);
+ return (1);
+ }
+
+ /* Set the rows value in the environment for curses. */
+ (void)snprintf(buf, sizeof(buf), "ROWS=%lu", val);
+ if (opt_putenv(buf))
+ return (1);
+
+ /* This is expensive, don't do it unless it's necessary. */
+ if (O_VAL(sp, O_LINES) == val)
+ return (0);
+
+ /* Set the value. */
+ O_VAL(sp, O_LINES) = val;
+
+ /*
+ * If no window value set, set a new default window and,
+ * optionally, a new scroll value.
+ */
+ if (!F_ISSET(&sp->opts[O_WINDOW], OPT_SET)) {
+ O_VAL(sp, O_WINDOW) = val - 1;
+ if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET))
+ O_VAL(sp, O_SCROLL) = val / 2;
+ }
+
+ F_SET(sp, S_RESIZE);
+ return (0);
+}
+
+DECL(f_lisp)
+{
+ msgq(sp, M_ERR, "The lisp option is not implemented.");
+ return (0);
+}
+
+DECL(f_list)
+{
+ if (turnoff)
+ O_CLR(sp, O_LIST);
+ else
+ O_SET(sp, O_LIST);
+
+ F_SET(sp, S_REFORMAT | S_REDRAW);
+ return (0);
+}
+
+DECL(f_matchtime)
+{
+ O_VAL(sp, O_MATCHTIME) = val;
+ return (0);
+}
+
+DECL(f_mesg)
+{
+ struct stat sb;
+ char *tty;
+
+ /* Find the tty. */
+ if ((tty = ttyname(STDERR_FILENO)) == NULL) {
+ msgq(sp, M_ERR, "ttyname: %s.", strerror(errno));
+ return (1);
+ }
+
+ /* Save the tty mode for later; only save it once. */
+ if (!F_ISSET(sp->gp, G_SETMODE)) {
+ F_SET(sp->gp, G_SETMODE);
+ if (stat(tty, &sb) < 0) {
+ msgq(sp, M_ERR, "%s: %s.", tty, strerror(errno));
+ return (1);
+ }
+ sp->gp->origmode = sb.st_mode;
+ }
+
+ if (turnoff) {
+ if (chmod(tty, sp->gp->origmode & ~S_IWGRP) < 0) {
+ msgq(sp, M_ERR, "messages not turned off: %s: %s.",
+ tty, strerror(errno));
+ return (1);
+ }
+ O_CLR(sp, O_MESG);
+ } else {
+ if (chmod(tty, sp->gp->origmode | S_IWGRP) < 0) {
+ msgq(sp, M_ERR, "messages not turned on: %s: %s.",
+ tty, strerror(errno));
+ return (1);
+ }
+ O_SET(sp, O_MESG);
+ }
+ return (0);
+}
+
+/*
+ * f_modeline --
+ * This has been documented in historical systems as both "modeline"
+ * and as "modelines". Regardless of the name, this option represents
+ * a security problem of mammoth proportions, not to mention a stunning
+ * example of what your intro CS professor referred to as the perils of
+ * mixing code and data. Don't add it, or I will kill you.
+ */
+DECL(f_modeline)
+{
+ if (!turnoff)
+ msgq(sp, M_ERR, "The modeline(s) option may never be set.");
+ return (0);
+}
+
+DECL(f_number)
+{
+ if (turnoff)
+ O_CLR(sp, O_NUMBER);
+ else
+ O_SET(sp, O_NUMBER);
+
+ F_SET(sp, S_REFORMAT | S_REDRAW);
+ return (0);
+}
+
+DECL(f_optimize)
+{
+ msgq(sp, M_ERR, "The optimize option is not implemented.");
+ return (0);
+}
+
+DECL(f_paragraph)
+{
+ if (strlen(str) & 1) {
+ msgq(sp, M_ERR,
+ "Paragraph options must be in sets of two characters.");
+ return (1);
+ }
+
+ if (F_ISSET(&sp->opts[O_PARAGRAPHS], OPT_ALLOCATED))
+ free(O_STR(sp, O_PARAGRAPHS));
+ if ((O_STR(sp, O_PARAGRAPHS) = strdup(str)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ F_SET(&sp->opts[O_PARAGRAPHS], OPT_ALLOCATED | OPT_SET);
+ return (0);
+}
+
+DECL(f_readonly)
+{
+ if (turnoff) {
+ O_CLR(sp, O_READONLY);
+ if (sp->frp != NULL)
+ F_CLR(sp->frp, FR_RDONLY);
+ } else {
+ O_SET(sp, O_READONLY);
+ if (sp->frp != NULL)
+ F_SET(sp->frp, FR_RDONLY);
+ }
+ return (0);
+}
+
+DECL(f_ruler)
+{
+ if (turnoff)
+ O_CLR(sp, O_RULER);
+ else
+ O_SET(sp, O_RULER);
+ return (0);
+}
+
+DECL(f_section)
+{
+ if (strlen(str) & 1) {
+ msgq(sp, M_ERR,
+ "Section options must be in sets of two characters.");
+ return (1);
+ }
+
+ if (F_ISSET(&sp->opts[O_SECTIONS], OPT_ALLOCATED))
+ free(O_STR(sp, O_SECTIONS));
+ if ((O_STR(sp, O_SECTIONS) = strdup(str)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ F_SET(&sp->opts[O_SECTIONS], OPT_ALLOCATED | OPT_SET);
+ return (0);
+}
+
+DECL(f_shiftwidth)
+{
+ if (val == 0) {
+ msgq(sp, M_ERR, "The shiftwidth can't be set to 0.");
+ return (1);
+ }
+ if (val > O_VAL(sp, O_COLUMNS)) {
+ msgq(sp, M_ERR,
+ "Shiftwidth can't be larger than screen size.");
+ return (1);
+ }
+ O_VAL(sp, O_SHIFTWIDTH) = val;
+ return (0);
+}
+
+DECL(f_sidescroll)
+{
+ if (val > O_VAL(sp, O_COLUMNS)) {
+ msgq(sp, M_ERR,
+ "Sidescroll can't be larger than screen size.");
+ return (1);
+ }
+ O_VAL(sp, O_SIDESCROLL) = val;
+ return (0);
+}
+
+/*
+ * f_sourceany --
+ * Historic vi, on startup, source'd $HOME/.exrc and ./.exrc, if they
+ * were owned by the user. The sourceany option was an undocumented
+ * feature of historic vi which permitted the startup source'ing of
+ * .exrc files the user didn't own. This is an obvious security problem,
+ * and we ignore the option.
+ */
+DECL(f_sourceany)
+{
+ if (!turnoff)
+ msgq(sp, M_ERR, "The sourceany option may never be set.");
+ return (0);
+}
+
+DECL(f_tabstop)
+{
+ if (val == 0) {
+ msgq(sp, M_ERR, "Tab stops can't be set to 0.");
+ return (1);
+ }
+#define MAXTABSTOP 20
+ if (val > MAXTABSTOP) {
+ msgq(sp, M_ERR,
+ "Tab stops can't be larger than %d.", MAXTABSTOP);
+ return (1);
+ }
+ if (val > O_VAL(sp, O_COLUMNS)) {
+ msgq(sp, M_ERR,
+ "Tab stops can't be larger than screen size.",
+ MAXTABSTOP);
+ return (1);
+ }
+ O_VAL(sp, O_TABSTOP) = val;
+
+ F_SET(sp, S_REFORMAT | S_REDRAW);
+ return (0);
+}
+
+DECL(f_tags)
+{
+ char *p;
+
+ /* Copy for user display. */
+ p = O_STR(sp, O_TAGS);
+ if ((O_STR(sp, O_TAGS) = strdup(str)) == NULL) {
+ O_STR(sp, O_TAGS) = p;
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ if (F_ISSET(&sp->opts[O_TAGS], OPT_ALLOCATED))
+ FREE(p, strlen(p) + 1);
+ F_SET(&sp->opts[O_TAGS], OPT_ALLOCATED | OPT_SET);
+ return (0);
+}
+
+DECL(f_term)
+{
+ char buf[256];
+
+ if (F_ISSET(&sp->opts[O_TERM], OPT_ALLOCATED))
+ free(O_STR(sp, O_TERM));
+ if ((O_STR(sp, O_TERM) = strdup(str)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ F_SET(&sp->opts[O_TERM], OPT_ALLOCATED | OPT_SET);
+
+ /* Set the terminal value in the environment for curses. */
+ (void)snprintf(buf, sizeof(buf), "TERM=%s", str);
+ if (opt_putenv(buf))
+ return (1);
+
+ if (set_window_size(sp, 0, 0))
+ return (1);
+ return (0);
+}
+
+DECL(f_w300)
+{
+ /* Historical behavior for w300 was < 1200. */
+ if (baud_from_bval(sp) >= 1200)
+ return (0);
+
+ if (CALL(f_window))
+ return (1);
+
+ if (val > O_VAL(sp, O_LINES) - 1)
+ val = O_VAL(sp, O_LINES) - 1;
+ O_VAL(sp, O_W300) = val;
+ return (0);
+}
+
+DECL(f_w1200)
+{
+ u_long v;
+
+ /* Historical behavior for w1200 was == 1200. */
+ v = baud_from_bval(sp);
+ if (v < 1200 || v > 4800)
+ return (0);
+
+ if (CALL(f_window))
+ return (1);
+
+ if (val > O_VAL(sp, O_LINES) - 1)
+ val = O_VAL(sp, O_LINES) - 1;
+ O_VAL(sp, O_W1200) = val;
+ return (0);
+}
+
+DECL(f_w9600)
+{
+ speed_t v;
+
+ /* Historical behavior for w9600 was > 1200. */
+ v = baud_from_bval(sp);
+ if (v <= 4800)
+ return (0);
+
+ if (CALL(f_window))
+ return (1);
+
+ if (val > O_VAL(sp, O_LINES) - 1)
+ val = O_VAL(sp, O_LINES) - 1;
+ O_VAL(sp, O_W9600) = val;
+ return (0);
+}
+
+DECL(f_window)
+{
+ if (val < MINIMUM_SCREEN_ROWS) {
+ msgq(sp, M_ERR, "Window too small, less than %d.",
+ MINIMUM_SCREEN_ROWS);
+ return (1);
+ }
+ if (val > O_VAL(sp, O_LINES) - 1)
+ val = O_VAL(sp, O_LINES) - 1;
+ O_VAL(sp, O_WINDOW) = val;
+ O_VAL(sp, O_SCROLL) = val / 2;
+
+ return (0);
+}
+
+DECL(f_wrapmargin)
+{
+ if (val > O_VAL(sp, O_COLUMNS)) {
+ msgq(sp, M_ERR,
+ "Wrapmargin value can't be larger than screen size.");
+ return (1);
+ }
+ O_VAL(sp, O_WRAPMARGIN) = val;
+ return (0);
+}
+
+/*
+ * opt_putenv --
+ * Put a value into the environment. We use putenv(3) because it's
+ * more portable. The following hack is because some moron decided
+ * to keep a reference to the memory passed to putenv(3), instead of
+ * having it allocate its own. Someone clearly needs to get promoted
+ * into management.
+ */
+static int
+opt_putenv(s)
+ char *s;
+{
+ char *t;
+
+ /* Memory leak. */
+ if ((t = strdup(s)) == NULL)
+ return (1);
+ return (putenv(t));
+}
diff --git a/usr.bin/vi/pathnames.h b/usr.bin/vi/pathnames.h
new file mode 100644
index 000000000000..be6352b9dfc1
--- /dev/null
+++ b/usr.bin/vi/pathnames.h
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 1991, 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.
+ *
+ * @(#)pathnames.h 8.5 (Berkeley) 12/21/93
+ */
+
+#define _PATH_BSHELL "/bin/sh"
+#define _PATH_DEVNULL "/dev/null"
+#define _PATH_EXRC ".exrc"
+#define _PATH_NEXRC ".nexrc"
+#define _PATH_PRESERVE "/var/tmp/vi.recover"
+#define _PATH_SENDMAIL "/usr/sbin/sendmail"
+#define _PATH_SYSEXRC "/etc/vi.exrc"
+#define _PATH_TAGS "tags /var/db/libc.tags /sys/kern/tags"
+#define _PATH_TMP "/tmp"
+#define _PATH_TTY "/dev/tty"
diff --git a/usr.bin/vi/recover.c b/usr.bin/vi/recover.c
new file mode 100644
index 000000000000..f9a483236abe
--- /dev/null
+++ b/usr.bin/vi/recover.c
@@ -0,0 +1,622 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)recover.c 8.40 (Berkeley) 12/21/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+/*
+ * We include <sys/file.h>, because the flock(2) #defines were
+ * found there on historical systems. We also include <fcntl.h>
+ * because the open(2) #defines are found there on newer systems.
+ */
+#include <sys/file.h>
+
+#include <netdb.h> /* MAXHOSTNAMELEN on some systems. */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "pathnames.h"
+
+/*
+ * Recovery code.
+ *
+ * The basic scheme is there's a btree file, whose name we specify. The first
+ * time a file is modified, and then at RCV_PERIOD intervals after that, the
+ * btree file is synced to disk. Each time a keystroke is requested for a file
+ * the terminal routines check to see if the file needs to be synced. This, of
+ * course means that the data structures had better be consistent each time the
+ * key routines are called.
+ *
+ * We don't use timers other than to flag that the file should be synced. This
+ * would require that the SCR and EXF data structures be locked, the dbopen(3)
+ * routines lock out the timers for each update, etc. It's just not worth it.
+ * The only way we can lose in the current scheme is if the file is saved, then
+ * the user types furiously for RCV_PERIOD - 1 seconds, and types nothing more.
+ * Not likely.
+ *
+ * When a file is first modified, a file which can be handed off to the mailer
+ * is created. The file contains normal headers, with two additions, which
+ * occur in THIS order, as the FIRST TWO headers:
+ *
+ * Vi-recover-file: file_name
+ * Vi-recover-path: recover_path
+ *
+ * Since newlines delimit the headers, this means that file names cannot
+ * have newlines in them, but that's probably okay.
+ *
+ * Btree files are named "vi.XXXX" and recovery files are named "recover.XXXX".
+ */
+
+#define VI_FHEADER "Vi-recover-file: "
+#define VI_PHEADER "Vi-recover-path: "
+
+static void rcv_alrm __P((int));
+static int rcv_mailfile __P((SCR *, EXF *));
+static void rcv_syncit __P((SCR *, int));
+
+/*
+ * rcv_tmp --
+ * Build a file name that will be used as the recovery file.
+ */
+int
+rcv_tmp(sp, ep, name)
+ SCR *sp;
+ EXF *ep;
+ char *name;
+{
+ struct stat sb;
+ int fd;
+ char *dp, *p, path[MAXPATHLEN];
+
+ /*
+ * If the recovery directory doesn't exist, try and create it. As
+ * the recovery files are themselves protected from reading/writing
+ * by other than the owner, the worst that can happen is that a user
+ * would have permission to remove other users recovery files. If
+ * the sticky bit has the BSD semantics, that too will be impossible.
+ */
+ dp = O_STR(sp, O_RECDIR);
+ if (stat(dp, &sb)) {
+ if (errno != ENOENT || mkdir(dp, 0)) {
+ msgq(sp, M_ERR, "Error: %s: %s", dp, strerror(errno));
+ return (1);
+ }
+ (void)chmod(dp, S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX);
+ }
+
+ /* Newlines delimit the mail messages. */
+ for (p = name; *p; ++p)
+ if (*p == '\n') {
+ msgq(sp, M_ERR,
+ "Files with newlines in the name are unrecoverable.");
+ return (1);
+ }
+
+ (void)snprintf(path, sizeof(path), "%s/vi.XXXXXX", dp);
+
+ /*
+ * !!!
+ * We depend on mkstemp(3) setting the permissions correctly.
+ * GP's, we do it ourselves, to keep the window as small as
+ * possible.
+ */
+ if ((fd = mkstemp(path)) == -1) {
+ msgq(sp, M_ERR, "Error: %s: %s", dp, strerror(errno));
+ return (1);
+ }
+ (void)chmod(path, S_IRUSR | S_IWUSR);
+ (void)close(fd);
+
+ if ((ep->rcv_path = strdup(path)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ (void)unlink(path);
+ return (1);
+ }
+
+ /* We believe the file is recoverable. */
+ F_SET(ep, F_RCV_ON);
+ return (0);
+}
+
+/*
+ * rcv_init --
+ * Force the file to be snapshotted for recovery.
+ */
+int
+rcv_init(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ struct itimerval value;
+ struct sigaction act;
+ recno_t lno;
+
+ F_CLR(ep, F_FIRSTMODIFY | F_RCV_ON);
+
+ /* Build file to mail to the user. */
+ if (rcv_mailfile(sp, ep))
+ goto err;
+
+ /* Force read of entire file. */
+ if (file_lline(sp, ep, &lno))
+ goto err;
+
+ /* Turn on a busy message, and sync it to backing store. */
+ busy_on(sp, 1, "Copying file for recovery...");
+ if (ep->db->sync(ep->db, R_RECNOSYNC)) {
+ msgq(sp, M_ERR, "Preservation failed: %s: %s",
+ ep->rcv_path, strerror(errno));
+ busy_off(sp);
+ goto err;
+ }
+ busy_off(sp);
+
+ if (!F_ISSET(sp->gp, G_RECOVER_SET)) {
+ /* Install the recovery timer handler. */
+ act.sa_handler = rcv_alrm;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ (void)sigaction(SIGALRM, &act, NULL);
+
+ /* Start the recovery timer. */
+ value.it_interval.tv_sec = value.it_value.tv_sec = RCV_PERIOD;
+ value.it_interval.tv_usec = value.it_value.tv_usec = 0;
+ if (setitimer(ITIMER_REAL, &value, NULL)) {
+ msgq(sp, M_ERR,
+ "Error: setitimer: %s", strerror(errno));
+err: msgq(sp, M_ERR,
+ "Recovery after system crash not possible.");
+ return (1);
+ }
+ }
+
+ /* We believe the file is recoverable. */
+ F_SET(ep, F_RCV_ON);
+ return (0);
+}
+
+/*
+ * rcv_alrm --
+ * Recovery timer interrupt handler.
+ */
+static void
+rcv_alrm(signo)
+ int signo;
+{
+ F_SET(__global_list, G_SIGALRM);
+}
+
+/*
+ * rcv_mailfile --
+ * Build the file to mail to the user.
+ */
+static int
+rcv_mailfile(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ struct passwd *pw;
+ uid_t uid;
+ FILE *fp;
+ time_t now;
+ int fd;
+ char *p, *t, host[MAXHOSTNAMELEN], path[MAXPATHLEN];
+
+ if ((pw = getpwuid(uid = getuid())) == NULL) {
+ msgq(sp, M_ERR, "Information on user id %u not found.", uid);
+ return (1);
+ }
+
+ (void)snprintf(path, sizeof(path),
+ "%s/recover.XXXXXX", O_STR(sp, O_RECDIR));
+ if ((fd = mkstemp(path)) == -1 || (fp = fdopen(fd, "w")) == NULL) {
+ msgq(sp, M_ERR,
+ "Error: %s: %s", O_STR(sp, O_RECDIR), strerror(errno));
+ if (fd != -1)
+ (void)close(fd);
+ return (1);
+ }
+
+ /*
+ * We keep an open lock on the file so that the recover option can
+ * distinguish between files that are live and those that need to
+ * be recovered. There's an obvious window between the mkstemp call
+ * and the lock, but it's pretty small.
+ */
+ if ((ep->rcv_fd = dup(fd)) != -1)
+ (void)flock(ep->rcv_fd, LOCK_EX | LOCK_NB);
+
+ if ((ep->rcv_mpath = strdup(path)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ (void)fclose(fp);
+ return (1);
+ }
+
+ t = FILENAME(sp->frp);
+ if ((p = strrchr(t, '/')) == NULL)
+ p = t;
+ else
+ ++p;
+ (void)time(&now);
+ (void)gethostname(host, sizeof(host));
+ (void)fprintf(fp, "%s%s\n%s%s\n%s\n%s\n%s%s\n%s%s\n%s\n\n",
+ VI_FHEADER, p, /* Non-standard. */
+ VI_PHEADER, ep->rcv_path, /* Non-standard. */
+ "Reply-To: root",
+ "From: root (Nvi recovery program)",
+ "To: ", pw->pw_name,
+ "Subject: Nvi saved the file ", p,
+ "Precedence: bulk"); /* For vacation(1). */
+ (void)fprintf(fp, "%s%.24s%s%s\n%s%s",
+ "On ", ctime(&now),
+ ", the user ", pw->pw_name,
+ "was editing a file named ", p);
+ if (p != t)
+ (void)fprintf(fp, " (%s)", t);
+ (void)fprintf(fp, "\n%s%s%s\n",
+ "on the machine ", host, ", when it was saved for\nrecovery.");
+ (void)fprintf(fp, "\n%s\n%s\n%s\n\n",
+ "You can recover most, if not all, of the changes",
+ "to this file using the -l and -r options to nvi(1)",
+ "or nex(1).");
+
+ if (fflush(fp) || ferror(fp)) {
+ msgq(sp, M_SYSERR, NULL);
+ (void)fclose(fp);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * rcv_sync --
+ * Sync the backing file.
+ */
+int
+rcv_sync(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ struct itimerval value;
+
+ if (ep->db->sync(ep->db, R_RECNOSYNC)) {
+ msgq(sp, M_ERR, "Automatic file backup failed: %s: %s",
+ ep->rcv_path, strerror(errno));
+ value.it_interval.tv_sec = value.it_interval.tv_usec = 0;
+ value.it_value.tv_sec = value.it_value.tv_usec = 0;
+ (void)setitimer(ITIMER_REAL, &value, NULL);
+ F_CLR(ep, F_RCV_ON);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * rcv_hup --
+ * Recovery SIGHUP interrupt handler. (Modem line dropped, or
+ * xterm window closed.)
+ */
+void
+rcv_hup()
+{
+ SCR *sp;
+
+ /*
+ * Walk the lists of screens, sync'ing the files; only sync
+ * each file once. Send email to the user for each file saved.
+ */
+ for (sp = __global_list->dq.cqh_first;
+ sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
+ rcv_syncit(sp, 1);
+ for (sp = __global_list->hq.cqh_first;
+ sp != (void *)&__global_list->hq; sp = sp->q.cqe_next)
+ rcv_syncit(sp, 1);
+
+ /*
+ * Die with the proper exit status. Don't bother using
+ * sigaction(2) 'cause we want the default behavior.
+ */
+ (void)signal(SIGHUP, SIG_DFL);
+ (void)kill(0, SIGHUP);
+
+ /* NOTREACHED */
+ exit (1);
+}
+
+/*
+ * rcv_term --
+ * Recovery SIGTERM interrupt handler. (Reboot or halt is running.)
+ */
+void
+rcv_term()
+{
+ SCR *sp;
+
+ /*
+ * Walk the lists of screens, sync'ing the files; only sync
+ * each file once.
+ */
+ for (sp = __global_list->dq.cqh_first;
+ sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
+ rcv_syncit(sp, 0);
+ for (sp = __global_list->hq.cqh_first;
+ sp != (void *)&__global_list->hq; sp = sp->q.cqe_next)
+ rcv_syncit(sp, 0);
+
+ /*
+ * Die with the proper exit status. Don't bother using
+ * sigaction(2) 'cause we want the default behavior.
+ */
+ (void)signal(SIGTERM, SIG_DFL);
+ (void)kill(0, SIGTERM);
+
+ /* NOTREACHED */
+ exit (1);
+}
+
+/*
+ * rcv_syncit --
+ * Sync the file, optionally send mail.
+ */
+static void
+rcv_syncit(sp, email)
+ SCR *sp;
+ int email;
+{
+ EXF *ep;
+ char comm[1024];
+
+ if ((ep = sp->ep) == NULL ||
+ !F_ISSET(ep, F_MODIFIED) || !F_ISSET(ep, F_RCV_ON))
+ return;
+
+ (void)ep->db->sync(ep->db, R_RECNOSYNC);
+ F_SET(ep, F_RCV_NORM);
+
+ /*
+ * !!!
+ * If you need to port this to a system that doesn't have sendmail,
+ * the -t flag being used causes sendmail to read the message for
+ * the recipients instead of us specifying them some other way.
+ */
+ if (email) {
+ (void)snprintf(comm, sizeof(comm),
+ "%s -t < %s", _PATH_SENDMAIL, ep->rcv_mpath);
+ (void)system(comm);
+ }
+ (void)file_end(sp, ep, 1);
+}
+
+/*
+ * people making love
+ * never exactly the same
+ * just like a snowflake
+ *
+ * rcv_list --
+ * List the files that can be recovered by this user.
+ */
+int
+rcv_list(sp)
+ SCR *sp;
+{
+ struct dirent *dp;
+ struct stat sb;
+ DIR *dirp;
+ FILE *fp;
+ int found;
+ char *p, file[1024];
+
+ if (chdir(O_STR(sp, O_RECDIR)) || (dirp = opendir(".")) == NULL) {
+ (void)fprintf(stderr,
+ "vi: %s: %s\n", O_STR(sp, O_RECDIR), strerror(errno));
+ return (1);
+ }
+
+ for (found = 0; (dp = readdir(dirp)) != NULL;) {
+ if (strncmp(dp->d_name, "recover.", 8))
+ continue;
+
+ /* If it's readable, it's recoverable. */
+ if ((fp = fopen(dp->d_name, "r")) == NULL)
+ continue;
+
+ /* If it's locked, it's live. */
+ if (flock(fileno(fp), LOCK_EX | LOCK_NB)) {
+ (void)fclose(fp);
+ continue;
+ }
+
+ /* Check the header, get the file name. */
+ if (fgets(file, sizeof(file), fp) == NULL ||
+ strncmp(file, VI_FHEADER, sizeof(VI_FHEADER) - 1) ||
+ (p = strchr(file, '\n')) == NULL) {
+ (void)fprintf(stderr,
+ "vi: %s: malformed recovery file.\n", dp->d_name);
+ goto next;
+ }
+ *p = '\0';
+
+ /* Get the last modification time. */
+ if (fstat(fileno(fp), &sb)) {
+ (void)fprintf(stderr,
+ "vi: %s: %s\n", dp->d_name, strerror(errno));
+ goto next;
+ }
+
+ /* Display. */
+ (void)printf("%s: %s",
+ file + sizeof(VI_FHEADER) - 1, ctime(&sb.st_mtime));
+ found = 1;
+
+next: (void)fclose(fp);
+ }
+ if (found == 0)
+ (void)printf("vi: no files to recover.\n");
+ (void)closedir(dirp);
+ return (0);
+}
+
+/*
+ * rcv_read --
+ * Start a recovered file as the file to edit.
+ */
+int
+rcv_read(sp, name)
+ SCR *sp;
+ char *name;
+{
+ struct dirent *dp;
+ struct stat sb;
+ DIR *dirp;
+ FREF *frp;
+ FILE *fp;
+ time_t rec_mtime;
+ int found, requested;
+ char *p, *t, *recp, *pathp;
+ char recpath[MAXPATHLEN], file[MAXPATHLEN], path[MAXPATHLEN];
+
+ if ((dirp = opendir(O_STR(sp, O_RECDIR))) == NULL) {
+ msgq(sp, M_ERR,
+ "%s: %s", O_STR(sp, O_RECDIR), strerror(errno));
+ return (1);
+ }
+
+ recp = pathp = NULL;
+ for (found = requested = 0; (dp = readdir(dirp)) != NULL;) {
+ if (strncmp(dp->d_name, "recover.", 8))
+ continue;
+
+ /* If it's readable, it's recoverable. */
+ (void)snprintf(recpath, sizeof(recpath),
+ "%s/%s", O_STR(sp, O_RECDIR), dp->d_name);
+ if ((fp = fopen(recpath, "r")) == NULL)
+ continue;
+
+ /* If it's locked, it's live. */
+ if (flock(fileno(fp), LOCK_EX | LOCK_NB)) {
+ (void)fclose(fp);
+ continue;
+ }
+
+ /* Check the headers. */
+ if (fgets(file, sizeof(file), fp) == NULL ||
+ strncmp(file, VI_FHEADER, sizeof(VI_FHEADER) - 1) ||
+ (p = strchr(file, '\n')) == NULL ||
+ fgets(path, sizeof(path), fp) == NULL ||
+ strncmp(path, VI_PHEADER, sizeof(VI_PHEADER) - 1) ||
+ (t = strchr(path, '\n')) == NULL) {
+ msgq(sp, M_ERR,
+ "%s: malformed recovery file.", recpath);
+ goto next;
+ }
+ ++found;
+ *t = *p = '\0';
+
+ /* Get the last modification time. */
+ if (fstat(fileno(fp), &sb)) {
+ msgq(sp, M_ERR,
+ "vi: %s: %s", dp->d_name, strerror(errno));
+ goto next;
+ }
+
+ /* Check the file name. */
+ if (strcmp(file + sizeof(VI_FHEADER) - 1, name))
+ goto next;
+
+ ++requested;
+
+ /* If we've found more than one, take the most recent. */
+ if (recp == NULL || rec_mtime < sb.st_mtime) {
+ p = recp;
+ t = pathp;
+ if ((recp = strdup(recpath)) == NULL) {
+ msgq(sp, M_ERR,
+ "vi: Error: %s.\n", strerror(errno));
+ recp = p;
+ goto next;
+ }
+ if ((pathp = strdup(path)) == NULL) {
+ msgq(sp, M_ERR,
+ "vi: Error: %s.\n", strerror(errno));
+ FREE(recp, strlen(recp) + 1);
+ recp = p;
+ pathp = t;
+ goto next;
+ }
+ if (p != NULL) {
+ FREE(p, strlen(p) + 1);
+ FREE(t, strlen(t) + 1);
+ }
+ rec_mtime = sb.st_mtime;
+ }
+
+next: (void)fclose(fp);
+ }
+ (void)closedir(dirp);
+
+ if (recp == NULL) {
+ msgq(sp, M_INFO,
+ "No files named %s, owned by you, to edit.", name);
+ return (1);
+ }
+ if (found) {
+ if (requested > 1)
+ msgq(sp, M_INFO,
+ "There are older versions of this file for you to recover.");
+ if (found > requested)
+ msgq(sp, M_INFO,
+ "There are other files that you can recover.");
+ }
+
+ /* Create the FREF structure, start the btree file. */
+ if ((frp = file_add(sp, NULL, name, 0)) == NULL ||
+ file_init(sp, frp, pathp + sizeof(VI_PHEADER) - 1, 0)) {
+ FREE(recp, strlen(recp) + 1);
+ FREE(pathp, strlen(pathp) + 1);
+ return (1);
+ }
+ sp->ep->rcv_mpath = recp;
+ return (0);
+}
diff --git a/usr.bin/vi/screen.c b/usr.bin/vi/screen.c
new file mode 100644
index 000000000000..e18bd02183dd
--- /dev/null
+++ b/usr.bin/vi/screen.c
@@ -0,0 +1,296 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)screen.c 8.51 (Berkeley) 1/11/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "vcmd.h"
+#include "excmd.h"
+#include "tag.h"
+
+/*
+ * screen_init --
+ * Do the default initialization of an SCR structure.
+ */
+int
+screen_init(orig, spp, flags)
+ SCR *orig, **spp;
+ u_int flags;
+{
+ SCR *sp;
+ size_t len;
+
+ *spp = NULL;
+ CALLOC_RET(orig, sp, SCR *, 1, sizeof(SCR));
+ *spp = sp;
+
+/* INITIALIZED AT SCREEN CREATE. */
+ sp->gp = __global_list; /* All ref the GS structure. */
+
+ LIST_INIT(&sp->msgq);
+ CIRCLEQ_INIT(&sp->frefq);
+
+ sp->ccnt = 2; /* Anything > 1 */
+
+ FD_ZERO(&sp->rdfd);
+
+ CIRCLEQ_INIT(&sp->tiq);
+
+/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
+ if (orig == NULL) {
+ sp->searchdir = NOTSET;
+ sp->csearchdir = CNOTSET;
+
+ switch (flags & S_SCREENS) {
+ case S_EX:
+ if (sex_screen_init(sp))
+ return (1);
+ break;
+ case S_VI_CURSES:
+ if (svi_screen_init(sp))
+ return (1);
+ break;
+ case S_VI_XAW:
+ if (xaw_screen_init(sp))
+ return (1);
+ break;
+ default:
+ abort();
+ }
+
+ sp->flags = flags;
+ } else {
+ if (orig->alt_name != NULL &&
+ (sp->alt_name = strdup(orig->alt_name)) == NULL)
+ goto mem;
+
+ /* Retain all searching/substitution information. */
+ if (F_ISSET(orig, S_SRE_SET)) {
+ F_SET(sp, S_SRE_SET);
+ sp->sre = orig->sre;
+ }
+ if (F_ISSET(orig, S_SUBRE_SET)) {
+ F_SET(sp, S_SUBRE_SET);
+ sp->subre = orig->subre;
+ }
+ sp->searchdir = orig->searchdir == NOTSET ? NOTSET : FORWARD;
+ sp->csearchdir = CNOTSET;
+ sp->lastckey = orig->lastckey;
+
+ if (orig->matchsize) {
+ len = orig->matchsize * sizeof(regmatch_t);
+ MALLOC(sp, sp->match, regmatch_t *, len);
+ if (sp->match == NULL)
+ goto mem;
+ sp->matchsize = orig->matchsize;
+ memmove(sp->match, orig->match, len);
+ }
+ if (orig->repl_len) {
+ MALLOC(sp, sp->repl, char *, orig->repl_len);
+ if (sp->repl == NULL)
+ goto mem;
+ sp->repl_len = orig->repl_len;
+ memmove(sp->repl, orig->repl, orig->repl_len);
+ }
+ if (orig->newl_len) {
+ len = orig->newl_len * sizeof(size_t);
+ MALLOC(sp, sp->newl, size_t *, len);
+ if (sp->newl == NULL)
+ goto mem;
+ sp->newl_len = orig->newl_len;
+ sp->newl_cnt = orig->newl_cnt;
+ memmove(sp->newl, orig->newl, len);
+ }
+
+ sp->saved_vi_mode = orig->saved_vi_mode;
+
+ if (opts_copy(orig, sp)) {
+mem: msgq(orig, M_SYSERR, "new screen attributes");
+ (void)screen_end(sp);
+ return (1);
+ }
+
+ sp->s_bell = orig->s_bell;
+ sp->s_bg = orig->s_bg;
+ sp->s_busy = orig->s_busy;
+ sp->s_change = orig->s_change;
+ sp->s_chposition = orig->s_chposition;
+ sp->s_clear = orig->s_clear;
+ sp->s_column = orig->s_column;
+ sp->s_confirm = orig->s_confirm;
+ sp->s_down = orig->s_down;
+ sp->s_edit = orig->s_edit;
+ sp->s_end = orig->s_end;
+ sp->s_ex_cmd = orig->s_ex_cmd;
+ sp->s_ex_run = orig->s_ex_run;
+ sp->s_ex_write = orig->s_ex_write;
+ sp->s_fg = orig->s_fg;
+ sp->s_fill = orig->s_fill;
+ sp->s_get = orig->s_get;
+ sp->s_key_read = orig->s_key_read;
+ sp->s_optchange = orig->s_optchange;
+ sp->s_position = orig->s_position;
+ sp->s_rabs = orig->s_rabs;
+ sp->s_refresh = orig->s_refresh;
+ sp->s_relative = orig->s_relative;
+ sp->s_rrel = orig->s_rrel;
+ sp->s_split = orig->s_split;
+ sp->s_suspend = orig->s_suspend;
+ sp->s_up = orig->s_up;
+
+ F_SET(sp, F_ISSET(orig, S_SCREENS));
+ }
+
+ if (xaw_screen_copy(orig, sp)) /* Init S_VI_XAW screen. */
+ return (1);
+ if (svi_screen_copy(orig, sp)) /* Init S_VI_CURSES screen. */
+ return (1);
+ if (sex_screen_copy(orig, sp)) /* Init S_EX screen. */
+ return (1);
+ if (v_screen_copy(orig, sp)) /* Init vi. */
+ return (1);
+ if (ex_screen_copy(orig, sp)) /* Init ex. */
+ return (1);
+
+ *spp = sp;
+ return (0);
+}
+
+/*
+ * screen_end --
+ * Release a screen.
+ */
+int
+screen_end(sp)
+ SCR *sp;
+{
+ int rval;
+
+ rval = 0;
+ if (xaw_screen_end(sp)) /* End S_VI_XAW screen. */
+ rval = 1;
+ if (svi_screen_end(sp)) /* End S_VI_CURSES screen. */
+ rval = 1;
+ if (sex_screen_end(sp)) /* End S_EX screen. */
+ rval = 1;
+ if (v_screen_end(sp)) /* End vi. */
+ rval = 1;
+ if (ex_screen_end(sp)) /* End ex. */
+ rval = 1;
+
+ /* Free FREF's. */
+ { FREF *frp;
+ while ((frp = sp->frefq.cqh_first) != (FREF *)&sp->frefq) {
+ CIRCLEQ_REMOVE(&sp->frefq, frp, q);
+ if (frp->cname != NULL)
+ free(frp->cname);
+ if (frp->name != NULL)
+ free(frp->name);
+ if (frp->tname != NULL)
+ free(frp->tname);
+ FREE(frp, sizeof(FREF));
+ }
+ }
+
+ /* Free any text input. */
+ text_lfree(&sp->tiq);
+
+ /* Free any script information. */
+ if (F_ISSET(sp, S_SCRIPT))
+ sscr_end(sp);
+
+ /* Free alternate file name. */
+ if (sp->alt_name != NULL)
+ FREE(sp->alt_name, strlen(sp->alt_name) + 1);
+
+ /* Free up search information. */
+ if (sp->match != NULL)
+ FREE(sp->match, sizeof(regmatch_t));
+ if (sp->repl != NULL)
+ FREE(sp->repl, sp->repl_len);
+ if (sp->newl != NULL)
+ FREE(sp->newl, sp->newl_len);
+
+ /* Free all the options */
+ opts_free(sp);
+
+ /*
+ * Free the message chain last, so previous failures have a place
+ * to put messages. Copy messages to (in order) a related screen,
+ * any screen, the global area.
+ */
+ { SCR *c_sp; MSG *mp, *next;
+ if ((c_sp = sp->q.cqe_prev) != (void *)&sp->gp->dq) {
+ if (F_ISSET(sp, S_BELLSCHED))
+ F_SET(c_sp, S_BELLSCHED);
+ } else if ((c_sp = sp->q.cqe_next) != (void *)&sp->gp->dq) {
+ if (F_ISSET(sp, S_BELLSCHED))
+ F_SET(c_sp, S_BELLSCHED);
+ } else if ((c_sp =
+ sp->gp->hq.cqh_first) != (void *)&sp->gp->hq) {
+ if (F_ISSET(sp, S_BELLSCHED))
+ F_SET(c_sp, S_BELLSCHED);
+ } else {
+ c_sp = NULL;
+ if (F_ISSET(sp, S_BELLSCHED))
+ F_SET(sp->gp, G_BELLSCHED);
+ }
+
+ for (mp = sp->msgq.lh_first; mp != NULL; mp = next) {
+ if (!F_ISSET(mp, M_EMPTY))
+ msg_app(sp->gp, c_sp,
+ mp->flags & M_INV_VIDEO, mp->mbuf, mp->len);
+ next = mp->q.le_next;
+ if (mp->mbuf != NULL)
+ FREE(mp->mbuf, mp->blen);
+ FREE(mp, sizeof(MSG));
+ }
+ }
+
+ /* Remove the screen from the displayed queue. */
+ CIRCLEQ_REMOVE(&sp->gp->dq, sp, q);
+
+ /* Free the screen itself. */
+ FREE(sp, sizeof(SCR));
+
+ return (rval);
+}
diff --git a/usr.bin/vi/screen.h b/usr.bin/vi/screen.h
new file mode 100644
index 000000000000..d95179352b48
--- /dev/null
+++ b/usr.bin/vi/screen.h
@@ -0,0 +1,314 @@
+/*-
+ * Copyright (c) 1992, 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.
+ *
+ * @(#)screen.h 8.81 (Berkeley) 12/29/93
+ */
+
+/*
+ * There are minimum values that vi has to have to display a screen. The
+ * row minimum is fixed at 1 line for the text, and 1 line for any error
+ * messages. The column calculation is a lot trickier. For example, you
+ * have to have enough columns to display the line number, not to mention
+ * guaranteeing that tabstop and shiftwidth values are smaller than the
+ * current column value. It's a lot simpler to have a fixed value and not
+ * worry about it.
+ *
+ * XXX
+ * MINIMUM_SCREEN_COLS is probably wrong.
+ */
+#define MINIMUM_SCREEN_ROWS 2
+#define MINIMUM_SCREEN_COLS 20
+ /* Line operations. */
+enum operation { LINE_APPEND, LINE_DELETE, LINE_INSERT, LINE_RESET };
+ /* Position values. */
+enum position { P_BOTTOM, P_FILL, P_MIDDLE, P_TOP };
+
+/*
+ * Structure for holding file references. Each SCR structure contains a
+ * linked list of these (the user's argument list) as well as pointers to
+ * the current and previous files. The structure contains the name of the
+ * file, along with the information that follows the name. A file has up
+ * to three "names". The tname field is the path of the temporary backing
+ * file, if any. The name field is the name the user originally used to
+ * specify the file to be edited. The cname field is the changed name if
+ * the user changed the name.
+ *
+ * Note that the read-only bit follows the file name, not the file itself.
+ *
+ * XXX
+ * The mtime field should be a struct timespec, but time_t is more portable.
+ */
+struct _fref {
+ CIRCLEQ_ENTRY(_fref) q; /* Linked list of file references. */
+ char *cname; /* Changed file name. */
+ char *name; /* File name. */
+ char *tname; /* Temporary file name. */
+
+ recno_t lno; /* 1-N: file cursor line. */
+ size_t cno; /* 0-N: file cursor column. */
+ time_t mtime; /* Last modification time. */
+
+#define FR_CHANGEWRITE 0x001 /* Name changed and then written. */
+#define FR_CURSORSET 0x002 /* If lno/cno valid. */
+#define FR_EDITED 0x004 /* If the file was ever edited. */
+#define FR_IGNORE 0x008 /* File isn't part of argument list. */
+#define FR_NEWFILE 0x010 /* File doesn't really exist yet. */
+#define FR_RDONLY 0x020 /* File is read-only. */
+ u_int flags;
+};
+
+/*
+ * There's a file name hierarchy -- if the user has changed the name, we
+ * use it, otherwise, we use the original name, if there was one, othewise
+ * use the temporary name.
+ */
+#define FILENAME(frp) \
+ ((frp)->cname != NULL) ? (frp)->cname : \
+ ((frp)->name != NULL) ? (frp)->name : (frp)->tname
+
+/*
+ * SCR --
+ * The screen structure. To the extent possible, all screen information
+ * is stored in the various private areas. The only information here
+ * is used by global routines or is shared by too many screens.
+ */
+struct _scr {
+/* INITIALIZED AT SCREEN CREATE. */
+ CIRCLEQ_ENTRY(_scr) q; /* Screens. */
+
+ GS *gp; /* Pointer to global area. */
+
+ SCR *nextdisp; /* Next display screen. */
+
+ EXF *ep; /* Screen's current EXF structure. */
+
+ MSGH msgq; /* Message list. */
+ /* FREF list. */
+ CIRCLEQ_HEAD(_frefh, _fref) frefq;
+ FREF *frp; /* FREF being edited. */
+ FREF *a_frp; /* Last argument list FREF edited. */
+ FREF *p_frp; /* Last FREF edited. */
+
+ u_long ccnt; /* Command count. */
+ u_long q_ccnt; /* Quit or ZZ command count. */
+
+ /* Screen's: */
+ size_t rows; /* 1-N: number of rows. */
+ size_t cols; /* 1-N: number of columns. */
+ size_t woff; /* 0-N: row offset in screen. */
+ size_t t_rows; /* 1-N: cur number of text rows. */
+ size_t t_maxrows; /* 1-N: max number of text rows. */
+ size_t t_minrows; /* 1-N: min number of text rows. */
+
+ /* Cursor's: */
+ recno_t lno; /* 1-N: file line. */
+ size_t cno; /* 0-N: file character in line. */
+
+ size_t rcm; /* Vi: 0-N: Column suck. */
+#define RCM_FNB 0x01 /* Column suck: first non-blank. */
+#define RCM_LAST 0x02 /* Column suck: last. */
+ u_int rcmflags;
+
+#define L_ADDED 0 /* Added lines. */
+#define L_CHANGED 1 /* Changed lines. */
+#define L_COPIED 2 /* Copied lines. */
+#define L_DELETED 3 /* Deleted lines. */
+#define L_JOINED 4 /* Joined lines. */
+#define L_MOVED 5 /* Moved lines. */
+#define L_PUT 6 /* Put lines. */
+#define L_LSHIFT 7 /* Left shift lines. */
+#define L_RSHIFT 8 /* Right shift lines. */
+#define L_YANKED 9 /* Yanked lines. */
+ recno_t rptlines[L_YANKED + 1];/* Ex/vi: lines changed by last op. */
+
+ FILE *stdfp; /* Ex output file pointer. */
+
+ char *if_name; /* Ex input file name, for messages. */
+ recno_t if_lno; /* Ex input file line, for messages. */
+
+ fd_set rdfd; /* Ex/vi: read fd select mask. */
+
+ TEXTH tiq; /* Ex/vi: text input queue. */
+
+ SCRIPT *script; /* Vi: script mode information .*/
+
+ char const *time_msg; /* ITIMER_REAL message. */
+ struct itimerval time_value; /* ITIMER_REAL saved value. */
+ struct sigaction time_handler; /* ITIMER_REAL saved handler. */
+
+ void *vi_private; /* Vi private area. */
+ void *ex_private; /* Ex private area. */
+ void *svi_private; /* Vi curses screen private area. */
+ void *xaw_private; /* Vi XAW screen private area. */
+
+/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
+ char *alt_name; /* Ex/vi: alternate file name. */
+
+ /* Ex/vi: search/substitute info. */
+ regex_t sre; /* Last search RE. */
+ regex_t subre; /* Last substitute RE. */
+ enum direction searchdir; /* File search direction. */
+ enum cdirection csearchdir; /* Character search direction. */
+ CHAR_T lastckey; /* Last search character. */
+ regmatch_t *match; /* Substitute match array. */
+ size_t matchsize; /* Substitute match array size. */
+ char *repl; /* Substitute replacement. */
+ size_t repl_len; /* Substitute replacement length.*/
+ size_t *newl; /* Newline offset array. */
+ size_t newl_len; /* Newline array size. */
+ size_t newl_cnt; /* Newlines in replacement. */
+
+ u_int saved_vi_mode; /* Saved vi display type. */
+
+ OPTION opts[O_OPTIONCOUNT]; /* Options. */
+
+/*
+ * SCREEN SUPPORT ROUTINES.
+ *
+ * A SCR * MUST be the first argument to these routines.
+ */
+ /* Ring the screen bell. */
+ void (*s_bell) __P((SCR *));
+ /* Background the screen. */
+ int (*s_bg) __P((SCR *));
+ /* Put up a busy message. */
+ int (*s_busy) __P((SCR *, char const *));
+ /* Change a screen line. */
+ int (*s_change) __P((SCR *, EXF *, recno_t, enum operation));
+ /* Return column close to specified. */
+ size_t (*s_chposition) __P((SCR *, EXF *, recno_t, size_t));
+ /* Clear the screen. */
+ int (*s_clear) __P((SCR *));
+ /* Return the logical cursor column. */
+ int (*s_column) __P((SCR *, EXF *, size_t *));
+ enum confirm /* Confirm an action with the user. */
+ (*s_confirm) __P((SCR *, EXF *, MARK *, MARK *));
+ /* Move down the screen. */
+ int (*s_down) __P((SCR *, EXF *, MARK *, recno_t, int));
+ /* Edit a file. */
+ int (*s_edit) __P((SCR *, EXF *));
+ /* End a screen. */
+ int (*s_end) __P((SCR *));
+ /* Run a single ex command. */
+ int (*s_ex_cmd) __P((SCR *, EXF *, EXCMDARG *, MARK *));
+ /* Run user's ex commands. */
+ int (*s_ex_run) __P((SCR *, EXF *, MARK *));
+ /* Screen's ex write function. */
+ int (*s_ex_write) __P((void *, const char *, int));
+ /* Foreground the screen. */
+ int (*s_fg) __P((SCR *, CHAR_T *));
+ /* Fill the screen's map. */
+ int (*s_fill) __P((SCR *, EXF *, recno_t, enum position));
+ enum input /* Get a line from the user. */
+ (*s_get) __P((SCR *, EXF *, TEXTH *, int, u_int));
+ enum input /* Get a key from the user. */
+ (*s_key_read) __P((SCR *, int *, struct timeval *));
+ /* Tell the screen an option changed. */
+ int (*s_optchange) __P((SCR *, int));
+ /* Return column at screen position. */
+ int (*s_position) __P((SCR *, EXF *,
+ MARK *, u_long, enum position));
+ /* Change the absolute screen size. */
+ int (*s_rabs) __P((SCR *, long));
+ /* Refresh the screen. */
+ int (*s_refresh) __P((SCR *, EXF *));
+ /* Return column close to last char. */
+ size_t (*s_relative) __P((SCR *, EXF *, recno_t));
+ /* Change the relative screen size. */
+ int (*s_rrel) __P((SCR *, long));
+ /* Split the screen. */
+ int (*s_split) __P((SCR *, ARGS *[]));
+ /* Suspend the screen. */
+ int (*s_suspend) __P((SCR *));
+ /* Move up the screen. */
+ int (*s_up) __P((SCR *, EXF *, MARK *, recno_t, int));
+
+/* Editor screens. */
+#define S_EX 0x0000001 /* Ex screen. */
+#define S_VI_CURSES 0x0000002 /* Vi: curses screen. */
+#define S_VI_XAW 0x0000004 /* Vi: Athena widgets screen. */
+
+#define IN_EX_MODE(sp) /* If in ex mode. */ \
+ (F_ISSET(sp, S_EX))
+#define IN_VI_MODE(sp) /* If in vi mode. */ \
+ (F_ISSET(sp, S_VI_CURSES | S_VI_XAW))
+#define S_SCREENS /* Screens. */ \
+ (S_EX | S_VI_CURSES | S_VI_XAW)
+
+/* Major screen/file changes. */
+#define S_EXIT 0x0000008 /* Exiting (not forced). */
+#define S_EXIT_FORCE 0x0000010 /* Exiting (forced). */
+#define S_FSWITCH 0x0000020 /* Switch files. */
+#define S_SSWITCH 0x0000040 /* Switch screens. */
+#define S_MAJOR_CHANGE /* Screen or file changes. */ \
+ (S_EXIT | S_EXIT_FORCE | S_FSWITCH | S_SSWITCH)
+
+#define S_BELLSCHED 0x0000080 /* Bell scheduled. */
+#define S_CONTINUE 0x0000100 /* Need to ask the user to continue. */
+#define S_EXSILENT 0x0000200 /* Ex batch script. */
+#define S_GLOBAL 0x0000400 /* Doing a global command. */
+#define S_INPUT 0x0000800 /* Doing text input. */
+#define S_INTERRUPTED 0x0001000 /* If have been interrupted. */
+#define S_INTERRUPTIBLE 0x0002000 /* If can be interrupted. */
+#define S_REDRAW 0x0004000 /* Redraw the screen. */
+#define S_REFORMAT 0x0008000 /* Reformat the screen. */
+#define S_REFRESH 0x0010000 /* Refresh the screen. */
+#define S_RENUMBER 0x0020000 /* Renumber the screen. */
+#define S_RESIZE 0x0040000 /* Resize the screen. */
+#define S_SCRIPT 0x0080000 /* Window is a shell script. */
+#define S_SRE_SET 0x0100000 /* The search RE has been set. */
+#define S_SUBRE_SET 0x0200000 /* The substitute RE has been set. */
+#define S_TIMER_SET 0x0400000 /* If a busy timer is running. */
+#define S_UPDATE_MODE 0x0800000 /* Don't repaint modeline. */
+ u_int flags;
+};
+
+/* Generic routines to start/stop a screen. */
+int screen_end __P((SCR *));
+int screen_init __P((SCR *, SCR **, u_int));
+
+/* Public interfaces to the underlying screens. */
+int ex_screen_copy __P((SCR *, SCR *));
+int ex_screen_end __P((SCR *));
+int ex_screen_init __P((SCR *));
+int sex_screen_copy __P((SCR *, SCR *));
+int sex_screen_end __P((SCR *));
+int sex_screen_init __P((SCR *));
+int svi_screen_copy __P((SCR *, SCR *));
+int svi_screen_end __P((SCR *));
+int svi_screen_init __P((SCR *));
+int v_screen_copy __P((SCR *, SCR *));
+int v_screen_end __P((SCR *));
+int v_screen_init __P((SCR *));
+int xaw_screen_copy __P((SCR *, SCR *));
+int xaw_screen_end __P((SCR *));
+int xaw_screen_init __P((SCR *));
diff --git a/usr.bin/vi/search.c b/usr.bin/vi/search.c
new file mode 100644
index 000000000000..22aadef48751
--- /dev/null
+++ b/usr.bin/vi/search.c
@@ -0,0 +1,860 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)search.c 8.32 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "interrupt.h"
+
+static int check_delta __P((SCR *, EXF *, long, recno_t));
+static int ctag_conv __P((SCR *, char **, int *));
+static int get_delta __P((SCR *, char **, long *, u_int *));
+static int resetup __P((SCR *, regex_t **, enum direction,
+ char *, char **, long *, u_int *));
+static void search_intr __P((int));
+
+/*
+ * resetup --
+ * Set up a search for a regular expression.
+ */
+static int
+resetup(sp, rep, dir, ptrn, epp, deltap, flagp)
+ SCR *sp;
+ regex_t **rep;
+ enum direction dir;
+ char *ptrn, **epp;
+ long *deltap;
+ u_int *flagp;
+{
+ u_int flags;
+ int delim, eval, re_flags, replaced;
+ char *p, *t;
+
+ /* Set return information the default. */
+ *deltap = 0;
+
+ /*
+ * Use saved pattern if no pattern supplied, or if only a delimiter
+ * character is supplied. Only the pattern was saved, historic vi
+ * did not reuse any delta supplied.
+ */
+ flags = *flagp;
+ if (ptrn == NULL)
+ goto prev;
+ if (ptrn[1] == '\0') {
+ if (epp != NULL)
+ *epp = ptrn + 1;
+ goto prev;
+ }
+ if (ptrn[0] == ptrn[1] && ptrn[2] == '\0') {
+ if (epp != NULL)
+ *epp = ptrn + 2;
+prev: if (!F_ISSET(sp, S_SRE_SET)) {
+ msgq(sp, M_ERR, "No previous search pattern.");
+ return (1);
+ }
+ *rep = &sp->sre;
+
+ /* Empty patterns set the direction. */
+ if (LF_ISSET(SEARCH_SET)) {
+ F_SET(sp, S_SRE_SET);
+ sp->searchdir = dir;
+ sp->sre = **rep;
+ }
+ return (0);
+ }
+
+ re_flags = 0; /* Set RE flags. */
+ if (O_ISSET(sp, O_EXTENDED))
+ re_flags |= REG_EXTENDED;
+ if (O_ISSET(sp, O_IGNORECASE))
+ re_flags |= REG_ICASE;
+
+ if (LF_ISSET(SEARCH_PARSE)) { /* Parse the string. */
+ /* Set delimiter. */
+ delim = *ptrn++;
+
+ /* Find terminating delimiter, handling escaped delimiters. */
+ for (p = t = ptrn;;) {
+ if (p[0] == '\0' || p[0] == delim) {
+ if (p[0] == delim)
+ ++p;
+ *t = '\0';
+ break;
+ }
+ if (p[1] == delim && p[0] == '\\')
+ ++p;
+ *t++ = *p++;
+ }
+
+ /*
+ * If characters after the terminating delimiter, it may
+ * be an error, or may be an offset. In either case, we
+ * return the end of the string, whatever it may be.
+ */
+ if (*p) {
+ if (get_delta(sp, &p, deltap, flagp))
+ return (1);
+ if (*p && LF_ISSET(SEARCH_TERM)) {
+ msgq(sp, M_ERR,
+ "Characters after search string and/or delta.");
+ return (1);
+ }
+ }
+ if (epp != NULL)
+ *epp = p;
+
+ /* Check for "/ " or other such silliness. */
+ if (*ptrn == '\0')
+ goto prev;
+
+ if (re_conv(sp, &ptrn, &replaced))
+ return (1);
+ } else if (LF_ISSET(SEARCH_TAG)) {
+ if (ctag_conv(sp, &ptrn, &replaced))
+ return (1);
+ re_flags &= ~(REG_EXTENDED | REG_ICASE);
+ }
+
+ /* Compile the RE. */
+ if (eval = regcomp(*rep, ptrn, re_flags))
+ re_error(sp, eval, *rep);
+ else if (LF_ISSET(SEARCH_SET)) {
+ F_SET(sp, S_SRE_SET);
+ sp->searchdir = dir;
+ sp->sre = **rep;
+ }
+
+ /* Free up any extra memory. */
+ if (replaced)
+ FREE_SPACE(sp, ptrn, 0);
+ return (eval);
+}
+
+/*
+ * ctag_conv --
+ * Convert a tags search path into something that the POSIX
+ * 1003.2 RE functions can handle.
+ */
+static int
+ctag_conv(sp, ptrnp, replacedp)
+ SCR *sp;
+ char **ptrnp;
+ int *replacedp;
+{
+ size_t blen, len;
+ int lastdollar;
+ char *bp, *p, *t;
+
+ *replacedp = 0;
+
+ len = strlen(p = *ptrnp);
+
+ /* Max memory usage is 2 times the length of the string. */
+ GET_SPACE_RET(sp, bp, blen, len * 2);
+
+ t = bp;
+
+ /* The last charcter is a '/' or '?', we just strip it. */
+ if (p[len - 1] == '/' || p[len - 1] == '?')
+ p[len - 1] = '\0';
+
+ /* The next-to-last character is a '$', and it's magic. */
+ if (p[len - 2] == '$') {
+ lastdollar = 1;
+ p[len - 2] = '\0';
+ } else
+ lastdollar = 0;
+
+ /* The first character is a '/' or '?', we just strip it. */
+ if (p[0] == '/' || p[0] == '?')
+ ++p;
+
+ /* The second character is a '^', and it's magic. */
+ if (p[0] == '^')
+ *t++ = *p++;
+
+ /*
+ * Escape every other magic character we can find, stripping the
+ * backslashes ctags inserts to escape the search delimiter
+ * characters.
+ */
+ while (p[0]) {
+ /* Ctags escapes the search delimiter characters. */
+ if (p[0] == '\\' && (p[1] == '/' || p[1] == '?'))
+ ++p;
+ else if (strchr("^.[]$*", p[0]))
+ *t++ = '\\';
+ *t++ = *p++;
+ }
+ if (lastdollar)
+ *t++ = '$';
+ *t++ = '\0';
+
+ *ptrnp = bp;
+ *replacedp = 1;
+ return (0);
+}
+
+/*
+ * search_intr --
+ * Set the interrupt bit in any screen that is interruptible.
+ *
+ * XXX
+ * In the future this may be a problem. The user should be able to move to
+ * another screen and keep typing while this runs. If so, and the user has
+ * more than one search/global (see ex/ex_global.c) running, it will be hard
+ * to decide which one to stop.
+ */
+static void
+search_intr(signo)
+ int signo;
+{
+ SCR *sp;
+
+ for (sp = __global_list->dq.cqh_first;
+ sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
+ if (F_ISSET(sp, S_INTERRUPTIBLE))
+ F_SET(sp, S_INTERRUPTED);
+}
+
+#define EMPTYMSG "File empty; nothing to search."
+#define EOFMSG "Reached end-of-file without finding the pattern."
+#define NOTFOUND "Pattern not found."
+#define SOFMSG "Reached top-of-file without finding the pattern."
+#define WRAPMSG "Search wrapped."
+
+int
+f_search(sp, ep, fm, rm, ptrn, eptrn, flagp)
+ SCR *sp;
+ EXF *ep;
+ MARK *fm, *rm;
+ char *ptrn, **eptrn;
+ u_int *flagp;
+{
+ DECLARE_INTERRUPTS;
+ regmatch_t match[1];
+ regex_t *re, lre;
+ recno_t lno;
+ size_t coff, len;
+ long delta;
+ u_int flags;
+ int eval, rval, wrapped;
+ char *l;
+
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ flags = *flagp;
+ if (lno == 0) {
+ if (LF_ISSET(SEARCH_MSG))
+ msgq(sp, M_INFO, EMPTYMSG);
+ return (1);
+ }
+
+ re = &lre;
+ if (resetup(sp, &re, FORWARD, ptrn, eptrn, &delta, flagp))
+ return (1);
+
+ /*
+ * Start searching immediately after the cursor. If at the end of the
+ * line, start searching on the next line. This is incompatible (read
+ * bug fix) with the historic vi -- searches for the '$' pattern never
+ * moved forward, and "-t foo" didn't work if "foo" was the first thing
+ * in the file.
+ */
+ if (LF_ISSET(SEARCH_FILE)) {
+ lno = 1;
+ coff = 0;
+ } else {
+ if ((l = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, fm->lno);
+ return (1);
+ }
+ if (fm->cno + 1 >= len) {
+ if (fm->lno == lno) {
+ if (!O_ISSET(sp, O_WRAPSCAN)) {
+ if (LF_ISSET(SEARCH_MSG))
+ msgq(sp, M_INFO, EOFMSG);
+ return (1);
+ }
+ lno = 1;
+ } else
+ lno = fm->lno + 1;
+ coff = 0;
+ } else {
+ lno = fm->lno;
+ coff = fm->cno + 1;
+ }
+ }
+
+ /*
+ * Set up busy message, interrupts.
+ *
+ * F_search is called from the ex_tagfirst() routine, which runs
+ * before the screen really exists. Make sure we don't step on
+ * anything.
+ */
+ if (sp->s_position != NULL)
+ busy_on(sp, 1, "Searching...");
+ SET_UP_INTERRUPTS(search_intr);
+
+ for (rval = 1, wrapped = 0;; ++lno, coff = 0) {
+ if (F_ISSET(sp, S_INTERRUPTED)) {
+ msgq(sp, M_INFO, "Interrupted.");
+ break;
+ }
+ if (wrapped && lno > fm->lno ||
+ (l = file_gline(sp, ep, lno, &len)) == NULL) {
+ if (wrapped) {
+ if (LF_ISSET(SEARCH_MSG))
+ msgq(sp, M_INFO, NOTFOUND);
+ break;
+ }
+ if (!O_ISSET(sp, O_WRAPSCAN)) {
+ if (LF_ISSET(SEARCH_MSG))
+ msgq(sp, M_INFO, EOFMSG);
+ break;
+ }
+ lno = 0;
+ wrapped = 1;
+ continue;
+ }
+
+ /* If already at EOL, just keep going. */
+ if (len && coff == len)
+ continue;
+
+ /* Set the termination. */
+ match[0].rm_so = coff;
+ match[0].rm_eo = len;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "F search: %lu from %u to %u\n",
+ lno, coff, len ? len - 1 : len);
+#endif
+ /* Search the line. */
+ eval = regexec(re, l, 1, match,
+ (match[0].rm_so == 0 ? 0 : REG_NOTBOL) | REG_STARTEND);
+ if (eval == REG_NOMATCH)
+ continue;
+ if (eval != 0) {
+ re_error(sp, eval, re);
+ break;
+ }
+
+ /* Warn if wrapped. */
+ if (wrapped && O_ISSET(sp, O_WARN) && LF_ISSET(SEARCH_MSG))
+ msgq(sp, M_INFO, WRAPMSG);
+
+ /*
+ * If an offset, see if it's legal. It's possible to match
+ * past the end of the line with $, so check for that case.
+ */
+ if (delta) {
+ if (check_delta(sp, ep, delta, lno))
+ break;
+ rm->lno = delta + lno;
+ rm->cno = 0;
+ } else {
+#if defined(DEBUG) && 0
+ TRACE(sp, "found: %qu to %qu\n",
+ match[0].rm_so, match[0].rm_eo);
+#endif
+ rm->lno = lno;
+ rm->cno = match[0].rm_so;
+
+ /*
+ * If a change command, it's possible to move beyond
+ * the end of a line. Historic vi generally got this
+ * wrong (try "c?$<cr>"). Not all that sure this gets
+ * it right, there are lots of strange cases.
+ */
+ if (!LF_ISSET(SEARCH_EOL) && rm->cno >= len)
+ rm->cno = len ? len - 1 : 0;
+ }
+ rval = 0;
+ break;
+ }
+
+interrupt_err:
+
+ /* Turn off busy message, interrupts. */
+ if (sp->s_position != NULL)
+ busy_off(sp);
+ TEAR_DOWN_INTERRUPTS;
+
+ return (rval);
+}
+
+int
+b_search(sp, ep, fm, rm, ptrn, eptrn, flagp)
+ SCR *sp;
+ EXF *ep;
+ MARK *fm, *rm;
+ char *ptrn, **eptrn;
+ u_int *flagp;
+{
+ DECLARE_INTERRUPTS;
+ regmatch_t match[1];
+ regex_t *re, lre;
+ recno_t lno;
+ size_t coff, len, last;
+ long delta;
+ u_int flags;
+ int eval, rval, wrapped;
+ char *l;
+
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ flags = *flagp;
+ if (lno == 0) {
+ if (LF_ISSET(SEARCH_MSG))
+ msgq(sp, M_INFO, EMPTYMSG);
+ return (1);
+ }
+
+ re = &lre;
+ if (resetup(sp, &re, BACKWARD, ptrn, eptrn, &delta, flagp))
+ return (1);
+
+ /* If in the first column, start searching on the previous line. */
+ if (fm->cno == 0) {
+ if (fm->lno == 1) {
+ if (!O_ISSET(sp, O_WRAPSCAN)) {
+ if (LF_ISSET(SEARCH_MSG))
+ msgq(sp, M_INFO, SOFMSG);
+ return (1);
+ }
+ } else
+ lno = fm->lno - 1;
+ } else
+ lno = fm->lno;
+
+ /* Turn on busy message, interrupts. */
+ busy_on(sp, 1, "Searching...");
+
+ if (F_ISSET(sp->gp, G_ISFROMTTY))
+ SET_UP_INTERRUPTS(search_intr);
+
+ for (rval = 1, wrapped = 0, coff = fm->cno;; --lno, coff = 0) {
+ if (F_ISSET(sp, S_INTERRUPTED)) {
+ msgq(sp, M_INFO, "Interrupted.");
+ break;
+ }
+ if (wrapped && lno < fm->lno || lno == 0) {
+ if (wrapped) {
+ if (LF_ISSET(SEARCH_MSG))
+ msgq(sp, M_INFO, NOTFOUND);
+ break;
+ }
+ if (!O_ISSET(sp, O_WRAPSCAN)) {
+ if (LF_ISSET(SEARCH_MSG))
+ msgq(sp, M_INFO, SOFMSG);
+ break;
+ }
+ if (file_lline(sp, ep, &lno))
+ goto err;
+ if (lno == 0) {
+ if (LF_ISSET(SEARCH_MSG))
+ msgq(sp, M_INFO, EMPTYMSG);
+ break;
+ }
+ ++lno;
+ wrapped = 1;
+ continue;
+ }
+
+ if ((l = file_gline(sp, ep, lno, &len)) == NULL)
+ goto err;
+
+ /* Set the termination. */
+ match[0].rm_so = 0;
+ match[0].rm_eo = coff ? coff : len;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "B search: %lu from 0 to %qu\n", lno, match[0].rm_eo);
+#endif
+ /* Search the line. */
+ eval = regexec(re, l, 1, match,
+ (match[0].rm_eo == len ? 0 : REG_NOTEOL) | REG_STARTEND);
+ if (eval == REG_NOMATCH)
+ continue;
+ if (eval != 0) {
+ re_error(sp, eval, re);
+ break;
+ }
+
+ /* Warn if wrapped. */
+ if (wrapped && O_ISSET(sp, O_WARN) && LF_ISSET(SEARCH_MSG))
+ msgq(sp, M_INFO, WRAPMSG);
+
+ if (delta) {
+ if (check_delta(sp, ep, delta, lno))
+ break;
+ rm->lno = delta + lno;
+ rm->cno = 0;
+ } else {
+#if defined(DEBUG) && 0
+ TRACE(sp, "found: %qu to %qu\n",
+ match[0].rm_so, match[0].rm_eo);
+#endif
+ /*
+ * Find the last acceptable one in this line. This
+ * is really painful, we need a cleaner interface to
+ * regexec to make this possible.
+ */
+ for (;;) {
+ last = match[0].rm_so;
+ match[0].rm_so = match[0].rm_eo + 1;
+ if (match[0].rm_so >= len ||
+ coff && match[0].rm_so >= coff)
+ break;
+ match[0].rm_eo = coff ? coff : len;
+ eval = regexec(re, l, 1, match,
+ (match[0].rm_so == 0 ? 0 : REG_NOTBOL) |
+ REG_STARTEND);
+ if (eval == REG_NOMATCH)
+ break;
+ if (eval != 0) {
+ re_error(sp, eval, re);
+ goto err;
+ }
+ }
+ rm->lno = lno;
+
+ /* See comment in f_search(). */
+ if (!LF_ISSET(SEARCH_EOL) && last >= len)
+ rm->cno = len ? len - 1 : 0;
+ else
+ rm->cno = last;
+ }
+ rval = 0;
+ break;
+ }
+
+ /* Turn off busy message, interrupts. */
+interrupt_err:
+err: busy_off(sp);
+
+ if (F_ISSET(sp->gp, G_ISFROMTTY))
+ TEAR_DOWN_INTERRUPTS;
+
+ return (rval);
+}
+
+/*
+ * re_conv --
+ * Convert vi's regular expressions into something that the
+ * the POSIX 1003.2 RE functions can handle.
+ *
+ * There are three conversions we make to make vi's RE's (specifically
+ * the global, search, and substitute patterns) work with POSIX RE's.
+ *
+ * 1: If O_MAGIC is not set, strip backslashes from the magic character
+ * set (.[]*~) that have them, and add them to the ones that don't.
+ * 2: If O_MAGIC is not set, the string "\~" is replaced with the text
+ * from the last substitute command's replacement string. If O_MAGIC
+ * is set, it's the string "~".
+ * 3: The pattern \<ptrn\> does "word" searches, convert it to use the
+ * new RE escapes.
+ */
+int
+re_conv(sp, ptrnp, replacedp)
+ SCR *sp;
+ char **ptrnp;
+ int *replacedp;
+{
+ size_t blen, needlen;
+ int magic;
+ char *bp, *p, *t;
+
+ /*
+ * First pass through, we figure out how much space we'll need.
+ * We do it in two passes, on the grounds that most of the time
+ * the user is doing a search and won't have magic characters.
+ * That way we can skip the malloc and memmove's.
+ */
+ for (p = *ptrnp, magic = 0, needlen = 0; *p != '\0'; ++p)
+ switch (*p) {
+ case '\\':
+ switch (*++p) {
+ case '<':
+ magic = 1;
+ needlen += sizeof(RE_WSTART);
+ break;
+ case '>':
+ magic = 1;
+ needlen += sizeof(RE_WSTOP);
+ break;
+ case '~':
+ if (!O_ISSET(sp, O_MAGIC)) {
+ magic = 1;
+ needlen += sp->repl_len;
+ }
+ break;
+ case '.':
+ case '[':
+ case ']':
+ case '*':
+ if (!O_ISSET(sp, O_MAGIC)) {
+ magic = 1;
+ needlen += 1;
+ }
+ break;
+ default:
+ needlen += 2;
+ }
+ break;
+ case '~':
+ if (O_ISSET(sp, O_MAGIC)) {
+ magic = 1;
+ needlen += sp->repl_len;
+ }
+ break;
+ case '.':
+ case '[':
+ case ']':
+ case '*':
+ if (!O_ISSET(sp, O_MAGIC)) {
+ magic = 1;
+ needlen += 2;
+ }
+ break;
+ default:
+ needlen += 1;
+ break;
+ }
+
+ if (!magic) {
+ *replacedp = 0;
+ return (0);
+ }
+
+ /*
+ * Get enough memory to hold the final pattern.
+ *
+ * XXX
+ * It's nul-terminated, for now.
+ */
+ GET_SPACE_RET(sp, bp, blen, needlen + 1);
+
+ for (p = *ptrnp, t = bp; *p != '\0'; ++p)
+ switch (*p) {
+ case '\\':
+ switch (*++p) {
+ case '<':
+ memmove(t, RE_WSTART, sizeof(RE_WSTART) - 1);
+ t += sizeof(RE_WSTART) - 1;
+ break;
+ case '>':
+ memmove(t, RE_WSTOP, sizeof(RE_WSTOP) - 1);
+ t += sizeof(RE_WSTOP) - 1;
+ break;
+ case '~':
+ if (O_ISSET(sp, O_MAGIC))
+ *t++ = '~';
+ else {
+ memmove(t, sp->repl, sp->repl_len);
+ t += sp->repl_len;
+ }
+ break;
+ case '.':
+ case '[':
+ case ']':
+ case '*':
+ if (O_ISSET(sp, O_MAGIC))
+ *t++ = '\\';
+ *t++ = *p;
+ break;
+ default:
+ *t++ = '\\';
+ *t++ = *p;
+ }
+ break;
+ case '~':
+ if (O_ISSET(sp, O_MAGIC)) {
+ memmove(t, sp->repl, sp->repl_len);
+ t += sp->repl_len;
+ } else
+ *t++ = '~';
+ break;
+ case '.':
+ case '[':
+ case ']':
+ case '*':
+ if (!O_ISSET(sp, O_MAGIC))
+ *t++ = '\\';
+ *t++ = *p;
+ break;
+ default:
+ *t++ = *p;
+ break;
+ }
+ *t = '\0';
+
+ *ptrnp = bp;
+ *replacedp = 1;
+ return (0);
+}
+
+/*
+ * get_delta --
+ * Get a line delta. The trickiness is that the delta can be pretty
+ * complicated, i.e. "+3-2+3++- ++" is allowed.
+ *
+ * !!!
+ * In historic vi, if you had a delta on a search pattern which was used as
+ * a motion command, the command became a line mode command regardless of the
+ * cursor positions. A fairly common trick is to use a delta of "+0" to make
+ * the command a line mode command. This is the only place that knows about
+ * delta's, so we set the return flag information here.
+ */
+static int
+get_delta(sp, dp, valp, flagp)
+ SCR *sp;
+ char **dp;
+ long *valp;
+ u_int *flagp;
+{
+ char *p;
+ long val, tval;
+
+ for (tval = 0, p = *dp; *p != '\0'; *flagp |= SEARCH_DELTA) {
+ if (isblank(*p)) {
+ ++p;
+ continue;
+ }
+ if (*p == '+' || *p == '-') {
+ if (!isdigit(*(p + 1))) {
+ if (*p == '+') {
+ if (tval == LONG_MAX)
+ goto overflow;
+ ++tval;
+ } else {
+ if (tval == LONG_MIN)
+ goto underflow;
+ --tval;
+ }
+ ++p;
+ continue;
+ }
+ } else
+ if (!isdigit(*p))
+ break;
+
+ errno = 0;
+ val = strtol(p, &p, 10);
+ if (errno == ERANGE) {
+ if (val == LONG_MAX)
+overflow: msgq(sp, M_ERR, "Delta value overflow.");
+ else if (val == LONG_MIN)
+underflow: msgq(sp, M_ERR, "Delta value underflow.");
+ else
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ if (val >= 0) {
+ if (LONG_MAX - val < tval)
+ goto overflow;
+ } else
+ if (-(LONG_MIN - tval) > val)
+ goto underflow;
+ tval += val;
+ }
+ *dp = p;
+ *valp = tval;
+ return (0);
+}
+
+/*
+ * check_delta --
+ * Check a line delta to see if it's legal.
+ */
+static int
+check_delta(sp, ep, delta, lno)
+ SCR *sp;
+ EXF *ep;
+ long delta;
+ recno_t lno;
+{
+ /* A delta can overflow a record number. */
+ if (delta < 0) {
+ if (lno < LONG_MAX && delta >= (long)lno) {
+ msgq(sp, M_ERR, "Search offset before line 1.");
+ return (1);
+ }
+ } else {
+ if (ULONG_MAX - lno < delta) {
+ msgq(sp, M_ERR, "Delta value overflow.");
+ return (1);
+ }
+ if (file_gline(sp, ep, lno + delta, NULL) == NULL) {
+ msgq(sp, M_ERR, "Search offset past end-of-file.");
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * re_error --
+ * Report a regular expression error.
+ */
+void
+re_error(sp, errcode, preg)
+ SCR *sp;
+ int errcode;
+ regex_t *preg;
+{
+ size_t s;
+ char *oe;
+
+ s = regerror(errcode, preg, "", 0);
+ if ((oe = malloc(s)) == NULL)
+ msgq(sp, M_SYSERR, NULL);
+ else {
+ (void)regerror(errcode, preg, oe, s);
+ msgq(sp, M_ERR, "RE error: %s", oe);
+ }
+ free(oe);
+}
diff --git a/usr.bin/vi/search.h b/usr.bin/vi/search.h
new file mode 100644
index 000000000000..00feff6027c8
--- /dev/null
+++ b/usr.bin/vi/search.h
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 1992, 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.
+ *
+ * @(#)search.h 8.7 (Berkeley) 11/18/93
+ */
+
+#define RE_WSTART "[[:<:]]" /* Not-in-word search patterns. */
+#define RE_WSTOP "[[:>:]]"
+
+#define SEARCH_DELTA 0x001 /* A delta part of the search.*/
+#define SEARCH_EOL 0x002 /* Offset past EOL is okay. */
+#define SEARCH_FILE 0x004 /* Search the entire file. */
+#define SEARCH_MSG 0x008 /* Display search warning messages. */
+#define SEARCH_PARSE 0x010 /* Parse the search pattern. */
+#define SEARCH_SET 0x020 /* Set search direction. */
+#define SEARCH_TAG 0x040 /* Search pattern is a tag pattern. */
+#define SEARCH_TERM 0x080 /* Search pattern should terminate. */
+
+enum direction { NOTSET, FORWARD, BACKWARD };
+enum cdirection { CNOTSET, FSEARCH, fSEARCH, TSEARCH, tSEARCH };
+
+/* Search functions. */
+int b_search __P((SCR *, EXF *, MARK *, MARK *, char *, char **, u_int *));
+int f_search __P((SCR *, EXF *, MARK *, MARK *, char *, char **, u_int *));
+int re_conv __P((SCR *, char **, int *));
+void re_error __P((SCR *, int, regex_t *));
diff --git a/usr.bin/vi/seq.c b/usr.bin/vi/seq.c
new file mode 100644
index 000000000000..4d6d44956b0e
--- /dev/null
+++ b/usr.bin/vi/seq.c
@@ -0,0 +1,299 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)seq.c 8.21 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "seq.h"
+#include "excmd.h"
+
+/*
+ * seq_set --
+ * Internal version to enter a sequence.
+ */
+int
+seq_set(sp, name, nlen, input, ilen, output, olen, stype, userdef)
+ SCR *sp;
+ char *name, *input, *output;
+ size_t nlen, ilen, olen;
+ enum seqtype stype;
+ int userdef;
+{
+ SEQ *lastqp, *qp;
+ CHAR_T *p;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "seq_set: name {%s} input {%s} output {%s}\n",
+ name ? name : "", input, output);
+#endif
+ /* Just replace the output field in any previous occurrence. */
+ if ((qp = seq_find(sp, &lastqp, input, ilen, stype, NULL)) != NULL) {
+ if ((p = v_strdup(sp, output, olen)) == NULL)
+ goto mem1;
+ FREE(qp->output, qp->olen);
+ qp->olen = olen;
+ qp->output = p;
+ return (0);
+ }
+
+ /* Allocate and initialize space. */
+ CALLOC(sp, qp, SEQ *, 1, sizeof(SEQ));
+ if (qp == NULL)
+ goto mem1;
+ if (name == NULL)
+ qp->name = NULL;
+ else if ((qp->name = v_strdup(sp, name, nlen)) == NULL)
+ goto mem2;
+ if ((qp->input = v_strdup(sp, input, ilen)) == NULL)
+ goto mem3;
+ if ((qp->output = v_strdup(sp, output, olen)) == NULL) {
+ FREE(qp->input, ilen);
+mem3: if (qp->name != NULL)
+ FREE(qp->name, nlen);
+mem2: FREE(qp, sizeof(SEQ));
+mem1: msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+
+ qp->stype = stype;
+ qp->nlen = nlen;
+ qp->ilen = ilen;
+ qp->olen = olen;
+ qp->flags = userdef ? S_USERDEF : 0;
+
+ /* Link into the chain. */
+ if (lastqp == NULL) {
+ LIST_INSERT_HEAD(&sp->gp->seqq, qp, q);
+ } else {
+ LIST_INSERT_AFTER(lastqp, qp, q);
+ }
+
+ /* Set the fast lookup bit. */
+ bit_set(sp->gp->seqb, qp->input[0]);
+
+ return (0);
+}
+
+/*
+ * seq_delete --
+ * Delete a sequence.
+ */
+int
+seq_delete(sp, input, ilen, stype)
+ SCR *sp;
+ char *input;
+ size_t ilen;
+ enum seqtype stype;
+{
+ SEQ *qp;
+
+ if ((qp = seq_find(sp, NULL, input, ilen, stype, NULL)) == NULL)
+ return (1);
+
+ LIST_REMOVE(qp, q);
+ if (qp->name != NULL)
+ FREE(qp->name, qp->nlen);
+ FREE(qp->input, qp->ilen);
+ FREE(qp->output, qp->olen);
+ FREE(qp, sizeof(SEQ));
+ return (0);
+}
+
+/*
+ * seq_find --
+ * Search the sequence list for a match to a buffer, if ispartial
+ * isn't NULL, partial matches count.
+ */
+SEQ *
+seq_find(sp, lastqp, input, ilen, stype, ispartialp)
+ SCR *sp;
+ SEQ **lastqp;
+ char *input;
+ size_t ilen;
+ enum seqtype stype;
+ int *ispartialp;
+{
+ SEQ *lqp, *qp;
+ int diff;
+
+ /*
+ * Ispartialp is a location where we return if there was a
+ * partial match, i.e. if the string were extended it might
+ * match something.
+ *
+ * XXX
+ * Overload the meaning of ispartialp; only the terminal key
+ * search doesn't want the search limited to complete matches,
+ * i.e. ilen may be longer than the match.
+ */
+ if (ispartialp != NULL)
+ *ispartialp = 0;
+ for (lqp = NULL, qp = sp->gp->seqq.lh_first;
+ qp != NULL; lqp = qp, qp = qp->q.le_next) {
+ /* Fast checks on the first character and type. */
+ if (qp->input[0] > input[0])
+ break;
+ if (qp->input[0] < input[0] || qp->stype != stype)
+ continue;
+
+ /* Check on the real comparison. */
+ diff = memcmp(qp->input, input, MIN(qp->ilen, ilen));
+ if (diff > 0)
+ break;
+ if (diff < 0)
+ continue;
+ /*
+ * If the entry is the same length as the string, return a
+ * match. If the entry is shorter than the string, return a
+ * match if called from the terminal key routine. Otherwise,
+ * keep searching for a complete match.
+ */
+ if (qp->ilen <= ilen) {
+ if (qp->ilen == ilen || ispartialp != NULL) {
+ if (lastqp != NULL)
+ *lastqp = lqp;
+ return (qp);
+ }
+ continue;
+ }
+ /*
+ * If the entry longer than the string, return partial match
+ * if called from the terminal key routine. Otherwise, no
+ * match.
+ */
+ if (ispartialp != NULL)
+ *ispartialp = 1;
+ break;
+ }
+ if (lastqp != NULL)
+ *lastqp = lqp;
+ return (NULL);
+}
+
+/*
+ * seq_dump --
+ * Display the sequence entries of a specified type.
+ */
+int
+seq_dump(sp, stype, isname)
+ SCR *sp;
+ enum seqtype stype;
+ int isname;
+{
+ CHNAME const *cname;
+ SEQ *qp;
+ int cnt, len, olen, tablen;
+ char *p;
+
+ cnt = 0;
+ cname = sp->gp->cname;
+ tablen = O_VAL(sp, O_TABSTOP);
+ for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next) {
+ if (stype != qp->stype)
+ continue;
+ ++cnt;
+ for (p = qp->input,
+ olen = qp->ilen, len = 0; olen > 0; --olen, ++len)
+ (void)ex_printf(EXCOOKIE, "%s", cname[*p++].name);
+ for (len = tablen - len % tablen; len; --len)
+ (void)ex_printf(EXCOOKIE, " ");
+
+ for (p = qp->output, olen = qp->olen; olen > 0; --olen)
+ (void)ex_printf(EXCOOKIE, "%s", cname[*p++].name);
+
+ if (isname && qp->name != NULL) {
+ for (len = tablen - len % tablen; len; --len)
+ (void)ex_printf(EXCOOKIE, " ");
+ for (p = qp->name, olen = qp->nlen; olen > 0; --olen)
+ (void)ex_printf(EXCOOKIE,
+ "%s", cname[*p++].name);
+ }
+ (void)ex_printf(EXCOOKIE, "\n");
+ }
+ return (cnt);
+}
+
+/*
+ * seq_save --
+ * Save the sequence entries to a file.
+ */
+int
+seq_save(sp, fp, prefix, stype)
+ SCR *sp;
+ FILE *fp;
+ char *prefix;
+ enum seqtype stype;
+{
+ CHAR_T esc;
+ SEQ *qp;
+ size_t olen;
+ int ch;
+ char *p;
+
+ /* Write a sequence command for all keys the user defined. */
+ (void)term_key_ch(sp, K_VLNEXT, &esc);
+ for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next) {
+ if (!F_ISSET(qp, S_USERDEF))
+ continue;
+ if (stype != qp->stype)
+ continue;
+ if (prefix)
+ (void)fprintf(fp, "%s", prefix);
+ for (p = qp->input, olen = qp->ilen; olen > 0; --olen) {
+ ch = *p++;
+ if (ch == esc || ch == '|' ||
+ isblank(ch) || term_key_val(sp, ch) == K_NL)
+ (void)putc(esc, fp);
+ (void)putc(ch, fp);
+ }
+ (void)putc(' ', fp);
+ for (p = qp->output, olen = qp->olen; olen > 0; --olen) {
+ ch = *p++;
+ if (ch == esc || ch == '|' ||
+ term_key_val(sp, ch) == K_NL)
+ (void)putc(esc, fp);
+ (void)putc(ch, fp);
+ }
+ (void)putc('\n', fp);
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/seq.h b/usr.bin/vi/seq.h
new file mode 100644
index 000000000000..b9d9ac6cfd66
--- /dev/null
+++ b/usr.bin/vi/seq.h
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 1992, 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.
+ *
+ * @(#)seq.h 8.7 (Berkeley) 12/2/93
+ */
+
+/*
+ * Map and abbreviation structures.
+ *
+ * The map structure is doubly linked list, sorted by input string and by
+ * input length within the string. (The latter is necessary so that short
+ * matches will happen before long matches when the list is searched.)
+ * Additionally, there is a bitmap which has bits set if there are entries
+ * starting with the corresponding character. This keeps us from walking
+ * the list unless it's necessary.
+ *
+ * XXX
+ * The fast-lookup bits are never turned off -- users don't usually unmap
+ * things, though, so it's probably not a big deal.
+ */
+ /* Sequence type. */
+enum seqtype { SEQ_ABBREV, SEQ_COMMAND, SEQ_INPUT };
+
+struct _seq {
+ LIST_ENTRY(_seq) q; /* Linked list of all sequences. */
+ enum seqtype stype; /* Sequence type. */
+ char *name; /* Sequence name (if any). */
+ size_t nlen; /* Name length. */
+ char *input; /* Sequence input keys. */
+ size_t ilen; /* Input keys length. */
+ char *output; /* Sequence output keys. */
+ size_t olen; /* Output keys length. */
+
+#define S_USERDEF 0x01 /* If sequence user defined. */
+ u_char flags;
+};
+
+int abbr_save __P((SCR *, FILE *));
+int map_save __P((SCR *, FILE *));
+int seq_delete __P((SCR *, char *, size_t, enum seqtype));
+int seq_dump __P((SCR *, enum seqtype, int));
+SEQ *seq_find __P((SCR *, SEQ **, char *, size_t, enum seqtype, int *));
+void seq_init __P((SCR *));
+int seq_save __P((SCR *, FILE *, char *, enum seqtype));
+int seq_set __P((SCR *, char *, size_t,
+ char *, size_t, char *, size_t, enum seqtype, int));
diff --git a/usr.bin/vi/sex/sex_confirm.c b/usr.bin/vi/sex/sex_confirm.c
new file mode 100644
index 000000000000..8b55fadedaae
--- /dev/null
+++ b/usr.bin/vi/sex/sex_confirm.c
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)sex_confirm.c 8.5 (Berkeley) 11/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "sex_screen.h"
+
+enum confirm
+sex_confirm(sp, ep, fp, tp)
+ SCR *sp;
+ EXF *ep;
+ MARK *fp, *tp;
+{
+ CH ikey;
+ recno_t cnt;
+
+ if (ex_print(sp, ep, fp, tp, 0))
+ return (CONF_QUIT);
+
+ for (cnt = fp->cno; cnt; --cnt)
+ (void)putc(' ', stdout);
+ for (cnt = tp->cno; cnt; --cnt)
+ (void)putc('^', stdout);
+ (void)fprintf(stdout, "[ynq]");
+
+ if (term_key(sp, &ikey, 0) != INP_OK)
+ return (CONF_QUIT);
+ switch (ikey.ch) {
+ case YES_CH:
+ return (CONF_YES);
+ case QUIT_CH:
+ return (CONF_QUIT);
+ default:
+ case NO_CH:
+ return (CONF_NO);
+ }
+ /* NOTREACHED */
+}
diff --git a/usr.bin/vi/sex/sex_get.c b/usr.bin/vi/sex/sex_get.c
new file mode 100644
index 000000000000..54b7343608d1
--- /dev/null
+++ b/usr.bin/vi/sex/sex_get.c
@@ -0,0 +1,323 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)sex_get.c 8.12 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "sex_screen.h"
+
+static void repaint __P((SCR *, int, char *, size_t));
+
+#define DISPLAY(wval, ch, col) { \
+ size_t __len; \
+ int __ch; \
+ if ((__ch = (ch)) == '\t') { \
+ __len = O_VAL(sp, O_TABSTOP) - \
+ ((col) % O_VAL(sp, O_TABSTOP)); \
+ (col) += (wval) = __len; \
+ while (__len--) \
+ putc(' ', stdout); \
+ } else { \
+ (col) += (wval) = cname[(__ch)].len; \
+ (void)fprintf(stdout, \
+ "%.*s", cname[(__ch)].len, cname[(__ch)].name); \
+ } \
+}
+
+#define ERASECH { \
+ for (cnt = tp->wd[tp->len]; cnt > 0; --cnt, --col) \
+ (void)fprintf(stdout, "%s", "\b \b"); \
+}
+
+/*
+ * sex_get --
+ * Fill a buffer from the terminal for ex.
+ */
+enum input
+sex_get(sp, ep, tiqh, prompt, flags)
+ SCR *sp;
+ EXF *ep;
+ TEXTH *tiqh;
+ int prompt;
+ u_int flags;
+{
+ enum { Q_NOTSET, Q_THISCHAR } quoted;
+ CHNAME const *cname; /* Character map. */
+ TEXT *tp; /* Input text structures. */
+ CH ikey; /* Input character. */
+ size_t col; /* 0-N: screen column. */
+ size_t cnt;
+ u_int iflags; /* Input flags. */
+ int rval;
+
+#ifdef DEBUG
+ if (LF_ISSET(~TXT_VALID_EX) || !LF_ISSET(TXT_CR))
+ abort();
+#endif
+ /*
+ * Get one TEXT structure with some initial buffer space, reusing
+ * the last one if it's big enough. (All TEXT bookkeeping fields
+ * default to 0 -- text_init() handles this.)
+ */
+ if (tiqh->cqh_first != (void *)tiqh) {
+ tp = tiqh->cqh_first;
+ if (tp->q.cqe_next != (void *)tiqh || tp->lb_len < 32) {
+ text_lfree(tiqh);
+ goto newtp;
+ }
+ tp->len = 0;
+ } else {
+newtp: if ((tp = text_init(sp, NULL, 0, 32)) == NULL)
+ return (INP_ERR);
+ CIRCLEQ_INSERT_HEAD(tiqh, tp, q);
+ }
+
+ cname = sp->gp->cname;
+ if (LF_ISSET(TXT_PROMPT) && O_ISSET(sp, O_PROMPT)) {
+ (void)fprintf(stdout, "%s", cname[prompt].name);
+ col = cname[prompt].len;
+ } else
+ col = 0;
+
+ iflags = LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT);
+ for (quoted = Q_NOTSET;;) {
+ (void)fflush(stdout);
+
+ if (rval = term_key(sp, &ikey, iflags))
+ return (rval);
+
+ BINC_RET(sp, tp->lb, tp->lb_len, tp->len + 1);
+ BINC_RET(sp, tp->wd, tp->wd_len, tp->len + 1);
+
+ if (quoted == Q_THISCHAR) {
+ ERASECH;
+ goto ins_ch;
+ }
+
+ switch (ikey.value) {
+ case K_CNTRLZ:
+ sex_suspend(sp);
+ /* FALLTHROUGH */
+ case K_CNTRLR:
+ repaint(sp, prompt, tp->lb, tp->len);
+ break;
+ case K_CR:
+ case K_NL:
+ if (LF_ISSET(TXT_NLECHO)) {
+ (void)putc('\r', stdout);
+ (void)putc('\n', stdout);
+ (void)fflush(stdout);
+ }
+ /* Terminate with a newline, needed by filter. */
+ tp->lb[tp->len] = '\0';
+ return (INP_OK);
+ case K_VERASE:
+ if (tp->len) {
+ --tp->len;
+ ERASECH;
+ }
+ break;
+ case K_VKILL:
+ for (; tp->len; --tp->len)
+ ERASECH;
+ break;
+ case K_VLNEXT:
+ (void)fprintf(stdout, "%s%c", cname['^'].name, '\b');
+ quoted = Q_THISCHAR;
+ break;
+ case K_VWERASE:
+ /* Move to the last non-space character. */
+ while (tp->len)
+ if (!isblank(tp->lb[--tp->len])) {
+ ++tp->len;
+ break;
+ } else
+ ERASECH;
+
+ /* Move to the last space character. */
+ while (tp->len)
+ if (isblank(tp->lb[--tp->len])) {
+ ++tp->len;
+ break;
+ } else
+ ERASECH;
+ break;
+ default:
+ins_ch: tp->lb[tp->len] = ikey.ch;
+ DISPLAY(tp->wd[tp->len], ikey.ch, col);
+ ++tp->len;
+ quoted = Q_NOTSET;
+ break;
+ }
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * sex_get_notty --
+ * Fill a buffer from the terminal for ex, but don't echo
+ * input.
+ */
+enum input
+sex_get_notty(sp, ep, tiqh, prompt, flags)
+ SCR *sp;
+ EXF *ep;
+ TEXTH *tiqh;
+ int prompt;
+ u_int flags;
+{
+ enum { Q_NOTSET, Q_THISCHAR } quoted;
+ CHNAME const *cname; /* Character map. */
+ TEXT *tp; /* Input text structures. */
+ CH ikey; /* Input character. */
+ size_t col; /* 0-N: screen column. */
+ u_int iflags; /* Input flags. */
+ int rval;
+
+#ifdef DEBUG
+ if (LF_ISSET(~TXT_VALID_EX) || !LF_ISSET(TXT_CR))
+ abort();
+#endif
+ /*
+ * Get one TEXT structure with some initial buffer space, reusing
+ * the last one if it's big enough. (All TEXT bookkeeping fields
+ * default to 0 -- text_init() handles this.)
+ */
+ if (tiqh->cqh_first != (void *)tiqh) {
+ tp = tiqh->cqh_first;
+ if (tp->q.cqe_next != (void *)tiqh || tp->lb_len < 32) {
+ text_lfree(tiqh);
+ goto newtp;
+ }
+ tp->len = 0;
+ } else {
+newtp: if ((tp = text_init(sp, NULL, 0, 32)) == NULL)
+ return (INP_ERR);
+ CIRCLEQ_INSERT_HEAD(tiqh, tp, q);
+ }
+
+ cname = sp->gp->cname;
+ col = 0;
+
+ iflags = LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT);
+ for (quoted = Q_NOTSET;;) {
+ if (rval = term_key(sp, &ikey, iflags))
+ return (rval);
+
+ BINC_RET(sp, tp->lb, tp->lb_len, tp->len + 1);
+ BINC_RET(sp, tp->wd, tp->wd_len, tp->len + 1);
+
+ if (quoted == Q_THISCHAR)
+ goto ins_ch;
+
+ switch (ikey.value) {
+ case K_CNTRLZ:
+ sex_suspend(sp);
+ /* FALLTHROUGH */
+ case K_CNTRLR:
+ break;
+ case K_CR:
+ case K_NL:
+ /* Terminate with a newline, needed by filter. */
+ tp->lb[tp->len] = '\0';
+ return (INP_OK);
+ case K_VERASE:
+ if (tp->len)
+ --tp->len;
+ break;
+ case K_VKILL:
+ tp->len = 0;
+ break;
+ case K_VLNEXT:
+ quoted = Q_THISCHAR;
+ break;
+ case K_VWERASE:
+ /* Move to the last non-space character. */
+ while (tp->len)
+ if (!isblank(tp->lb[--tp->len])) {
+ ++tp->len;
+ break;
+ }
+
+ /* Move to the last space character. */
+ while (tp->len)
+ if (isblank(tp->lb[--tp->len])) {
+ ++tp->len;
+ break;
+ }
+ break;
+ default:
+ins_ch: tp->lb[tp->len] = ikey.ch;
+ ++tp->len;
+ quoted = Q_NOTSET;
+ break;
+ }
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * repaint --
+ * Repaint the line.
+ */
+static void
+repaint(sp, prompt, p, len)
+ SCR *sp;
+ int prompt;
+ char *p;
+ size_t len;
+{
+ CHNAME const *cname;
+ size_t col;
+ u_char width;
+
+ cname = sp->gp->cname;
+
+ (void)putc('\n', stdout);
+ if (prompt && O_ISSET(sp, O_PROMPT)) { /* Display prompt. */
+ (void)fprintf(stdout, "%s", cname[prompt].name);
+ col = cname[prompt].len;
+ } else
+ col = 0;
+
+ while (len--)
+ DISPLAY(width, *p++, col);
+}
diff --git a/usr.bin/vi/sex/sex_refresh.c b/usr.bin/vi/sex/sex_refresh.c
new file mode 100644
index 000000000000..0ba589299080
--- /dev/null
+++ b/usr.bin/vi/sex/sex_refresh.c
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)sex_refresh.c 8.5 (Berkeley) 11/18/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "sex_screen.h"
+
+/*
+ * sex_refresh --
+ * In ex, just display any messages.
+ */
+int
+sex_refresh(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ MSG *mp;
+
+ if (F_ISSET(sp, S_RESIZE)) {
+ sp->rows = O_VAL(sp, O_LINES);
+ sp->cols = O_VAL(sp, O_COLUMNS);
+ F_CLR(sp, S_RESIZE);
+ }
+
+ /* Ring the bell. */
+ if (F_ISSET(sp, S_BELLSCHED)) {
+ sex_bell(sp);
+ F_CLR(sp, S_BELLSCHED);
+ }
+
+ for (mp = sp->msgq.lh_first;
+ mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next) {
+ (void)fprintf(stdout, "%.*s\n", (int)mp->len, mp->mbuf);
+ F_SET(mp, M_EMPTY);
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/sex/sex_screen.c b/usr.bin/vi/sex/sex_screen.c
new file mode 100644
index 000000000000..d087d9f17476
--- /dev/null
+++ b/usr.bin/vi/sex/sex_screen.c
@@ -0,0 +1,214 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)sex_screen.c 8.30 (Berkeley) 12/23/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "sex_screen.h"
+#include "../svi/svi_screen.h"
+
+static void sex_abort __P((void));
+static int sex_noop __P((void));
+static int sex_nope __P((SCR *));
+
+/*
+ * sex_screen_init --
+ * Initialize the ex screen.
+ */
+int
+sex_screen_init(sp)
+ SCR *sp;
+{
+ /* Initialize support routines. */
+ sp->s_bell = sex_bell;
+ sp->s_bg = (int (*)())sex_nope;
+ sp->s_busy = (int (*)())sex_busy;
+ sp->s_change = (int (*)())sex_noop;
+ sp->s_chposition = (size_t (*)())sex_abort;
+ sp->s_clear = (int (*)())sex_noop;
+ sp->s_column = (int (*)())sex_abort;
+ sp->s_confirm = sex_confirm;
+ sp->s_down = (int (*)())sex_abort;
+ sp->s_edit = sex_screen_edit;
+ sp->s_end = (int (*)())sex_noop;
+ sp->s_ex_cmd = (int (*)())sex_abort;
+ sp->s_ex_run = (int (*)())sex_abort;
+ sp->s_ex_write = (int (*)())sex_abort;
+ sp->s_fg = (int (*)())sex_nope;
+ sp->s_fill = (int (*)())sex_abort;
+ sp->s_get = F_ISSET(sp->gp,
+ G_ISFROMTTY) ? sex_get : sex_get_notty;
+ sp->s_key_read = sex_key_read;
+ sp->s_optchange = (int (*)())sex_noop;
+ sp->s_position = (int (*)())sex_abort;
+ sp->s_rabs = (int (*)())sex_nope;
+ sp->s_refresh = sex_refresh;
+ sp->s_relative = (size_t (*)())sex_abort;
+ sp->s_rrel = (int (*)())sex_nope;
+ sp->s_split = (int (*)())sex_nope;
+ sp->s_suspend = sex_suspend;
+ sp->s_up = (int (*)())sex_abort;
+
+ return (0);
+}
+
+/*
+ * sex_screen_copy --
+ * Copy to a new screen.
+ */
+int
+sex_screen_copy(orig, sp)
+ SCR *orig, *sp;
+{
+ return (0);
+}
+
+/*
+ * sex_screen_end --
+ * End a screen.
+ */
+int
+sex_screen_end(sp)
+ SCR *sp;
+{
+ return (0);
+}
+
+/*
+ * sex_screen_edit --
+ * Main ex screen loop. The ex screen is relatively uncomplicated.
+ * As long as it has a stdio FILE pointer for output, it's happy.
+ */
+int
+sex_screen_edit(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ struct termios rawt, t;
+ GS *saved_gp;
+ int force, rval;
+
+ /* Initialize the terminal state. */
+ if (F_ISSET(sp->gp, G_ISFROMTTY))
+ SEX_RAW(t, rawt);
+
+ /* Write to the terminal. */
+ sp->stdfp = stdout;
+
+ for (;;) {
+ sp->rows = O_VAL(sp, O_LINES);
+ sp->cols = O_VAL(sp, O_COLUMNS);
+
+ /*
+ * Run ex. If ex fails, sex data structures
+ * may be corrupted, be careful what you do.
+ */
+ if (rval = ex(sp, sp->ep)) {
+ if (F_ISSET(ep, F_RCV_ON)) {
+ F_SET(ep, F_RCV_NORM);
+ (void)rcv_sync(sp, sp->ep);
+ }
+ (void)file_end(sp, sp->ep, 1);
+ (void)screen_end(sp); /* General SCR info. */
+ break;
+ }
+
+ saved_gp = sp->gp;
+
+ force = 0;
+ switch (F_ISSET(sp, S_MAJOR_CHANGE)) {
+ case S_EXIT_FORCE:
+ force = 1;
+ /* FALLTHROUGH */
+ case S_EXIT:
+ F_CLR(sp, S_EXIT_FORCE | S_EXIT);
+ if (file_end(sp, sp->ep, force))
+ break;
+ (void)screen_end(sp); /* General SCR info. */
+ goto ret;
+ case 0: /* Changing from ex mode. */
+ goto ret;
+ case S_FSWITCH:
+ F_CLR(sp, S_FSWITCH);
+ break;
+ case S_SSWITCH:
+ default:
+ abort();
+ }
+ }
+
+ /* Reset the terminal state. */
+ret: if (F_ISSET(sp->gp, G_ISFROMTTY) && SEX_NORAW(t))
+ rval = 1;
+ return (rval);
+}
+
+/*
+ * sex_abort --
+ * Fake function. Die.
+ */
+static void
+sex_abort()
+{
+ abort();
+}
+
+/*
+ * sex_noop --
+ * Fake function. Do nothing.
+ */
+static int
+sex_noop()
+{
+ return (0);
+}
+
+/*
+ * sex_nope --
+ * Fake function. Not in ex, you don't.
+ */
+static int
+sex_nope(sp)
+ SCR *sp;
+{
+ msgq(sp, M_ERR, "Command not applicable to ex mode.");
+ return (1);
+}
diff --git a/usr.bin/vi/sex/sex_screen.h b/usr.bin/vi/sex/sex_screen.h
new file mode 100644
index 000000000000..419a2f274ed1
--- /dev/null
+++ b/usr.bin/vi/sex/sex_screen.h
@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 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.
+ *
+ * @(#)sex_screen.h 8.12 (Berkeley) 11/29/93
+ */
+
+#define SEX_NORAW(t) \
+ tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &(t))
+
+#define SEX_RAW(t, rawt) { \
+ if (tcgetattr(STDIN_FILENO, &(t))) \
+ return (1); \
+ (rawt) = (t); \
+ (rawt).c_iflag &= ~(IGNBRK|BRKINT|PARMRK|INLCR|IGNCR|ICRNL); \
+ (rawt).c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); \
+ (rawt).c_cc[VMIN] = 1; \
+ (rawt).c_cc[VTIME] = 0; \
+ if (tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &(rawt))) \
+ return (1); \
+}
+
+void sex_bell __P((SCR *));
+void sex_busy __P((SCR *, char const *));
+enum confirm
+ sex_confirm __P((SCR *, EXF *, MARK *, MARK *));
+enum input
+ sex_get __P((SCR *, EXF *, TEXTH *, int, u_int));
+enum input
+ sex_get_notty __P((SCR *, EXF *, TEXTH *, int, u_int));
+enum input
+ sex_key_read __P((SCR *, int *, struct timeval *));
+int sex_refresh __P((SCR *, EXF *));
+int sex_screen_copy __P((SCR *, SCR *));
+int sex_screen_edit __P((SCR *, EXF *));
+int sex_screen_end __P((SCR *));
+int sex_split __P((SCR *, char *[]));
+int sex_suspend __P((SCR *));
diff --git a/usr.bin/vi/sex/sex_term.c b/usr.bin/vi/sex/sex_term.c
new file mode 100644
index 000000000000..4e3980f21efd
--- /dev/null
+++ b/usr.bin/vi/sex/sex_term.c
@@ -0,0 +1,227 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)sex_term.c 8.24 (Berkeley) 12/20/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "script.h"
+
+/*
+ * sex_key_read --
+ * Read characters from the input.
+ */
+enum input
+sex_key_read(sp, nrp, timeout)
+ SCR *sp;
+ int *nrp;
+ struct timeval *timeout;
+{
+ struct timeval t, *tp;
+ IBUF *tty;
+ SCR *tsp;
+ int maxfd, nr;
+
+ *nrp = 0;
+ tty = sp->gp->tty;
+
+ /*
+ * We're about to block; check for signals. If a signal received,
+ * clear it immediately, so that if it's reset while being serviced
+ * we won't miss it.
+ *
+ * Signal recipients set global flags. If one is set, we either
+ * set local flags or call handling routines. None of this has
+ * anything to do with input keys, but it's something that can't
+ * be done asynchronously without doing a lot of locking to handle
+ * race conditions, and which needs to be done periodically.
+ */
+sigchk: while (F_ISSET(sp->gp,
+ G_SIGALRM | G_SIGHUP | G_SIGTERM | G_SIGWINCH)) {
+ if (F_ISSET(sp->gp, G_SIGALRM)) {
+ F_CLR(sp->gp, G_SIGALRM);
+ for (tsp = sp->gp->dq.cqh_first;
+ tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next)
+ if (tsp->ep != NULL &&
+ F_ISSET(tsp->ep, F_RCV_ON))
+ F_SET(tsp->ep, F_RCV_ALRM);
+ }
+ if (F_ISSET(sp->ep, F_RCV_ALRM)) {
+ F_CLR(sp->ep, F_RCV_ALRM);
+ (void)rcv_sync(sp, sp->ep);
+ }
+ if (F_ISSET(sp->gp, G_SIGHUP)) {
+ F_CLR(sp->gp, G_SIGHUP);
+ rcv_hup();
+ /* NOTREACHED */
+ }
+ if (F_ISSET(sp->gp, G_SIGTERM)) {
+ F_CLR(sp->gp, G_SIGTERM);
+ rcv_term();
+ /* NOTREACHED */
+ }
+ if (F_ISSET(sp->gp, G_SIGWINCH)) {
+ F_CLR(sp->gp, G_SIGWINCH);
+ set_window_size(sp, 0, 1);
+ F_SET(sp, S_RESIZE);
+ (void)sp->s_refresh(sp, sp->ep);
+ }
+ }
+
+ /*
+ * There are three cases here:
+ *
+ * 1: A read from a file or a pipe. In this case, the reads
+ * never timeout regardless. This means that we can hang
+ * when trying to complete a map, but we're going to hang
+ * on the next read anyway.
+ */
+ if (!F_ISSET(sp->gp, G_ISFROMTTY)) {
+ if ((nr = read(STDIN_FILENO,
+ tty->ch + tty->next + tty->cnt,
+ tty->len - (tty->next + tty->cnt))) > 0) {
+ tty->cnt += *nrp = nr;
+ return (INP_OK);
+ }
+ return (INP_EOF);
+ }
+
+ /*
+ * 2: A read with an associated timeout. In this case, we are trying
+ * to complete a map sequence. Ignore script windows and timeout
+ * as specified. If input arrives, we fall into #3, but because
+ * timeout isn't NULL, don't read anything but command input.
+ *
+ * If interrupted, go back and check to see what it was.
+ */
+ if (timeout != NULL) {
+ if (F_ISSET(sp, S_SCRIPT))
+ FD_CLR(sp->script->sh_master, &sp->rdfd);
+ FD_SET(STDIN_FILENO, &sp->rdfd);
+ for (;;) {
+ switch (select(STDIN_FILENO + 1,
+ &sp->rdfd, NULL, NULL, timeout)) {
+ case -1: /* Error or interrupt. */
+ if (errno == EINTR)
+ goto sigchk;
+ goto err;
+ case 1: /* Characters ready. */
+ break;
+ case 0: /* Timeout. */
+ return (INP_OK);
+ }
+ break;
+ }
+ }
+
+ /*
+ * 3: At this point, we'll take anything that comes. Select on the
+ * command file descriptor and the file descriptor for the script
+ * window if there is one. Poll the fd's, increasing the timeout
+ * each time each time we don't get anything until we're blocked
+ * on I/O.
+ *
+ * If interrupted, go back and check to see what it was.
+ */
+ for (t.tv_sec = t.tv_usec = 0;;) {
+ /*
+ * Reset each time -- sscr_input() may call other
+ * routines which could reset bits.
+ */
+ if (timeout == NULL && F_ISSET(sp, S_SCRIPT)) {
+ tp = &t;
+
+ FD_SET(STDIN_FILENO, &sp->rdfd);
+ if (F_ISSET(sp, S_SCRIPT)) {
+ FD_SET(sp->script->sh_master, &sp->rdfd);
+ maxfd =
+ MAX(STDIN_FILENO, sp->script->sh_master);
+ } else
+ maxfd = STDIN_FILENO;
+ } else {
+ tp = NULL;
+
+ FD_SET(STDIN_FILENO, &sp->rdfd);
+ if (F_ISSET(sp, S_SCRIPT))
+ FD_CLR(sp->script->sh_master, &sp->rdfd);
+ maxfd = STDIN_FILENO;
+ }
+
+ switch (select(maxfd + 1, &sp->rdfd, NULL, NULL, tp)) {
+ case -1: /* Error or interrupt. */
+ if (errno == EINTR)
+ goto sigchk;
+err: msgq(sp, M_SYSERR, "select");
+ return (INP_ERR);
+ case 0: /* Timeout. */
+ if (t.tv_usec) {
+ ++t.tv_sec;
+ t.tv_usec = 0;
+ } else
+ t.tv_usec += 500000;
+ continue;
+ }
+
+ if (timeout == NULL && F_ISSET(sp, S_SCRIPT) &&
+ FD_ISSET(sp->script->sh_master, &sp->rdfd)) {
+ sscr_input(sp);
+ continue;
+ }
+
+ switch (nr = read(STDIN_FILENO,
+ tty->ch + tty->next + tty->cnt,
+ tty->len - (tty->next + tty->cnt))) {
+ case 0: /* EOF. */
+ return (INP_EOF);
+ case -1: /* Error or interrupt. */
+ if (errno == EINTR)
+ goto sigchk;
+ msgq(sp, M_SYSERR, "read");
+ return (INP_ERR);
+ default:
+ tty->cnt += *nrp = nr;
+ return (INP_OK);
+ }
+ /* NOTREACHED */
+ }
+ /* NOTREACHED */
+}
diff --git a/usr.bin/vi/sex/sex_util.c b/usr.bin/vi/sex/sex_util.c
new file mode 100644
index 000000000000..25eb17f049c3
--- /dev/null
+++ b/usr.bin/vi/sex/sex_util.c
@@ -0,0 +1,97 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)sex_util.c 8.9 (Berkeley) 12/23/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "sex_screen.h"
+
+/*
+ * sex_bell --
+ * Ring the bell.
+ */
+void
+sex_bell(sp)
+ SCR *sp;
+{
+ (void)write(STDOUT_FILENO, "\07", 1); /* \a */
+}
+
+void
+sex_busy(sp, msg)
+ SCR *sp;
+ char const *msg;
+{
+ (void)fprintf(stdout, "%s\n", msg);
+ (void)fflush(stdout);
+}
+
+/*
+ * sex_suspend --
+ * Suspend an ex screen.
+ */
+int
+sex_suspend(sp)
+ SCR *sp;
+{
+ struct termios t;
+ int rval;
+
+ /* Save ex/vi terminal settings, and restore the original ones. */
+ if (F_ISSET(sp->gp, G_ISFROMTTY)) {
+ (void)tcgetattr(STDIN_FILENO, &t);
+ (void)tcsetattr(STDIN_FILENO,
+ TCSADRAIN, &sp->gp->original_termios);
+ }
+
+ /* Kill the process group. */
+ F_SET(sp->gp, G_SLEEPING);
+ if (rval = kill(0, SIGTSTP))
+ msgq(sp, M_SYSERR, "SIGTSTP");
+ F_CLR(sp->gp, G_SLEEPING);
+
+ /* Restore ex/vi terminal settings. */
+ if (F_ISSET(sp->gp, G_ISFROMTTY))
+ (void)tcsetattr(STDIN_FILENO, TCSADRAIN, &t);
+
+ return (rval);
+}
diff --git a/usr.bin/vi/svi/svi_confirm.c b/usr.bin/vi/svi/svi_confirm.c
new file mode 100644
index 000000000000..01546d7d7716
--- /dev/null
+++ b/usr.bin/vi/svi/svi_confirm.c
@@ -0,0 +1,84 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)svi_confirm.c 8.5 (Berkeley) 11/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <curses.h>
+
+#include "vi.h"
+#include "svi_screen.h"
+
+enum confirm
+svi_confirm(sp, ep, fp, tp)
+ SCR *sp;
+ EXF *ep;
+ MARK *fp, *tp;
+{
+ CH ikey;
+ size_t oldy, oldx;
+
+ /*
+ * Refresh the cursor first -- this means that we won't have to
+ * set S_UPDATE_MODE to keep refresh from erasing the mode line
+ * or SVI_CUR_INVALID because we sneaked the cursor off somewhere
+ * else.
+ */
+ sp->lno = fp->lno;
+ sp->cno = fp->cno;
+ if (svi_paint(sp, ep))
+ return (CONF_QUIT);
+
+ getyx(stdscr, oldy, oldx);
+ MOVE(sp, INFOLINE(sp), 0);
+ clrtoeol();
+ ADDNSTR(CONFSTRING, sizeof(CONFSTRING) - 1);
+ MOVEA(sp, oldy, oldx);
+ refresh();
+
+ if (term_key(sp, &ikey, 0) != INP_OK)
+ return (CONF_QUIT);
+ switch (ikey.ch) {
+ case YES_CH:
+ return (CONF_YES);
+ case QUIT_CH:
+ return (CONF_QUIT);
+ default:
+ case NO_CH:
+ return (CONF_NO);
+ }
+ /* NOTREACHED */
+}
diff --git a/usr.bin/vi/svi/svi_ex.c b/usr.bin/vi/svi/svi_ex.c
new file mode 100644
index 000000000000..7a83d409b3cd
--- /dev/null
+++ b/usr.bin/vi/svi/svi_ex.c
@@ -0,0 +1,476 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)svi_ex.c 8.36 (Berkeley) 12/23/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <curses.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "vcmd.h"
+#include "excmd.h"
+#include "svi_screen.h"
+#include "../sex/sex_screen.h"
+
+static int svi_ex_done __P((SCR *, EXF *, MARK *));
+static int svi_ex_scroll __P((SCR *, int, int, CH *));
+
+/*
+ * svi_ex_cmd --
+ * Execute an ex command.
+ */
+int
+svi_ex_cmd(sp, ep, exp, rp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *exp;
+ MARK *rp;
+{
+ SVI_PRIVATE *svp;
+ int rval;
+
+ svp = SVP(sp);
+ svp->exlcontinue = svp->exlinecount = svp->extotalcount = 0;
+
+ (void)svi_busy(sp, NULL);
+ rval = exp->cmd->fn(sp, ep, exp);
+
+ /* No longer interruptible. */
+ F_CLR(sp, S_INTERRUPTIBLE);
+
+ (void)msg_rpt(sp, 0);
+ (void)ex_fflush(EXCOOKIE);
+
+ /*
+ * If displayed anything, figure out if we have to wait. If the
+ * screen wasn't trashed, only one line and there are no waiting
+ * messages, don't wait, but don't overwrite it with mode information
+ * either. If there's a screen under this one, change the line to
+ * inverse video.
+ */
+ if (svp->extotalcount > 0)
+ if (!F_ISSET(sp, S_REFRESH) && svp->extotalcount == 1 &&
+ (sp->msgq.lh_first == NULL ||
+ F_ISSET(sp->msgq.lh_first, M_EMPTY)))
+ F_SET(sp, S_UPDATE_MODE);
+ else
+ (void)svi_ex_scroll(sp, 1, 0, NULL);
+ return (svi_ex_done(sp, ep, rp) || rval);
+}
+
+/*
+ * svi_ex_run --
+ * Execute strings of ex commands.
+ */
+int
+svi_ex_run(sp, ep, rp)
+ SCR *sp;
+ EXF *ep;
+ MARK *rp;
+{
+ enum input (*get) __P((SCR *, EXF *, TEXTH *, int, u_int));
+ struct termios rawt, t;
+ CH ikey;
+ SVI_PRIVATE *svp;
+ TEXT *tp;
+ int flags, in_exmode, rval;
+
+ svp = SVP(sp);
+ svp->exlcontinue = svp->exlinecount = svp->extotalcount = 0;
+
+ /*
+ * There's some tricky stuff going on here to handle when a user has
+ * mapped a key to multiple ex commands. Historic practice was that
+ * vi ran without any special actions, as if the user were entering
+ * the characters, until ex trashed the screen, e.g. something like a
+ * '!' command. At that point, we no longer know what the screen
+ * looks like, so we can't afford to overwrite anything. The solution
+ * is to go into real ex mode until we get to the end of the command
+ * strings.
+ */
+ get = svi_get;
+ flags = TXT_BS | TXT_PROMPT;
+ for (in_exmode = rval = 0;;) {
+ if (get(sp, ep, &sp->tiq, ':', flags) != INP_OK) {
+ rval = 1;
+ break;
+ }
+
+ /*
+ * Len is 0 if the user backspaced over the prompt,
+ * 1 if only a CR was entered.
+ */
+ tp = sp->tiq.cqh_first;
+ if (tp->len == 0)
+ break;
+
+ if (!in_exmode)
+ (void)svi_busy(sp, NULL);
+
+ (void)ex_icmd(sp, ep, tp->lb, tp->len);
+ (void)ex_fflush(EXCOOKIE);
+
+ /*
+ * The file or screen may have changed, in which case,
+ * the main editor loop takes care of it.
+ */
+ if (F_ISSET(sp, S_MAJOR_CHANGE))
+ break;
+
+ /*
+ * If continue not required, and one or no lines, and there
+ * are no waiting messages, don't wait, but don't overwrite
+ * it with mode information either. If there's a screen under
+ * this one, change the line to inverse video.
+ */
+ if (!F_ISSET(sp, S_CONTINUE) &&
+ (svp->extotalcount == 0 || svp->extotalcount == 1 &&
+ (sp->msgq.lh_first == NULL ||
+ F_ISSET(sp->msgq.lh_first, M_EMPTY)))) {
+ if (svp->extotalcount == 1)
+ F_SET(sp, S_UPDATE_MODE);
+ break;
+ }
+
+ /* If the screen is trashed, go into ex mode. */
+ if (!in_exmode && F_ISSET(sp, S_REFRESH)) {
+ /* Initialize the terminal state. */
+ if (F_ISSET(sp->gp, G_ISFROMTTY)) {
+ SEX_RAW(t, rawt);
+ get = sex_get;
+ } else
+ get = sex_get_notty;
+ flags = TXT_CR | TXT_NLECHO | TXT_PROMPT;
+ in_exmode = 1;
+ }
+
+ /*
+ * If the user hasn't already indicated that they're done,
+ * they may continue in ex mode by entering a ':'.
+ */
+ if (F_ISSET(sp, S_INTERRUPTED))
+ break;
+
+ /* No longer interruptible. */
+ F_CLR(sp, S_INTERRUPTIBLE);
+
+ if (in_exmode) {
+ (void)write(STDOUT_FILENO,
+ CONTMSG, sizeof(CONTMSG) - 1);
+ for (;;) {
+ if (term_user_key(sp, &ikey) != INP_OK) {
+ rval = 1;
+ goto ret;
+ }
+ if (ikey.ch == ' ' || ikey.ch == ':')
+ break;
+ if (ikey.value == K_CR || ikey.value == K_NL)
+ break;
+ sex_bell(sp);
+ }
+ } else
+ (void)svi_ex_scroll(sp, 1, 1, &ikey);
+ if (ikey.ch != ':')
+ break;
+
+ if (in_exmode)
+ (void)write(STDOUT_FILENO, "\r\n", 2);
+ else {
+ ++svp->extotalcount;
+ ++svp->exlinecount;
+ }
+ }
+
+ret: if (in_exmode) {
+ /* Reset the terminal state. */
+ if (F_ISSET(sp->gp, G_ISFROMTTY) && SEX_NORAW(t))
+ rval = 1;
+ F_SET(sp, S_REFRESH);
+ } else
+ if (svi_ex_done(sp, ep, rp))
+ rval = 1;
+ F_CLR(sp, S_CONTINUE);
+ return (rval);
+}
+
+/*
+ * svi_ex_done --
+ * Cleanup from dipping into ex.
+ */
+static int
+svi_ex_done(sp, ep, rp)
+ SCR *sp;
+ EXF *ep;
+ MARK *rp;
+{
+ SMAP *smp;
+ SVI_PRIVATE *svp;
+ recno_t lno;
+ size_t cnt, len;
+
+ /*
+ * The file or screen may have changed, in which case,
+ * the main editor loop takes care of it.
+ */
+ if (F_ISSET(sp, S_MAJOR_CHANGE))
+ return (0);
+
+ /*
+ * Otherwise, the only cursor modifications will be real, however, the
+ * underlying line may have changed; don't trust anything. This code
+ * has been a remarkably fertile place for bugs.
+ *
+ * Repaint the entire screen if at least half the screen is trashed.
+ * Else, repaint only over the overwritten lines. The "-2" comes
+ * from one for the mode line and one for the fact that it's an offset.
+ * Note the check for small screens.
+ *
+ * Don't trust ANYTHING.
+ */
+ svp = SVP(sp);
+ if (svp->extotalcount >= HALFTEXT(sp))
+ F_SET(sp, S_REDRAW);
+ else
+ for (cnt = sp->rows - 2; svp->extotalcount--; --cnt)
+ if (cnt > sp->t_rows) {
+ MOVE(sp, cnt, 0);
+ clrtoeol();
+ } else {
+ smp = HMAP + cnt;
+ SMAP_FLUSH(smp);
+ if (svi_line(sp, ep, smp, NULL, NULL))
+ return (1);
+ }
+ /*
+ * Do a reality check on a cursor value, and make sure it's okay.
+ * If necessary, change it. Ex keeps track of the line number,
+ * but ex doesn't care about the column and it may have disappeared.
+ */
+ if (file_gline(sp, ep, sp->lno, &len) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0)
+ GETLINE_ERR(sp, sp->lno);
+ sp->lno = 1;
+ sp->cno = 0;
+ } else if (sp->cno >= len)
+ sp->cno = len ? len - 1 : 0;
+
+ rp->lno = sp->lno;
+ rp->cno = sp->cno;
+ return (0);
+}
+
+/*
+ * svi_ex_write --
+ * Write out the ex messages.
+ *
+ * There is no tab or character translation going on, so, whatever the ex
+ * and/or curses routines do with special characters is all that gets done.
+ * This is probably okay, I don't see any reason that user's tab settings
+ * should affect ex output, and ex should have displayed everything else
+ * exactly as it wanted it on the screen.
+ */
+int
+svi_ex_write(cookie, line, llen)
+ void *cookie;
+ const char *line;
+ int llen;
+{
+ SCR *sp;
+ SVI_PRIVATE *svp;
+ size_t oldy, oldx;
+ int len, rlen;
+ const char *p;
+
+ /*
+ * XXX
+ * If it's a 4.4BSD system, we could just use fpurge(3).
+ * This shouldn't be too expensive, though.
+ */
+ sp = cookie;
+ svp = SVP(sp);
+ if (F_ISSET(sp, S_INTERRUPTED))
+ return (llen);
+
+ p = line; /* In case of a write of 0. */
+ for (rlen = llen; llen;) {
+ /* Get the next line. */
+ if ((p = memchr(line, '\n', llen)) == NULL)
+ len = llen;
+ else
+ len = p - line;
+
+ /*
+ * The max is sp->cols characters, and we may
+ * have already written part of the line.
+ */
+ if (len + svp->exlcontinue > sp->cols)
+ len = sp->cols - svp->exlcontinue;
+
+ /*
+ * If the first line output, do nothing.
+ * If the second line output, draw the divider line.
+ * If drew a full screen, remove the divider line.
+ * If it's a continuation line, move to the continuation
+ * point, else, move the screen up.
+ */
+ if (svp->exlcontinue == 0) {
+ if (svp->extotalcount == 1) {
+ MOVE(sp, INFOLINE(sp) - 1, 0);
+ clrtoeol();
+ if (svi_divider(sp))
+ return (-1);
+ F_SET(svp, SVI_DIVIDER);
+ ++svp->extotalcount;
+ ++svp->exlinecount;
+ }
+ if (svp->extotalcount == sp->t_maxrows &&
+ F_ISSET(svp, SVI_DIVIDER)) {
+ --svp->extotalcount;
+ --svp->exlinecount;
+ F_CLR(svp, SVI_DIVIDER);
+ }
+ if (svp->extotalcount != 0 &&
+ svi_ex_scroll(sp, 0, 0, NULL))
+ return (-1);
+ MOVE(sp, INFOLINE(sp), 0);
+ ++svp->extotalcount;
+ ++svp->exlinecount;
+ if (F_ISSET(sp, S_INTERRUPTIBLE) &&
+ F_ISSET(sp, S_INTERRUPTED))
+ break;
+ } else
+ MOVE(sp, INFOLINE(sp), svp->exlcontinue);
+
+ /* Display the line. */
+ if (len)
+ ADDNSTR(line, len);
+
+ /* Clear to EOL. */
+ getyx(stdscr, oldy, oldx);
+ if (oldx < sp->cols)
+ clrtoeol();
+
+ /* If we loop, it's a new line. */
+ svp->exlcontinue = 0;
+
+ /* Reset for the next line. */
+ line += len;
+ llen -= len;
+ if (p != NULL) {
+ ++line;
+ --llen;
+ }
+ }
+ /* Refresh the screen, even if it's a partial. */
+ refresh();
+
+ /* Set up next continuation line. */
+ if (p == NULL)
+ getyx(stdscr, oldy, svp->exlcontinue);
+ return (rlen);
+}
+
+/*
+ * svi_ex_scroll --
+ * Scroll the screen for ex output.
+ */
+static int
+svi_ex_scroll(sp, mustwait, colon_ok, chp)
+ SCR *sp;
+ int mustwait, colon_ok;
+ CH *chp;
+{
+ CH ikey;
+ SVI_PRIVATE *svp;
+
+ /*
+ * Scroll the screen. Instead of scrolling the entire screen, delete
+ * the line above the first line output so preserve the maximum amount
+ * of the screen.
+ */
+ svp = SVP(sp);
+ if (svp->extotalcount >= sp->rows) {
+ MOVE(sp, 0, 0);
+ } else
+ MOVE(sp, INFOLINE(sp) - svp->extotalcount, 0);
+
+ deleteln();
+
+ /* If there are screens below us, push them back into place. */
+ if (sp->q.cqe_next != (void *)&sp->gp->dq) {
+ MOVE(sp, INFOLINE(sp), 0);
+ insertln();
+ }
+
+ /* If just displayed a full screen, wait. */
+ if (mustwait || svp->exlinecount == sp->t_maxrows) {
+ MOVE(sp, INFOLINE(sp), 0);
+ if (F_ISSET(sp, S_INTERRUPTIBLE)) {
+ ADDNSTR(CONTMSG_I, (int)sizeof(CONTMSG_I) - 1);
+ } else {
+ ADDNSTR(CONTMSG, (int)sizeof(CONTMSG) - 1);
+ }
+ clrtoeol();
+ refresh();
+ for (;;) {
+ if (term_user_key(sp, &ikey) != INP_OK)
+ return (-1);
+ if (ikey.ch == ' ')
+ break;
+ if (colon_ok && ikey.ch == ':')
+ break;
+ if (ikey.value == K_CR || ikey.value == K_NL)
+ break;
+ if (ikey.ch == QUIT_CH &&
+ F_ISSET(sp, S_INTERRUPTIBLE)) {
+ F_SET(sp, S_INTERRUPTED);
+ break;
+ }
+ svi_bell(sp);
+ }
+ if (chp != NULL)
+ *chp = ikey;
+ svp->exlinecount = 0;
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/svi/svi_get.c b/usr.bin/vi/svi/svi_get.c
new file mode 100644
index 000000000000..46f5f0e240cb
--- /dev/null
+++ b/usr.bin/vi/svi/svi_get.c
@@ -0,0 +1,150 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)svi_get.c 8.19 (Berkeley) 1/2/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <curses.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "vi.h"
+#include "vcmd.h"
+#include "svi_screen.h"
+
+/*
+ * svi_get --
+ * Fill a buffer from the terminal for vi.
+ */
+enum input
+svi_get(sp, ep, tiqh, prompt, flags)
+ SCR *sp;
+ EXF *ep;
+ TEXTH *tiqh;
+ int prompt;
+ u_int flags;
+{
+ MARK save;
+ SMAP *esmp;
+ recno_t bot_lno, top_lno;
+ size_t bot_off, cnt, top_off;
+ int eval;
+
+ /*
+ * The approach used is to fake like the user is doing input on
+ * the last line of the screen. This makes all of the scrolling
+ * work correctly, and allows us the use of the vi text editing
+ * routines, not to mention practically infinite length ex commands.
+ *
+ * Save the current location.
+ */
+ bot_lno = TMAP->lno;
+ bot_off = TMAP->off;
+ top_lno = HMAP->lno;
+ top_off = HMAP->off;
+ save.lno = sp->lno;
+ save.cno = sp->cno;
+
+ /*
+ * If it's a small screen, TMAP may be small for the screen.
+ * Fix it, filling in fake lines as we go.
+ */
+ if (ISSMALLSCREEN(sp))
+ for (esmp = HMAP + (sp->t_maxrows - 1); TMAP < esmp; ++TMAP) {
+ TMAP[1].lno = TMAP[0].lno + 1;
+ TMAP[1].off = 1;
+ }
+
+ /* Build the fake entry. */
+ TMAP[1].lno = TMAP[0].lno + 1;
+ TMAP[1].off = 1;
+ SMAP_FLUSH(&TMAP[1]);
+ ++TMAP;
+
+ /* Move to it. */
+ sp->lno = TMAP[0].lno;
+ sp->cno = 0;
+
+ if (O_ISSET(sp, O_ALTWERASE))
+ LF_SET(TXT_ALTWERASE);
+ if (O_ISSET(sp, O_TTYWERASE))
+ LF_SET(TXT_TTYWERASE);
+ LF_SET(TXT_APPENDEOL |
+ TXT_CR | TXT_ESCAPE | TXT_INFOLINE | TXT_MAPINPUT);
+
+ /* Don't update the modeline for now. */
+ F_SET(SVP(sp), SVI_INFOLINE);
+
+ eval = v_ntext(sp, ep, tiqh, NULL, NULL, 0, NULL, prompt, 0, flags);
+
+ F_CLR(SVP(sp), SVI_INFOLINE);
+
+ /* Put it all back. */
+ --TMAP;
+ sp->lno = save.lno;
+ sp->cno = save.cno;
+
+ /*
+ * If it's a small screen, TMAP may be wrong. Clear any
+ * lines that might have been overwritten.
+ */
+ if (ISSMALLSCREEN(sp)) {
+ for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) {
+ MOVE(sp, cnt, 0);
+ clrtoeol();
+ }
+ TMAP = HMAP + (sp->t_rows - 1);
+ }
+
+ /*
+ * The map may be wrong if the user entered more than one
+ * (logical) line. Fix it. If the user entered a whole
+ * screen, this will be slow, but it's not worth caring.
+ */
+ while (bot_lno != TMAP->lno || bot_off != TMAP->off)
+ if (svi_sm_1down(sp, ep))
+ return (INP_ERR);
+
+ /*
+ * Invalidate the cursor, the line never really existed. This fixes
+ * a bug where the user searches for the last line on the screen + 1
+ * and the refresh routine thinks that's where we just were.
+ */
+ F_SET(SVP(sp), SVI_CUR_INVALID);
+
+ return (eval ? INP_ERR : INP_OK);
+}
diff --git a/usr.bin/vi/svi/svi_line.c b/usr.bin/vi/svi/svi_line.c
new file mode 100644
index 000000000000..2370a0f5eea8
--- /dev/null
+++ b/usr.bin/vi/svi/svi_line.c
@@ -0,0 +1,421 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)svi_line.c 8.18 (Berkeley) 1/22/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <curses.h>
+#include <string.h>
+
+#include "vi.h"
+#include "svi_screen.h"
+
+#if defined(DEBUG) && 0
+#define TABCH '-'
+#define TABSTR "--------------------"
+#else
+#define TABSTR " "
+#define TABCH ' '
+#endif
+
+/*
+ * svi_line --
+ * Update one line on the screen.
+ */
+int
+svi_line(sp, ep, smp, yp, xp)
+ SCR *sp;
+ EXF *ep;
+ SMAP *smp;
+ size_t *xp, *yp;
+{
+ CHNAME const *cname;
+ SMAP *tsmp;
+ size_t chlen, cols_per_screen, cno_cnt, len, scno, skip_screens;
+ size_t offset_in_char, offset_in_line;
+ size_t oldy, oldx;
+ int ch, is_cached, is_infoline, is_partial, is_tab, listset;
+ char *p, nbuf[10];
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "svi_line: row %u: line: %u off: %u\n",
+ smp - HMAP, smp->lno, smp->off);
+#endif
+
+ /*
+ * Assume that, if the cache entry for the line is filled in, the
+ * line is already on the screen, and all we need to do is return
+ * the cursor position. If the calling routine doesn't need the
+ * cursor position, we can just return.
+ */
+ is_cached = SMAP_CACHE(smp);
+ if (yp == NULL && is_cached)
+ return (0);
+
+ /*
+ * A nasty side effect of this routine is that it returns the screen
+ * position for the "current" character. Not pretty, but this is the
+ * only routine that really knows what's out there.
+ *
+ * Move to the line. This routine can be called by svi_sm_position(),
+ * which uses it to fill in the cache entry so it can figure out what
+ * the real contents of the screen are. Because of this, we have to
+ * return to whereever we started from.
+ */
+ getyx(stdscr, oldy, oldx);
+ MOVE(sp, smp - HMAP, 0);
+
+ /* Get the character map. */
+ cname = sp->gp->cname;
+
+ /* Get a copy of the line. */
+ p = file_gline(sp, ep, smp->lno, &len);
+
+ /*
+ * Special case if we're printing the info/mode line. Skip printing
+ * the leading number, as well as other minor setup. If painting the
+ * line between two screens, it's always in reverse video. The only
+ * time this code paints the mode line is when the user is entering
+ * text for a ":" command, so we can put the code here instead of
+ * dealing with the empty line logic below. This is a kludge, but it's
+ * pretty much confined to this module.
+ *
+ * Set the number of screens to skip until a character is displayed.
+ * Left-right screens are special, because we don't bother building
+ * a buffer to be skipped over.
+ *
+ * Set the number of columns for this screen.
+ */
+ cols_per_screen = sp->cols;
+ if (is_infoline = ISINFOLINE(sp, smp)) {
+ listset = 0;
+ if (O_ISSET(sp, O_LEFTRIGHT))
+ skip_screens = 0;
+ else
+ skip_screens = smp->off - 1;
+ } else {
+ listset = O_ISSET(sp, O_LIST);
+ skip_screens = smp->off - 1;
+
+ /*
+ * If O_NUMBER is set and it's line number 1 or the line exists
+ * and this is the first screen of a folding line or any left-
+ * right line, display the line number.
+ */
+ if (O_ISSET(sp, O_NUMBER)) {
+ cols_per_screen -= O_NUMBER_LENGTH;
+ if ((smp->lno == 1 || p != NULL) && skip_screens == 0) {
+ (void)snprintf(nbuf,
+ sizeof(nbuf), O_NUMBER_FMT, smp->lno);
+ ADDSTR(nbuf);
+ }
+ }
+ }
+
+ /*
+ * Special case non-existent lines and the first line of an empty
+ * file. In both cases, the cursor position is 0, but corrected
+ * for the O_NUMBER field if it was displayed.
+ */
+ if (p == NULL || len == 0) {
+ /* Fill in the cursor. */
+ if (yp != NULL && smp->lno == sp->lno) {
+ *yp = smp - HMAP;
+ *xp = sp->cols - cols_per_screen;
+ }
+
+ /* If the line is on the screen, quit. */
+ if (is_cached)
+ goto ret;
+
+ /* Set line cacheing information. */
+ smp->c_sboff = smp->c_eboff = 0;
+ smp->c_scoff = smp->c_eclen = 0;
+
+ if (p == NULL) {
+ if (smp->lno != 1)
+ ADDCH(listset && skip_screens == 0 ? '$' : '~');
+ } else if (listset && skip_screens == 0)
+ ADDCH('$');
+
+ clrtoeol();
+ MOVEA(sp, oldy, oldx);
+ return (0);
+ }
+
+ /*
+ * If we wrote a line that's this or a previous one, we can do this
+ * much more quickly -- we cached the starting and ending positions
+ * of that line. The way it works is we keep information about the
+ * lines displayed in the SMAP. If we're painting the screen in
+ * the forward, this saves us from reformatting the physical line for
+ * every line on the screen. This wins big on binary files with 10K
+ * lines.
+ *
+ * Test for the first screen of the line, then the current screen line,
+ * then the line behind us, then do the hard work. Note, it doesn't
+ * do us any good to have a line in front of us -- it would be really
+ * hard to try and figure out tabs in the reverse direction, i.e. how
+ * many spaces a tab takes up in the reverse direction depends on
+ * what characters preceded it.
+ */
+ if (smp->off == 1) {
+ smp->c_sboff = offset_in_line = 0;
+ smp->c_scoff = offset_in_char = 0;
+ p = &p[offset_in_line];
+ } else if (is_cached) {
+ offset_in_line = smp->c_sboff;
+ offset_in_char = smp->c_scoff;
+ p = &p[offset_in_line];
+ if (skip_screens != 0)
+ cols_per_screen = sp->cols;
+ } else if (smp != HMAP &&
+ SMAP_CACHE(tsmp = smp - 1) && tsmp->lno == smp->lno) {
+ if (tsmp->c_eclen != tsmp->c_ecsize) {
+ offset_in_line = tsmp->c_eboff;
+ offset_in_char = tsmp->c_eclen;
+ } else {
+ offset_in_line = tsmp->c_eboff + 1;
+ offset_in_char = 0;
+ }
+
+ /* Put starting info for this line in the cache. */
+ smp->c_sboff = offset_in_line;
+ smp->c_scoff = offset_in_char;
+ p = &p[offset_in_line];
+ if (skip_screens != 0)
+ cols_per_screen = sp->cols;
+ } else {
+ offset_in_line = 0;
+ offset_in_char = 0;
+
+ /* This is the loop that skips through screens. */
+ if (skip_screens == 0) {
+ smp->c_sboff = offset_in_line;
+ smp->c_scoff = offset_in_char;
+ } else for (scno = 0; offset_in_line < len; ++offset_in_line) {
+ scno += chlen =
+ (ch = *(u_char *)p++) == '\t' && !listset ?
+ TAB_OFF(sp, scno) : cname[ch].len;
+ if (scno < cols_per_screen)
+ continue;
+ /*
+ * Reset cols_per_screen to second and subsequent line
+ * length.
+ */
+ scno -= cols_per_screen;
+ cols_per_screen = sp->cols;
+
+ /*
+ * If crossed the last skipped screen boundary, start
+ * displaying the characters.
+ */
+ if (--skip_screens)
+ continue;
+
+ /* Put starting info for this line in the cache. */
+ if (scno) {
+ smp->c_sboff = offset_in_line;
+ smp->c_scoff = offset_in_char = chlen - scno;
+ --p;
+ } else {
+ smp->c_sboff = ++offset_in_line;
+ smp->c_scoff = 0;
+ }
+ break;
+ }
+ }
+
+ /*
+ * Set the number of characters to skip before reaching the cursor
+ * character. Offset by 1 and use 0 as a flag value. Svi_line is
+ * called repeatedly with a valid pointer to a cursor position.
+ * Don't fill anything in unless it's the right line and the right
+ * character, and the right part of the character...
+ */
+ if (yp == NULL ||
+ smp->lno != sp->lno || sp->cno < offset_in_line ||
+ offset_in_line + cols_per_screen < sp->cno) {
+ cno_cnt = 0;
+ /* If the line is on the screen, quit. */
+ if (is_cached)
+ goto ret;
+ } else
+ cno_cnt = (sp->cno - offset_in_line) + 1;
+
+ /* This is the loop that actually displays characters. */
+ for (is_partial = 0, scno = 0;
+ offset_in_line < len; ++offset_in_line, offset_in_char = 0) {
+ if ((ch = *(u_char *)p++) == '\t' && !listset) {
+ scno += chlen = TAB_OFF(sp, scno) - offset_in_char;
+ is_tab = 1;
+ } else {
+ scno += chlen = cname[ch].len - offset_in_char;
+ is_tab = 0;
+ }
+
+ /*
+ * Only display up to the right-hand column. Set a flag if
+ * the entire character wasn't displayed for use in setting
+ * the cursor. If reached the end of the line, set the cache
+ * info for the screen. Don't worry about there not being
+ * characters to display on the next screen, its lno/off won't
+ * match up in that case.
+ */
+ if (scno >= cols_per_screen) {
+ smp->c_ecsize = chlen;
+ chlen -= scno - cols_per_screen;
+ smp->c_eclen = chlen;
+ smp->c_eboff = offset_in_line;
+ if (scno > cols_per_screen)
+ is_partial = 1;
+
+ /* Terminate the loop. */
+ offset_in_line = len;
+ }
+
+ /*
+ * If the caller wants the cursor value, and this was the
+ * cursor character, set the value. There are two ways to
+ * put the cursor on a character -- if it's normal display
+ * mode, it goes on the last column of the character. If
+ * it's input mode, it goes on the first. In normal mode,
+ * set the cursor only if the entire character was displayed.
+ */
+ if (cno_cnt &&
+ --cno_cnt == 0 && (F_ISSET(sp, S_INPUT) || !is_partial)) {
+ *yp = smp - HMAP;
+ if (F_ISSET(sp, S_INPUT))
+ *xp = scno - chlen;
+ else
+ *xp = scno - 1;
+ if (O_ISSET(sp, O_NUMBER) &&
+ !is_infoline && smp->off == 1)
+ *xp += O_NUMBER_LENGTH;
+
+ /* If the line is on the screen, quit. */
+ if (is_cached)
+ goto ret;
+ }
+
+ /* If the line is on the screen, don't display anything. */
+ if (is_cached)
+ continue;
+
+ /*
+ * Display the character. If it's a tab and tabs aren't some
+ * ridiculous length, do it fast. (We do tab expansion here
+ * because curses doesn't have a way to set the tab length.)
+ */
+ if (is_tab) {
+ if (chlen <= sizeof(TABSTR) - 1) {
+ ADDNSTR(TABSTR, chlen);
+ } else
+ while (chlen--)
+ ADDCH(TABCH);
+ } else
+ ADDNSTR(cname[ch].name + offset_in_char, chlen);
+ }
+
+ if (scno < cols_per_screen) {
+ /* If didn't paint the whole line, update the cache. */
+ smp->c_ecsize = smp->c_eclen = cname[ch].len;
+ smp->c_eboff = len - 1;
+
+ /*
+ * If not the info/mode line, and O_LIST set, and at the
+ * end of the line, and the line ended on this screen,
+ * add a trailing $.
+ */
+ if (listset) {
+ ++scno;
+ ADDCH('$');
+ }
+
+ /* If still didn't paint the whole line, clear the rest. */
+ if (scno < cols_per_screen)
+ clrtoeol();
+ }
+
+ret: MOVEA(sp, oldy, oldx);
+ return (0);
+}
+
+/*
+ * svi_number --
+ * Repaint the numbers on all the lines.
+ */
+int
+svi_number(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ SMAP *smp;
+ recno_t lno;
+ size_t oldy, oldx;
+ char *lp, *p, nbuf[10];
+
+ /*
+ * Try and avoid getting the last line in the file, by getting the
+ * line after the last line in the screen -- if it exists, we know
+ * we have to to number all the lines in the screen. Get the one
+ * after the last instead of the last, so that the info line doesn't
+ * fool us.
+ *
+ * If that test fails, we have to check each line for existence.
+ *
+ * XXX
+ * The problem is that file_lline will lie, and tell us that the
+ * info line is the last line in the file.
+ */
+ lp = file_gline(sp, ep, TMAP->lno + 1, NULL);
+
+ getyx(stdscr, oldy, oldx);
+ for (smp = HMAP; smp <= TMAP; ++smp) {
+ if (smp->off != 1)
+ continue;
+ if (ISINFOLINE(sp, smp))
+ break;
+ if (smp->lno != 1 && lp == NULL &&
+ (p = file_gline(sp, ep, smp->lno, NULL)) == NULL)
+ break;
+ MOVE(sp, smp - HMAP, 0);
+ (void)snprintf(nbuf, sizeof(nbuf), O_NUMBER_FMT, smp->lno);
+ ADDSTR(nbuf);
+ }
+ MOVEA(sp, oldy, oldx);
+ return (0);
+}
diff --git a/usr.bin/vi/svi/svi_refresh.c b/usr.bin/vi/svi/svi_refresh.c
new file mode 100644
index 000000000000..353c08178e20
--- /dev/null
+++ b/usr.bin/vi/svi/svi_refresh.c
@@ -0,0 +1,839 @@
+/*-
+ * Copyright (c) 1992, 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
+static char sccsid[] = "@(#)svi_refresh.c 8.43 (Berkeley) 12/23/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <curses.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "svi_screen.h"
+#include "sex/sex_screen.h"
+
+static int svi_modeline __P((SCR *, EXF *));
+static int svi_msgflush __P((SCR *));
+
+int
+svi_refresh(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ SCR *tsp;
+ u_int paintbits;
+
+ /*
+ * 1: Resize the screen.
+ *
+ * Notice that a resize is requested, and set up everything so that
+ * the file gets reinitialized. Done here, instead of in the vi loop
+ * because there may be other initialization that other screens need
+ * to do. The actual changing of the row/column values was done by
+ * calling the ex options code which put them into the environment,
+ * which is used by curses. Stupid, but ugly.
+ */
+ if (F_ISSET(sp, S_RESIZE)) {
+ /* Reinitialize curses. */
+ if (svi_curses_end(sp) || svi_curses_init(sp))
+ return (1);
+
+ /* Lose any svi_screens() cached information. */
+ SVP(sp)->ss_lno = OOBLNO;
+
+ /*
+ * Fill the map, incidentally losing any svi_line()
+ * cached information.
+ */
+ if (sp->s_fill(sp, ep, sp->lno, P_FILL))
+ return (1);
+ F_CLR(sp, S_RESIZE | S_REFORMAT);
+ F_SET(sp, S_REDRAW);
+ }
+
+ /*
+ * 2: S_REFRESH
+ *
+ * If S_REFRESH is set in the current screen, repaint everything
+ * that we can find.
+ */
+ if (F_ISSET(sp, S_REFRESH))
+ for (tsp = sp->gp->dq.cqh_first;
+ tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next)
+ if (tsp != sp)
+ F_SET(tsp, S_REDRAW);
+ /*
+ * 3: Related or dirtied screens, or screens with messages.
+ *
+ * If related screens share a view into a file, they may have been
+ * modified as well. Refresh any screens with paint or dirty bits
+ * set, or where messages are waiting. Finally, if we refresh any
+ * screens other than the current one, the cursor will be trashed.
+ */
+ paintbits = S_REDRAW | S_REFORMAT | S_REFRESH;
+ if (O_ISSET(sp, O_NUMBER))
+ paintbits |= S_RENUMBER;
+ for (tsp = sp->gp->dq.cqh_first;
+ tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next)
+ if (tsp != sp &&
+ (F_ISSET(tsp, paintbits) ||
+ F_ISSET(SVP(tsp), SVI_SCREENDIRTY) ||
+ tsp->msgq.lh_first != NULL &&
+ !F_ISSET(tsp->msgq.lh_first, M_EMPTY))) {
+ (void)svi_paint(tsp, tsp->ep);
+ F_CLR(SVP(tsp), SVI_SCREENDIRTY);
+ F_SET(SVP(sp), SVI_CUR_INVALID);
+ }
+
+ /*
+ * 4: Refresh the current screen.
+ *
+ * Always refresh the current screen, it may be a cursor movement.
+ * Also, always do it last -- that way, S_REFRESH can be set in
+ * the current screen only, and the screen won't flash.
+ */
+ F_CLR(sp, SVI_SCREENDIRTY);
+ return (svi_paint(sp, ep));
+}
+
+/*
+ * svi_paint --
+ * This is the guts of the vi curses screen code. The idea is that
+ * the SCR structure passed in contains the new coordinates of the
+ * screen. What makes this hard is that we don't know how big
+ * characters are, doing input can put the cursor in illegal places,
+ * and we're frantically trying to avoid repainting unless it's
+ * absolutely necessary. If you change this code, you'd better know
+ * what you're doing. It's subtle and quick to anger.
+ */
+int
+svi_paint(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ CHNAME const *cname;
+ SMAP *smp, tmp;
+ SVI_PRIVATE *svp;
+ recno_t lastline, lcnt;
+ size_t cwtotal, cnt, len, x, y;
+ int ch, didpaint;
+ char *p;
+
+#define LNO sp->lno
+#define OLNO svp->olno
+#define CNO sp->cno
+#define OCNO svp->ocno
+#define SCNO svp->sc_col
+
+ didpaint = 0;
+ svp = SVP(sp);
+
+ /*
+ * 1: Reformat the lines.
+ *
+ * If the lines themselves have changed (:set list, for example),
+ * fill in the map from scratch. Adjust the screen that's being
+ * displayed if the leftright flag is set.
+ */
+ if (F_ISSET(sp, S_REFORMAT)) {
+ /* Toss svi_screens() cached information. */
+ SVP(sp)->ss_lno = OOBLNO;
+
+ /* Toss svi_line() cached information. */
+ if (svi_sm_fill(sp, ep, HMAP->lno, P_TOP))
+ return (1);
+ if (O_ISSET(sp, O_LEFTRIGHT) &&
+ (cnt = svi_screens(sp, ep, LNO, &CNO)) != 1)
+ for (smp = HMAP; smp <= TMAP; ++smp)
+ smp->off = cnt;
+ F_CLR(sp, S_REFORMAT);
+ F_SET(sp, S_REDRAW);
+ }
+
+ /*
+ * 2: Line movement.
+ *
+ * Line changes can cause the top line to change as well. As
+ * before, if the movement is large, the screen is repainted.
+ *
+ * 2a: Tiny screens.
+ *
+ * Tiny screens cannot be permitted into the "scrolling" parts of
+ * the smap code for two reasons. If the screen size is 1 line,
+ * HMAP == TMAP and the code will quickly drop core. If the screen
+ * size is 2, none of the divisions by 2 will work, and scrolling
+ * won't work. In fact, because no line change will be less than
+ * HALFTEXT(sp), we always ending up "filling" the map, with a
+ * P_MIDDLE flag, which isn't what the user wanted. Tiny screens
+ * can go into the "fill" portions of the smap code, however.
+ */
+ if (sp->t_rows <= 2) {
+ if (LNO < HMAP->lno) {
+ if (svi_sm_fill(sp, ep, LNO, P_TOP))
+ return (1);
+ } else if (LNO > TMAP->lno)
+ if (svi_sm_fill(sp, ep, LNO, P_BOTTOM))
+ return (1);
+ if (sp->t_rows == 1) {
+ HMAP->off = svi_screens(sp, ep, LNO, &CNO);
+ goto paint;
+ }
+ F_SET(sp, S_REDRAW);
+ goto adjust;
+ }
+
+ /*
+ * 2b: Small screens.
+ *
+ * Users can use the window, w300, w1200 and w9600 options to make
+ * the screen artificially small. The behavior of these options
+ * in the historic vi wasn't all that consistent, and, in fact, it
+ * was never documented how various screen movements affected the
+ * screen size. Generally, one of three things would happen:
+ * 1: The screen would expand in size, showing the line
+ * 2: The screen would scroll, showing the line
+ * 3: The screen would compress to its smallest size and
+ * repaint.
+ * In general, scrolling didn't cause compression (200^D was handled
+ * the same as ^D), movement to a specific line would (:N where N
+ * was 1 line below the screen caused a screen compress), and cursor
+ * movement would scroll if it was 11 lines or less, and compress if
+ * it was more than 11 lines. (And, no, I have no idea where the 11
+ * comes from.)
+ *
+ * What we do is try and figure out if the line is less than half of
+ * a full screen away. If it is, we expand the screen if there's
+ * room, and then scroll as necessary. The alternative is to compress
+ * and repaint.
+ *
+ * !!!
+ * This code is a special case from beginning to end. Unfortunately,
+ * home modems are still slow enough that it's worth having.
+ *
+ * XXX
+ * If the line a really long one, i.e. part of the line is on the
+ * screen but the column offset is not, we'll end up in the adjust
+ * code, when we should probably have compressed the screen.
+ */
+ if (ISSMALLSCREEN(sp))
+ if (LNO < HMAP->lno) {
+ lcnt = svi_sm_nlines(sp, ep, HMAP, LNO, sp->t_maxrows);
+ if (lcnt <= HALFSCREEN(sp))
+ for (; lcnt && sp->t_rows != sp->t_maxrows;
+ --lcnt, ++sp->t_rows) {
+ ++TMAP;
+ if (svi_sm_1down(sp, ep))
+ return (1);
+ }
+ else
+ goto small_fill;
+ } else if (LNO > TMAP->lno) {
+ lcnt = svi_sm_nlines(sp, ep, TMAP, LNO, sp->t_maxrows);
+ if (lcnt <= HALFSCREEN(sp))
+ for (; lcnt && sp->t_rows != sp->t_maxrows;
+ --lcnt, ++sp->t_rows) {
+ if (svi_sm_next(sp, ep, TMAP, TMAP + 1))
+ return (1);
+ ++TMAP;
+ if (svi_line(sp, ep, TMAP, NULL, NULL))
+ return (1);
+ }
+ else {
+small_fill: MOVE(sp, INFOLINE(sp), 0);
+ clrtoeol();
+ for (; sp->t_rows > sp->t_minrows;
+ --sp->t_rows, --TMAP) {
+ MOVE(sp, TMAP - HMAP, 0);
+ clrtoeol();
+ }
+ if (svi_sm_fill(sp, ep, LNO, P_FILL))
+ return (1);
+ F_SET(sp, S_REDRAW);
+ goto adjust;
+ }
+ }
+
+ /*
+ * 3a: Line down.
+ */
+ if (LNO >= HMAP->lno) {
+ if (LNO <= TMAP->lno)
+ goto adjust;
+
+ /*
+ * If less than half a screen away, scroll down until the
+ * line is on the screen.
+ */
+ lcnt = svi_sm_nlines(sp, ep, TMAP, LNO, HALFTEXT(sp));
+ if (lcnt < HALFTEXT(sp)) {
+ while (lcnt--)
+ if (svi_sm_1up(sp, ep))
+ return (1);
+ goto adjust;
+ }
+
+ /*
+ * If less than a full screen from the bottom of the file, put
+ * the last line of the file on the bottom of the screen. The
+ * calculation is safe because we know there's at least one
+ * full screen of lines, otherwise couldn't have gotten here.
+ */
+ if (file_lline(sp, ep, &lastline))
+ return (1);
+ tmp.lno = LNO;
+ tmp.off = 1;
+ lcnt = svi_sm_nlines(sp, ep, &tmp, lastline, sp->t_rows);
+ if (lcnt < sp->t_rows) {
+ if (svi_sm_fill(sp, ep, lastline, P_BOTTOM))
+ return (1);
+ F_SET(sp, S_REDRAW);
+ goto adjust;
+ }
+
+ /*
+ * If more than a full screen from the last line of the file,
+ * put the new line in the middle of the screen.
+ */
+ goto middle;
+ }
+
+ /*
+ * 3b: Line up.
+ *
+ * If less than half a screen away, scroll up until the line is
+ * the first line on the screen.
+ */
+ lcnt = svi_sm_nlines(sp, ep, HMAP, LNO, HALFTEXT(sp));
+ if (lcnt < HALFTEXT(sp)) {
+ while (lcnt--)
+ if (svi_sm_1down(sp, ep))
+ return (1);
+ goto adjust;
+ }
+
+ /*
+ * If less than half a screen from the top of the file, put the first
+ * line of the file at the top of the screen. Otherwise, put the line
+ * in the middle of the screen.
+ */
+ tmp.lno = 1;
+ tmp.off = 1;
+ lcnt = svi_sm_nlines(sp, ep, &tmp, LNO, HALFTEXT(sp));
+ if (lcnt < HALFTEXT(sp)) {
+ if (svi_sm_fill(sp, ep, 1, P_TOP))
+ return (1);
+ } else
+middle: if (svi_sm_fill(sp, ep, LNO, P_MIDDLE))
+ return (1);
+ F_SET(sp, S_REDRAW);
+
+ /*
+ * At this point we know part of the line is on the screen. Since
+ * scrolling is done using logical lines, not physical, all of the
+ * line may not be on the screen. While that's not necessarily bad,
+ * if the part the cursor is on isn't there, we're going to lose.
+ * This can be tricky; if the line covers the entire screen, lno
+ * may be the same as both ends of the map, that's why we test BOTH
+ * the top and the bottom of the map. This isn't a problem for
+ * left-right scrolling, the cursor movement code handles the problem.
+ *
+ * There's a performance issue here if editing *really* long lines.
+ * This gets to the right spot by scrolling, and, in a binary, by
+ * scrolling hundreds of lines. If the adjustment looks like it's
+ * going to be a serious problem, refill the screen and repaint.
+ */
+adjust: if (!O_ISSET(sp, O_LEFTRIGHT) &&
+ (LNO == HMAP->lno || LNO == TMAP->lno)) {
+ cnt = svi_screens(sp, ep, LNO, &CNO);
+ if (LNO == HMAP->lno && cnt < HMAP->off)
+ if ((HMAP->off - cnt) > HALFTEXT(sp)) {
+ HMAP->off = cnt;
+ svi_sm_fill(sp, ep, OOBLNO, P_TOP);
+ F_SET(sp, S_REDRAW);
+ } else
+ while (cnt < HMAP->off)
+ if (svi_sm_1down(sp, ep))
+ return (1);
+ if (LNO == TMAP->lno && cnt > TMAP->off)
+ if ((cnt - TMAP->off) > HALFTEXT(sp)) {
+ TMAP->off = cnt;
+ svi_sm_fill(sp, ep, OOBLNO, P_BOTTOM);
+ F_SET(sp, S_REDRAW);
+ } else
+ while (cnt > TMAP->off)
+ if (svi_sm_1up(sp, ep))
+ return (1);
+ }
+
+ /* If the screen needs to be repainted, skip cursor optimization. */
+ if (F_ISSET(sp, S_REDRAW))
+ goto paint;
+
+ /*
+ * 4: Cursor movements.
+ *
+ * Decide cursor position. If the line has changed, the cursor has
+ * moved over a tab, or don't know where the cursor was, reparse the
+ * line. Note, if we think that the cursor "hasn't moved", reparse
+ * the line. This is 'cause if it hasn't moved, we've almost always
+ * lost track of it.
+ *
+ * Otherwise, we've just moved over fixed-width characters, and can
+ * calculate the left/right scrolling and cursor movement without
+ * reparsing the line. Note that we don't know which (if any) of
+ * the characters between the old and new cursor positions changed.
+ *
+ * XXX
+ * With some work, it should be possible to handle tabs quickly, at
+ * least in obvious situations, like moving right and encountering
+ * a tab, without reparsing the whole line.
+ */
+
+ /* If the line we're working with has changed, reparse. */
+ if (F_ISSET(SVP(sp), SVI_CUR_INVALID) || LNO != OLNO) {
+ F_CLR(SVP(sp), SVI_CUR_INVALID);
+ goto slow;
+ }
+
+ /* Otherwise, if nothing's changed, go fast. */
+ if (CNO == OCNO)
+ goto fast;
+
+ /*
+ * Get the current line. If this fails, we either have an empty
+ * file and can just repaint, or there's a real problem. This
+ * isn't a performance issue because there aren't any ways to get
+ * here repeatedly.
+ */
+ if ((p = file_gline(sp, ep, LNO, &len)) == NULL) {
+ if (file_lline(sp, ep, &lastline))
+ return (1);
+ if (lastline == 0)
+ goto slow;
+ GETLINE_ERR(sp, LNO);
+ return (1);
+ }
+
+#ifdef DEBUG
+ /* This is just a test. */
+ if (CNO >= len && len != 0) {
+ msgq(sp, M_ERR, "Error: %s/%d: cno (%u) >= len (%u)",
+ tail(__FILE__), __LINE__, CNO, len);
+ return (1);
+ }
+#endif
+ /*
+ * The basic scheme here is to look at the characters in between
+ * the old and new positions and decide how big they are on the
+ * screen, and therefore, how many screen positions to move.
+ */
+ cname = sp->gp->cname;
+ if (CNO < OCNO) {
+ /*
+ * 4a: Cursor moved left.
+ *
+ * Point to the old character. The old cursor position can
+ * be past EOL if, for example, we just deleted the rest of
+ * the line. In this case, since we don't know the width of
+ * the characters we traversed, we have to do it slowly.
+ */
+ p += OCNO;
+ cnt = (OCNO - CNO) + 1;
+ if (OCNO >= len)
+ goto slow;
+
+ /*
+ * Quit sanity check -- it's hard to figure out exactly when
+ * we cross a screen boundary as we do in the cursor right
+ * movement. If cnt is so large that we're going to cross the
+ * boundary no matter what, stop now.
+ */
+ if (SCNO + 1 + MAX_CHARACTER_COLUMNS < cnt)
+ goto lscreen;
+
+ /*
+ * Count up the widths of the characters. If it's a tab
+ * character, go do it the the slow way.
+ */
+ for (cwtotal = 0; cnt--; cwtotal += cname[ch].len)
+ if ((ch = *(u_char *)p--) == '\t')
+ goto slow;
+
+ /*
+ * Decrement the screen cursor by the total width of the
+ * characters minus 1.
+ */
+ cwtotal -= 1;
+
+ /*
+ * If we're moving left, and there's a wide character in the
+ * current position, go to the end of the character.
+ */
+ if (cname[ch].len > 1)
+ cwtotal -= cname[ch].len - 1;
+
+ /*
+ * If the new column moved us out of the current screen,
+ * calculate a new screen.
+ */
+ if (SCNO < cwtotal) {
+lscreen: if (O_ISSET(sp, O_LEFTRIGHT)) {
+ for (smp = HMAP; smp <= TMAP; ++smp)
+ --smp->off;
+ goto paint;
+ }
+ goto slow;
+ }
+ SCNO -= cwtotal;
+ } else {
+ /*
+ * 4b: Cursor moved right.
+ *
+ * Point to the first character to the right.
+ */
+ p += OCNO + 1;
+ cnt = CNO - OCNO;
+
+ /*
+ * Count up the widths of the characters. If it's a tab
+ * character, go do it the the slow way. If we cross a
+ * screen boundary, we can quit.
+ */
+ for (cwtotal = SCNO; cnt--;) {
+ if ((ch = *(u_char *)p++) == '\t')
+ goto slow;
+ if ((cwtotal += cname[ch].len) >= SCREEN_COLS(sp))
+ break;
+ }
+
+ /*
+ * Increment the screen cursor by the total width of the
+ * characters.
+ */
+ SCNO = cwtotal;
+
+ /*
+ * If the new column moved us out of the current screen,
+ * calculate a new screen.
+ */
+ if (SCNO >= SCREEN_COLS(sp)) {
+ if (O_ISSET(sp, O_LEFTRIGHT)) {
+ SCNO -= SCREEN_COLS(sp);
+ for (smp = HMAP; smp <= TMAP; ++smp)
+ ++smp->off;
+ goto paint;
+ }
+ goto slow;
+ }
+ }
+
+ /*
+ * 4c: Fast cursor update.
+ *
+ * Retrieve the current cursor position, and correct it
+ * for split screens.
+ */
+fast: getyx(stdscr, y, x);
+ y -= sp->woff;
+ goto number;
+
+ /*
+ * 4d: Slow cursor update.
+ *
+ * Walk through the map and find the current line. If doing left-right
+ * scrolling and the cursor movement has changed the screen displayed,
+ * scroll the screen left or right, unless we're updating the info line
+ * in which case we just scroll that one line. Then update the screen
+ * lines for this file line until we have a new screen cursor position.
+ */
+slow: for (smp = HMAP; smp->lno != LNO; ++smp);
+ if (O_ISSET(sp, O_LEFTRIGHT)) {
+ cnt = svi_screens(sp, ep, LNO, &CNO) % SCREEN_COLS(sp);
+ if (cnt != HMAP->off) {
+ if (ISINFOLINE(sp, smp))
+ smp->off = cnt;
+ else
+ for (smp = HMAP; smp <= TMAP; ++smp)
+ smp->off = cnt;
+ goto paint;
+ }
+ }
+ for (y = -1; smp <= TMAP && smp->lno == LNO; ++smp) {
+ if (svi_line(sp, ep, smp, &y, &SCNO))
+ return (1);
+ if (y != -1)
+ break;
+ }
+ goto number;
+
+ /*
+ * 5: Repaint the entire screen.
+ *
+ * Lost big, do what you have to do. We flush the cache as S_REDRAW
+ * gets set when the screen isn't worth fixing, and it's simpler to
+ * repaint. So, don't trust anything that we think we know about it.
+ */
+paint: for (smp = HMAP; smp <= TMAP; ++smp)
+ SMAP_FLUSH(smp);
+ for (smp = HMAP; smp <= TMAP; ++smp)
+ if (svi_line(sp, ep, smp, &y, &SCNO))
+ return (1);
+ /*
+ * If it's a small screen and we're redrawing, clear the unused lines,
+ * ex may have overwritten them.
+ */
+ if (F_ISSET(sp, S_REDRAW)) {
+ if (ISSMALLSCREEN(sp))
+ for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) {
+ MOVE(sp, cnt, 0);
+ clrtoeol();
+ }
+ F_CLR(sp, S_REDRAW);
+ }
+
+ didpaint = 1;
+
+ /*
+ * 6: Repaint the line numbers.
+ *
+ * If O_NUMBER is set and the S_RENUMBER bit is set, and we didn't
+ * repaint the screen, repaint all of the line numbers, they've
+ * changed.
+ */
+number: if (O_ISSET(sp, O_NUMBER) && F_ISSET(sp, S_RENUMBER) && !didpaint) {
+ if (svi_number(sp, ep))
+ return (1);
+ F_CLR(sp, S_RENUMBER);
+ }
+
+ /*
+ * 7: Refresh the screen.
+ *
+ * If the screen was corrupted, refresh it.
+ */
+ if (F_ISSET(sp, S_REFRESH)) {
+ wrefresh(curscr);
+ F_CLR(sp, S_REFRESH);
+ }
+
+ if (F_ISSET(sp, S_BELLSCHED))
+ svi_bell(sp);
+ /*
+ * If the bottom line isn't in use by the colon command:
+ *
+ * Display any messages. Don't test S_UPDATE_MODE. The
+ * message printing routine set it to avoid anyone else
+ * destroying the message we're about to display.
+ *
+ * If the bottom line isn't in use by anyone, put out the
+ * standard status line.
+ */
+ if (!F_ISSET(SVP(sp), SVI_INFOLINE))
+ if (sp->msgq.lh_first != NULL &&
+ !F_ISSET(sp->msgq.lh_first, M_EMPTY))
+ svi_msgflush(sp);
+ else if (!F_ISSET(sp, S_UPDATE_MODE))
+ svi_modeline(sp, ep);
+
+ /* Update saved information. */
+ OCNO = CNO;
+ OLNO = LNO;
+
+ /* Place the cursor. */
+ MOVE(sp, y, SCNO);
+
+ /* Flush it all out. */
+ refresh();
+
+ return (0);
+}
+
+/*
+ * svi_msgflush --
+ * Flush any accumulated messages.
+ */
+static int
+svi_msgflush(sp)
+ SCR *sp;
+{
+ CH ikey;
+ CHAR_T ch;
+ CHNAME const *cname;
+ MSG *mp;
+ size_t chlen, len;
+ char *p;
+
+#define MCONTMSG " [More ...]"
+
+ /* Display the messages. */
+ cname = sp->gp->cname;
+ for (mp = sp->msgq.lh_first, p = NULL;
+ mp != NULL && !F_ISSET(mp, M_EMPTY); mp = mp->q.le_next) {
+ p = mp->mbuf;
+
+lcont: /* Move to the message line and clear it. */
+ MOVE(sp, INFOLINE(sp), 0);
+ clrtoeol();
+
+ /*
+ * Turn on standout mode if requested, or, if we've split
+ * the screen and need a divider.
+ */
+ if (F_ISSET(mp, M_INV_VIDEO) ||
+ sp->q.cqe_next != (void *)&sp->gp->dq)
+ standout();
+
+ /*
+ * Print up to the "more" message. Avoid the last character
+ * in the last line, some hardware doesn't like it.
+ */
+ if (svi_ncols(sp, p, mp->len, NULL) < sp->cols - 1)
+ len = sp->cols - 1;
+ else
+ len = (sp->cols - sizeof(MCONTMSG)) - 1;
+ for (;; ++p) {
+ if (!mp->len)
+ break;
+ ch = *(u_char *)p;
+ chlen = cname[ch].len;
+ if (chlen >= len)
+ break;
+ len -= chlen;
+ --mp->len;
+ ADDNSTR(cname[ch].name, chlen);
+ }
+
+ /*
+ * If more, print continue message. If user key fails,
+ * keep showing the messages anyway.
+ */
+ if (mp->len || (mp->q.le_next != NULL &&
+ !F_ISSET(mp->q.le_next, M_EMPTY))) {
+ ADDNSTR(MCONTMSG, sizeof(MCONTMSG) - 1);
+ refresh();
+ for (;;) {
+ if (term_user_key(sp, &ikey) != INP_OK)
+ break;
+ if (ikey.value == K_CR ||
+ ikey.value == K_NL || ikey.ch == ' ')
+ break;
+ svi_bell(sp);
+ }
+ }
+
+ /* Turn off standout mode. */
+ if (F_ISSET(mp, M_INV_VIDEO) ||
+ sp->q.cqe_next != (void *)&sp->gp->dq)
+ standend();
+
+ if (mp->len)
+ goto lcont;
+
+ refresh();
+ F_SET(mp, M_EMPTY);
+ }
+ return (0);
+}
+
+#define RULERSIZE 15
+#define MODESIZE (RULERSIZE + 15)
+
+/*
+ * svi_modeline --
+ * Update the mode line.
+ */
+static int
+svi_modeline(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ char *s, buf[RULERSIZE];
+
+ MOVE(sp, INFOLINE(sp), 0);
+ clrtoeol();
+
+ /* Display a dividing line if not the bottom screen. */
+ if (sp->q.cqe_next != (void *)&sp->gp->dq)
+ svi_divider(sp);
+
+ /* Display the ruler. */
+ if (O_ISSET(sp, O_RULER) && sp->cols > RULERSIZE + 2) {
+ MOVE(sp, INFOLINE(sp), sp->cols / 2 - RULERSIZE / 2);
+ clrtoeol();
+ (void)snprintf(buf,
+ sizeof(buf), "%lu,%lu", sp->lno, sp->cno + 1);
+ ADDSTR(buf);
+ }
+
+ /* Show the modified bit. */
+ if (O_ISSET(sp, O_SHOWDIRTY) &&
+ F_ISSET(ep, F_MODIFIED) && sp->cols > MODESIZE) {
+ MOVE(sp, INFOLINE(sp), sp->cols - 9);
+ ADDSTR("*");
+ }
+
+ /*
+ * Show the mode. Leave the last character blank, in case it's a
+ * really dumb terminal with hardware scroll. Second, don't try
+ * to *paint* the last character, SunOS 4.1.1 and Ultrix 4.2 curses
+ * won't let you paint the last character in the screen.
+ */
+ if (O_ISSET(sp, O_SHOWMODE) && sp->cols > MODESIZE) {
+ MOVE(sp, INFOLINE(sp), sp->cols - 8);
+ s = F_ISSET(sp, S_INPUT) ? " Input" : "Command";
+ ADDSTR(s);
+ }
+
+ return (0);
+}
+
+/*
+ * svi_divider --
+ * Draw a dividing line between the screens.
+ */
+int
+svi_divider(sp)
+ SCR *sp;
+{
+ size_t len;
+
+#define DIVIDESTR "+=+=+=+=+=+=+=+"
+ len = sizeof(DIVIDESTR) - 1 > sp->cols ?
+ sp->cols : sizeof(DIVIDESTR) - 1;
+ ADDNSTR(DIVIDESTR, len);
+ return (0);
+}
diff --git a/usr.bin/vi/svi/svi_relative.c b/usr.bin/vi/svi/svi_relative.c
new file mode 100644
index 000000000000..7daa972c8575
--- /dev/null
+++ b/usr.bin/vi/svi/svi_relative.c
@@ -0,0 +1,205 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)svi_relative.c 8.7 (Berkeley) 12/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "vi.h"
+#include "svi_screen.h"
+
+/*
+ * svi_column --
+ * Return the logical column of the cursor.
+ */
+int
+svi_column(sp, ep, cp)
+ SCR *sp;
+ EXF *ep;
+ size_t *cp;
+{
+ size_t col;
+
+ col = SVP(sp)->sc_col;
+ if (O_ISSET(sp, O_NUMBER))
+ col -= O_NUMBER_LENGTH;
+ *cp = col;
+ return (0);
+}
+
+/*
+ * svi_relative --
+ * Return the physical column from the line that will display a
+ * character closest to the currently most attractive character
+ * position. If it's not easy, uses the underlying routine that
+ * really figures it out. It's broken into two parts because the
+ * svi_lrelative routine handles "logical" offsets, which nobody
+ * but the screen routines understand.
+ */
+size_t
+svi_relative(sp, ep, lno)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+{
+ size_t cno;
+
+ /* First non-blank character. */
+ if (sp->rcmflags == RCM_FNB) {
+ cno = 0;
+ (void)nonblank(sp, ep, lno, &cno);
+ return (cno);
+ }
+
+ /* First character is easy, and common. */
+ if (sp->rcmflags != RCM_LAST && sp->rcm == 0)
+ return (0);
+
+ return (svi_lrelative(sp, ep, lno, 1));
+}
+
+/*
+ * svi_lrelative --
+ * Return the physical column from the line that will display a
+ * character closest to the currently most attractive character
+ * position. The offset is for the commands that move logical
+ * distances, i.e. if it's a logical scroll the closest physical
+ * distance is based on the logical line, not the physical line.
+ */
+size_t
+svi_lrelative(sp, ep, lno, off)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ size_t off;
+{
+ CHNAME const *cname;
+ size_t len, llen, scno;
+ int ch, listset;
+ char *lp, *p;
+
+ /* Need the line to go any further. */
+ if ((lp = file_gline(sp, ep, lno, &len)) == NULL)
+ return (0);
+
+ /* Empty lines are easy. */
+ if (len == 0)
+ return (0);
+
+ /* Last character is easy, and common. */
+ if (sp->rcmflags == RCM_LAST)
+ return (len - 1);
+
+ /* Discard logical lines. */
+ cname = sp->gp->cname;
+ listset = O_ISSET(sp, O_LIST);
+ for (scno = 0, p = lp, llen = len; --off;) {
+ for (; len && scno < sp->cols; --len)
+ SCNO_INCREMENT;
+ if (len == 0)
+ return (llen - 1);
+ scno -= sp->cols;
+ }
+
+ /* Step through the line until reach the right character. */
+ while (len--) {
+ SCNO_INCREMENT;
+ if (scno >= sp->rcm) {
+ /* Get the offset of this character. */
+ len = p - lp;
+
+ /*
+ * May be the next character, not this one,
+ * so check to see if we've gone too far.
+ */
+ if (scno == sp->rcm)
+ return (len < llen - 1 ? len : llen - 1);
+ /* It's this character. */
+ return (len - 1);
+ }
+ }
+ /* No such character; return start of last character. */
+ return (llen - 1);
+}
+
+/*
+ * svi_chposition --
+ * Return the physical column from the line that will display a
+ * character closest to the specified column.
+ */
+size_t
+svi_chposition(sp, ep, lno, cno)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ size_t cno;
+{
+ CHNAME const *cname;
+ size_t len, llen, scno;
+ int ch, listset;
+ char *lp, *p;
+
+ /* Need the line to go any further. */
+ if ((lp = file_gline(sp, ep, lno, &llen)) == NULL)
+ return (0);
+
+ /* Empty lines are easy. */
+ if (llen == 0)
+ return (0);
+
+ /* Step through the line until reach the right character. */
+ cname = sp->gp->cname;
+ listset = O_ISSET(sp, O_LIST);
+ for (scno = 0, len = llen, p = lp; len--;) {
+ SCNO_INCREMENT;
+ if (scno >= cno) {
+ /* Get the offset of this character. */
+ len = p - lp;
+
+ /*
+ * May be the next character, not this one,
+ * so check to see if we've gone too far.
+ */
+ if (scno == cno)
+ return (len < llen - 1 ? len : llen - 1);
+ /* It's this character. */
+ return (len - 1);
+ }
+ }
+ /* No such character; return start of last character. */
+ return (llen - 1);
+}
diff --git a/usr.bin/vi/svi/svi_screen.c b/usr.bin/vi/svi/svi_screen.c
new file mode 100644
index 000000000000..16e2c36d8609
--- /dev/null
+++ b/usr.bin/vi/svi/svi_screen.c
@@ -0,0 +1,534 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)svi_screen.c 8.57 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <curses.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "vcmd.h"
+#include "excmd.h"
+#include "svi_screen.h"
+#include "sex/sex_screen.h"
+
+static void d_to_h __P((SCR *, char *));
+static int svi_initscr_kluge __P((SCR *, struct termios *));
+static void svi_keypad __P((SCR *, int));
+static void svi_keypad_pc __P((int));
+
+/*
+ * svi_screen_init --
+ * Initialize a screen.
+ */
+int
+svi_screen_init(sp)
+ SCR *sp;
+{
+ /* Initialize support routines. */
+ sp->s_bell = svi_bell;
+ sp->s_bg = svi_bg;
+ sp->s_busy = svi_busy;
+ sp->s_change = svi_change;
+ sp->s_chposition = svi_chposition;
+ sp->s_clear = svi_clear;
+ sp->s_column = svi_column;
+ sp->s_confirm = svi_confirm;
+ sp->s_down = svi_sm_down;
+ sp->s_edit = svi_screen_edit;
+ sp->s_end = svi_screen_end;
+ sp->s_ex_cmd = svi_ex_cmd;
+ sp->s_ex_run = svi_ex_run;
+ sp->s_ex_write = svi_ex_write;
+ sp->s_fg = svi_fg;
+ sp->s_fill = svi_sm_fill;
+ sp->s_get = svi_get;
+ sp->s_key_read = sex_key_read;
+ sp->s_optchange = svi_optchange;
+ sp->s_position = svi_sm_position;
+ sp->s_rabs = svi_rabs;
+ sp->s_refresh = svi_refresh;
+ sp->s_relative = svi_relative;
+ sp->s_rrel = svi_rrel;
+ sp->s_split = svi_split;
+ sp->s_suspend = svi_suspend;
+ sp->s_up = svi_sm_up;
+
+ return (0);
+}
+
+/*
+ * svi_screen_copy --
+ * Copy to a new screen.
+ */
+int
+svi_screen_copy(orig, sp)
+ SCR *orig, *sp;
+{
+ SVI_PRIVATE *osvi, *nsvi;
+
+ /* Create the private screen structure. */
+ CALLOC_RET(orig, nsvi, SVI_PRIVATE *, 1, sizeof(SVI_PRIVATE));
+ sp->svi_private = nsvi;
+
+/* INITIALIZED AT SCREEN CREATE. */
+ /* Lose svi_screens() cached information. */
+ nsvi->ss_lno = OOBLNO;
+
+/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
+ if (orig == NULL) {
+ } else {
+ osvi = SVP(orig);
+ nsvi->srows = osvi->srows;
+ if (osvi->VB != NULL && (nsvi->VB = strdup(osvi->VB)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+
+ F_SET(nsvi, F_ISSET(osvi, SVI_NO_VBELL));
+ }
+ return (0);
+}
+
+/*
+ * svi_screen_end --
+ * End a screen.
+ */
+int
+svi_screen_end(sp)
+ SCR *sp;
+{
+ int rval;
+
+ rval = 0;
+
+ /*
+ * XXX
+ * If this is the last screen, end curses
+ * while we still have screen information.
+ */
+ if (sp->q.cqe_prev == (void *)&sp->gp->dq &&
+ sp->q.cqe_next == (void *)&sp->gp->dq &&
+ sp->gp->hq.cqh_first == (void *)&sp->gp->hq &&
+ svi_curses_end(sp))
+ rval = 1;
+
+ /* Free screen map. */
+ if (HMAP != NULL)
+ FREE(HMAP, SIZE_HMAP(sp) * sizeof(SMAP));
+
+ /* Free visual bell string. */
+ if (SVP(sp)->VB != NULL)
+ FREE(SVP(sp)->VB, strlen(SVP(sp)->VB) + 1);
+
+ /* Free private memory. */
+ FREE(SVP(sp), sizeof(SVI_PRIVATE));
+ sp->svi_private = NULL;
+
+ return (rval);
+}
+
+/*
+ * We use a single curses "window" for each vi screen. The model would be
+ * simpler with two windows (one for the text, and one for the modeline)
+ * because scrolling the text window down would work correctly then, not
+ * affecting the mode line. As it is we have to play games to make it look
+ * right. The reason for this choice is that it would be difficult for
+ * curses to optimize the movement, i.e. detect that the downward scroll
+ * isn't going to change the modeline, set the scrolling region on the
+ * terminal and only scroll the first part of the text window. (Even if
+ * curses did detect it, the set-scrolling-region terminal commands can't
+ * be used by curses because it's indeterminate where the cursor ends up
+ * after they are sent.)
+ */
+/*
+ * svi_screen_edit --
+ * Main vi curses screen loop.
+ */
+int
+svi_screen_edit(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ SCR *tsp;
+ int force;
+
+ /* Get refresh to init curses. */
+ F_SET(sp, S_RESIZE);
+
+ for (;;) {
+ /* Reset the cursor. */
+ F_SET(SVP(sp), SVI_CUR_INVALID);
+
+ /*
+ * Run vi. If vi fails, svi data structures may be
+ * corrupted, be extremely careful what you free up.
+ */
+ if (vi(sp, sp->ep)) {
+ if (F_ISSET(ep, F_RCV_ON)) {
+ F_SET(ep, F_RCV_NORM);
+ (void)rcv_sync(sp, sp->ep);
+ }
+ (void)file_end(sp, sp->ep, 1); /* End the file. */
+ (void)svi_curses_end(sp); /* End curses. */
+ (void)screen_end(sp); /* End the screen. */
+ return (1);
+ }
+
+ force = 0;
+ switch (F_ISSET(sp, S_MAJOR_CHANGE)) {
+ case S_EXIT_FORCE:
+ force = 1;
+ /* FALLTHROUGH */
+ case S_EXIT:
+ F_CLR(sp, S_EXIT_FORCE | S_EXIT);
+ if (file_end(sp, sp->ep, force))/* End the file. */
+ break;
+ (void)svi_join(sp, &tsp); /* Find a new screen. */
+ if (tsp == NULL)
+ (void)svi_swap(sp, &tsp, NULL);
+ (void)screen_end(sp); /* End the screen. */
+ if ((sp = tsp) == NULL) /* Switch. */
+ return (0);
+ break;
+ case 0: /* Exit vi mode. */
+ (void)svi_curses_end(sp); /* End curses. */
+ d_to_h(sp, "Exit from vi");
+ return (0);
+ case S_FSWITCH: /* File switch. */
+ F_CLR(sp, S_FSWITCH);
+ F_SET(sp, S_REFORMAT);
+ break;
+ case S_SSWITCH: /* Screen switch. */
+ F_CLR(sp, S_SSWITCH);
+ sp = sp->nextdisp;
+ break;
+ default:
+ abort();
+ }
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * svi_curses_init --
+ * Initialize curses.
+ */
+int
+svi_curses_init(sp)
+ SCR *sp;
+{
+ struct termios t;
+ int ixoff, ixon;
+ char *p, kbuf[2048];
+
+ /*
+ * Vi wants raw mode, excepting flow control characters. Both
+ * cbreak and raw modes have problems for us. In cbreak mode,
+ * we have to turn all the signals off explicitly. In raw mode,
+ * we have to turn flow control back on. Raw chosen for no strong
+ * reason. In both cases we have to periodically turn various
+ * signals on.
+ */
+ if (tcgetattr(STDIN_FILENO, &t)) {
+ msgq(sp, M_SYSERR, "tcgetattr");
+ return (1);
+ }
+ ixon = t.c_iflag & IXON;
+ ixoff = t.c_iflag & IXOFF;
+
+ /*
+ * The initscr() in SunOS curses flushes the terminal driver queue.
+ * I have no idea if this stark raving insanity appears elsewhere,
+ * but since the SunOS curses is likely derived from the System III
+ * or System V versions, here's the workaround.
+ */
+ if (svi_initscr_kluge(sp, &t))
+ return (1);
+
+ /*
+ * Start the screen. Initscr() doesn't provide useful error values
+ * or messages. Generally, either malloc failed or the terminal
+ * was unknown or lacked some necesssary feature. Try and guess so
+ * the user isn't even more pissed off because of the error message.
+ */
+ errno = 0;
+ if (initscr() == NULL) {
+ if (errno)
+ msgq(sp, M_SYSERR, "initscr failed");
+ else
+ msgq(sp, M_ERR, "Error: initscr failed.");
+ if ((p = getenv("TERM")) == NULL)
+ msgq(sp, M_ERR,
+ "Error: no terminal environment variable set.");
+ else if (tgetent(kbuf, p) != 1)
+ msgq(sp, M_ERR,
+"Error: %s: unknown terminal type, or terminal lacking necessary features.", p);
+ else
+ msgq(sp, M_ERR,
+ "Error: %s: terminal type lacking necessary features.", p);
+ return (1);
+ }
+
+ /*
+ * !!!
+ * If raw isn't turning off echo and newlines, something's wrong.
+ * However, just in case...
+ */
+ noecho(); /* No character echo. */
+ nonl(); /* No CR/NL translation. */
+ raw(); /* No special characters. */
+ idlok(stdscr, 1); /* Use hardware insert/delete line. */
+
+ /*
+ * Vi wants the cursor keys in application mode. The historic version
+ * of curses had no way to do this, and the newer versions (System V)
+ * only enable it through the wgetch() interface. Do it roughly, here.
+ */
+ svi_keypad(sp, 1);
+
+ /*
+ * XXX
+ * Major compatibility kluge. When we call the curses raw() routine,
+ * XON/XOFF flow control is turned off. Old terminals like to have
+ * it, so if it's originally set for the tty, we turn it back on. For
+ * some unknown reason, this causes System V curses to NOT correctly
+ * restore the terminal modes when SIGTSTP is received.
+ */
+ if ((ixon || ixoff) &&
+ !tcgetattr(STDIN_FILENO, &sp->gp->s5_curses_botch)) {
+ t = sp->gp->s5_curses_botch;
+ if (ixon)
+ t.c_iflag |= IXON;
+ if (ixoff)
+ t.c_iflag |= IXOFF;
+ F_SET(sp->gp, G_CURSES_S5CB);
+ (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t);
+ }
+
+ /*
+ * The first screen in the list gets it all. All other screens
+ * are hidden and lose their maps.
+ */
+ d_to_h(sp, "Window resize");
+
+ /* Initialize terminal values. */
+ SVP(sp)->srows = O_VAL(sp, O_LINES);
+
+ /*
+ * Initialize screen values.
+ *
+ * Small windows: see svi/svi_refresh.c:svi_refresh, section 3b.
+ *
+ * Setup:
+ * t_minrows is the minimum rows to display
+ * t_maxrows is the maximum rows to display (rows - 1)
+ * t_rows is the rows currently being displayed
+ */
+ sp->rows = SVP(sp)->srows;
+ sp->cols = O_VAL(sp, O_COLUMNS);
+ sp->woff = 0;
+ sp->t_rows = sp->t_minrows = O_VAL(sp, O_WINDOW);
+ if (sp->t_rows > sp->rows - 1) {
+ sp->t_minrows = sp->t_rows = sp->rows - 1;
+ msgq(sp, M_INFO,
+ "Windows option value is too large, max is %u", sp->t_rows);
+ }
+ sp->t_maxrows = sp->rows - 1;
+
+ /* Create the screen map. */
+ if ((HMAP = malloc(SIZE_HMAP(sp) * sizeof(SMAP))) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ memset(HMAP, 0, SIZE_HMAP(sp) * sizeof(SMAP));
+ TMAP = HMAP + (sp->t_rows - 1);
+
+ F_SET(sp->gp, G_CURSES_INIT); /* Curses initialized. */
+ F_SET(SVP(sp), SVI_CUR_INVALID); /* Cursor is invalid. */
+ return (0);
+}
+
+/*
+ * svi_rrel --
+ * Change the relative size of the current screen.
+ */
+int
+svi_rrel(sp, count)
+ SCR *sp;
+ long count;
+{
+ /* Can't grow beyond the size of the window. */
+ if (count > O_VAL(sp, O_WINDOW))
+ count = O_VAL(sp, O_WINDOW);
+
+ sp->t_minrows = sp->t_rows = count;
+ if (sp->t_rows > sp->rows - 1)
+ sp->t_minrows = sp->t_rows = sp->rows - 1;
+ TMAP = HMAP + (sp->t_rows - 1);
+ F_SET(sp, S_REDRAW);
+ return (0);
+}
+
+/*
+ * svi_curses_end --
+ * Move to the bottom of the screen, end curses.
+ */
+int
+svi_curses_end(sp)
+ SCR *sp;
+{
+ /* We get called before anything has been initialized. */
+ if (!F_ISSET(sp->gp, G_CURSES_INIT))
+ return (0);
+ F_CLR(sp->gp, G_CURSES_INIT);
+
+ /* Move to the bottom of the screen. */
+ if (move(INFOLINE(sp), 0) == OK) {
+ clrtoeol();
+ refresh();
+ }
+
+ /*
+ * XXX
+ * See comment in svi_curses_init().
+ */
+ if (F_ISSET(sp->gp, G_CURSES_S5CB))
+ (void)tcsetattr(STDIN_FILENO,
+ TCSASOFT | TCSADRAIN, &sp->gp->s5_curses_botch);
+ F_CLR(sp->gp, G_CURSES_S5CB);
+
+ svi_keypad(sp, 0);
+ endwin();
+ return (0);
+}
+
+/*
+ * svi_keypad --
+ * Put the keypad/cursor arrows into or out of application mode.
+ */
+static void
+svi_keypad(sp, on)
+ SCR *sp;
+ int on;
+{
+ char *sbp, *t, kbuf[2048], sbuf[128];
+
+ if (tgetent(kbuf, O_STR(sp, O_TERM)) != 1)
+ return;
+ sbp = sbuf;
+ if ((t = tgetstr(on ? "ks" : "ke", &sbp)) == NULL)
+ return;
+ (void)tputs(t, 0, svi_keypad_pc);
+}
+
+/*
+ * svi_keypad_pc --
+ * putchar routine for tputs().
+ */
+static void
+svi_keypad_pc(argch)
+ int argch;
+{
+ char ch;
+
+ ch = argch;
+ (void)write(STDOUT_FILENO, &ch, sizeof(ch));
+}
+
+/*
+ * svi_initscr_kluge --
+ * Read all of the waiting keys before calling initscr().
+ */
+static int
+svi_initscr_kluge(sp, tp)
+ SCR *sp;
+ struct termios *tp;
+{
+ struct termios rawt;
+ int rval;
+
+ /*
+ * Turn off canonical input processing so that we get partial
+ * lines as well as complete ones. Also, set the MIN/TIME
+ * values -- System V and SMI systems overload VMIN and VTIME,
+ * such that VMIN is the same as the VEOF element, and VTIME is
+ * the same as the VEOL element. This means, that if VEOF was
+ * ^D, the default VMIN is 4. Majorly stupid.
+ */
+ rawt = *tp;
+ rawt.c_cc[VMIN] = 1;
+ rawt.c_cc[VTIME] = 0;
+ rawt.c_lflag &= ~ICANON;
+ if (tcsetattr(STDIN_FILENO, TCSASOFT | TCSANOW, &rawt))
+ return (1);
+ rval = term_key_queue(sp);
+ if (tcsetattr(STDIN_FILENO, TCSASOFT | TCSANOW, tp) || rval)
+ return (1);
+ return (0);
+}
+
+/*
+ * d_to_h --
+ * Move all but the current screen to the hidden queue.
+ */
+static void
+d_to_h(sp, emsg)
+ SCR *sp;
+ char *emsg;
+{
+ SCR *tsp;
+ int hidden;
+
+ for (hidden = 0;
+ (tsp = sp->gp->dq.cqh_first) != (void *)&sp->gp->dq; ++hidden) {
+ if (_HMAP(tsp) != NULL)
+ FREE(_HMAP(tsp), SIZE_HMAP(tsp) * sizeof(SMAP));
+ CIRCLEQ_REMOVE(&sp->gp->dq, tsp, q);
+ CIRCLEQ_INSERT_TAIL(&sp->gp->hq, tsp, q);
+ }
+ CIRCLEQ_REMOVE(&sp->gp->hq, sp, q);
+ CIRCLEQ_INSERT_TAIL(&sp->gp->dq, sp, q);
+ if (hidden > 1)
+ msgq(sp, M_INFO,
+ "%s backgrounded %d screens; use :display to list the screens.",
+ emsg, hidden - 1);
+}
diff --git a/usr.bin/vi/svi/svi_screen.h b/usr.bin/vi/svi/svi_screen.h
new file mode 100644
index 000000000000..4bf754473df9
--- /dev/null
+++ b/usr.bin/vi/svi/svi_screen.h
@@ -0,0 +1,238 @@
+/*-
+ * Copyright (c) 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.
+ *
+ * @(#)svi_screen.h 8.30 (Berkeley) 12/22/93
+ */
+
+/*
+ * Structure for mapping lines to the screen. An SMAP is an array, with one
+ * structure element per screen line, which holds information describing the
+ * physical line which is displayed in the screen line. The first two fields
+ * (lno and off) are all that are necessary to describe a line. The rest of
+ * the information is useful to keep information from being re-calculated.
+ *
+ * Lno is the line number. Off is the screen offset into the line. For
+ * example, the pair 2:1 would be the first screen of line 2, and 2:2 would
+ * be the second. If doing left-right scrolling, all of the offsets will be
+ * the same, i.e. for the second screen, 1:2, 2:2, 3:2, etc. If doing the
+ * standard vi scrolling, it will be staggered, i.e. 1:1, 1:2, 1:3, 2:1, 3:1,
+ * etc.
+ *
+ * The SMAP is always as large as the physical screen, plus a slot for the
+ * info line, so that there is room to add any screen into another one at
+ * screen exit.
+ */
+typedef struct _smap {
+ recno_t lno; /* 1-N: Physical file line number. */
+ size_t off; /* 1-N: Screen offset in the line. */
+
+ /* svi_line() cache information. */
+ size_t c_sboff; /* 0-N: offset of first character byte. */
+ size_t c_eboff; /* 0-N: offset of last character byte. */
+ u_char c_scoff; /* 0-N: offset into the first character. */
+ u_char c_eclen; /* 1-N: columns from the last character. */
+ u_char c_ecsize; /* 1-N: size of the last character. */
+} SMAP;
+
+ /* Macros to flush/test cached information. */
+#define SMAP_CACHE(smp) ((smp)->c_ecsize != 0)
+#define SMAP_FLUSH(smp) ((smp)->c_ecsize = 0)
+
+typedef struct _svi_private {
+/* INITIALIZED AT SCREEN CREATE. */
+ SMAP *h_smap; /* First slot of the line map. */
+ SMAP *t_smap; /* Last slot of the line map. */
+
+ size_t exlinecount; /* Ex overwrite count. */
+ size_t extotalcount; /* Ex overwrite count. */
+ size_t exlcontinue; /* Ex line continue value. */
+
+ /* svi_screens() cache information. */
+ recno_t ss_lno; /* 1-N: Line number. */
+ size_t ss_screens; /* Return value. */
+
+ recno_t olno; /* 1-N: old cursor file line. */
+ size_t ocno; /* 0-N: old file cursor column. */
+ size_t sc_col; /* 0-N: LOGICAL screen column. */
+
+/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
+ size_t srows; /* 1-N: Rows in the terminal/window. */
+
+ char *VB; /* Visual bell termcap string. */
+
+#define SVI_CUR_INVALID 0x001 /* Cursor position is unknown. */
+#define SVI_DIVIDER 0x002 /* Screen divider is displayed. */
+#define SVI_INFOLINE 0x004 /* The infoline is being used by v_ntext(). */
+#define SVI_NO_VBELL 0x008 /* No visual bell available. */
+#define SVI_SCREENDIRTY 0x010 /* Screen needs refreshing. */
+ u_int flags;
+} SVI_PRIVATE;
+
+#define SVP(sp) ((SVI_PRIVATE *)((sp)->svi_private))
+#define HMAP (SVP(sp)->h_smap)
+#define TMAP (SVP(sp)->t_smap)
+#define _HMAP(sp) (SVP(sp)->h_smap)
+#define _TMAP(sp) (SVP(sp)->t_smap)
+
+/*
+ * One extra slot is always allocated for the map so that we can use
+ * it to do vi :colon command input; see svi_get().
+ */
+#define SIZE_HMAP(sp) (SVP(sp)->srows + 1)
+
+#define O_NUMBER_FMT "%7lu " /* O_NUMBER format, length. */
+#define O_NUMBER_LENGTH 8
+ /* Columns on a screen. */
+#define SCREEN_COLS(sp) \
+ ((O_ISSET(sp, O_NUMBER) ? (sp)->cols - O_NUMBER_LENGTH : (sp)->cols))
+
+#define HALFSCREEN(sp) ((sp)->t_maxrows / 2) /* Half the screen. */
+#define HALFTEXT(sp) ((sp)->t_rows / 2) /* Half the text. */
+
+#define INFOLINE(sp) ((sp)->t_maxrows) /* Info line test, offset. */
+#define ISINFOLINE(sp, smp) (((smp) - HMAP) == INFOLINE(sp))
+
+ /* Small screen test. */
+#define ISSMALLSCREEN(sp) ((sp)->t_minrows != (sp)->t_maxrows)
+
+ /* Next tab offset. */
+#define TAB_OFF(sp, c) (O_VAL(sp, O_TABSTOP) - (c) % O_VAL(sp, O_TABSTOP))
+
+
+#define SCNO_INCREMENT /* Step through line. */\
+ scno += (ch = *(u_char *)p++) == '\t' && !listset ? \
+ TAB_OFF(sp, scno) : cname[ch].len
+
+/* Move in a screen (absolute), and fail if it doesn't work. */
+#define MOVEA(sp, lno, cno) { \
+ if (move(lno, cno) == ERR) { \
+ msgq(sp, M_ERR, \
+ "Error: %s/%d: move:l(%u), c(%u), abs.", \
+ tail(__FILE__), __LINE__, lno, cno); \
+ return (1); \
+ } \
+}
+
+/* Move in a window, and fail if it doesn't work. */
+#define MOVE(sp, lno, cno) { \
+ size_t __lno = (sp)->woff + (lno); \
+ if (move(__lno, cno) == ERR) { \
+ msgq(sp, M_ERR, \
+ "Error: %s/%d: move:l(%u), c(%u), o(%u).", \
+ tail(__FILE__), __LINE__, lno, cno, sp->woff); \
+ return (1); \
+ } \
+}
+
+/* Add a character. */
+#define ADDCH(ch) { \
+ int __ch = (ch); \
+ ADDNSTR(cname[__ch].name, cname[__ch].len); \
+}
+
+/* Add a string len bytes long. */
+#define ADDNSTR(str, len) { \
+ if (addnstr(str, len) == ERR) { \
+ int __x, __y; \
+ getyx(stdscr, __y, __x); \
+ msgq(sp, M_ERR, "Error: %s/%d: addnstr: (%d/%u).", \
+ tail(__FILE__), __LINE__, __y, __x); \
+ return (1); \
+ } \
+}
+
+/* Add a string. */
+#define ADDSTR(str) { \
+ if (addstr(str) == ERR) { \
+ int __x, __y; \
+ getyx(stdscr, __y, __x); \
+ msgq(sp, M_ERR, "Error: %s/%d: addstr: (%d/%u).", \
+ tail(__FILE__), __LINE__, __y, __x); \
+ return (1); \
+ } \
+}
+
+/* Public routines. */
+void svi_bell __P((SCR *));
+int svi_bg __P((SCR *));
+int svi_busy __P((SCR *, char const *));
+int svi_change __P((SCR *, EXF *, recno_t, enum operation));
+size_t svi_chposition __P((SCR *, EXF *, recno_t, size_t));
+int svi_column __P((SCR *, EXF *, size_t *));
+enum confirm
+ svi_confirm __P((SCR *, EXF *, MARK *, MARK *));
+int svi_clear __P((SCR *));
+int svi_ex_cmd __P((SCR *, EXF *, struct _excmdarg *, MARK *));
+int svi_ex_run __P((SCR *, EXF *, MARK *));
+int svi_ex_write __P((void *, const char *, int));
+int svi_fg __P((SCR *, CHAR_T *));
+enum input
+ svi_get __P((SCR *, EXF *, TEXTH *, int, u_int));
+int svi_optchange __P((SCR *, int));
+int svi_rabs __P((SCR *, long));
+int svi_refresh __P((SCR *, EXF *));
+size_t svi_relative __P((SCR *, EXF *, recno_t));
+int svi_rrel __P((SCR *, long));
+int svi_screen_copy __P((SCR *, SCR *));
+int svi_screen_edit __P((SCR *, EXF *));
+int svi_screen_end __P((SCR *));
+int svi_sm_down __P((SCR *, EXF *, MARK *, recno_t, int));
+int svi_sm_fill __P((SCR *, EXF *, recno_t, enum position));
+int svi_sm_position __P((SCR *, EXF *, MARK *, u_long, enum position));
+int svi_sm_up __P((SCR *, EXF *, MARK *, recno_t, int));
+int svi_split __P((SCR *, ARGS *[]));
+int svi_suspend __P((SCR *));
+int svi_swap __P((SCR *, SCR **, char *));
+
+/* Private routines. */
+int svi_curses_end __P((SCR *));
+int svi_curses_init __P((SCR *));
+int svi_divider __P((SCR *));
+int svi_init __P((SCR *));
+int svi_join __P((SCR *, SCR **));
+int svi_line __P((SCR *, EXF *, SMAP *, size_t *, size_t *));
+size_t svi_lrelative __P((SCR *, EXF *, recno_t, size_t));
+size_t svi_ncols __P((SCR *, u_char *, size_t, size_t *));
+int svi_number __P((SCR *, EXF *));
+int svi_paint __P((SCR *, EXF *));
+size_t svi_screens __P((SCR *, EXF *, recno_t, size_t *));
+int svi_sm_1down __P((SCR *, EXF *));
+int svi_sm_1up __P((SCR *, EXF *));
+int svi_sm_cursor __P((SCR *, EXF *, SMAP **));
+int svi_sm_next __P((SCR *, EXF *, SMAP *, SMAP *));
+recno_t svi_sm_nlines __P((SCR *, EXF *, SMAP *, recno_t, size_t));
+int svi_sm_prev __P((SCR *, EXF *, SMAP *, SMAP *));
+
+/* Private debugging routines. */
+#ifdef DEBUG
+int svi_gdbrefresh __P((void));
+#endif
diff --git a/usr.bin/vi/svi/svi_smap.c b/usr.bin/vi/svi/svi_smap.c
new file mode 100644
index 000000000000..5bcb6e220f3d
--- /dev/null
+++ b/usr.bin/vi/svi/svi_smap.c
@@ -0,0 +1,1041 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)svi_smap.c 8.29 (Berkeley) 11/30/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <curses.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "vcmd.h"
+#include "svi_screen.h"
+
+static int svi_deleteln __P((SCR *, int));
+static int svi_insertln __P((SCR *, int));
+static int svi_sm_delete __P((SCR *, EXF *, recno_t));
+static int svi_sm_insert __P((SCR *, EXF *, recno_t));
+static int svi_sm_reset __P((SCR *, EXF *, recno_t));
+
+/*
+ * svi_change --
+ * Make a change to the screen.
+ */
+int
+svi_change(sp, ep, lno, op)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ enum operation op;
+{
+ SMAP *p;
+ size_t oldy, oldx;
+
+ /* Appending is the same as inserting, if the line is incremented. */
+ if (op == LINE_APPEND) {
+ ++lno;
+ op = LINE_INSERT;
+ }
+
+ /* Ignore the change if the line is after the map. */
+ if (lno > TMAP->lno)
+ return (0);
+
+ /*
+ * If the line is before the map, and it's a decrement, decrement
+ * the map. If it's an increment, increment the map. Otherwise,
+ * ignore it.
+ */
+ if (lno < HMAP->lno) {
+ switch (op) {
+ case LINE_APPEND:
+ abort();
+ /* NOTREACHED */
+ case LINE_DELETE:
+ for (p = HMAP; p <= TMAP; ++p)
+ --p->lno;
+ if (sp->lno >= lno)
+ --sp->lno;
+ F_SET(sp, S_RENUMBER);
+ break;
+ case LINE_INSERT:
+ for (p = HMAP; p <= TMAP; ++p)
+ ++p->lno;
+ if (sp->lno >= lno)
+ ++sp->lno;
+ F_SET(sp, S_RENUMBER);
+ break;
+ case LINE_RESET:
+ break;
+ }
+ return (0);
+ }
+
+ F_SET(SVP(sp), SVI_SCREENDIRTY);
+
+ /* Flush cached information from svi_screens(). */
+ SVP(sp)->ss_lno = OOBLNO;
+
+ /* Invalidate the cursor, if it's on this line. */
+ if (sp->lno == lno)
+ F_SET(SVP(sp), SVI_CUR_INVALID);
+
+ getyx(stdscr, oldy, oldx);
+
+ switch (op) {
+ case LINE_DELETE:
+ if (svi_sm_delete(sp, ep, lno))
+ return (1);
+ F_SET(sp, S_RENUMBER);
+ break;
+ case LINE_INSERT:
+ if (svi_sm_insert(sp, ep, lno))
+ return (1);
+ F_SET(sp, S_RENUMBER);
+ break;
+ case LINE_RESET:
+ if (svi_sm_reset(sp, ep, lno))
+ return (1);
+ break;
+ default:
+ abort();
+ }
+
+ MOVEA(sp, oldy, oldx);
+
+ return (0);
+}
+
+/*
+ * svi_sm_fill --
+ * Fill in the screen map, placing the specified line at the
+ * right position. There isn't any way to tell if an SMAP
+ * entry has been filled in, so this routine had better be
+ * called with P_FILL set before anything else is done.
+ *
+ * !!!
+ * Unexported interface: if lno is OOBLNO, P_TOP means that the HMAP
+ * slot is already filled in, P_BOTTOM means that the TMAP slot is
+ * already filled in, and we just finish up the job.
+ */
+int
+svi_sm_fill(sp, ep, lno, pos)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ enum position pos;
+{
+ SMAP *p, tmp;
+
+ /* Flush all cached information from the SMAP. */
+ for (p = HMAP; p <= TMAP; ++p)
+ SMAP_FLUSH(p);
+
+ switch (pos) {
+ case P_FILL:
+ tmp.lno = 1;
+ tmp.off = 1;
+
+ /* See if less than half a screen from the top. */
+ if (svi_sm_nlines(sp, ep,
+ &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
+ lno = 1;
+ goto top;
+ }
+
+ /* See if less than half a screen from the bottom. */
+ if (file_lline(sp, ep, &tmp.lno))
+ return (1);
+ tmp.off = svi_screens(sp, ep, tmp.lno, NULL);
+ if (svi_sm_nlines(sp, ep,
+ &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
+ TMAP->lno = tmp.lno;
+ TMAP->off = tmp.off;
+ goto bottom;
+ }
+ goto middle;
+ case P_TOP:
+ if (lno != OOBLNO) {
+top: HMAP->lno = lno;
+ HMAP->off = 1;
+ }
+ /* If we fail, just punt. */
+ for (p = HMAP; p < TMAP; ++p)
+ if (svi_sm_next(sp, ep, p, p + 1))
+ goto err;
+ break;
+ case P_MIDDLE:
+ /* If we fail, guess that the file is too small. */
+middle: p = HMAP + (TMAP - HMAP) / 2;
+ for (p->lno = lno, p->off = 1; p > HMAP; --p)
+ if (svi_sm_prev(sp, ep, p, p - 1)) {
+ lno = 1;
+ goto top;
+ }
+
+ /* If we fail, just punt. */
+ p = HMAP + (TMAP - HMAP) / 2;
+ for (; p < TMAP; ++p)
+ if (svi_sm_next(sp, ep, p, p + 1))
+ goto err;
+ break;
+ case P_BOTTOM:
+ if (lno != OOBLNO) {
+ TMAP->lno = lno;
+ TMAP->off = svi_screens(sp, ep, lno, NULL);
+ }
+ /* If we fail, guess that the file is too small. */
+bottom: for (p = TMAP; p > HMAP; --p)
+ if (svi_sm_prev(sp, ep, p, p - 1)) {
+ lno = 1;
+ goto top;
+ }
+ break;
+ }
+ return (0);
+
+ /*
+ * Try and put *something* on the screen. If this fails,
+ * we have a serious hard error.
+ */
+err: HMAP->lno = 1;
+ HMAP->off = 1;
+ for (p = HMAP; p < TMAP; ++p)
+ if (svi_sm_next(sp, ep, p, p + 1))
+ return (1);
+ return (0);
+}
+
+/*
+ * For the routines svi_sm_reset, svi_sm_delete and svi_sm_insert: if the
+ * screen only contains one line, or, if the line is the entire screen, this
+ * gets fairly exciting. Skip the fun and simply return if there's only one
+ * line in the screen, or just call fill. Fill may not be entirely accurate,
+ * i.e. we may be painting the screen with something not even close to the
+ * cursor, but it's not like we're into serious performance issues here, and
+ * the refresh routine will fix it for us.
+ */
+#define TOO_WEIRD { \
+ if (cnt_orig >= sp->t_rows) { \
+ if (cnt_orig == 1) \
+ return (0); \
+ if (file_gline(sp, ep, lno, NULL) == NULL) \
+ if (file_lline(sp, ep, &lno)) \
+ return (1); \
+ F_SET(sp, S_REDRAW); \
+ return (svi_sm_fill(sp, ep, lno, P_TOP)); \
+ } \
+}
+
+/*
+ * svi_sm_delete --
+ * Delete a line out of the SMAP.
+ */
+static int
+svi_sm_delete(sp, ep, lno)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+{
+ SMAP *p, *t;
+ size_t cnt_orig;
+
+ /*
+ * Find the line in the map, and count the number of screen lines
+ * which display any part of the deleted line.
+ */
+ for (p = HMAP; p->lno != lno; ++p);
+ for (cnt_orig = 1, t = p + 1;
+ t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
+
+ TOO_WEIRD;
+
+ /* Delete that many lines from the screen. */
+ MOVE(sp, p - HMAP, 0);
+ if (svi_deleteln(sp, cnt_orig))
+ return (1);
+
+ /* Shift the screen map up. */
+ memmove(p, p + cnt_orig, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP));
+
+ /* Decrement the line numbers for the rest of the map. */
+ for (t = TMAP - cnt_orig; p <= t; ++p)
+ --p->lno;
+
+ /* Display the new lines. */
+ for (p = TMAP - cnt_orig;;) {
+ if (p < TMAP && svi_sm_next(sp, ep, p, p + 1))
+ return (1);
+ /* svi_sm_next() flushed the cache. */
+ if (svi_line(sp, ep, ++p, NULL, NULL))
+ return (1);
+ if (p == TMAP)
+ break;
+ }
+ return (0);
+}
+
+/*
+ * svi_sm_insert --
+ * Insert a line into the SMAP.
+ */
+static int
+svi_sm_insert(sp, ep, lno)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+{
+ SMAP *p, *t;
+ size_t cnt_orig, cnt;
+
+ /*
+ * Find the line in the map, find out how many screen lines
+ * needed to display the line.
+ */
+ for (p = HMAP; p->lno != lno; ++p);
+ cnt_orig = svi_screens(sp, ep, lno, NULL);
+
+ TOO_WEIRD;
+
+ /*
+ * The lines left in the screen override the number of screen
+ * lines in the inserted line.
+ */
+ cnt = (TMAP - p) + 1;
+ if (cnt_orig > cnt)
+ cnt_orig = cnt;
+
+ /* Push down that many lines. */
+ MOVE(sp, p - HMAP, 0);
+ if (svi_insertln(sp, cnt_orig))
+ return (1);
+
+ /* Shift the screen map down. */
+ memmove(p + cnt_orig, p, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP));
+
+ /* Increment the line numbers for the rest of the map. */
+ for (t = p + cnt_orig; t <= TMAP; ++t)
+ ++t->lno;
+
+ /* Fill in the SMAP for the new lines, and display. */
+ for (cnt = 1, t = p; cnt <= cnt_orig; ++t, ++cnt) {
+ t->lno = lno;
+ t->off = cnt;
+ SMAP_FLUSH(t);
+ if (svi_line(sp, ep, t, NULL, NULL))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * svi_sm_reset --
+ * Reset a line in the SMAP.
+ */
+static int
+svi_sm_reset(sp, ep, lno)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+{
+ SMAP *p, *t;
+ size_t cnt_orig, cnt_new, cnt, diff;
+
+ /*
+ * See if the number of on-screen rows taken up by the old display
+ * for the line is the same as the number needed for the new one.
+ * If so, repaint, otherwise do it the hard way.
+ */
+ for (p = HMAP; p->lno != lno; ++p);
+ for (cnt_orig = 0, t = p; t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
+ cnt_new = svi_screens(sp, ep, lno, NULL);
+
+ TOO_WEIRD;
+
+ if (cnt_orig == cnt_new) {
+ do {
+ SMAP_FLUSH(p);
+ if (svi_line(sp, ep, p, NULL, NULL))
+ return (1);
+ } while (++p < t);
+ return (0);
+ }
+
+ if (cnt_orig < cnt_new) {
+ /* Get the difference. */
+ diff = cnt_new - cnt_orig;
+
+ /*
+ * The lines left in the screen override the number of screen
+ * lines in the inserted line.
+ */
+ cnt = (TMAP - p) + 1;
+ if (diff > cnt)
+ diff = cnt;
+
+ /* Push down the extra lines. */
+ MOVE(sp, p - HMAP, 0);
+ if (svi_insertln(sp, diff))
+ return (1);
+
+ /* Shift the screen map down. */
+ memmove(p + diff, p, (((TMAP - p) - diff) + 1) * sizeof(SMAP));
+
+ /* Fill in the SMAP for the replaced line, and display. */
+ for (cnt = 1, t = p; cnt_new-- && t <= TMAP; ++t, ++cnt) {
+ t->lno = lno;
+ t->off = cnt;
+ SMAP_FLUSH(t);
+ if (svi_line(sp, ep, t, NULL, NULL))
+ return (1);
+ }
+ } else {
+ /* Get the difference. */
+ diff = cnt_orig - cnt_new;
+
+ /* Delete that many lines from the screen. */
+ MOVE(sp, p - HMAP, 0);
+ if (svi_deleteln(sp, diff))
+ return (1);
+
+ /* Shift the screen map up. */
+ memmove(p, p + diff, (((TMAP - p) - diff) + 1) * sizeof(SMAP));
+
+ /* Fill in the SMAP for the replaced line, and display. */
+ for (cnt = 1, t = p; cnt_new--; ++t, ++cnt) {
+ t->lno = lno;
+ t->off = cnt;
+ SMAP_FLUSH(t);
+ if (svi_line(sp, ep, t, NULL, NULL))
+ return (1);
+ }
+
+ /* Display the new lines at the bottom of the screen. */
+ for (t = TMAP - diff;;) {
+ if (t < TMAP && svi_sm_next(sp, ep, t, t + 1))
+ return (1);
+ /* svi_sm_next() flushed the cache. */
+ if (svi_line(sp, ep, ++t, NULL, NULL))
+ return (1);
+ if (t == TMAP)
+ break;
+ }
+ }
+ return (0);
+}
+
+/*
+ * svi_sm_up --
+ * Scroll the SMAP up count logical lines.
+ */
+int
+svi_sm_up(sp, ep, rp, count, cursor_move)
+ SCR *sp;
+ EXF *ep;
+ MARK *rp;
+ recno_t count;
+ int cursor_move;
+{
+ SMAP *p, svmap, tmp;
+ int ignore_cursor;
+
+ /* Set the default return position. */
+ rp->lno = sp->lno;
+ rp->cno = sp->cno;
+
+ /*
+ * Invalidate the cursor. The line is probably going to change,
+ * but if cursor_move isn't set it may not. In any case, this
+ * routine moves the cursor to draw things.
+ */
+ F_SET(SVP(sp), SVI_CUR_INVALID);
+
+ /*
+ * There are two forms of this command, one where the cursor tries to
+ * follow the line, and one where it doesn't. In the latter, we try
+ * and keep the cursor at the same position on the screen, but, if the
+ * screen is small enough and the line length large enough, the cursor
+ * can end up in very strange places. Probably not worth fixing.
+ *
+ * Find the line in the SMAP -- ignore the cursor if it wasn't on the
+ * screen.
+ */
+ if (svi_sm_cursor(sp, ep, &p))
+ return (1);
+ if (p == NULL)
+ ignore_cursor = 1;
+ else {
+ svmap = *p;
+ ignore_cursor = 0;
+ }
+
+ /*
+ * Check to see if movement is possible. Lots of checks...
+ *
+ * Find out if it's possible to move past the end of the map. If
+ * that's okay because we think that we can move the cursor down
+ * in the map, check to make sure that the map isn't mostly empty.
+ */
+ if (svi_sm_next(sp, ep, TMAP, &tmp))
+ return (1);
+ if (tmp.lno > TMAP->lno &&
+ !file_gline(sp, ep, tmp.lno, NULL) ||
+ tmp.off > svi_screens(sp, ep, tmp.lno, NULL)) {
+ if (!cursor_move || ignore_cursor || p == TMAP) {
+ v_eof(sp, ep, NULL);
+ return (1);
+ }
+ if (svi_sm_next(sp, ep, p, &tmp))
+ return (1);
+ if (!file_gline(sp, ep, tmp.lno, NULL) ||
+ tmp.off > svi_screens(sp, ep, tmp.lno, NULL)) {
+ v_eof(sp, ep, NULL);
+ return (1);
+ }
+ }
+
+ /*
+ * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
+ *
+ * If it's a small screen, and the movement is small, open up the
+ * screen. Otherwise, compress and repaint. If we compress, we
+ * ignore the cursor, the movement is too large to care.
+ */
+ if (ISSMALLSCREEN(sp))
+ if (count <= HALFTEXT(sp)) {
+ for (; count && sp->t_rows != sp->t_maxrows;
+ --count, ++sp->t_rows) {
+ if (svi_sm_next(sp, ep, TMAP, &tmp))
+ return (1);
+ if (TMAP->lno != tmp.lno &&
+ !file_gline(sp, ep, tmp.lno, NULL))
+ break;
+ *++TMAP = tmp;
+ /* svi_sm_next() flushed the cache. */
+ if (svi_line(sp, ep, TMAP, NULL, NULL))
+ return (1);
+ }
+ if (count == 0)
+ return (0);
+ } else {
+ MOVE(sp, INFOLINE(sp), 0);
+ clrtoeol();
+ for (;
+ sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) {
+ MOVE(sp, TMAP - HMAP, 0);
+ clrtoeol();
+ }
+ ignore_cursor = 1;
+ }
+
+ for (; count; --count) {
+ /* Decide what would show up on the screen. */
+ if (svi_sm_next(sp, ep, TMAP, &tmp))
+ return (1);
+
+ /* If the line doesn't exist, we're done. */
+ if (TMAP->lno != tmp.lno && !file_gline(sp, ep, tmp.lno, NULL))
+ break;
+
+ /* Scroll the screen cursor up one logical line. */
+ if (svi_sm_1up(sp, ep))
+ return (1);
+ if (!cursor_move && !ignore_cursor && p > HMAP)
+ --p;
+ }
+
+ /* If ignoring the cursor, we're done. */
+ if (ignore_cursor)
+ return (0);
+
+ if (cursor_move) {
+ /*
+ * If we didn't move enough, head toward EOF. Check to make
+ * sure the lines actually, if the file is smaller than the
+ * screen they may not.
+ */
+ for (; count; --count, ++p)
+ if (p == TMAP || !file_gline(sp, ep, p[1].lno, NULL))
+ break;
+ } else {
+ /*
+ * If the line itself moved, invalidate the cursor, because
+ * the comparison with the old line/new line won't be right
+ */
+ F_SET(SVP(sp), SVI_CUR_INVALID);
+
+ /* If didn't move enough, it's an error. */
+ if (count) {
+ v_eof(sp, ep, NULL);
+ return (1);
+ }
+
+ /* If the cursor moved off the screen, move it to the top. */
+ if (sp->lno < HMAP->lno)
+ p = HMAP;
+ }
+ /*
+ * On a logical movement, we try and keep the cursor as close as
+ * possible to the last position, but also set it up so that the
+ * next "real" movement will return the cursor to the closest position
+ * to the last real movement.
+ */
+ if (p->lno != svmap.lno || p->off != svmap.off) {
+ rp->lno = p->lno;
+ rp->cno = svi_lrelative(sp, ep, p->lno, p->off);
+ }
+ return (0);
+}
+
+/*
+ * svi_sm_1up --
+ * Scroll the SMAP up one.
+ */
+int
+svi_sm_1up(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ /*
+ * Delete the top line of the screen. Shift the screen map up.
+ * Display a new line at the bottom of the screen.
+ */
+ MOVE(sp, 0, 0);
+ if (svi_deleteln(sp, 1))
+ return (1);
+
+ /* One-line screens can fail. */
+ if (HMAP == TMAP) {
+ if (svi_sm_next(sp, ep, TMAP, TMAP))
+ return (1);
+ } else {
+ memmove(HMAP, HMAP + 1, (sp->rows - 1) * sizeof(SMAP));
+ if (svi_sm_next(sp, ep, TMAP - 1, TMAP))
+ return (1);
+ }
+ /* svi_sm_next() flushed the cache. */
+ if (svi_line(sp, ep, TMAP, NULL, NULL))
+ return (1);
+ return (0);
+}
+
+/*
+ * svi_deleteln --
+ * Delete a line a la curses, make sure to put the information
+ * line and other screens back.
+ */
+static int
+svi_deleteln(sp, cnt)
+ SCR *sp;
+ int cnt;
+{
+ size_t oldy, oldx;
+
+ getyx(stdscr, oldy, oldx);
+ while (cnt--) {
+ deleteln();
+ MOVE(sp, INFOLINE(sp) - 1, 0);
+ insertln();
+ MOVEA(sp, oldy, oldx);
+ }
+ return (0);
+}
+
+/*
+ * svi_sm_down --
+ * Scroll the SMAP down count logical lines.
+ */
+int
+svi_sm_down(sp, ep, rp, count, cursor_move)
+ SCR *sp;
+ EXF *ep;
+ MARK *rp;
+ recno_t count;
+ int cursor_move;
+{
+ SMAP *p, svmap;
+ int ignore_cursor;
+
+ /* Set the default return position. */
+ rp->lno = sp->lno;
+ rp->cno = sp->cno;
+
+ /*
+ * Invalidate the cursor. The line is probably going to change,
+ * but if cursor_move isn't set it may not. In any case, this
+ * routine moves the cursor to draw things.
+ */
+ F_SET(SVP(sp), SVI_CUR_INVALID);
+
+ /*
+ * There are two forms of this command, one where the cursor tries to
+ * follow the line, and one where it doesn't. In the latter, we try
+ * and keep the cursor at the same position on the screen, but, if the
+ * screen is small enough and the line length large enough, the cursor
+ * can end up in very strange places. Probably not worth fixing.
+ *
+ * Find the line in the SMAP -- ignore the cursor if it wasn't on the
+ * screen.
+ */
+ if (svi_sm_cursor(sp, ep, &p))
+ return (1);
+ if (p == NULL)
+ ignore_cursor = 1;
+ else {
+ svmap = *p;
+ ignore_cursor = 0;
+ }
+
+ /* Check to see if movement is possible. */
+ if (HMAP->lno == 1 && HMAP->off == 1 &&
+ (!cursor_move || ignore_cursor || p == HMAP)) {
+ v_sof(sp, NULL);
+ return (1);
+ }
+
+ /*
+ * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
+ *
+ * If it's a small screen, and the movement is small, open up the
+ * screen. Otherwise, compress and repaint. If we compress, we
+ * ignore the cursor, the movement is too large to care.
+ */
+ if (ISSMALLSCREEN(sp))
+ if (count <= HALFTEXT(sp)) {
+ for (; count && sp->t_rows != sp->t_maxrows &&
+ (HMAP->lno > 1 || HMAP->off > 1);
+ --count, ++sp->t_rows) {
+ ++TMAP;
+ if (svi_sm_1down(sp, ep))
+ return (1);
+ if (!cursor_move)
+ ++p;
+ }
+ if (count == 0)
+ return (0);
+ } else {
+ MOVE(sp, INFOLINE(sp), 0);
+ clrtoeol();
+ for (;
+ sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) {
+ MOVE(sp, TMAP - HMAP, 0);
+ clrtoeol();
+ }
+ ignore_cursor = 1;
+ }
+
+ for (; count; --count) {
+ /* If the line doesn't exist, we're done. */
+ if (HMAP->lno == 1 && HMAP->off == 1)
+ break;
+
+ /* Scroll the screen and cursor down one logical line. */
+ if (svi_sm_1down(sp, ep))
+ return (1);
+ if (!cursor_move && !ignore_cursor && p < TMAP)
+ ++p;
+ }
+
+ /* If ignoring the cursor, we're done. */
+ if (ignore_cursor)
+ return (0);
+
+ if (cursor_move) {
+ /* If we didn't move enough, move to SOF. */
+ if (count)
+ p = HMAP;
+ } else {
+ /*
+ * If the line itself moved, invalidate the cursor, because
+ * the comparison with the old line/new line won't be right.
+ */
+ F_SET(SVP(sp), SVI_CUR_INVALID);
+
+ /* If didn't move enough, it's an error. */
+ if (count) {
+ v_sof(sp, NULL);
+ return (1);
+ }
+
+ /* If the cursor moved off the screen, move it to the bottom. */
+ if (sp->lno > TMAP->lno)
+ p = TMAP;
+ }
+
+ /*
+ * On a logical movement, we try and keep the cursor as close as
+ * possible to the last position, but also set it up so that the
+ * next "real" movement will return the cursor to the closest position
+ * to the last real movement.
+ */
+ if (p->lno != svmap.lno || p->off != svmap.off) {
+ rp->lno = p->lno;
+ rp->cno = svi_lrelative(sp, ep, p->lno, p->off);
+ }
+ return (0);
+}
+
+/*
+ * svi_sm_1down --
+ * Scroll the SMAP down one.
+ */
+int
+svi_sm_1down(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ /*
+ * Clear the bottom line of the screen, insert a line at the top
+ * of the screen. Shift the screen map down, display a new line
+ * at the top of the screen.
+ */
+ MOVE(sp, sp->t_rows, 0);
+ clrtoeol();
+ MOVE(sp, 0, 0);
+ if (svi_insertln(sp, 1))
+ return (1);
+ memmove(HMAP + 1, HMAP, (sp->rows - 1) * sizeof(SMAP));
+ if (svi_sm_prev(sp, ep, HMAP + 1, HMAP))
+ return (1);
+ /* svi_sm_prev() flushed the cache. */
+ if (svi_line(sp, ep, HMAP, NULL, NULL))
+ return (1);
+ return (0);
+}
+
+/*
+ * svi_insertln --
+ * Insert a line a la curses, make sure to put the information
+ * line and other screens back.
+ */
+static int
+svi_insertln(sp, cnt)
+ SCR *sp;
+ int cnt;
+{
+ size_t oldy, oldx;
+
+ getyx(stdscr, oldy, oldx);
+ while (cnt--) {
+ MOVE(sp, INFOLINE(sp) - 1, 0);
+ deleteln();
+ MOVEA(sp, oldy, oldx);
+ insertln();
+ }
+ return (0);
+}
+
+/*
+ * svi_sm_next --
+ * Fill in the next entry in the SMAP.
+ */
+int
+svi_sm_next(sp, ep, p, t)
+ SCR *sp;
+ EXF *ep;
+ SMAP *p, *t;
+{
+ size_t lcnt;
+
+ SMAP_FLUSH(t);
+ if (O_ISSET(sp, O_LEFTRIGHT)) {
+ t->lno = p->lno + 1;
+ t->off = p->off;
+ } else {
+ lcnt = svi_screens(sp, ep, p->lno, NULL);
+ if (lcnt == p->off) {
+ t->lno = p->lno + 1;
+ t->off = 1;
+ } else {
+ t->lno = p->lno;
+ t->off = p->off + 1;
+ }
+ }
+ return (0);
+}
+
+/*
+ * svi_sm_prev --
+ * Fill in the previous entry in the SMAP.
+ */
+int
+svi_sm_prev(sp, ep, p, t)
+ SCR *sp;
+ EXF *ep;
+ SMAP *p, *t;
+{
+ SMAP_FLUSH(t);
+ if (O_ISSET(sp, O_LEFTRIGHT)) {
+ t->lno = p->lno - 1;
+ t->off = p->off;
+ } else if (p->off != 1) {
+ t->lno = p->lno;
+ t->off = p->off - 1;
+ } else {
+ t->lno = p->lno - 1;
+ t->off = svi_screens(sp, ep, t->lno, NULL);
+ }
+ return (t->lno == 0);
+}
+
+/*
+ * svi_sm_cursor --
+ * Return the SMAP entry referenced by the cursor.
+ */
+int
+svi_sm_cursor(sp, ep, smp)
+ SCR *sp;
+ EXF *ep;
+ SMAP **smp;
+{
+ SMAP *p;
+
+ /* See if the cursor is not in the map. */
+ if (sp->lno < HMAP->lno || sp->lno > TMAP->lno) {
+ *smp = NULL;
+ return (0);
+ }
+
+ /* Find the first occurence of the line. */
+ for (p = HMAP; p->lno != sp->lno; ++p);
+
+ /* Fill in the map information until we find the right line. */
+ for (; p <= TMAP; ++p) {
+ /* Short lines are common and easy to detect. */
+ if (p != TMAP && (p + 1)->lno != p->lno) {
+ *smp = p;
+ return (0);
+ }
+ if (!SMAP_CACHE(p) && svi_line(sp, ep, p, NULL, NULL))
+ return (1);
+ if (p->c_eboff >= sp->cno) {
+ *smp = p;
+ return (0);
+ }
+ }
+
+ /* It was past the end of the map after all. */
+ *smp = NULL;
+ return (0);
+}
+
+/*
+ * svi_sm_position --
+ * Return the line/column of the top, middle or last line on the screen.
+ * (The vi H, M and L commands.) Here because only the screen routines
+ * know what's really out there.
+ */
+int
+svi_sm_position(sp, ep, rp, cnt, pos)
+ SCR *sp;
+ EXF *ep;
+ MARK *rp;
+ u_long cnt;
+ enum position pos;
+{
+ SMAP *smp;
+ recno_t last;
+
+ switch (pos) {
+ case P_TOP:
+ if (cnt > TMAP - HMAP)
+ goto err;
+ smp = HMAP + cnt;
+ break;
+ case P_MIDDLE:
+ if (cnt > (TMAP - HMAP) / 2)
+ goto err;
+ smp = (HMAP + (TMAP - HMAP) / 2) + cnt;
+ goto eof;
+ case P_BOTTOM:
+ if (cnt > TMAP - HMAP) {
+err: msgq(sp, M_BERR, "Movement past the end-of-screen.");
+ return (1);
+ }
+ smp = TMAP - cnt;
+eof: if (file_gline(sp, ep, smp->lno, NULL) == NULL) {
+ if (file_lline(sp, ep, &last))
+ return (1);
+ for (; smp->lno > last && smp > HMAP; --smp);
+ }
+ break;
+ default:
+ abort();
+ }
+
+ if (!SMAP_CACHE(smp) && svi_line(sp, ep, smp, NULL, NULL))
+ return (1);
+ rp->lno = smp->lno;
+ rp->cno = smp->c_sboff;
+
+ return (0);
+}
+
+/*
+ * svi_sm_nlines --
+ * Return the number of screen lines from an SMAP entry to the
+ * start of some file line, less than a maximum value.
+ */
+recno_t
+svi_sm_nlines(sp, ep, from_sp, to_lno, max)
+ SCR *sp;
+ EXF *ep;
+ SMAP *from_sp;
+ recno_t to_lno;
+ size_t max;
+{
+ recno_t lno, lcnt;
+
+ if (O_ISSET(sp, O_LEFTRIGHT))
+ return (from_sp->lno > to_lno ?
+ from_sp->lno - to_lno : to_lno - from_sp->lno);
+
+ if (from_sp->lno == to_lno)
+ return (from_sp->off - 1);
+
+ if (from_sp->lno > to_lno) {
+ lcnt = from_sp->off - 1; /* Correct for off-by-one. */
+ for (lno = from_sp->lno; --lno >= to_lno && lcnt <= max;)
+ lcnt += svi_screens(sp, ep, lno, NULL);
+ } else {
+ lno = from_sp->lno;
+ lcnt = (svi_screens(sp, ep, lno, NULL) - from_sp->off) + 1;
+ for (; ++lno < to_lno && lcnt <= max;)
+ lcnt += svi_screens(sp, ep, lno, NULL);
+ }
+ return (lcnt);
+}
diff --git a/usr.bin/vi/svi/svi_split.c b/usr.bin/vi/svi/svi_split.c
new file mode 100644
index 000000000000..f6be03d38365
--- /dev/null
+++ b/usr.bin/vi/svi/svi_split.c
@@ -0,0 +1,581 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)svi_split.c 8.29 (Berkeley) 12/22/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <curses.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "svi_screen.h"
+
+/*
+ * svi_split --
+ * Split the screen.
+ */
+int
+svi_split(sp, argv)
+ SCR *sp;
+ ARGS *argv[];
+{
+ MSG *mp, *next;
+ SCR *tsp, saved_sp;
+ SVI_PRIVATE saved_svp;
+ SMAP *smp;
+ size_t cnt, half;
+ int issmallscreen, splitup;
+
+ /* Check to see if it's possible. */
+ half = sp->rows / 2;
+ if (half < MINIMUM_SCREEN_ROWS) {
+ msgq(sp, M_ERR, "Screen must be larger than %d to split",
+ MINIMUM_SCREEN_ROWS);
+ return (1);
+ }
+
+ /* Get a new screen. */
+ if (screen_init(sp, &tsp, 0))
+ return (1);
+ MALLOC(sp, _HMAP(tsp), SMAP *, SIZE_HMAP(sp) * sizeof(SMAP));
+ if (_HMAP(tsp) == NULL)
+ return (1);
+
+ /*
+ * We're about to modify the current screen. Save the contents
+ * in case something goes horribly, senselessly wrong.
+ */
+ saved_sp = *sp;
+ saved_svp = *SVP(sp);
+
+/* INITIALIZED AT SCREEN CREATE. */
+
+/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
+ /*
+ * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
+ * Set a flag so we know to fix the screen up later.
+ */
+ issmallscreen = ISSMALLSCREEN(sp);
+
+ /*
+ * Split the screen, and link the screens together. If the cursor
+ * is in the top half of the current screen, the new screen goes
+ * under the current screen. Else, it goes above the current screen.
+ *
+ * The columns in the screen don't change.
+ */
+ tsp->cols = sp->cols;
+
+ cnt = svi_sm_cursor(sp, sp->ep, &smp) ? 0 : smp - HMAP;
+ if (cnt <= half) { /* Parent is top half. */
+ /* Child. */
+ tsp->rows = sp->rows - half;
+ tsp->woff = sp->woff + half;
+ tsp->t_maxrows = tsp->rows - 1;
+
+ /* Parent. */
+ sp->rows = half;
+ sp->t_maxrows = sp->rows - 1;
+
+ splitup = 0;
+ } else { /* Parent is bottom half. */
+ /* Child. */
+ tsp->rows = sp->rows - half;
+ tsp->woff = sp->woff;
+ tsp->t_maxrows = tsp->rows - 1;
+
+ /* Parent. */
+ sp->rows = half;
+ sp->woff += tsp->rows;
+ sp->t_maxrows = sp->rows - 1;
+
+ /* Shift the parent's map down. */
+ memmove(_HMAP(sp),
+ _HMAP(sp) + tsp->rows, sp->t_maxrows * sizeof(SMAP));
+
+ splitup = 1;
+ }
+
+ /*
+ * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
+ *
+ * The child may have different screen options sizes than the
+ * parent, so use them. Make sure that the text counts aren't
+ * larger than the new screen sizes.
+ */
+ if (issmallscreen) {
+ /* Fix the text line count for the parent. */
+ if (splitup)
+ sp->t_rows -= tsp->rows;
+
+ /* Fix the parent screen. */
+ if (sp->t_rows > sp->t_maxrows)
+ sp->t_rows = sp->t_maxrows;
+ if (sp->t_minrows > sp->t_maxrows)
+ sp->t_minrows = sp->t_maxrows;
+
+ /* Fix the child screen. */
+ tsp->t_minrows = tsp->t_rows = O_VAL(sp, O_WINDOW);
+ if (tsp->t_rows > tsp->t_maxrows)
+ tsp->t_rows = tsp->t_maxrows;
+ if (tsp->t_minrows > tsp->t_maxrows)
+ tsp->t_minrows = tsp->t_maxrows;
+
+ /*
+ * If we split up, i.e. the child is on top, lines that
+ * were painted in the parent may not be painted in the
+ * child. Clear any lines not being used in the child
+ * screen.
+ *
+ */
+ if (splitup)
+ for (cnt = tsp->t_rows; ++cnt <= tsp->t_maxrows;) {
+ MOVE(tsp, cnt, 0)
+ clrtoeol();
+ }
+ } else {
+ sp->t_minrows = sp->t_rows = sp->rows - 1;
+
+ /*
+ * The new screen may be a small screen, even though the
+ * parent was not. Don't complain if O_WINDOW is too large,
+ * we're splitting the screen so the screen is much smaller
+ * than normal. Clear any lines not being used in the child
+ * screen.
+ */
+ tsp->t_minrows = tsp->t_rows = O_VAL(sp, O_WINDOW);
+ if (tsp->t_rows > tsp->rows - 1)
+ tsp->t_minrows = tsp->t_rows = tsp->rows - 1;
+ else
+ for (cnt = tsp->t_rows; ++cnt <= tsp->t_maxrows;) {
+ MOVE(tsp, cnt, 0)
+ clrtoeol();
+ }
+ }
+
+ /* Adjust the ends of both maps. */
+ _TMAP(sp) = _HMAP(sp) + (sp->t_rows - 1);
+ _TMAP(tsp) = _HMAP(tsp) + (tsp->t_rows - 1);
+
+ /*
+ * In any case, if the size of the scrolling region hasn't been
+ * modified by the user, reset it so it's reasonable for the split
+ * screen.
+ */
+ if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET)) {
+ O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2;
+ O_VAL(tsp, O_SCROLL) = sp->t_maxrows / 2;
+ }
+
+ /*
+ * If files specified, build the file list, else, link to the
+ * current file.
+ */
+ if (argv == NULL) {
+ if (file_add(tsp, NULL, FILENAME(sp->frp), 0) == NULL)
+ goto err;
+ } else
+ for (; (*argv)->len != 0; ++argv)
+ if (file_add(tsp, NULL, (*argv)->bp, 0) == NULL)
+ goto err;
+
+ /* Set up the argument and current FREF pointers. */
+ if ((tsp->frp = file_first(tsp)) == NULL) {
+ msgq(sp, M_ERR, "No files in the file list.");
+ goto err;
+ }
+
+ tsp->a_frp = tsp->frp;
+
+ /*
+ * Copy the file state flags, start the file. Fill the child's
+ * screen map. If the file is unchanged, keep the screen and
+ * cursor the same.
+ */
+ if (argv == NULL) {
+ tsp->ep = sp->ep;
+ ++sp->ep->refcnt;
+
+ tsp->frp->flags = sp->frp->flags;
+ tsp->frp->lno = sp->lno;
+ tsp->frp->cno = sp->cno;
+ F_SET(tsp->frp, FR_CURSORSET);
+
+ /* Copy the parent's map into the child's map. */
+ memmove(_HMAP(tsp), _HMAP(sp), tsp->t_rows * sizeof(SMAP));
+ } else {
+ if (file_init(tsp, tsp->frp, NULL, 0))
+ goto err;
+ (void)svi_sm_fill(tsp, tsp->ep, 1, P_TOP);
+ }
+
+ /* Everything's initialized, put the screen on the displayed queue.*/
+ if (splitup) {
+ /* Link in before the parent. */
+ CIRCLEQ_INSERT_BEFORE(&sp->gp->dq, sp, tsp, q);
+ } else {
+ /* Link in after the parent. */
+ CIRCLEQ_INSERT_AFTER(&sp->gp->dq, sp, tsp, q);
+ }
+
+ /* Clear the current information lines in both screens. */
+ MOVE(sp, INFOLINE(sp), 0);
+ clrtoeol();
+ MOVE(tsp, INFOLINE(tsp), 0);
+ clrtoeol();
+
+ /* Redraw the status line for the parent screen. */
+ (void)status(sp, sp->ep, sp->lno, 0);
+
+ /* Save the parent screen's cursor information. */
+ sp->frp->lno = sp->lno;
+ sp->frp->cno = sp->cno;
+ F_SET(sp->frp, FR_CURSORSET);
+
+ /* Completely redraw the child screen. */
+ F_SET(tsp, S_REDRAW);
+
+ /* Switch screens. */
+ sp->nextdisp = tsp;
+ F_SET(sp, S_SSWITCH);
+ return (0);
+
+ /* Recover the original screen. */
+err: *sp = saved_sp;
+ *SVP(sp) = saved_svp;
+
+ /* Copy any (probably error) messages in the new screen. */
+ for (mp = tsp->msgq.lh_first; mp != NULL; mp = next) {
+ if (!F_ISSET(mp, M_EMPTY))
+ msg_app(sp->gp, sp,
+ mp->flags & M_INV_VIDEO, mp->mbuf, mp->len);
+ next = mp->q.le_next;
+ if (mp->mbuf != NULL)
+ free(mp->mbuf);
+ free(mp);
+ }
+
+ /* Free the new screen. */
+ free(_HMAP(tsp));
+ free(SVP(tsp));
+ FREE(tsp, sizeof(SCR));
+ return (1);
+}
+
+/*
+ * svi_bg --
+ * Background the screen, and switch to the next one.
+ */
+int
+svi_bg(csp)
+ SCR *csp;
+{
+ SCR *sp;
+
+ /* Try and join with another screen. */
+ if ((svi_join(csp, &sp)))
+ return (1);
+ if (sp == NULL) {
+ msgq(csp, M_ERR,
+ "You may not background your only displayed screen.");
+ return (1);
+ }
+
+ /* Move the old screen to the hidden queue. */
+ CIRCLEQ_REMOVE(&csp->gp->dq, csp, q);
+ CIRCLEQ_INSERT_TAIL(&csp->gp->hq, csp, q);
+
+ /* Switch screens. */
+ csp->nextdisp = sp;
+ F_SET(csp, S_SSWITCH);
+
+ return (0);
+}
+
+/*
+ * svi_join --
+ * Join the screen into a related screen, if one exists,
+ * and return that screen.
+ */
+int
+svi_join(csp, nsp)
+ SCR *csp, **nsp;
+{
+ SCR *sp;
+ size_t cnt;
+
+ /*
+ * If a split screen, add space to parent/child. Make no effort
+ * to clean up the screen's values. If it's not exiting, we'll
+ * get it when the user asks to show it again.
+ */
+ if ((sp = csp->q.cqe_prev) == (void *)&csp->gp->dq) {
+ if ((sp = csp->q.cqe_next) == (void *)&csp->gp->dq) {
+ *nsp = NULL;
+ return (0);
+ }
+ sp->woff = csp->woff;
+ }
+ sp->rows += csp->rows;
+ if (ISSMALLSCREEN(sp)) {
+ sp->t_maxrows += csp->rows;
+ for (cnt = sp->t_rows; ++cnt <= sp->t_maxrows;) {
+ MOVE(sp, cnt, 0);
+ clrtoeol();
+ }
+ TMAP = HMAP + (sp->t_rows - 1);
+ } else {
+ sp->t_maxrows += csp->rows;
+ sp->t_rows = sp->t_minrows = sp->t_maxrows;
+ TMAP = HMAP + (sp->t_rows - 1);
+ if (svi_sm_fill(sp, sp->ep, sp->lno, P_FILL))
+ return (1);
+ F_SET(sp, S_REDRAW);
+ }
+
+ /*
+ * If the size of the scrolling region hasn't been modified by
+ * the user, reset it so it's reasonable for the new screen.
+ */
+ if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET))
+ O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2;
+
+ *nsp = sp;
+ return (0);
+}
+
+/*
+ * svi_fg --
+ * Background the current screen, and foreground a new one.
+ */
+int
+svi_fg(csp, name)
+ SCR *csp;
+ CHAR_T *name;
+{
+ SCR *sp;
+
+ if (svi_swap(csp, &sp, name))
+ return (1);
+ if (sp == NULL) {
+ if (name == NULL)
+ msgq(csp, M_ERR, "There are no background screens.");
+ else
+ msgq(csp, M_ERR,
+ "There's no background screen editing a file named %s.",
+ name);
+ return (1);
+ }
+
+ /* Move the old screen to the hidden queue. */
+ CIRCLEQ_REMOVE(&csp->gp->dq, csp, q);
+ CIRCLEQ_INSERT_TAIL(&csp->gp->hq, csp, q);
+
+ return (0);
+}
+
+/*
+ * svi_swap --
+ * Swap the current screen with a hidden one.
+ */
+int
+svi_swap(csp, nsp, name)
+ SCR *csp, **nsp;
+ char *name;
+{
+ SCR *sp;
+ int issmallscreen;
+
+ /* Find the screen, or, if name is NULL, the first screen. */
+ for (sp = csp->gp->hq.cqh_first;
+ sp != (void *)&csp->gp->hq; sp = sp->q.cqe_next)
+ if (name == NULL || !strcmp(FILENAME(sp->frp), name))
+ break;
+ if (sp == (void *)&csp->gp->hq) {
+ *nsp = NULL;
+ return (0);
+ }
+ *nsp = sp;
+
+ /* Save the old screen's cursor information. */
+ csp->frp->lno = csp->lno;
+ csp->frp->cno = csp->cno;
+ F_SET(csp->frp, FR_CURSORSET);
+
+ /* Switch screens. */
+ csp->nextdisp = sp;
+ F_SET(csp, S_SSWITCH);
+
+ /* Initialize terminal information. */
+ SVP(sp)->srows = SVP(csp)->srows;
+
+ issmallscreen = ISSMALLSCREEN(sp);
+
+ /* Initialize screen information. */
+ sp->rows = csp->rows;
+ sp->cols = csp->cols;
+ sp->woff = csp->woff;
+
+ /*
+ * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
+ *
+ * The new screens may have different screen options sizes than the
+ * old one, so use them. Make sure that text counts aren't larger
+ * than the new screen sizes.
+ */
+ if (issmallscreen) {
+ sp->t_minrows = sp->t_rows = O_VAL(sp, O_WINDOW);
+ if (sp->t_rows > csp->t_maxrows)
+ sp->t_rows = sp->t_maxrows;
+ if (sp->t_minrows > csp->t_maxrows)
+ sp->t_minrows = sp->t_maxrows;
+ } else
+ sp->t_rows = sp->t_maxrows = sp->t_minrows = sp->rows - 1;
+
+ /*
+ * If the size of the scrolling region hasn't been modified by
+ * the user, reset it so it's reasonable for the new screen.
+ */
+ if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET))
+ O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2;
+
+ /*
+ * Don't change the screen's cursor information other than to
+ * note that the cursor is wrong.
+ */
+ F_SET(SVP(sp), SVI_CUR_INVALID);
+
+ /*
+ * The HMAP may be NULL, if the screen got resized and
+ * a bunch of screens had to be hidden.
+ */
+ if (HMAP == NULL)
+ MALLOC_RET(sp, HMAP, SMAP *, SIZE_HMAP(sp) * sizeof(SMAP));
+ TMAP = HMAP + (sp->t_rows - 1);
+
+ /* Fill the map. */
+ if (svi_sm_fill(sp, sp->ep, sp->lno, P_FILL))
+ return (1);
+
+ /*
+ * The new screen replaces the old screen in the parent/child list.
+ * We insert the new screen after the old one. If we're exiting,
+ * the exit will delete the old one, if we're foregrounding, the fg
+ * code will move the old one to the hidden queue.
+ */
+ CIRCLEQ_REMOVE(&sp->gp->hq, sp, q);
+ CIRCLEQ_INSERT_AFTER(&csp->gp->dq, csp, sp, q);
+
+ F_SET(sp, S_REDRAW);
+ return (0);
+}
+
+/*
+ * svi_rabs --
+ * Change the absolute size of the current screen.
+ */
+int
+svi_rabs(sp, count)
+ SCR *sp;
+ long count;
+{
+ SCR *g, *s;
+
+ /*
+ * Figure out which screens will grow, which will shrink, and
+ * make sure it's possible.
+ */
+ if (count == 0)
+ return (0);
+ if (count < 0) {
+ count = -count;
+ s = sp;
+ if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count)
+ goto toosmall;
+ if ((g = sp->q.cqe_prev) == (void *)&sp->gp->dq) {
+ if ((g = sp->q.cqe_next) == (void *)&sp->gp->dq)
+ goto toobig;
+ g->woff -= count;
+ } else
+ s->woff += count;
+ } else {
+ g = sp;
+ if ((s = sp->q.cqe_next) != (void *)&sp->gp->dq)
+ if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count)
+ s = NULL;
+ else
+ s->woff += count;
+ else
+ s = NULL;
+ if (s == NULL) {
+ if ((s = sp->q.cqe_prev) == (void *)&sp->gp->dq) {
+toobig: msgq(sp, M_BERR, "The screen cannot %s.",
+ count < 0 ? "shrink" : "grow");
+ return (1);
+ }
+ if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count) {
+toosmall: msgq(sp, M_BERR,
+ "The screen can only shrink to %d rows.",
+ MINIMUM_SCREEN_ROWS);
+ return (1);
+ }
+ g->woff -= count;
+ }
+ }
+
+ /* Update the screens. */
+ g->rows += count;
+ g->t_rows += count;
+ if (g->t_minrows == g->t_maxrows)
+ g->t_minrows += count;
+ g->t_maxrows += count;
+ _TMAP(g) = _HMAP(g) + (g->t_rows - 1);
+ (void)status(g, g->ep, g->lno, 0);
+ F_SET(g, S_REDRAW);
+
+ s->rows -= count;
+ s->t_rows -= count;
+ s->t_maxrows -= count;
+ if (s->t_minrows > s->t_maxrows)
+ s->t_minrows = s->t_maxrows;
+ _TMAP(s) = _HMAP(s) + (s->t_rows - 1);
+ (void)status(s, s->ep, s->lno, 0);
+ F_SET(s, S_REDRAW);
+
+ return (0);
+}
diff --git a/usr.bin/vi/svi/svi_util.c b/usr.bin/vi/svi/svi_util.c
new file mode 100644
index 000000000000..0da531d8db26
--- /dev/null
+++ b/usr.bin/vi/svi/svi_util.c
@@ -0,0 +1,314 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)svi_util.c 8.26 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <curses.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "vcmd.h"
+#include "excmd.h"
+#include "svi_screen.h"
+
+/*
+ * svi_screens --
+ * Return the number of screens required by the line, or,
+ * if a column is specified, by the column within the line.
+ */
+size_t
+svi_screens(sp, ep, lno, cnop)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ size_t *cnop;
+{
+ size_t cols, len, screens;
+ char *p;
+
+ /*
+ * Check for single cached value. The cache is because, if
+ * the line is large, this routine gets called repeatedly.
+ * One other hack, lots of time the user is on column one,
+ * which is an easy one.
+ */
+ if (cnop == NULL) {
+ if (SVP(sp)->ss_lno == lno)
+ return (SVP(sp)->ss_screens);
+ } else if (*cnop == 0)
+ return (1);
+
+ /* Get a copy of the line. */
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL || len == 0)
+ return (1);
+
+ /* Figure out how many columns the line/column needs. */
+ cols = svi_ncols(sp, p, len, cnop);
+
+ /* Leading number if O_NUMBER option set. */
+ if (O_ISSET(sp, O_NUMBER))
+ cols += O_NUMBER_LENGTH;
+
+ /* Trailing '$' if O_LIST option set. */
+ if (O_ISSET(sp, O_LIST) && cnop == NULL)
+ cols += sp->gp->cname['$'].len;
+
+ screens = (cols / sp->cols + (cols % sp->cols ? 1 : 0));
+ if (cnop == NULL) {
+ SVP(sp)->ss_lno = lno;
+ SVP(sp)->ss_screens = screens;
+ }
+ return (screens);
+}
+
+/*
+ * svi_ncols --
+ * Return the number of columns required by the line, or,
+ * if a column is specified, by the column within the line.
+ */
+size_t
+svi_ncols(sp, p, len, cnop)
+ SCR *sp;
+ u_char *p;
+ size_t len, *cnop;
+{
+ CHNAME const *cname;
+ size_t cno_cnt, scno;
+ int ch, listset;
+
+ cname = sp->gp->cname;
+ listset = O_ISSET(sp, O_LIST);
+
+ if (cnop == NULL)
+ for (scno = 0; len; --len)
+ SCNO_INCREMENT;
+ else
+ for (cno_cnt = *cnop, scno = 0; len; --len) {
+ SCNO_INCREMENT;
+ if (cno_cnt == 0)
+ break;
+ --cno_cnt;
+ }
+ return (scno);
+}
+
+/*
+ * bell_putchar --
+ * Functional version of putchar, for tputs.
+ */
+static void
+bell_putchar(ch)
+ int ch;
+{
+ (void)putchar(ch);
+}
+
+/*
+ * vbell --
+ * Set up the visual bell information. Broken out into a
+ * separate routine so don't allocate 4K every time we beep.
+ */
+static int
+vbell(sp)
+ SCR *sp;
+{
+ size_t len;
+ char *s, *t, b1[2048], b2[2048];
+
+ /* Get the termcap information. */
+ s = O_STR(sp, O_TERM);
+ if (tgetent(b1, s) != 1) {
+ msgq(sp, M_ERR, "No termcap entry for %s", s);
+ return (1);
+ }
+
+ /* Get the visual bell string. */
+ t = b2;
+ if (tgetstr("vb", &t) == NULL) {
+ msgq(sp, M_VINFO,
+ "No visual bell for %s terminal type", s);
+ return (1);
+ }
+ len = t - b2;
+ MALLOC_RET(sp, s, char *, len);
+ memmove(s, b2, len);
+ if (SVP(sp)->VB != NULL)
+ free(SVP(sp)->VB);
+ SVP(sp)->VB = t;
+ return (0);
+}
+
+/*
+ * svi_bell --
+ * Ring the bell.
+ */
+void
+svi_bell(sp)
+ SCR *sp;
+{
+ if (O_ISSET(sp, O_FLASH) && !F_ISSET(SVP(sp), SVI_NO_VBELL))
+ if (SVP(sp)->VB != NULL) {
+ (void)tputs(SVP(sp)->VB, 1, bell_putchar);
+ (void)fflush(stdout);
+ } else {
+ if (vbell(sp))
+ F_SET(SVP(sp), SVI_NO_VBELL);
+ svi_bell(sp);
+ }
+ else
+ (void)write(STDOUT_FILENO, "\007", 1); /* '\a' */
+ F_CLR(sp, S_BELLSCHED);
+}
+
+/*
+ * svi_optchange --
+ * Screen specific "option changed" routine.
+ */
+int
+svi_optchange(sp, opt)
+ SCR *sp;
+ int opt;
+{
+ switch (opt) {
+ case O_TERM:
+ /* Toss any saved visual bell information. */
+ if (SVP(sp)->VB != NULL) {
+ FREE(SVP(sp)->VB, strlen(SVP(sp)->VB) + 1);
+ SVP(sp)->VB = NULL;
+ }
+ F_CLR(SVP(sp), SVI_NO_VBELL);
+ F_SET(sp, S_RESIZE);
+ break;
+ case O_WINDOW:
+ if (svi_rrel(sp, O_VAL(sp, O_WINDOW)))
+ return (1);
+ break;
+ }
+
+ (void)v_optchange(sp, opt);
+ (void)ex_optchange(sp, opt);
+
+ return (0);
+}
+
+/*
+ * svi_busy --
+ * Put the cursor somewhere so the user will think we're busy.
+ */
+int
+svi_busy(sp, msg)
+ SCR *sp;
+ char const *msg;
+{
+ MOVE(sp, INFOLINE(sp), 0);
+ if (msg) {
+ ADDSTR(msg);
+ clrtoeol();
+ }
+ refresh();
+ F_SET(SVP(sp), SVI_CUR_INVALID);
+ return (0);
+}
+
+/*
+ * svi_clear --
+ * Clear from the row down to the end of the screen.
+ */
+int
+svi_clear(sp)
+ SCR *sp;
+{
+ size_t oldy, oldx, row;
+
+ getyx(stdscr, oldy, oldx);
+ for (row = SVP(sp)->srows - 1; row >= oldy; --row) {
+ MOVEA(sp, row, 0);
+ clrtoeol();
+ }
+ MOVEA(sp, oldy, oldx);
+ refresh();
+ return (0);
+}
+
+/*
+ * svi_suspend --
+ * Suspend the svi screen; don't kill the process group, curses is
+ * expected to do that for us.
+ */
+int
+svi_suspend(sp)
+ SCR *sp;
+{
+ struct termios t;
+ int rval;
+
+ /*
+ * XXX
+ * See comment in svi_curses_init().
+ */
+ if (F_ISSET(sp->gp, G_CURSES_S5CB)) {
+ (void)tcgetattr(STDIN_FILENO, &t);
+ (void)tcsetattr(STDIN_FILENO,
+ TCSASOFT | TCSADRAIN, &sp->gp->s5_curses_botch);
+ }
+
+ F_SET(sp->gp, G_SLEEPING);
+ if (rval = kill(getpid(), SIGTSTP))
+ msgq(sp, M_SYSERR, "SIGTSTP");
+ F_CLR(sp->gp, G_SLEEPING);
+
+ if (F_ISSET(sp->gp, G_CURSES_S5CB))
+ (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t);
+
+ return (rval);
+}
+
+/*
+ * svi_gdbrefresh --
+ * Stub routine so can flush out screen changes using gdb.
+ */
+#ifdef DEBUG
+int
+svi_gdbrefresh()
+{
+ refresh();
+ return (0);
+}
+#endif
diff --git a/usr.bin/vi/term.c b/usr.bin/vi/term.c
new file mode 100644
index 000000000000..d23fa80c4d45
--- /dev/null
+++ b/usr.bin/vi/term.c
@@ -0,0 +1,687 @@
+/*-
+ * Copyright (c) 1991, 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
+static char sccsid[] = "@(#)term.c 8.41 (Berkeley) 1/23/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <curses.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "seq.h"
+
+static int keycmp __P((const void *, const void *));
+
+/*
+ * If we're reading less than 20 characters, up the size of the tty buffer.
+ * This shouldn't ever happen, other than the first time through, but it's
+ * possible if a map is large enough.
+ */
+#define term_read_grow(sp, tty) \
+ (tty)->len - (tty)->cnt >= 20 ? 0 : __term_read_grow(sp, tty)
+static int __term_read_grow __P((SCR *, IBUF *));
+
+/*
+ * XXX
+ * THIS REQUIRES THAT ALL SCREENS SHARE A TERMINAL TYPE.
+ */
+typedef struct _tklist {
+ char *ts; /* Key's termcap string. */
+ char *output; /* Corresponding vi command. */
+ char *name; /* Name. */
+} TKLIST;
+static TKLIST const tklist[] = {
+ {"kA", "O", "insert line"},
+ {"kD", "x", "delete character"},
+ {"kd", "j", "cursor down"},
+ {"kE", "D", "delete to eol"},
+ {"kF", "\004", "scroll down"},
+ {"kH", "$", "go to eol"},
+ {"kh", "^", "go to sol"},
+ {"kI", "i", "insert at cursor"},
+ {"kL", "dd", "delete line"},
+ {"kl", "h", "cursor left"},
+ {"kN", "\006", "page down"},
+ {"kP", "\002", "page up"},
+ {"kR", "\025", "scroll up"},
+ {"kS", "dG", "delete to end of screen"},
+ {"kr", "l", "cursor right"},
+ {"ku", "k", "cursor up"},
+ {NULL},
+};
+
+/*
+ * XXX
+ * THIS REQUIRES THAT ALL SCREENS SHARE A SPECIAL KEY SET.
+ */
+typedef struct _keylist {
+ u_char value; /* Special value. */
+ CHAR_T ch; /* Key. */
+} KEYLIST;
+static KEYLIST keylist[] = {
+ {K_CARAT, '^'},
+ {K_CNTRLR, '\022'},
+ {K_CNTRLT, '\024'},
+ {K_CNTRLZ, '\032'},
+ {K_COLON, ':'},
+ {K_CR, '\r'},
+ {K_ESCAPE, '\033'},
+ {K_FORMFEED, '\f'},
+ {K_NL, '\n'},
+ {K_RIGHTBRACE, '}'},
+ {K_RIGHTPAREN, ')'},
+ {K_TAB, '\t'},
+ {K_VEOF, '\004'},
+ {K_VERASE, '\b'},
+ {K_VINTR, '\003'},
+ {K_VKILL, '\025'},
+ {K_VLNEXT, '\026'},
+ {K_VWERASE, '\027'},
+ {K_ZERO, '0'},
+};
+
+/*
+ * term_init --
+ * Initialize the special key lookup table, and the special keys
+ * defined by the terminal's termcap entry.
+ */
+int
+term_init(sp)
+ SCR *sp;
+{
+ extern CHNAME const asciiname[]; /* XXX */
+ GS *gp;
+ KEYLIST *kp;
+ TKLIST const *tkp;
+ cc_t ch;
+ int cnt;
+ char *sbp, *t, buf[2 * 1024], sbuf[128];
+
+ /*
+ * XXX
+ * 8-bit, ASCII only, for now. Recompilation should get you
+ * any 8-bit character set, as long as nul isn't a character.
+ */
+ gp = sp->gp;
+ gp->cname = asciiname; /* XXX */
+
+ /* Set keys found in the termios structure. */
+#define TERMSET(name, val) { \
+ if ((ch = gp->original_termios.c_cc[name]) != _POSIX_VDISABLE) \
+ for (kp = keylist;; ++kp) \
+ if (kp->value == (val)) { \
+ kp->ch = ch; \
+ break; \
+ } \
+}
+/*
+ * VEOF, VERASE, VKILL are required by POSIX 1003.1-1990,
+ * VWERASE is a 4.4BSD extension.
+ */
+#ifdef VEOF
+ TERMSET(VEOF, K_VEOF);
+#endif
+#ifdef VERASE
+ TERMSET(VERASE, K_VERASE);
+#endif
+#ifdef VINTR
+ TERMSET(VINTR, K_VINTR);
+#endif
+#ifdef VKILL
+ TERMSET(VKILL, K_VKILL);
+#endif
+#ifdef VWERASE
+ TERMSET(VWERASE, K_VWERASE);
+#endif
+
+ /* Sort the special key list. */
+ qsort(keylist,
+ sizeof(keylist) / sizeof(keylist[0]), sizeof(keylist[0]), keycmp);
+
+ /* Initialize the fast lookup table. */
+ CALLOC_RET(sp,
+ gp->special_key, u_char *, MAX_FAST_KEY + 1, sizeof(u_char));
+ for (gp->max_special = 0, kp = keylist,
+ cnt = sizeof(keylist) / sizeof(keylist[0]); cnt--; ++kp) {
+ if (gp->max_special < kp->value)
+ gp->max_special = kp->value;
+ if (kp->ch <= MAX_FAST_KEY)
+ gp->special_key[kp->ch] = kp->value;
+ }
+
+ /* Set key sequences found in the termcap entry. */
+ switch (tgetent(buf, O_STR(sp, O_TERM))) {
+ case -1:
+ msgq(sp, M_ERR,
+ "tgetent: %s: %s.", O_STR(sp, O_TERM), strerror(errno));
+ return (0);
+ case 0:
+ msgq(sp, M_ERR,
+ "%s: unknown terminal type.", O_STR(sp, O_TERM));
+ return (0);
+ }
+
+ for (tkp = tklist; tkp->name != NULL; ++tkp) {
+ sbp = sbuf;
+ if ((t = tgetstr(tkp->ts, &sbp)) == NULL)
+ continue;
+ if (seq_set(sp, tkp->name, strlen(tkp->name), t, strlen(t),
+ tkp->output, strlen(tkp->output), SEQ_COMMAND, 0))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * term_push --
+ * Push keys onto the front of a buffer.
+ *
+ * There is a single input buffer in ex/vi. Characters are read onto the
+ * end of the buffer by the terminal input routines, and pushed onto the
+ * front of the buffer various other functions in ex/vi. Each key has an
+ * associated flag value, which indicates if it has already been quoted,
+ * if it is the result of a mapping or an abbreviation.
+ */
+int
+term_push(sp, s, len, cmap, flags)
+ SCR *sp;
+ CHAR_T *s; /* Characters. */
+ size_t len; /* Number of chars. */
+ u_int cmap; /* Map count. */
+ u_int flags; /* CH_* flags. */
+{
+ IBUF *tty;
+ size_t nlen;
+
+ /* If we have room, stuff the keys into the buffer. */
+ tty = sp->gp->tty;
+ if (len <= tty->next ||
+ (tty->ch != NULL && tty->cnt == 0 && len <= tty->len)) {
+ if (tty->cnt != 0)
+ tty->next -= len;
+ tty->cnt += len;
+ memmove(tty->ch + tty->next, s, len * sizeof(CHAR_T));
+ memset(tty->chf + tty->next, flags, len);
+ memset(tty->cmap + tty->next, cmap, len);
+ return (0);
+ }
+
+ /* Get enough space plus a little extra. */
+ nlen = tty->cnt + len;
+ if (nlen > tty->len) {
+ size_t olen;
+
+ nlen += 64;
+ olen = tty->len;
+ BINC_RET(sp, tty->ch, olen, nlen * sizeof(tty->ch[0]));
+ olen = tty->len;
+ BINC_RET(sp, tty->chf, olen, nlen * sizeof(tty->chf[0]));
+ BINC_RET(sp, tty->cmap, tty->len, nlen * sizeof(tty->cmap[0]));
+ }
+
+ /*
+ * If there are currently characters in the queue, shift them up,
+ * leaving some extra room.
+ */
+#define TERM_PUSH_SHIFT 30
+ if (tty->cnt) {
+ memmove(tty->ch + TERM_PUSH_SHIFT + len,
+ tty->ch + tty->next, tty->cnt * sizeof(tty->ch[0]));
+ memmove(tty->chf + TERM_PUSH_SHIFT + len,
+ tty->chf + tty->next, tty->cnt * sizeof(tty->chf[0]));
+ memmove(tty->cmap + TERM_PUSH_SHIFT + len,
+ tty->cmap + tty->next, tty->cnt * sizeof(tty->cmap[0]));
+ }
+
+ /* Put the new characters into the queue. */
+ tty->next = TERM_PUSH_SHIFT;
+ tty->cnt += len;
+ memmove(tty->ch + TERM_PUSH_SHIFT, s, len * sizeof(tty->ch[0]));
+ memset(tty->chf + TERM_PUSH_SHIFT, flags, len * sizeof(tty->chf[0]));
+ memset(tty->cmap + TERM_PUSH_SHIFT, cmap, len * sizeof(tty->cmap[0]));
+ return (0);
+}
+
+/*
+ * Remove characters from the queue, simultaneously clearing the
+ * flag and map counts.
+ */
+#define QREM_HEAD(q, len) { \
+ size_t __off = (q)->next; \
+ if (len == 1) { \
+ tty->chf[__off] = 0; \
+ tty->cmap[__off] = 0; \
+ } else { \
+ memset(tty->chf + __off, 0, len); \
+ memset(tty->cmap + __off, 0, len); \
+ } \
+ if (((q)->cnt -= len) == 0) \
+ (q)->next = 0; \
+ else \
+ (q)->next += len; \
+}
+#define QREM_TAIL(q, len) { \
+ size_t __off = (q)->next + (q)->cnt - 1; \
+ if (len == 1) { \
+ tty->chf[__off] = 0; \
+ tty->cmap[__off] = 0; \
+ } else { \
+ memset(tty->chf + __off, 0, len); \
+ memset(tty->cmap + __off, 0, len); \
+ } \
+ if (((q)->cnt -= len) == 0) \
+ (q)->next = 0; \
+}
+
+/*
+ * term_key --
+ * Get the next key.
+ *
+ * !!!
+ * The flag TXT_MAPNODIGIT probably needs some explanation. First, the idea
+ * of mapping keys is that one or more keystrokes act like a function key.
+ * What's going on is that vi is reading a number, and the character following
+ * the number may or may not be mapped (TXT_MAPCOMMAND). For example, if the
+ * user is entering the z command, a valid command is "z40+", and we don't want
+ * to map the '+', i.e. if '+' is mapped to "xxx", we don't want to change it
+ * into "z40xxx". However, if the user enters "35x", we want to put all of the
+ * characters through the mapping code.
+ *
+ * Historical practice is a bit muddled here. (Surprise!) It always permitted
+ * mapping digits as long as they weren't the first character of the map, e.g.
+ * ":map ^A1 xxx" was okay. It also permitted the mapping of the digits 1-9
+ * (the digit 0 was a special case as it doesn't indicate the start of a count)
+ * as the first character of the map, but then ignored those mappings. While
+ * it's probably stupid to map digits, vi isn't your mother.
+ *
+ * The way this works is that the TXT_MAPNODIGIT causes term_key to return the
+ * end-of-digit without "looking" at the next character, i.e. leaving it as the
+ * user entered it. Presumably, the next term_key call will tell us how the
+ * user wants it handled.
+ *
+ * There is one more complication. Users might map keys to digits, and, as
+ * it's described above, the commands "map g 1G|d2g" would return the keys
+ * "d2<end-of-digits>1G", when the user probably wanted "d21<end-of-digits>G".
+ * So, if a map starts off with a digit we continue as before, otherwise, we
+ * pretend that we haven't mapped the character and return <end-of-digits>.
+ *
+ * Now that that's out of the way, let's talk about Energizer Bunny macros.
+ * It's easy to create macros that expand to a loop, e.g. map x 3x. It's
+ * fairly easy to detect this example, because it's all internal to term_key.
+ * If we're expanding a macro and it gets big enough, at some point we can
+ * assume it's looping and kill it. The examples that are tough are the ones
+ * where the parser is involved, e.g. map x "ayyx"byy. We do an expansion
+ * on 'x', and get "ayyx"byy. We then return the first 4 characters, and then
+ * find the looping macro again. There is no way that we can detect this
+ * without doing a full parse of the command, because the character that might
+ * cause the loop (in this case 'x') may be a literal character, e.g. the map
+ * map x "ayy"xyy"byy is perfectly legal and won't cause a loop.
+ *
+ * Historic vi tried to detect looping macros by disallowing obvious cases in
+ * the map command, maps that that ended with the same letter as they started
+ * (which wrongly disallowed "map x 'x"), and detecting macros that expanded
+ * too many times before keys were returned to the command parser. It didn't
+ * get many (most?) of the tricky cases right, however, and it was certainly
+ * possible to create macros that ran forever. And, even if it did figure out
+ * what was going on, the user was usually tossed into ex mode. Finally, any
+ * changes made before vi realized that the macro was recursing were left in
+ * place. This implementation counts how many times each input character has
+ * been mapped. If it reaches some arbitrary value, we flush all mapped keys
+ * and return an error.
+ *
+ * XXX
+ * The final issue is recovery. It would be possible to undo all of the work
+ * that was done by the macro if we entered a record into the log so that we
+ * knew when the macro started, and, in fact, this might be worth doing at some
+ * point. Given that this might make the log grow unacceptably (consider that
+ * cursor keys are done with maps), for now we leave any changes made in place.
+ */
+enum input
+term_key(sp, chp, flags)
+ SCR *sp;
+ CH *chp;
+ u_int flags;
+{
+ enum input rval;
+ struct timeval t, *tp;
+ CHAR_T ch;
+ GS *gp;
+ IBUF *tty;
+ SEQ *qp;
+ int cmap, ispartial, nr;
+
+ gp = sp->gp;
+ tty = gp->tty;
+
+ /*
+ * If the queue is empty, read more keys in. Since no timeout is
+ * requested, s_key_read will either return an error or will read
+ * some number of characters.
+ */
+loop: if (tty->cnt == 0) {
+ if (term_read_grow(sp, tty))
+ return (INP_ERR);
+ if (rval = sp->s_key_read(sp, &nr, NULL))
+ return (rval);
+ /*
+ * If there's something on the mode line that we wanted
+ * the user to see, they just entered a character so we
+ * can presume they saw it.
+ */
+ if (F_ISSET(sp, S_UPDATE_MODE))
+ F_CLR(sp, S_UPDATE_MODE);
+ }
+
+ /* If the key is mappable and should be mapped, look it up. */
+ if (!(tty->chf[tty->next] & CH_NOMAP) &&
+ LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT)) {
+ /* Set up timeout value. */
+ if (O_ISSET(sp, O_TIMEOUT)) {
+ tp = &t;
+ t.tv_sec = O_VAL(sp, O_KEYTIME) / 10;
+ t.tv_usec = (O_VAL(sp, O_KEYTIME) % 10) * 100000L;
+ } else
+ tp = NULL;
+
+ /* Get the next key. */
+newmap: ch = tty->ch[tty->next];
+ if (ch < MAX_BIT_SEQ && !bit_test(gp->seqb, ch))
+ goto nomap;
+
+ /* Search the map. */
+remap: qp = seq_find(sp, NULL, &tty->ch[tty->next], tty->cnt,
+ LF_ISSET(TXT_MAPCOMMAND) ? SEQ_COMMAND : SEQ_INPUT,
+ &ispartial);
+
+ /*
+ * If get a partial match, read more characters and retry
+ * the map. If no characters read, return the characters
+ * unmapped.
+ */
+ if (ispartial) {
+ if (term_read_grow(sp, tty))
+ return (INP_ERR);
+ if (rval = sp->s_key_read(sp, &nr, tp))
+ return (rval);
+ if (nr)
+ goto remap;
+ goto nomap;
+ }
+
+ /* If no map, return the character. */
+ if (qp == NULL)
+ goto nomap;
+
+ /*
+ * If looking for the end of a digit string, and the first
+ * character of the map is it, pretend we haven't seen the
+ * character.
+ */
+ if (LF_ISSET(TXT_MAPNODIGIT) && !isdigit(qp->output[0]))
+ goto not_digit_ch;
+
+ /*
+ * Only permit a character to be remapped a certain number
+ * of times before we figure that it's not going to finish.
+ */
+ if ((cmap = tty->cmap[tty->next]) > MAX_MAP_COUNT) {
+ term_map_flush(sp, "Character remapped too many times");
+ return (INP_ERR);
+ }
+
+ /* Delete the mapped characters from the queue. */
+ QREM_HEAD(tty, qp->ilen);
+
+ /* If remapping characters, push the character on the queue. */
+ if (O_ISSET(sp, O_REMAP)) {
+ if (term_push(sp, qp->output, qp->olen, ++cmap, 0))
+ return (INP_ERR);
+ goto newmap;
+ }
+
+ /* Else, push the characters on the queue and return one. */
+ if (term_push(sp, qp->output, qp->olen, 0, CH_NOMAP))
+ return (INP_ERR);
+ }
+
+nomap: ch = tty->ch[tty->next];
+ if (LF_ISSET(TXT_MAPNODIGIT) && !isdigit(ch)) {
+not_digit_ch: chp->ch = NOT_DIGIT_CH;
+ chp->value = 0;
+ chp->flags = 0;
+ return (INP_OK);
+ }
+
+ /* Fill in the return information. */
+ chp->ch = ch;
+ chp->flags = tty->chf[tty->next];
+ chp->value = term_key_val(sp, ch);
+
+ /* Delete the character from the queue. */
+ QREM_HEAD(tty, 1);
+
+ /*
+ * O_BEAUTIFY eliminates all control characters except
+ * escape, form-feed, newline and tab.
+ */
+ if (isprint(ch) ||
+ !LF_ISSET(TXT_BEAUTIFY) || !O_ISSET(sp, O_BEAUTIFY) ||
+ chp->value == K_ESCAPE || chp->value == K_FORMFEED ||
+ chp->value == K_NL || chp->value == K_TAB)
+ return (INP_OK);
+
+ goto loop;
+}
+
+/*
+ * term_ab_flush --
+ * Flush any abbreviated keys.
+ */
+void
+term_ab_flush(sp, msg)
+ SCR *sp;
+ char *msg;
+{
+ IBUF *tty;
+
+ tty = sp->gp->tty;
+ if (!tty->cnt || !(tty->chf[tty->next] & CH_ABBREVIATED))
+ return;
+ do {
+ QREM_HEAD(tty, 1);
+ } while (tty->cnt && tty->chf[tty->next] & CH_ABBREVIATED);
+ msgq(sp, M_ERR, "%s: keys flushed.", msg);
+
+}
+/*
+ * term_map_flush --
+ * Flush any mapped keys.
+ */
+void
+term_map_flush(sp, msg)
+ SCR *sp;
+ char *msg;
+{
+ IBUF *tty;
+
+ tty = sp->gp->tty;
+ if (!tty->cnt || !tty->cmap[tty->next])
+ return;
+ do {
+ QREM_HEAD(tty, 1);
+ } while (tty->cnt && tty->cmap[tty->next]);
+ msgq(sp, M_ERR, "%s: keys flushed.", msg);
+
+}
+
+/*
+ * term_user_key --
+ * Get the next key, but require the user enter one.
+ */
+enum input
+term_user_key(sp, chp)
+ SCR *sp;
+ CH *chp;
+{
+ enum input rval;
+ IBUF *tty;
+ int nr;
+
+ /*
+ * Read any keys the user has waiting. Make the race
+ * condition as short as possible.
+ */
+ if (rval = term_key_queue(sp))
+ return (rval);
+
+ /* Wait and read another key. */
+ if (rval = sp->s_key_read(sp, &nr, NULL))
+ return (rval);
+
+ /* Fill in the return information. */
+ tty = sp->gp->tty;
+ chp->ch = tty->ch[tty->next + (tty->cnt - 1)];
+ chp->flags = 0;
+ chp->value = term_key_val(sp, chp->ch);
+
+ QREM_TAIL(tty, 1);
+ return (INP_OK);
+}
+
+/*
+ * term_key_queue --
+ * Read the keys off of the terminal queue until it's empty.
+ */
+int
+term_key_queue(sp)
+ SCR *sp;
+{
+ enum input rval;
+ struct timeval t;
+ IBUF *tty;
+ int nr;
+
+ t.tv_sec = 0;
+ t.tv_usec = 0;
+ for (tty = sp->gp->tty;;) {
+ if (term_read_grow(sp, tty))
+ return (INP_ERR);
+ if (rval = sp->s_key_read(sp, &nr, &t))
+ return (rval);
+ if (nr == 0)
+ break;
+ }
+ return (INP_OK);
+}
+
+/*
+ * term_key_ch --
+ * Fill in the key for a value.
+ */
+int
+term_key_ch(sp, val, chp)
+ SCR *sp;
+ int val;
+ CHAR_T *chp;
+{
+ KEYLIST *kp;
+
+ for (kp = keylist;; ++kp)
+ if (kp->value == val) {
+ *chp = kp->ch;
+ return (0);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * __term_key_val --
+ * Fill in the value for a key. This routine is the backup
+ * for the term_key_val() macro.
+ */
+int
+__term_key_val(sp, ch)
+ SCR *sp;
+ ARG_CHAR_T ch;
+{
+ KEYLIST k, *kp;
+
+ k.ch = ch;
+ kp = bsearch(&k, keylist,
+ sizeof(keylist) / sizeof(keylist[0]), sizeof(keylist[0]), keycmp);
+ return (kp == NULL ? 0 : kp->value);
+}
+
+/*
+ * __term_read_grow --
+ * Grow the terminal queue. This routine is the backup for
+ * the term_read_grow() macro.
+ */
+static int
+__term_read_grow(sp, tty)
+ SCR *sp;
+ IBUF *tty;
+{
+ size_t alen, len, nlen;
+
+ nlen = tty->len + 64;
+ alen = tty->len - (tty->next + tty->cnt);
+
+ len = tty->len;
+ BINC_RET(sp, tty->ch, len, nlen * sizeof(tty->ch[0]));
+ memset(tty->ch + tty->next + tty->cnt, 0, alen * sizeof(tty->ch[0]));
+
+ len = tty->len;
+ BINC_RET(sp, tty->chf, len, nlen * sizeof(tty->chf[0]));
+ memset(tty->chf + tty->next + tty->cnt, 0, alen * sizeof(tty->chf[0]));
+
+ BINC_RET(sp, tty->cmap, tty->len, nlen * sizeof(tty->cmap[0]));
+ memset(tty->cmap +
+ tty->next + tty->cnt, 0, alen * sizeof(tty->cmap[0]));
+ return (0);
+}
+
+static int
+keycmp(ap, bp)
+ const void *ap, *bp;
+{
+ return (((KEYLIST *)ap)->ch - ((KEYLIST *)bp)->ch);
+}
diff --git a/usr.bin/vi/term.h b/usr.bin/vi/term.h
new file mode 100644
index 000000000000..67f7f5d2822c
--- /dev/null
+++ b/usr.bin/vi/term.h
@@ -0,0 +1,184 @@
+/*-
+ * Copyright (c) 1991, 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.
+ *
+ * @(#)term.h 8.26 (Berkeley) 1/7/94
+ */
+
+/* Structure to return a character and associated information. */
+struct _ch {
+ CHAR_T ch; /* Character. */
+
+#define K_CARAT 1
+#define K_CNTRLR 2
+#define K_CNTRLT 3
+#define K_CNTRLZ 4
+#define K_COLON 5
+#define K_CR 6
+#define K_ESCAPE 7
+#define K_FORMFEED 8
+#define K_NL 9
+#define K_RIGHTBRACE 10
+#define K_RIGHTPAREN 11
+#define K_TAB 12
+#define K_VEOF 13
+#define K_VERASE 14
+#define K_VINTR 15
+#define K_VKILL 16
+#define K_VLNEXT 17
+#define K_VWERASE 18
+#define K_ZERO 19
+ u_char value; /* Special character flag values. */
+
+#define CH_ABBREVIATED 0x01 /* Character from an abbreviation. */
+#define CH_NOMAP 0x02 /* Do not attempt to map the character. */
+#define CH_QUOTED 0x04 /* Character is already quoted. */
+ u_char flags;
+};
+
+/*
+ * Structure for the key input buffer.
+ *
+ * MAX_MAP_COUNT was chosen based on the vi maze script, which remaps
+ * characters roughly 250 times.
+ */
+struct _ibuf {
+ CHAR_T *ch; /* Array of characters. */
+ u_char *chf; /* Array of character flags (CH_*). */
+#define MAX_MAP_COUNT 270 /* Maximum times a character can remap. */
+ u_char *cmap; /* Number of times character has been mapped. */
+
+ size_t cnt; /* Count of remaining characters. */
+ size_t len; /* Array length. */
+ size_t next; /* Offset of next array entry. */
+};
+ /* Return if more keys in queue. */
+#define KEYS_WAITING(sp) ((sp)->gp->tty->cnt)
+#define MAPPED_KEYS_WAITING(sp) \
+ (KEYS_WAITING(sp) && sp->gp->tty->cmap[sp->gp->tty->next])
+
+/*
+ * Structure to name a character. Used both as an interface to the
+ * screen and to name objects named by characters in error messages.
+ */
+struct _chname {
+ char *name; /* Character name. */
+ u_char len; /* Length of the character name. */
+};
+
+/*
+ * Routines that return a key as a side-effect return:
+ *
+ * INP_OK Returning a character; must be 0.
+ * INP_EOF EOF.
+ * INP_ERR Error.
+ *
+ * The vi structure depends on the key routines being able to return INP_EOF
+ * multiple times without failing -- eventually enough things will end due to
+ * INP_EOF that vi will reach the command level for the screen, at which point
+ * the exit flags will be set and vi will exit.
+ */
+enum input { INP_OK=0, INP_EOF, INP_ERR };
+
+/*
+ * Routines that return a confirmation return:
+ *
+ * CONF_NO User answered no.
+ * CONF_QUIT User answered quit, eof or an error.
+ * CONF_YES User answered yes.
+ */
+enum confirm { CONF_NO, CONF_QUIT, CONF_YES };
+
+/*
+ * Ex/vi commands are generally separated by whitespace characters. We
+ * can't use the standard isspace(3) macro because it returns true for
+ * characters like ^K in the ASCII character set. The 4.4BSD isblank(3)
+ * macro does exactly what we want, but it's not portable yet.
+ *
+ * XXX
+ * Note side effect, ch is evaluated multiple times.
+ */
+#ifndef isblank
+#define isblank(ch) ((ch) == ' ' || (ch) == '\t')
+#endif
+
+/* Various special characters, messages. */
+#define CURSOR_CH ' ' /* Cursor character. */
+#define END_CH '$' /* End of a range. */
+#define HEX_CH 'x' /* Leading hex number. */
+#define NOT_DIGIT_CH 'a' /* A non-isdigit() character. */
+#define NO_CH 'n' /* No. */
+#define QUIT_CH 'q' /* Quit. */
+#define YES_CH 'y' /* Yes. */
+#define CONFSTRING "confirm? [ynq]"
+#define CONTMSG "Enter return to continue: "
+#define CONTMSG_I "Enter return to continue [q to quit]: "
+
+/* Flags describing how input is handled. */
+#define TXT_AICHARS 0x000001 /* Leading autoindent chars. */
+#define TXT_ALTWERASE 0x000002 /* Option: altwerase. */
+#define TXT_APPENDEOL 0x000004 /* Appending after EOL. */
+#define TXT_AUTOINDENT 0x000008 /* Autoindent set this line. */
+#define TXT_BEAUTIFY 0x000010 /* Only printable characters. */
+#define TXT_BS 0x000020 /* Backspace returns the buffer. */
+#define TXT_CNTRLT 0x000040 /* Control-T is an indent special. */
+#define TXT_CR 0x000080 /* CR returns the buffer. */
+#define TXT_EMARK 0x000100 /* End of replacement mark. */
+#define TXT_ESCAPE 0x000200 /* Escape returns the buffer. */
+#define TXT_INFOLINE 0x000400 /* Editing the info line. */
+#define TXT_MAPCOMMAND 0x000800 /* Apply the command map. */
+#define TXT_MAPINPUT 0x001000 /* Apply the input map. */
+#define TXT_MAPNODIGIT 0x002000 /* Return to a digit. */
+#define TXT_NLECHO 0x004000 /* Echo the newline. */
+#define TXT_OVERWRITE 0x008000 /* Overwrite characters. */
+#define TXT_PROMPT 0x010000 /* Display a prompt. */
+#define TXT_RECORD 0x020000 /* Record for replay. */
+#define TXT_REPLACE 0x040000 /* Replace; don't delete overwrite. */
+#define TXT_REPLAY 0x080000 /* Replay the last input. */
+#define TXT_RESOLVE 0x100000 /* Resolve the text into the file. */
+#define TXT_SHOWMATCH 0x200000 /* Option: showmatch. */
+#define TXT_TTYWERASE 0x400000 /* Option: ttywerase. */
+#define TXT_WRAPMARGIN 0x800000 /* Option: wrapmargin. */
+
+#define TXT_VALID_EX \
+ (TXT_BEAUTIFY | TXT_CR | TXT_NLECHO | TXT_PROMPT)
+
+/* Support keyboard routines. */
+int __term_key_val __P((SCR *, ARG_CHAR_T));
+void term_ab_flush __P((SCR *, char *));
+int term_init __P((SCR *));
+enum input term_key __P((SCR *, CH *, u_int));
+int term_key_ch __P((SCR *, int, CHAR_T *));
+int term_key_queue __P((SCR *));
+void term_map_flush __P((SCR *, char *));
+int term_push __P((SCR *, CHAR_T *, size_t, u_int, u_int));
+enum input term_user_key __P((SCR *, CH *));
+int term_waiting __P((SCR *));
diff --git a/usr.bin/vi/timer.c b/usr.bin/vi/timer.c
new file mode 100644
index 000000000000..e2bcfb1c8852
--- /dev/null
+++ b/usr.bin/vi/timer.c
@@ -0,0 +1,166 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)timer.c 8.7 (Berkeley) 12/2/93";
+#endif /* not lint */
+
+#include <sys/time.h>
+
+#include "vi.h"
+
+static void busy_handler __P((int));
+
+/*
+ * XXX
+ * There are two uses of timers in nvi. The first is to push the recovery
+ * information out to disk at periodic intervals. The second is to display
+ * a "busy" message if an operation takes too long. Rather than solve this
+ * in a general fashion, we depend on the fact that only a single screen in
+ * a window is active at a time, and that there are only two parts of the
+ * systems that use timers.
+ *
+ * It would be nice to reimplement this with multiple timers, a la POSIX
+ * 1003.1, but not many systems offer them yet.
+ */
+
+/*
+ * busy_on --
+ * Display a message if too much time passes.
+ */
+void
+busy_on(sp, seconds, msg)
+ SCR *sp;
+ int seconds;
+ char const *msg;
+{
+ struct itimerval value;
+ struct sigaction act;
+
+ /* No busy messages in batch mode. */
+ if (F_ISSET(sp, S_EXSILENT))
+ return;
+
+ /* Turn off the current timer, saving its current value. */
+ value.it_interval.tv_sec = value.it_value.tv_sec = 0;
+ value.it_interval.tv_usec = value.it_value.tv_usec = 0;
+ if (setitimer(ITIMER_REAL, &value, &sp->time_value))
+ return;
+
+ /*
+ * Decrement the original timer by the number of seconds
+ * we're going to wait.
+ */
+ if (sp->time_value.it_value.tv_sec > seconds)
+ sp->time_value.it_value.tv_sec -= seconds;
+ else
+ sp->time_value.it_value.tv_sec = 1;
+
+ /* Reset the handler, saving its current value. */
+ act.sa_handler = busy_handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ (void)sigaction(SIGALRM, &act, &sp->time_handler);
+
+ /* Reset the timer. */
+ value.it_value.tv_sec = seconds;
+ value.it_interval.tv_sec = 0;
+ value.it_interval.tv_usec = value.it_value.tv_usec = 0;
+ (void)setitimer(ITIMER_REAL, &value, NULL);
+
+ sp->time_msg = msg;
+ F_SET(sp, S_TIMER_SET);
+}
+
+/*
+ * busy_off --
+ * Reset the timer handlers.
+ */
+void
+busy_off(sp)
+ SCR *sp;
+{
+ struct itimerval ovalue, value;
+
+ /* No busy messages in batch mode. */
+ if (F_ISSET(sp, S_EXSILENT))
+ return;
+
+ /* If the timer flag isn't set, it must have fired. */
+ if (!F_ISSET(sp, S_TIMER_SET))
+ return;
+
+ /* Ignore it if first on one of following system calls. */
+ F_CLR(sp, S_TIMER_SET);
+
+ /* Turn off the current timer. */
+ value.it_interval.tv_sec = value.it_value.tv_sec = 0;
+ value.it_interval.tv_usec = value.it_value.tv_usec = 0;
+ if (setitimer(ITIMER_REAL, &value, &ovalue))
+ return;
+
+ /* If the timer wasn't running, we're done. */
+ if (sp->time_handler.sa_handler == SIG_DFL)
+ return;
+
+ /*
+ * Increment the old timer by the number of seconds
+ * remaining in the new one.
+ */
+ sp->time_value.it_value.tv_sec += ovalue.it_value.tv_sec;
+
+ /* Reset the handler to the original handler. */
+ (void)sigaction(SIGALRM, &sp->time_handler, NULL);
+
+ /* Reset the timer. */
+ (void)setitimer(ITIMER_REAL, &sp->time_value, NULL);
+}
+
+/*
+ * busy_handler --
+ * Display a message when the timer goes off, and restore the
+ * timer to its original values.
+ */
+static void
+busy_handler(signo)
+ int signo;
+{
+ SCR *sp;
+
+ for (sp = __global_list->dq.cqh_first;
+ sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
+ if (F_ISSET(sp, S_TIMER_SET)) {
+ sp->s_busy(sp, sp->time_msg);
+ busy_off(sp);
+ }
+}
diff --git a/usr.bin/vi/trace.c b/usr.bin/vi/trace.c
new file mode 100644
index 000000000000..f97e47bdc7db
--- /dev/null
+++ b/usr.bin/vi/trace.c
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 1992, 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.
+ *
+ * @(#)trace.c 8.1 (Berkeley) 6/9/93
+ */
+
+#ifdef DEBUG
+#include <sys/types.h>
+
+#include "vi.h"
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#ifdef __STDC__
+TRACE(SCR *sp, const char *fmt, ...)
+#else
+TRACE(sp, fmt, va_alist)
+ SCR *sp;
+ char *fmt;
+ va_dcl
+#endif
+{
+ FILE *tfp;
+ va_list ap;
+
+ if ((tfp = sp->gp->tracefp) == NULL)
+ return;
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)vfprintf(tfp, fmt, ap);
+ va_end(ap);
+
+ (void)fflush(tfp);
+}
+#endif
diff --git a/usr.bin/vi/util.c b/usr.bin/vi/util.c
new file mode 100644
index 000000000000..79a06516af48
--- /dev/null
+++ b/usr.bin/vi/util.c
@@ -0,0 +1,578 @@
+/*-
+ * Copyright (c) 1991, 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
+static char sccsid[] = "@(#)util.c 8.34 (Berkeley) 12/23/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <ctype.h>
+#include <curses.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "vi.h"
+
+/*
+ * msgq --
+ * Display a message.
+ */
+void
+#ifdef __STDC__
+msgq(SCR *sp, enum msgtype mt, const char *fmt, ...)
+#else
+msgq(sp, mt, fmt, va_alist)
+ SCR *sp;
+ enum msgtype mt;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+ int len;
+ char msgbuf[1024];
+
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ /*
+ * It's possible to enter msg when there's no screen to hold
+ * the message. Always check sp before using it, and, if it's
+ * NULL, use __global_list.
+ */
+ switch (mt) {
+ case M_BERR:
+ if (sp != NULL && !O_ISSET(sp, O_VERBOSE)) {
+ F_SET(sp, S_BELLSCHED);
+ return;
+ }
+ mt = M_ERR;
+ break;
+ case M_VINFO:
+ if (sp != NULL && !O_ISSET(sp, O_VERBOSE))
+ return;
+ mt = M_INFO;
+ /* FALLTHROUGH */
+ case M_INFO:
+ if (F_ISSET(sp, S_EXSILENT))
+ return;
+ break;
+ case M_ERR:
+ case M_SYSERR:
+ break;
+ default:
+ abort();
+ }
+
+ /* Length is the min length of the message or the buffer. */
+ if (mt == M_SYSERR)
+ if (sp->if_name != NULL)
+ len = snprintf(msgbuf, sizeof(msgbuf),
+ "Error: %s, %d: %s%s%s.", sp->if_name, sp->if_lno,
+ fmt == NULL ? "" : fmt, fmt == NULL ? "" : ": ",
+ strerror(errno));
+ else
+ len = snprintf(msgbuf, sizeof(msgbuf),
+ "Error: %s%s%s.",
+ fmt == NULL ? "" : fmt, fmt == NULL ? "" : ": ",
+ strerror(errno));
+ else {
+ len = sp->if_name == NULL ? 0 : snprintf(msgbuf, sizeof(msgbuf),
+ "%s, %d: ", sp->if_name, sp->if_lno);
+ len += vsnprintf(msgbuf + len, sizeof(msgbuf) - len, fmt, ap);
+ }
+
+ /*
+ * If len >= the size, some characters were discarded.
+ * Ignore trailing nul.
+ */
+ if (len >= sizeof(msgbuf))
+ len = sizeof(msgbuf) - 1;
+
+ msg_app(__global_list, sp, mt == M_ERR ? 1 : 0, msgbuf, len);
+}
+
+/*
+ * msg_app --
+ * Append a message into the queue. This can fail, but there's
+ * nothing we can do if it does.
+ */
+void
+msg_app(gp, sp, inv_video, p, len)
+ GS *gp;
+ SCR *sp;
+ int inv_video;
+ char *p;
+ size_t len;
+{
+ static int reenter; /* STATIC: Re-entrancy check. */
+ MSG *mp, *nmp;
+
+ /*
+ * It's possible to reenter msg when it allocates space.
+ * We're probably dead anyway, but no reason to drop core.
+ */
+ if (reenter)
+ return;
+ reenter = 1;
+
+ /*
+ * Find an empty structure, or allocate a new one. Use the
+ * screen structure if possible, otherwise the global one.
+ */
+ if (sp != NULL) {
+ if ((mp = sp->msgq.lh_first) == NULL) {
+ CALLOC(sp, mp, MSG *, 1, sizeof(MSG));
+ if (mp == NULL)
+ goto ret;
+ LIST_INSERT_HEAD(&sp->msgq, mp, q);
+ goto store;
+ }
+ } else if ((mp = gp->msgq.lh_first) == NULL) {
+ CALLOC(sp, mp, MSG *, 1, sizeof(MSG));
+ if (mp == NULL)
+ goto ret;
+ LIST_INSERT_HEAD(&gp->msgq, mp, q);
+ goto store;
+ }
+ while (!F_ISSET(mp, M_EMPTY) && mp->q.le_next != NULL)
+ mp = mp->q.le_next;
+ if (!F_ISSET(mp, M_EMPTY)) {
+ CALLOC(sp, nmp, MSG *, 1, sizeof(MSG));
+ if (nmp == NULL)
+ goto ret;
+ LIST_INSERT_AFTER(mp, nmp, q);
+ mp = nmp;
+ }
+
+ /* Get enough memory for the message. */
+store: if (len > mp->blen && binc(sp, &mp->mbuf, &mp->blen, len))
+ goto ret;
+
+ /* Store the message. */
+ memmove(mp->mbuf, p, len);
+ mp->len = len;
+ mp->flags = inv_video ? M_INV_VIDEO : 0;
+
+ret: reenter = 0;
+}
+
+/*
+ * msgrpt --
+ * Report on the lines that changed.
+ *
+ * !!!
+ * Historic vi documentation (USD:15-8) claimed that "The editor will also
+ * always tell you when a change you make affects text which you cannot see."
+ * This isn't true -- edit a large file and do "100d|1". We don't implement
+ * this semantic as it would require that we track each line that changes
+ * during a command instead of just keeping count.
+ */
+int
+msg_rpt(sp, is_message)
+ SCR *sp;
+ int is_message;
+{
+ static const char *const action[] = {
+ "added", "changed", "copied", "deleted", "joined", "moved",
+ "put", "left shifted", "right shifted", "yanked", NULL,
+ };
+ recno_t total;
+ u_long rval;
+ int first, cnt;
+ size_t blen, len;
+ const char *const *ap;
+ char *bp, *p, number[40];
+
+ if (F_ISSET(sp, S_EXSILENT))
+ return (0);
+
+ if ((rval = O_VAL(sp, O_REPORT)) == 0)
+ goto norpt;
+
+ GET_SPACE_RET(sp, bp, blen, 512);
+ p = bp;
+
+ total = 0;
+ for (ap = action, cnt = 0, first = 1; *ap != NULL; ++ap, ++cnt)
+ if (sp->rptlines[cnt] != 0) {
+ total += sp->rptlines[cnt];
+ len = snprintf(number, sizeof(number),
+ "%s%lu line%s %s", first ? "" : "; ",
+ sp->rptlines[cnt],
+ sp->rptlines[cnt] > 1 ? "s" : "", *ap);
+ memmove(p, number, len);
+ p += len;
+ first = 0;
+ }
+
+ /*
+ * If nothing to report, return. Note that the number of lines
+ * must be > than the user's value, not >=. This is historic
+ * practice and means that users cannot report on single line
+ * changes.
+ */
+ if (total > rval) {
+ *p = '\0';
+
+ if (is_message)
+ msgq(sp, M_INFO, "%s", bp);
+ else
+ ex_printf(EXCOOKIE, "%s\n", bp);
+ }
+
+ FREE_SPACE(sp, bp, blen);
+
+ /* Clear after each report. */
+norpt: memset(sp->rptlines, 0, sizeof(sp->rptlines));
+ return (0);
+}
+
+/*
+ * binc --
+ * Increase the size of a buffer.
+ */
+int
+binc(sp, argp, bsizep, min)
+ SCR *sp; /* MAY BE NULL */
+ void *argp;
+ size_t *bsizep, min;
+{
+ void *bpp;
+ size_t csize;
+
+ /* If already larger than the minimum, just return. */
+ csize = *bsizep;
+ if (min && csize >= min)
+ return (0);
+
+ csize += MAX(min, 256);
+ bpp = *(char **)argp;
+
+ /* For non-ANSI C realloc implementations. */
+ if (bpp == NULL)
+ bpp = malloc(csize * sizeof(CHAR_T));
+ else
+ bpp = realloc(bpp, csize * sizeof(CHAR_T));
+ if (bpp == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ *bsizep = 0;
+ return (1);
+ }
+ *(char **)argp = bpp;
+ *bsizep = csize;
+ return (0);
+}
+
+/*
+ * nonblank --
+ * Set the column number of the first non-blank character
+ * including or after the starting column. On error, set
+ * the column to 0, it's safest.
+ */
+int
+nonblank(sp, ep, lno, cnop)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ size_t *cnop;
+{
+ char *p;
+ size_t cnt, len, off;
+
+ /* Default. */
+ off = *cnop;
+ *cnop = 0;
+
+ /* Get the line. */
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ return (0);
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+
+ /* Set the offset. */
+ if (len == 0 || off >= len)
+ return (0);
+
+ for (cnt = off, p = &p[off],
+ len -= off; len && isblank(*p); ++cnt, ++p, --len);
+
+ /* Set the return. */
+ *cnop = len ? cnt : cnt - 1;
+ return (0);
+}
+
+/*
+ * tail --
+ * Return tail of a path.
+ */
+char *
+tail(path)
+ char *path;
+{
+ char *p;
+
+ if ((p = strrchr(path, '/')) == NULL)
+ return (path);
+ return (p + 1);
+}
+
+/*
+ * set_window_size --
+ * Set the window size, the row may be provided as an argument.
+ */
+int
+set_window_size(sp, set_row, ign_env)
+ SCR *sp;
+ u_int set_row;
+ int ign_env;
+{
+ struct winsize win;
+ size_t col, row;
+ int user_set;
+ ARGS *argv[2], a, b;
+ char *s, buf[2048];
+
+ /*
+ * Get the screen rows and columns. If the values are wrong, it's
+ * not a big deal -- as soon as the user sets them explicitly the
+ * environment will be set and the screen package will use the new
+ * values.
+ *
+ * Try TIOCGWINSZ.
+ */
+ row = col = 0;
+#ifdef TIOCGWINSZ
+ if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) {
+ row = win.ws_row;
+ col = win.ws_col;
+ }
+#endif
+
+ /* If TIOCGWINSZ failed, or had entries of 0, try termcap. */
+ if (row == 0 || col == 0) {
+ s = NULL;
+ if (F_ISSET(&sp->opts[O_TERM], OPT_SET))
+ s = O_STR(sp, O_TERM);
+ else
+ s = getenv("TERM");
+ if (s != NULL && tgetent(buf, s) == 1) {
+ if (row == 0)
+ row = tgetnum("li");
+ if (col == 0)
+ col = tgetnum("co");
+ }
+ }
+ /* If nothing else, well, it's probably a VT100. */
+ if (row == 0)
+ row = 24;
+ if (col == 0)
+ col = 80;
+
+ /*
+ * POSIX 1003.2 requires the environment to override, however,
+ * if we're here because of a signal, we don't want to use the
+ * old values.
+ */
+ if (!ign_env) {
+ if ((s = getenv("LINES")) != NULL)
+ row = strtol(s, NULL, 10);
+ if ((s = getenv("COLUMNS")) != NULL)
+ col = strtol(s, NULL, 10);
+ }
+
+ /* But, if we got an argument for the rows, use it. */
+ if (set_row)
+ row = set_row;
+
+ a.bp = buf;
+ b.bp = NULL;
+ b.len = 0;
+ argv[0] = &a;
+ argv[1] = &b;;
+
+ /*
+ * Tell the options code that the screen size has changed.
+ * Since the user didn't do the set, clear the set bits.
+ */
+ user_set = F_ISSET(&sp->opts[O_LINES], OPT_SET);
+ a.len = snprintf(buf, sizeof(buf), "lines=%u", row);
+ if (opts_set(sp, argv))
+ return (1);
+ if (user_set)
+ F_CLR(&sp->opts[O_LINES], OPT_SET);
+ user_set = F_ISSET(&sp->opts[O_COLUMNS], OPT_SET);
+ a.len = snprintf(buf, sizeof(buf), "columns=%u", col);
+ if (opts_set(sp, argv))
+ return (1);
+ if (user_set)
+ F_CLR(&sp->opts[O_COLUMNS], OPT_SET);
+ return (0);
+}
+
+/*
+ * set_alt_name --
+ * Set the alternate file name.
+ *
+ * Swap the alternate file name. It's a routine because I wanted some place
+ * to hang this comment. The alternate file name (normally referenced using
+ * the special character '#' during file expansion) is set by many
+ * operations. In the historic vi, the commands "ex", and "edit" obviously
+ * set the alternate file name because they switched the underlying file.
+ * Less obviously, the "read", "file", "write" and "wq" commands set it as
+ * well. In this implementation, some new commands have been added to the
+ * list. Where it gets interesting is that the alternate file name is set
+ * multiple times by some commands. If an edit attempt fails (for whatever
+ * reason, like the current file is modified but as yet unwritten), it is
+ * set to the file name that the user was unable to edit. If the edit
+ * succeeds, it is set to the last file name that was edited. Good fun.
+ *
+ * If the user edits a temporary file, there are time when there isn't an
+ * alternative file name. A name argument of NULL turns it off.
+ */
+void
+set_alt_name(sp, name)
+ SCR *sp;
+ char *name;
+{
+ if (sp->alt_name != NULL)
+ FREE(sp->alt_name, strlen(sp->alt_name) + 1);
+ if (name == NULL)
+ sp->alt_name = NULL;
+ else if ((sp->alt_name = strdup(name)) == NULL)
+ msgq(sp, M_SYSERR, NULL);
+}
+
+/*
+ * baud_from_bval --
+ * Return the baud rate using the standard defines.
+ */
+u_long
+baud_from_bval(sp)
+ SCR *sp;
+{
+ speed_t v;
+
+ switch (v = cfgetospeed(&sp->gp->original_termios)) {
+ case B50:
+ return (50);
+ case B75:
+ return (75);
+ case B110:
+ return (110);
+ case B134:
+ return (134);
+ case B150:
+ return (150);
+ case B200:
+ return (200);
+ case B300:
+ return (300);
+ case B600:
+ return (600);
+ case B1200:
+ return (1200);
+ case B1800:
+ return (1800);
+ case B2400:
+ return (2400);
+ case B4800:
+ return (4800);
+ case B0: /* Hangup -- ignore. */
+ case B9600:
+ return (9600);
+ case B19200:
+ return (19200);
+ case B38400:
+ return (38400);
+ default:
+ /*
+ * EXTA and EXTB aren't required by POSIX 1003.1, and
+ * are almost certainly the same as some of the above
+ * values, so they can't be part of the case statement.
+ */
+#ifdef EXTA
+ if (v == EXTA)
+ return (19200);
+#endif
+#ifdef EXTB
+ if (v == EXTB)
+ return (38400);
+#endif
+#ifdef B57600
+ if (v == B57600)
+ return (57600);
+#endif
+#ifdef B115200
+ if (v == B115200)
+ return (115200);
+#endif
+ msgq(sp, M_ERR, "Unknown terminal baud rate %u.\n", v);
+ return (9600);
+ }
+}
+
+/*
+ * v_strdup --
+ * Strdup for wide character strings with an associated length.
+ */
+CHAR_T *
+v_strdup(sp, str, len)
+ SCR *sp;
+ CHAR_T *str;
+ size_t len;
+{
+ CHAR_T *copy;
+
+ MALLOC(sp, copy, CHAR_T *, len);
+ if (copy == NULL)
+ return (NULL);
+ memmove(copy, str, len * sizeof(CHAR_T));
+ return (copy);
+}
diff --git a/usr.bin/vi/vi.1 b/usr.bin/vi/vi.1
new file mode 100644
index 000000000000..7c39b10c5ae2
--- /dev/null
+++ b/usr.bin/vi/vi.1
@@ -0,0 +1,451 @@
+.\" Copyright (c) 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)vi.1 8.3 (Berkeley) 3/19/94
+.\"
+.Dd "March 19, 1994"
+.Dt EX/VI 1
+.Os
+.Sh NAME
+.Nm ex, vi, view
+.Nd text editors
+.Sh SYNOPSIS
+.Nm \&ex
+.Op Fl eFlRsv
+.Op Fl c Ar cmd
+.Op Fl r Ar file
+.Op Fl t Ar tag
+.Op Fl w Ar size
+.Op Fl x Ar \&aw
+.Op Ar "file ..."
+.Nm \&vi
+.Op Fl eFlRv
+.Op Fl c Ar cmd
+.Op Fl r Ar file
+.Op Fl t Ar tag
+.Op Fl w Ar size
+.Op Fl x Ar \&aw
+.Op Ar "file ..."
+.Nm view
+.Op Fl eFlRv
+.Op Fl c Ar cmd
+.Op Fl r Ar file
+.Op Fl t Ar tag
+.Op Fl w Ar size
+.Op Fl x Ar \&aw
+.Op Ar "file ..."
+.Sh DESCRIPTION
+.Nm \&Vi
+is a screen oriented text editor.
+.Nm \&Ex
+is a line-oriented text editor.
+.Nm \&Ex
+and
+.Nm \&vi
+are different interfaces to the same program,
+and it is possible to switch back and forth during an edit session.
+.Nm View
+is the equivalent of using the
+.Fl R
+(read-only) option of
+.Nm \&vi .
+.Pp
+This manual page is the one provided with the
+.Nm ex/vi
+versions of the
+.Nm ex/vi
+text editors.
+.Nm Ex/vi
+are intended as bug-for-bug compatible replacements for the original
+Fourth Berkeley Software Distribution (4BSD)
+.Nm \&ex
+and
+.Nm \&vi
+programs.
+For the rest of this manual page,
+.Nm ex/vi
+is used only when it's necessary to distinguish it from the historic
+implementations of
+.Nm ex/vi .
+.Pp
+This manual page is intended for users already familiar with
+.Nm ex/vi .
+Anyone else should almost certainly read a good tutorial on the
+editor before this manual page.
+If you're in an unfamiliar environment, and you absolutely have to
+get work done immediately, read the section near the end of this
+manual page, entitled FAST STARTUP.
+It's probably enough to get you going.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl c
+Execute
+.Ar cmd
+immediately after starting the edit session.
+Particularly useful for initial positioning in the file, however
+.Ar cmd
+is not limited to positioning commands.
+This is the POSIX 1003.2 interface for the historic
+.Dq "+cmd"
+syntax.
+.Nm Ex/vi
+supports both the old and new syntax.
+.It Fl e
+Start editing in ex mode, as if the command name were
+.Nm \&ex .
+.It Fl F
+Don't copy the entire file when first starting to edit.
+(The default is to make a copy in case someone else modifies
+the file during your edit session.)
+.It Fl l
+List the files that may be recovered using the
+.Fl r
+option of
+.Nm \&vi .
+This is the new interface for the historic syntax of the
+.Fl r
+option without a file argument.
+.Nm Ex/vi
+supports both the old and new syntax.
+.It Fl R
+Start editing in read-only mode, as if the command name was
+.Nm view ,
+or the readonly option was set.
+.It Fl r
+Recover the specified file.
+.It Fl s
+Enter batch mode; applicable only to
+.Nm \&ex
+edit sessions.
+Batch mode is useful when running
+.Nm \&ex
+scripts.
+Prompts, informative messages and other user oriented message
+are turned off,
+and no startup files or environmental variables are read.
+This is the POSIX 1003.2 interface for the historic
+.Dq \&\-
+argument.
+.Nm \&Ex/vi
+supports both the old and new syntax.
+.It Fl t
+Start editing at the specified tag.
+(See
+.Xr ctags 1 ).
+.It Fl w
+Set the initial window size to the specified number of lines.
+.It Fl v
+Start editing in vi mode, as if the command name was
+.Nm \&vi
+or
+.Nm view .
+.It Fl x
+Reserved for X11 interfaces.
+.Em "No X11 support is currently implemented."
+.El
+.Pp
+.Nm Ex/vi
+exit 0 on success, and greater than 0 if an error occurs.
+.Sh ENVIRONMENTAL VARIABLES
+.Bl -tag -width XXXX -compact
+.It Ev COLUMNS
+The number of columns on the screen.
+This value overrides any system or terminal specific values.
+If the COLUMNS environmental variable is not set when
+.Nm ex/vi
+runs, or the
+.Sy columns
+option is explicitly reset by the user,
+.Nm ex/vi
+enters the value into the environment.
+.It Ev EXINIT
+A list of
+.Nm \&ex
+startup commands.
+.It Ev HOME
+The user's home directory, used as the initial directory path
+for the startup
+.Pa $HOME/.exrc
+file.
+This value is also used as the default directory for the
+.Nm \&vi
+.Sy \&cd
+command.
+.It Ev LINES
+The number of rows on the screen.
+This value overrides any system or terminal specific values.
+If the LINES environmental variable is not set when
+.Nm ex/vi
+runs, or the
+.Sy lines
+option is explicitly reset by the user,
+.Nm ex/vi
+enters the value into the environment.
+.It Ev SHELL
+The user's shell of choice (see also the
+.Sy shell
+option).
+.It Ev TERM
+The user's terminal type.
+The default is the type
+.Dq unknown .
+If the TERM environmental variable is not set when
+.Nm ex/vi
+runs, or the
+.Sy term
+option is explicitly reset by the user,
+.Nm ex/vi
+enters the value into the environment.
+.It Ev TMPDIR
+The location used to stored temporary files (see also the
+.Sy directory
+option).
+.El
+.Sh SET OPTIONS
+#include <set.opt.roff>
+.Sh FAST STARTUP
+This section will tell you the minimum amount that you need to
+do simple editing tasks using
+.Nm \&vi .
+If you've never used any screen editor before, you're likely to have
+problems even with this simple introduction.
+In that case you should find someone that already knows
+.Nm \&vi
+and have them walk you through this section.
+.Pp
+.Nm \&Vi
+is a screen editor.
+This means that it takes up almost the entire screen, displaying part
+of the file on each screen line, except for the last line of the screen.
+The last line of the screen is used for you to give commands to
+.Nm \&vi ,
+and for
+.Nm \&vi
+to give information to you.
+.Pp
+The other fact that you need to understand is that
+.Nm \&vi
+is a modeful editor, i.e. you are either entering text or you
+are executing commands, and you have to be in the right mode
+to do one or the other.
+You will be in command mode when you first start editing a file.
+There are commands that switch you into input mode.
+There is only one key that takes you out of input mode,
+and that is the <escape> key.
+(Key names are written using less-than and greater-than signs, e.g.
+<escape> means the
+.Dq escape
+key, usually labeled
+.Dq esc
+on your terminal's keyboard.)
+If you're ever confused as to which mode you're in,
+keep entering the <escape> key until
+.Nm \&vi
+beeps at you.
+(Generally,
+.Nm \&vi
+will beep at you if you try and do something that's not allowed.
+It will also display error messages.)
+.Pp
+To start editing a file, enter the command
+.Dq Li "vi file_name<carriage-return>" .
+The command you should enter as soon as you start editing is
+.Dq Li ":set verbose showmode<carriage-return>" .
+This will make the editor give you verbose error messages and display
+the current mode at the bottom of the screen.
+.Pp
+The commands to move around the file are:
+.Bl -tag -width XXXX -compact
+.It Sy h
+Move the cursor left one character.
+.It Sy j
+Move the cursor down one line.
+.It Sy k
+Move the cursor up one line.
+.It Sy l
+Move the cursor right one character.
+.It Sy <cursor-arrows>
+The cursor arrow keys should work, too.
+.It Sy /text<carriage-return>
+Search for the string
+.Dq text
+in the file, and move the cursor to its first character.
+.El
+.Pp
+The commands to enter new text are:
+.Bl -tag -width XXXX -compact
+.It Sy a
+Append new text,
+.Em after
+the cursor.
+.It Sy i
+Insert new text,
+.Em before
+the cursor.
+.It Sy o
+Open a new line below the line the cursor is on, and start
+entering text.
+.It Sy O
+Open a new line above the line the cursor is on, and start
+entering text.
+.It Sy <escape>
+Once you've entered input mode using the one of the
+.Sy \&a ,
+.Sy \&i ,
+.Sy \&O ,
+or
+.Sy \&o
+commands, use
+.Sy <escape>
+to quit entering text and return to command mode.
+.El
+.Pp
+The commands to copy text are:
+.Bl -tag -width XXXX -compact
+.It Sy yy
+Copy the line the cursor is on.
+.It Sy p
+Append the copied line after the line the cursor is on.
+.El
+.Pp
+The commands to delete text are:
+.Bl -tag -width XXXX -compact
+.It Sy dd
+Delete the line the cursor is on.
+.It Sy x
+Delete the character the cursor is on.
+.El
+.Pp
+The commands to write the file are:
+.Bl -tag -width XXXX -compact
+.It Sy :w<carriage-return>
+Write the file back to the file with the name that you originally used
+as an argument on the
+.Nm \&vi
+command line.
+.It Sy :w file_name<carriage-return>
+Write the file back to the file with the name
+.Dq file_name .
+.El
+.Pp
+The commands to quit editing and exit the editor are:
+.Bl -tag -width XXXX -compact
+.It Sy :q<carriage-return>
+Quit editing and leave vi (if you've modified the file, but not
+saved your changes,
+.Nm \&vi
+will refuse to quit).
+.It Sy :q!<carriage-return>
+Quit, discarding any modifications that you may have made.
+.El
+.Pp
+One final caution.
+Unusual characters can take up more than one column on the screen,
+and long lines can take up more than a single screen line.
+The above commands work on
+.Dq physical
+characters and lines, i.e. they affect the entire line no matter
+how many screen lines it takes up and the entire character no matter
+how many screen columns it takes up.
+.Sh BUGS
+See the file
+.Pa vi/docs/bugs.current
+for a list of the known bugs in this version.
+.Sh FILES
+.Bl -tag -width /var/tmp/vi.recover -compact
+.It Pa /bin/sh
+The default user shell.
+.It Pa /etc/vi.exrc
+System-wide vi startup file.
+.It Pa /tmp
+Temporary file directory.
+.It Pa /var/tmp/vi.recover
+Recovery file directory.
+.It Pa $HOME/.exrc
+user's home directory startup file.
+.It Pa .exrc
+local directory startup file.
+.El
+.Sh SEE ALSO
+.Xr ctags 1 ,
+.Xr more 1 ,
+.Xr curses 3 ,
+.Xr dbopen 3
+.sp
+The
+.Dq "Vi Quick Reference"
+card.
+.sp
+.Dq "An Introduction to Display Editing with Vi" ,
+found in the
+.Dq "UNIX User's Manual Supplementary Documents" .
+.sp
+.Dq "Edit: A tutorial" ,
+found in the
+.Dq "UNIX User's Manual Supplementary Documents" .
+.sp
+.Dq "\&Ex Reference Manual (Version 3.7)" ,
+found in the
+.Dq "UNIX User's Manual Supplementary Documents" .
+.Pp
+.Nm Nroff/troff
+source for the previous three documents are distributed with
+.Nm ex/vi
+in the
+.Pa vi/docs/USD.doc
+directory of the
+.Nm ex/vi
+source code.
+.sp
+The files
+.Dq autowrite ,
+.Dq input ,
+.Dq quoting ,
+and
+.Dq structures ,
+found in the
+.Pa vi/docs/internals
+directory of the
+.Nm ex/vi
+source code.
+.Sh HISTORY
+The
+.Nm ex/vi
+replacements for the
+.Nm ex/vi
+editor first appeared in 4.4BSD.
+.Sh STANDARDS
+.Nm \&Ex/vi
+is close to IEEE Std1003.2 (``POSIX'').
+That document differs from historical
+.Nm ex/vi
+practice in several places; there are changes to be made on both sides.
diff --git a/usr.bin/vi/vi.h b/usr.bin/vi/vi.h
new file mode 100644
index 000000000000..a979925c2b05
--- /dev/null
+++ b/usr.bin/vi/vi.h
@@ -0,0 +1,165 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vi.h 8.33 (Berkeley) 1/9/94
+ */
+
+/* System includes. */
+#include <queue.h> /* Required by screen.h. */
+#include <sys/time.h> /* Required by screen.h. */
+
+#include <bitstring.h> /* Required by screen.h. */
+#include <limits.h> /* Required by screen.h. */
+#include <signal.h> /* Required by screen.h. */
+#include <stdio.h> /* Required by screen.h. */
+#include <termios.h> /* Required by gs.h. */
+
+/*
+ * Required by screen.h. This is the first include that can pull
+ * in "compat.h". Should be after every other system include.
+ */
+#include <regex.h>
+
+/*
+ * Forward structure declarations. Not pretty, but the include files
+ * are far too interrelated for a clean solution.
+ */
+typedef struct _cb CB;
+typedef struct _ch CH;
+typedef struct _chname CHNAME;
+typedef struct _excmdarg EXCMDARG;
+typedef struct _exf EXF;
+typedef struct _fref FREF;
+typedef struct _gs GS;
+typedef struct _ibuf IBUF;
+typedef struct _mark MARK;
+typedef struct _msg MSG;
+typedef struct _option OPTION;
+typedef struct _optlist OPTLIST;
+typedef struct _scr SCR;
+typedef struct _script SCRIPT;
+typedef struct _seq SEQ;
+typedef struct _tag TAG;
+typedef struct _tagf TAGF;
+typedef struct _text TEXT;
+
+/*
+ * Fundamental character types.
+ *
+ * CHAR_T An integral type that can hold any character.
+ * ARG_CHAR_T The type of a CHAR_T when passed as an argument using
+ * traditional promotion rules. It should also be able
+ * to be compared against any CHAR_T for equality without
+ * problems.
+ * MAX_CHAR_T The maximum value of any character.
+ *
+ * If no integral type can hold a character, don't even try the port.
+ */
+typedef u_char CHAR_T;
+typedef u_int ARG_CHAR_T;
+#define MAX_CHAR_T 0xff
+
+/* The maximum number of columns any character can take up on a screen. */
+#define MAX_CHARACTER_COLUMNS 4
+
+/*
+ * Local includes.
+ */
+#include <db.h> /* Required by exf.h; includes compat.h. */
+
+#include "search.h" /* Required by screen.h. */
+#include "args.h" /* Required by options.h. */
+#include "options.h" /* Required by screen.h. */
+#include "term.h" /* Required by screen.h. */
+
+#include "msg.h" /* Required by gs.h. */
+#include "cut.h" /* Required by gs.h. */
+#include "gs.h" /* Required by screen.h. */
+#include "screen.h" /* Required by exf.h. */
+#include "mark.h" /* Required by exf.h. */
+#include "exf.h"
+#include "log.h"
+#include "mem.h"
+
+#if FWOPEN_NOT_AVAILABLE /* See PORT/clib/fwopen.c. */
+#define EXCOOKIE sp
+int ex_fflush __P((SCR *));
+int ex_printf __P((SCR *, const char *, ...));
+FILE *fwopen __P((SCR *, void *));
+#else
+#define EXCOOKIE sp->stdfp
+#define ex_fflush fflush
+#define ex_printf fprintf
+#endif
+
+/* Macros to set/clear/test flags. */
+#define F_SET(p, f) (p)->flags |= (f)
+#define F_CLR(p, f) (p)->flags &= ~(f)
+#define F_ISSET(p, f) ((p)->flags & (f))
+
+#define LF_INIT(f) flags = (f)
+#define LF_SET(f) flags |= (f)
+#define LF_CLR(f) flags &= ~(f)
+#define LF_ISSET(f) (flags & (f))
+
+/*
+ * XXX
+ * MIN/MAX have traditionally been in <sys/param.h>. Don't
+ * try to get them from there, it's just not worth the effort.
+ */
+#ifndef MAX
+#define MAX(_a,_b) ((_a)<(_b)?(_b):(_a))
+#endif
+#ifndef MIN
+#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b))
+#endif
+
+/* Function prototypes that don't seem to belong anywhere else. */
+u_long baud_from_bval __P((SCR *));
+char *charname __P((SCR *, ARG_CHAR_T));
+void busy_off __P((SCR *));
+void busy_on __P((SCR *, int, char const *));
+int nonblank __P((SCR *, EXF *, recno_t, size_t *));
+void set_alt_name __P((SCR *, char *));
+int set_window_size __P((SCR *, u_int, int));
+int status __P((SCR *, EXF *, recno_t, int));
+char *tail __P((char *));
+CHAR_T *v_strdup __P((SCR *, CHAR_T *, size_t));
+
+#ifdef DEBUG
+void TRACE __P((SCR *, const char *, ...));
+#endif
+
+/* Digraphs (not currently real). */
+int digraph __P((SCR *, int, int));
+int digraph_init __P((SCR *));
+void digraph_save __P((SCR *, int));
diff --git a/usr.bin/vi/xaw/xaw_screen.c b/usr.bin/vi/xaw/xaw_screen.c
new file mode 100644
index 000000000000..ec76a0668cf9
--- /dev/null
+++ b/usr.bin/vi/xaw/xaw_screen.c
@@ -0,0 +1,85 @@
+/*-
+ * Copyright (c) 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
+static char sccsid[] = "@(#)xaw_screen.c 8.4 (Berkeley) 1/11/94";
+#endif /* not lint */
+
+#include "vi.h"
+
+/*
+ * xaw_init --
+ * Athena widget screen initialization.
+ */
+int
+xaw_screen_init(sp)
+ SCR *sp;
+{
+ msgq(sp, M_ERR, "The Athena widget screen not yet implemented.");
+ return (1);
+}
+
+/*
+ * xaw_screen_copy --
+ * Copy to a new screen.
+ */
+int
+xaw_screen_copy(orig, sp)
+ SCR *orig, *sp;
+{
+ return (0);
+}
+
+/*
+ * xaw_screen_end --
+ * End a screen.
+ */
+int
+xaw_screen_end(sp)
+ SCR *sp;
+{
+ return (0);
+}
+
+/*
+ * xaw --
+ * Main vi Athena widget screen loop.
+ */
+int
+xaw(sp, ep, spp)
+ SCR *sp, **spp;
+ EXF *ep;
+{
+ *spp = NULL;
+ return (0);
+}